diff --git a/.bazelrc b/.bazelrc index e0ab5a34335..214258e775a 100644 --- a/.bazelrc +++ b/.bazelrc @@ -1,3 +1,9 @@ -build --repo_env=CC=clang --repo_env=CXX=clang++ --cxxopt="-std=c++20" +common --enable_platform_specific_config + +build --repo_env=CC=clang --repo_env=CXX=clang++ + +build:linux --cxxopt=-std=c++20 +build:macos --cxxopt=-std=c++20 --cpu=darwin_x86_64 +build:windows --cxxopt=/std:c++20 --cxxopt=/Zc:preprocessor try-import %workspace%/local.bazelrc diff --git a/.github/workflows/check-implicit-this.yml b/.github/workflows/check-implicit-this.yml new file mode 100644 index 00000000000..8539c7b3330 --- /dev/null +++ b/.github/workflows/check-implicit-this.yml @@ -0,0 +1,22 @@ +name: "Check implicit this warnings" + +on: workflow_dispatch + +jobs: + check: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + - name: Check that implicit this warnings is enabled for all packs + shell: bash + run: | + EXIT_CODE=0 + packs="$(find . -iname 'qlpack.yml')" + for pack_file in ${packs}; do + option="$(yq '.warnOnImplicitThis' ${pack_file})" + if [ "${option}" != "true" ]; then + echo "warnOnImplicitThis property must be set to 'true' for pack ${pack_file}" + EXIT_CODE=1 + fi + done + exit "${EXIT_CODE}" diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index bb25a64ebfb..2cf6f530354 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -5,9 +5,9 @@ repos: rev: v3.2.0 hooks: - id: trailing-whitespace - exclude: /test/.*$(? { FlowCheckNode() { castNode(this.asNode()) or clearsContentCached(this.asNode(), _) or - expectsContentCached(this.asNode(), _) + expectsContentCached(this.asNode(), _) or + neverSkipInPathGraph(this.asNode()) } } diff --git a/cpp/ql/lib/semmle/code/cpp/dataflow/internal/DataFlowPrivate.qll b/cpp/ql/lib/semmle/code/cpp/dataflow/internal/DataFlowPrivate.qll index 115989e3dea..b380748fb3c 100644 --- a/cpp/ql/lib/semmle/code/cpp/dataflow/internal/DataFlowPrivate.qll +++ b/cpp/ql/lib/semmle/code/cpp/dataflow/internal/DataFlowPrivate.qll @@ -235,6 +235,12 @@ class CastNode extends Node { CastNode() { none() } // stub implementation } +/** + * Holds if `n` should never be skipped over in the `PathGraph` and in path + * explanations. + */ +predicate neverSkipInPathGraph(Node n) { none() } + class DataFlowCallable = Function; class DataFlowExpr = Expr; diff --git a/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/DataFlowImpl.qll b/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/DataFlowImpl.qll index 984c5ae2018..284fff191ae 100644 --- a/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/DataFlowImpl.qll +++ b/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/DataFlowImpl.qll @@ -2021,7 +2021,8 @@ module Impl { FlowCheckNode() { castNode(this.asNode()) or clearsContentCached(this.asNode(), _) or - expectsContentCached(this.asNode(), _) + expectsContentCached(this.asNode(), _) or + neverSkipInPathGraph(this.asNode()) } } diff --git a/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/DataFlowPrivate.qll b/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/DataFlowPrivate.qll index 33ff6f74775..59dfe5ed9d4 100644 --- a/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/DataFlowPrivate.qll +++ b/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/DataFlowPrivate.qll @@ -193,86 +193,89 @@ private class SingleUseOperandNode0 extends OperandNode0, TSingleUseOperandNode0 SingleUseOperandNode0() { this = TSingleUseOperandNode0(op) } } -/** - * INTERNAL: Do not use. - * - * A node that represents the indirect value of an operand in the IR - * after `index` number of loads. - * - * Note: Unlike `RawIndirectOperand`, a value of type `IndirectOperand` may - * be an `OperandNode`. - */ -class IndirectOperand extends Node { - Operand operand; - int indirectionIndex; - - IndirectOperand() { - this.(RawIndirectOperand).getOperand() = operand and - this.(RawIndirectOperand).getIndirectionIndex() = indirectionIndex - or - nodeHasOperand(this, Ssa::getIRRepresentationOfIndirectOperand(operand, indirectionIndex), - indirectionIndex - 1) +private module IndirectOperands { + /** + * INTERNAL: Do not use. + * + * A node that represents the indirect value of an operand in the IR + * after `index` number of loads. + * + * Note: Unlike `RawIndirectOperand`, a value of type `IndirectOperand` may + * be an `OperandNode`. + */ + abstract class IndirectOperand extends Node { + /** Gets the underlying operand and the underlying indirection index. */ + abstract predicate hasOperandAndIndirectionIndex(Operand operand, int indirectionIndex); } - /** Gets the underlying operand. */ - Operand getOperand() { result = operand } + private class IndirectOperandFromRaw extends IndirectOperand instanceof RawIndirectOperand { + override predicate hasOperandAndIndirectionIndex(Operand operand, int indirectionIndex) { + operand = RawIndirectOperand.super.getOperand() and + indirectionIndex = RawIndirectOperand.super.getIndirectionIndex() + } + } - /** Gets the underlying indirection index. */ - int getIndirectionIndex() { result = indirectionIndex } + private class IndirectOperandFromIRRepr extends IndirectOperand { + Operand operand; + int indirectionIndex; - /** - * Holds if this `IndirectOperand` is represented directly in the IR instead of - * a `RawIndirectionOperand` with operand `op` and indirection index `index`. - */ - predicate isIRRepresentationOf(Operand op, int index) { - this instanceof OperandNode and - ( - op = operand and - index = indirectionIndex - ) + IndirectOperandFromIRRepr() { + exists(Operand repr | + repr = Ssa::getIRRepresentationOfIndirectOperand(operand, indirectionIndex) and + nodeHasOperand(this, repr, indirectionIndex - 1) + ) + } + + override predicate hasOperandAndIndirectionIndex(Operand op, int index) { + op = operand and index = indirectionIndex + } } } -/** - * INTERNAL: Do not use. - * - * A node that represents the indirect value of an instruction in the IR - * after `index` number of loads. - * - * Note: Unlike `RawIndirectInstruction`, a value of type `IndirectInstruction` may - * be an `InstructionNode`. - */ -class IndirectInstruction extends Node { - Instruction instr; - int indirectionIndex; +import IndirectOperands - IndirectInstruction() { - this.(RawIndirectInstruction).getInstruction() = instr and - this.(RawIndirectInstruction).getIndirectionIndex() = indirectionIndex - or - nodeHasInstruction(this, Ssa::getIRRepresentationOfIndirectInstruction(instr, indirectionIndex), - indirectionIndex - 1) +private module IndirectInstructions { + /** + * INTERNAL: Do not use. + * + * A node that represents the indirect value of an instruction in the IR + * after `index` number of loads. + * + * Note: Unlike `RawIndirectInstruction`, a value of type `IndirectInstruction` may + * be an `InstructionNode`. + */ + abstract class IndirectInstruction extends Node { + /** Gets the underlying operand and the underlying indirection index. */ + abstract predicate hasInstructionAndIndirectionIndex(Instruction instr, int index); } - /** Gets the underlying instruction. */ - Instruction getInstruction() { result = instr } + private class IndirectInstructionFromRaw extends IndirectInstruction instanceof RawIndirectInstruction + { + override predicate hasInstructionAndIndirectionIndex(Instruction instr, int index) { + instr = RawIndirectInstruction.super.getInstruction() and + index = RawIndirectInstruction.super.getIndirectionIndex() + } + } - /** Gets the underlying indirection index. */ - int getIndirectionIndex() { result = indirectionIndex } + private class IndirectInstructionFromIRRepr extends IndirectInstruction { + Instruction instr; + int indirectionIndex; - /** - * Holds if this `IndirectInstruction` is represented directly in the IR instead of - * a `RawIndirectionInstruction` with instruction `i` and indirection index `index`. - */ - predicate isIRRepresentationOf(Instruction i, int index) { - this instanceof InstructionNode and - ( - i = instr and - index = indirectionIndex - ) + IndirectInstructionFromIRRepr() { + exists(Instruction repr | + repr = Ssa::getIRRepresentationOfIndirectInstruction(instr, indirectionIndex) and + nodeHasInstruction(this, repr, indirectionIndex - 1) + ) + } + + override predicate hasInstructionAndIndirectionIndex(Instruction i, int index) { + i = instr and index = indirectionIndex + } } } +import IndirectInstructions + /** Gets the callable in which this node occurs. */ DataFlowCallable nodeGetEnclosingCallable(Node n) { result = n.getEnclosingCallable() } @@ -318,9 +321,11 @@ private class PrimaryArgumentNode extends ArgumentNode, OperandNode { private class SideEffectArgumentNode extends ArgumentNode, SideEffectOperandNode { override predicate argumentOf(DataFlowCall dfCall, ArgumentPosition pos) { - this.getCallInstruction() = dfCall and - pos.(IndirectionPosition).getArgumentIndex() = this.getArgumentIndex() and - pos.(IndirectionPosition).getIndirectionIndex() = super.getIndirectionIndex() + exists(int indirectionIndex | + pos = TIndirectionPosition(argumentIndex, pragma[only_bind_into](indirectionIndex)) and + this.getCallInstruction() = dfCall and + super.hasAddressOperandAndIndirectionIndex(_, pragma[only_bind_into](indirectionIndex)) + ) } } @@ -648,13 +653,16 @@ predicate jumpStep(Node n1, Node n2) { * Holds if data can flow from `node1` to `node2` via an assignment to `f`. * Thus, `node2` references an object with a field `f` that contains the * value of `node1`. + * + * The boolean `certain` is true if the destination address does not involve + * any pointer arithmetic, and false otherwise. */ -predicate storeStep(Node node1, Content c, PostFieldUpdateNode node2) { +predicate storeStepImpl(Node node1, Content c, PostFieldUpdateNode node2, boolean certain) { exists(int indirectionIndex1, int numberOfLoads, StoreInstruction store | nodeHasInstruction(node1, store, pragma[only_bind_into](indirectionIndex1)) and node2.getIndirectionIndex() = 1 and numberOfLoadsFromOperand(node2.getFieldAddress(), store.getDestinationAddressOperand(), - numberOfLoads) + numberOfLoads, certain) | exists(FieldContent fc | fc = c | fc.getField() = node2.getUpdatedField() and @@ -668,21 +676,34 @@ predicate storeStep(Node node1, Content c, PostFieldUpdateNode node2) { ) } +/** + * Holds if data can flow from `node1` to `node2` via an assignment to `f`. + * Thus, `node2` references an object with a field `f` that contains the + * value of `node1`. + */ +predicate storeStep(Node node1, Content c, PostFieldUpdateNode node2) { + storeStepImpl(node1, c, node2, _) +} + /** * Holds if `operandFrom` flows to `operandTo` using a sequence of conversion-like * operations and exactly `n` `LoadInstruction` operations. */ -private predicate numberOfLoadsFromOperandRec(Operand operandFrom, Operand operandTo, int ind) { +private predicate numberOfLoadsFromOperandRec( + Operand operandFrom, Operand operandTo, int ind, boolean certain +) { exists(Instruction load | Ssa::isDereference(load, operandFrom) | - operandTo = operandFrom and ind = 0 + operandTo = operandFrom and ind = 0 and certain = true or - numberOfLoadsFromOperand(load.getAUse(), operandTo, ind - 1) + numberOfLoadsFromOperand(load.getAUse(), operandTo, ind - 1, certain) ) or - exists(Operand op, Instruction instr | + exists(Operand op, Instruction instr, boolean isPointerArith, boolean certain0 | instr = op.getDef() and - conversionFlow(operandFrom, instr, _, _) and - numberOfLoadsFromOperand(op, operandTo, ind) + conversionFlow(operandFrom, instr, isPointerArith, _) and + numberOfLoadsFromOperand(op, operandTo, ind, certain0) + | + if isPointerArith = true then certain = false else certain = certain0 ) } @@ -690,13 +711,16 @@ private predicate numberOfLoadsFromOperandRec(Operand operandFrom, Operand opera * Holds if `operandFrom` flows to `operandTo` using a sequence of conversion-like * operations and exactly `n` `LoadInstruction` operations. */ -private predicate numberOfLoadsFromOperand(Operand operandFrom, Operand operandTo, int n) { - numberOfLoadsFromOperandRec(operandFrom, operandTo, n) +private predicate numberOfLoadsFromOperand( + Operand operandFrom, Operand operandTo, int n, boolean certain +) { + numberOfLoadsFromOperandRec(operandFrom, operandTo, n, certain) or not Ssa::isDereference(_, operandFrom) and not conversionFlow(operandFrom, _, _, _) and operandFrom = operandTo and - n = 0 + n = 0 and + certain = true } // Needed to join on both an operand and an index at the same time. @@ -726,7 +750,7 @@ predicate readStep(Node node1, Content c, Node node2) { // The `1` here matches the `node2.getIndirectionIndex() = 1` conjunct // in `storeStep`. nodeHasOperand(node1, fa1.getObjectAddressOperand(), 1) and - numberOfLoadsFromOperand(fa1, operand, numberOfLoads) + numberOfLoadsFromOperand(fa1, operand, numberOfLoads, _) | exists(FieldContent fc | fc = c | fc.getField() = fa1.getField() and @@ -744,7 +768,33 @@ predicate readStep(Node node1, Content c, Node node2) { * Holds if values stored inside content `c` are cleared at node `n`. */ predicate clearsContent(Node n, Content c) { - none() // stub implementation + n = + any(PostUpdateNode pun, Content d | d.impliesClearOf(c) and storeStepImpl(_, d, pun, true) | pun) + .getPreUpdateNode() and + ( + // The crement operations and pointer addition and subtraction self-assign. We do not + // want to clear the contents if it is indirectly pointed at by any of these operations, + // as part of the contents might still be accessible afterwards. If there is no such + // indirection clearing the contents is safe. + not exists(Operand op, Cpp::Operation p | + n.(IndirectOperand).hasOperandAndIndirectionIndex(op, _) and + ( + p instanceof Cpp::AssignPointerAddExpr or + p instanceof Cpp::AssignPointerSubExpr or + p instanceof Cpp::CrementOperation + ) + | + p.getAnOperand() = op.getUse().getAst() + ) + or + forex(PostUpdateNode pun, Content d | + pragma[only_bind_into](d).impliesClearOf(pragma[only_bind_into](c)) and + storeStepImpl(_, d, pun, true) and + pun.getPreUpdateNode() = n + | + c.getIndirectionIndex() = d.getIndirectionIndex() + ) + ) } /** @@ -783,6 +833,12 @@ class CastNode extends Node { CastNode() { none() } // stub implementation } +/** + * Holds if `n` should never be skipped over in the `PathGraph` and in path + * explanations. + */ +predicate neverSkipInPathGraph(Node n) { none() } + /** * A function that may contain code or a variable that may contain itself. When * flow crosses from one _enclosing callable_ to another, the interprocedural @@ -839,7 +895,7 @@ predicate additionalLambdaFlowStep(Node nodeFrom, Node nodeTo, boolean preserves * One example would be to allow flow like `p.foo = p.bar;`, which is disallowed * by default as a heuristic. */ -predicate allowParameterReturnInSelf(ParameterNode p) { none() } +predicate allowParameterReturnInSelf(ParameterNode p) { p instanceof IndirectParameterNode } private predicate fieldHasApproxName(Field f, string s) { s = f.getName().charAt(0) and diff --git a/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/DataFlowUtil.qll b/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/DataFlowUtil.qll index 9a3fd679f23..209d0246832 100644 --- a/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/DataFlowUtil.qll +++ b/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/DataFlowUtil.qll @@ -274,7 +274,7 @@ class Node extends TIRDataFlowNode { * represents the value of `**x` going into `f`. */ Expr asIndirectArgument(int index) { - this.(SideEffectOperandNode).getIndirectionIndex() = index and + this.(SideEffectOperandNode).hasAddressOperandAndIndirectionIndex(_, index) and result = this.(SideEffectOperandNode).getArgument() } @@ -317,7 +317,7 @@ class Node extends TIRDataFlowNode { index = 0 and result = this.(ExplicitParameterNode).getParameter() or - this.(IndirectParameterNode).getIndirectionIndex() = index and + this.(IndirectParameterNode).hasInstructionAndIndirectionIndex(_, index) and result = this.(IndirectParameterNode).getParameter() } @@ -577,15 +577,20 @@ class SsaPhiNode extends Node, TSsaPhiNode { * * A node representing a value after leaving a function. */ -class SideEffectOperandNode extends Node, IndirectOperand { +class SideEffectOperandNode extends Node instanceof IndirectOperand { CallInstruction call; int argumentIndex; - SideEffectOperandNode() { operand = call.getArgumentOperand(argumentIndex) } + SideEffectOperandNode() { + IndirectOperand.super.hasOperandAndIndirectionIndex(call.getArgumentOperand(argumentIndex), _) + } CallInstruction getCallInstruction() { result = call } - Operand getAddressOperand() { result = operand } + /** Gets the underlying operand and the underlying indirection index. */ + predicate hasAddressOperandAndIndirectionIndex(Operand operand, int indirectionIndex) { + IndirectOperand.super.hasOperandAndIndirectionIndex(operand, indirectionIndex) + } int getArgumentIndex() { result = argumentIndex } @@ -665,10 +670,10 @@ class InitialGlobalValue extends Node, TInitialGlobalValue { * * A node representing an indirection of a parameter. */ -class IndirectParameterNode extends Node, IndirectInstruction { +class IndirectParameterNode extends Node instanceof IndirectInstruction { InitializeParameterInstruction init; - IndirectParameterNode() { this.getInstruction() = init } + IndirectParameterNode() { IndirectInstruction.super.hasInstructionAndIndirectionIndex(init, _) } int getArgumentIndex() { init.hasIndex(result) } @@ -677,7 +682,12 @@ class IndirectParameterNode extends Node, IndirectInstruction { override Declaration getEnclosingCallable() { result = this.getFunction() } - override Declaration getFunction() { result = this.getInstruction().getEnclosingFunction() } + override Declaration getFunction() { result = init.getEnclosingFunction() } + + /** Gets the underlying operand and the underlying indirection index. */ + predicate hasInstructionAndIndirectionIndex(Instruction instr, int index) { + IndirectInstruction.super.hasInstructionAndIndirectionIndex(instr, index) + } override Location getLocationImpl() { result = this.getParameter().getLocation() } @@ -699,7 +709,8 @@ class IndirectReturnNode extends Node { IndirectReturnNode() { this instanceof FinalParameterNode or - this.(IndirectOperand).getOperand() = any(ReturnValueInstruction ret).getReturnAddressOperand() + this.(IndirectOperand) + .hasOperandAndIndirectionIndex(any(ReturnValueInstruction ret).getReturnAddressOperand(), _) } override Declaration getEnclosingCallable() { result = this.getFunction() } @@ -722,7 +733,7 @@ class IndirectReturnNode extends Node { int getIndirectionIndex() { result = this.(FinalParameterNode).getIndirectionIndex() or - result = this.(IndirectOperand).getIndirectionIndex() + this.(IndirectOperand).hasOperandAndIndirectionIndex(_, result) } } @@ -1106,7 +1117,8 @@ predicate exprNodeShouldBeInstruction(Node node, Expr e) { /** Holds if `node` should be an `IndirectInstruction` that maps `node.asIndirectExpr()` to `e`. */ predicate indirectExprNodeShouldBeIndirectInstruction(IndirectInstruction node, Expr e) { exists(Instruction instr | - instr = node.getInstruction() and not indirectExprNodeShouldBeIndirectOperand(_, e) + node.hasInstructionAndIndirectionIndex(instr, _) and + not indirectExprNodeShouldBeIndirectOperand(_, e) | e = instr.(VariableAddressInstruction).getAst().(Expr).getFullyConverted() or @@ -1307,8 +1319,8 @@ pragma[noinline] private predicate indirectParameterNodeHasArgumentIndexAndIndex( IndirectParameterNode node, int argumentIndex, int indirectionIndex ) { - node.getArgumentIndex() = argumentIndex and - node.getIndirectionIndex() = indirectionIndex + node.hasInstructionAndIndirectionIndex(_, indirectionIndex) and + node.getArgumentIndex() = argumentIndex } /** A synthetic parameter to model the pointed-to object of a pointer parameter. */ @@ -1479,18 +1491,14 @@ VariableNode variableNode(Variable v) { */ Node uninitializedNode(LocalVariable v) { none() } -pragma[noinline] predicate hasOperandAndIndex(IndirectOperand indirectOperand, Operand operand, int indirectionIndex) { - indirectOperand.getOperand() = operand and - indirectOperand.getIndirectionIndex() = indirectionIndex + indirectOperand.hasOperandAndIndirectionIndex(operand, indirectionIndex) } -pragma[noinline] predicate hasInstructionAndIndex( IndirectInstruction indirectInstr, Instruction instr, int indirectionIndex ) { - indirectInstr.getInstruction() = instr and - indirectInstr.getIndirectionIndex() = indirectionIndex + indirectInstr.hasInstructionAndIndirectionIndex(instr, indirectionIndex) } cached @@ -1656,8 +1664,7 @@ module ExprFlowCached { private predicate isIndirectBaseOfArrayAccess(IndirectOperand n, Expr e) { exists(LoadInstruction load, PointerArithmeticInstruction pai | pai = load.getSourceAddress() and - pai.getLeftOperand() = n.getOperand() and - n.getIndirectionIndex() = 1 and + n.hasOperandAndIndirectionIndex(pai.getLeftOperand(), 1) and e = load.getConvertedResultExpression() ) } @@ -1825,6 +1832,20 @@ class Content extends TContent { predicate hasLocationInfo(string path, int sl, int sc, int el, int ec) { path = "" and sl = 0 and sc = 0 and el = 0 and ec = 0 } + + /** Gets the indirection index of this `Content`. */ + abstract int getIndirectionIndex(); + + /** + * INTERNAL: Do not use. + * + * Holds if a write to this `Content` implies that `c` is + * also cleared. + * + * For example, a write to a field `f` implies that any content of + * the form `*f` is also cleared. + */ + abstract predicate impliesClearOf(Content c); } /** A reference through a non-union instance field. */ @@ -1842,10 +1863,21 @@ class FieldContent extends Content, TFieldContent { Field getField() { result = f } + /** Gets the indirection index of this `FieldContent`. */ pragma[inline] - int getIndirectionIndex() { + override int getIndirectionIndex() { pragma[only_bind_into](result) = pragma[only_bind_out](indirectionIndex) } + + override predicate impliesClearOf(Content c) { + exists(FieldContent fc | + fc = c and + fc.getField() = f and + // If `this` is `f` then `c` is cleared if it's of the + // form `*f`, `**f`, etc. + fc.getIndirectionIndex() >= indirectionIndex + ) + } } /** A reference through an instance field of a union. */ @@ -1870,9 +1902,21 @@ class UnionContent extends Content, TUnionContent { /** Gets the indirection index of this `UnionContent`. */ pragma[inline] - int getIndirectionIndex() { + override int getIndirectionIndex() { pragma[only_bind_into](result) = pragma[only_bind_out](indirectionIndex) } + + override predicate impliesClearOf(Content c) { + exists(UnionContent uc | + uc = c and + uc.getUnion() = u and + // If `this` is `u` then `c` is cleared if it's of the + // form `*u`, `**u`, etc. (and we ignore `bytes` because + // we know the entire union is overwritten because it's a + // union). + uc.getIndirectionIndex() >= indirectionIndex + ) + } } /** diff --git a/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/PrintIRFieldFlowSteps.qll b/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/PrintIRFieldFlowSteps.qll index f0286c00cbc..c0976f8c3e9 100644 --- a/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/PrintIRFieldFlowSteps.qll +++ b/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/PrintIRFieldFlowSteps.qll @@ -13,7 +13,7 @@ class FieldFlowPropertyProvider extends IRPropertyProvider { override string getOperandProperty(Operand operand, string key) { exists(PostFieldUpdateNode pfun, Content content | key = "store " + content.toString() and - operand = pfun.getPreUpdateNode().(IndirectOperand).getOperand() and + pfun.getPreUpdateNode().(IndirectOperand).hasOperandAndIndirectionIndex(operand, _) and result = strictconcat(string element, Node node | storeStep(node, content, pfun) and @@ -25,7 +25,7 @@ class FieldFlowPropertyProvider extends IRPropertyProvider { or exists(Node node2, Content content | key = "read " + content.toString() and - operand = node2.(IndirectOperand).getOperand() and + node2.(IndirectOperand).hasOperandAndIndirectionIndex(operand, _) and result = strictconcat(string element, Node node1 | readStep(node1, content, node2) and diff --git a/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/PrintIRUtilities.qll b/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/PrintIRUtilities.qll index 5c6cdebf800..5cca78588f0 100644 --- a/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/PrintIRUtilities.qll +++ b/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/PrintIRUtilities.qll @@ -18,9 +18,12 @@ private string stars(int k) { } string starsForNode(Node node) { - result = stars(node.(IndirectInstruction).getIndirectionIndex()) - or - result = stars(node.(IndirectOperand).getIndirectionIndex()) + exists(int indirectionIndex | + node.(IndirectInstruction).hasInstructionAndIndirectionIndex(_, indirectionIndex) or + node.(IndirectOperand).hasOperandAndIndirectionIndex(_, indirectionIndex) + | + result = stars(indirectionIndex) + ) or not node instanceof IndirectInstruction and not node instanceof IndirectOperand and diff --git a/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/SsaInternalsCommon.qll b/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/SsaInternalsCommon.qll index d8571b8b74a..56702bd79a9 100644 --- a/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/SsaInternalsCommon.qll +++ b/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/SsaInternalsCommon.qll @@ -117,6 +117,16 @@ private int countIndirections(Type t) { else ( result = any(Indirection ind | ind.getType() = t).getNumberOfIndirections() or + // If there is an indirection for the type, but we cannot count the number of indirections + // it means we couldn't reach a non-indirection type by stripping off indirections. This + // can occur if an iterator specifies itself as the value type. In this case we default to + // 1 indirection fore the type. + exists(Indirection ind | + ind.getType() = t and + not exists(ind.getNumberOfIndirections()) and + result = 1 + ) + or not exists(Indirection ind | ind.getType() = t) and result = 0 ) @@ -263,7 +273,7 @@ private module IteratorIndirections { // Taint through `operator+=` and `operator-=` on iterators. call.getStaticCallTarget() instanceof Iterator::IteratorAssignArithmeticOperator and node2.(IndirectArgumentOutNode).getPreUpdateNode() = node1 and - node1.(IndirectOperand).getOperand() = call.getArgumentOperand(0) and + node1.(IndirectOperand).hasOperandAndIndirectionIndex(call.getArgumentOperand(0), _) and node1.getType().getUnspecifiedType() = this ) } @@ -796,7 +806,7 @@ private module Cached { address.getDef() = instr and isDereference(load, address) and isUseImpl(address, _, indirectionIndex - 1) and - result = instr + result = load ) } diff --git a/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/TaintTrackingUtil.qll b/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/TaintTrackingUtil.qll index c3b8765a72a..028f5bad9da 100644 --- a/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/TaintTrackingUtil.qll +++ b/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/TaintTrackingUtil.qll @@ -160,7 +160,7 @@ predicate modeledTaintStep(DataFlow::Node nodeIn, DataFlow::Node nodeOut) { FunctionInput modelIn, FunctionOutput modelOut | indirectArgument = callInput(call, modelIn) and - indirectArgument.getAddressOperand() = nodeIn.asOperand() and + indirectArgument.hasAddressOperandAndIndirectionIndex(nodeIn.asOperand(), _) and call.getStaticCallTarget() = func and ( func.(DataFlowFunction).hasDataFlow(modelIn, modelOut) diff --git a/cpp/ql/lib/semmle/code/cpp/models/implementations/Strcpy.qll b/cpp/ql/lib/semmle/code/cpp/models/implementations/Strcpy.qll index 10b160dee47..ea371de958a 100644 --- a/cpp/ql/lib/semmle/code/cpp/models/implementations/Strcpy.qll +++ b/cpp/ql/lib/semmle/code/cpp/models/implementations/Strcpy.qll @@ -108,7 +108,7 @@ class StrcpyFunction extends ArrayFunction, DataFlowFunction, TaintFunction, Sid // these may do only a partial copy of the input buffer to the output // buffer exists(this.getParamSize()) and - input.isParameter(this.getParamSrc()) and + input.isParameterDeref(this.getParamSrc()) and ( output.isParameterDeref(this.getParamDest()) or output.isReturnValueDeref() diff --git a/cpp/ql/lib/semmle/code/cpp/rangeanalysis/new/internal/semantic/SemanticSSA.qll b/cpp/ql/lib/semmle/code/cpp/rangeanalysis/new/internal/semantic/SemanticSSA.qll index 29580c2c507..9bc55e4fa80 100644 --- a/cpp/ql/lib/semmle/code/cpp/rangeanalysis/new/internal/semantic/SemanticSSA.qll +++ b/cpp/ql/lib/semmle/code/cpp/rangeanalysis/new/internal/semantic/SemanticSSA.qll @@ -70,6 +70,27 @@ predicate semBackEdge(SemSsaPhiNode phi, SemSsaVariable inp, SemSsaReadPositionP // Conservatively assume that every edge is a back edge if we don't have dominance information. ( phi.getBasicBlock().bbDominates(edge.getOrigBlock()) or + irreducibleSccEdge(edge.getOrigBlock(), phi.getBasicBlock()) or not edge.getOrigBlock().hasDominanceInformation() ) } + +/** + * Holds if the edge from b1 to b2 is part of a multiple-entry cycle in an irreducible control flow + * graph. + * + * An ireducible control flow graph is one where the usual dominance-based back edge detection does + * not work, because there is a cycle with multiple entry points, meaning there are + * mutually-reachable basic blocks where neither dominates the other. For such a graph, we first + * remove all detectable back-edges using the normal condition that the predecessor block is + * dominated by the successor block, then mark all edges in a cycle in the resulting graph as back + * edges. + */ +private predicate irreducibleSccEdge(SemBasicBlock b1, SemBasicBlock b2) { + trimmedEdge(b1, b2) and trimmedEdge+(b2, b1) +} + +private predicate trimmedEdge(SemBasicBlock pred, SemBasicBlock succ) { + pred.getASuccessor() = succ and + not succ.bbDominates(pred) +} diff --git a/cpp/ql/src/CHANGELOG.md b/cpp/ql/src/CHANGELOG.md index 4991b66538f..ca314dcd6d7 100644 --- a/cpp/ql/src/CHANGELOG.md +++ b/cpp/ql/src/CHANGELOG.md @@ -1,3 +1,9 @@ +## 0.6.3 + +### New Queries + +* Added a new query, `cpp/overrun-write`, to detect buffer overflows in C-style functions that manipulate buffers. + ## 0.6.2 No user-facing changes. diff --git a/cpp/ql/src/Security/CWE/CWE-119/OverrunWriteProductFlow.ql b/cpp/ql/src/Security/CWE/CWE-119/OverrunWriteProductFlow.ql index 0d8648aac0a..a9af2d08f51 100644 --- a/cpp/ql/src/Security/CWE/CWE-119/OverrunWriteProductFlow.ql +++ b/cpp/ql/src/Security/CWE/CWE-119/OverrunWriteProductFlow.ql @@ -5,7 +5,7 @@ * @kind path-problem * @problem.severity error * @security-severity 9.3 - * @precision medium + * @precision low * @id cpp/overrun-write * @tags reliability * security diff --git a/cpp/ql/src/change-notes/2023-05-24-overrun-write-query.md b/cpp/ql/src/change-notes/released/0.6.3.md similarity index 80% rename from cpp/ql/src/change-notes/2023-05-24-overrun-write-query.md rename to cpp/ql/src/change-notes/released/0.6.3.md index 32195223fcd..d9421d55250 100644 --- a/cpp/ql/src/change-notes/2023-05-24-overrun-write-query.md +++ b/cpp/ql/src/change-notes/released/0.6.3.md @@ -1,4 +1,5 @@ ---- -category: newQuery ---- +## 0.6.3 + +### New Queries + * Added a new query, `cpp/overrun-write`, to detect buffer overflows in C-style functions that manipulate buffers. diff --git a/cpp/ql/src/codeql-pack.release.yml b/cpp/ql/src/codeql-pack.release.yml index 5501a2a1cc5..b7dafe32c5d 100644 --- a/cpp/ql/src/codeql-pack.release.yml +++ b/cpp/ql/src/codeql-pack.release.yml @@ -1,2 +1,2 @@ --- -lastReleaseVersion: 0.6.2 +lastReleaseVersion: 0.6.3 diff --git a/cpp/ql/src/experimental/Security/CWE/CWE-193/ConstantSizeArrayOffByOne.ql b/cpp/ql/src/experimental/Security/CWE/CWE-193/ConstantSizeArrayOffByOne.ql index aa0358a99ad..42623d37328 100644 --- a/cpp/ql/src/experimental/Security/CWE/CWE-193/ConstantSizeArrayOffByOne.ql +++ b/cpp/ql/src/experimental/Security/CWE/CWE-193/ConstantSizeArrayOffByOne.ql @@ -14,7 +14,7 @@ import semmle.code.cpp.rangeanalysis.new.internal.semantic.analysis.RangeAnalysi import semmle.code.cpp.rangeanalysis.new.internal.semantic.SemanticExprSpecific import semmle.code.cpp.ir.IR import semmle.code.cpp.ir.dataflow.DataFlow -import FieldAddressToDerefFlow::PathGraph +import ArrayAddressToDerefFlow::PathGraph pragma[nomagic] Instruction getABoundIn(SemBound b, IRFunction func) { @@ -78,28 +78,45 @@ predicate isInvalidPointerDerefSink2(DataFlow::Node sink, Instruction i, string ) } -pragma[nomagic] -predicate arrayTypeHasSizes(ArrayType arr, int baseTypeSize, int arraySize) { - arr.getBaseType().getSize() = baseTypeSize and - arr.getArraySize() = arraySize +predicate arrayTypeCand(ArrayType arrayType) { + any(Variable v).getUnspecifiedType() = arrayType and + exists(arrayType.getByteSize()) } -predicate pointerArithOverflow0( - PointerArithmeticInstruction pai, Field f, int size, int bound, int delta -) { - not f.getNamespace() instanceof StdNamespace and - arrayTypeHasSizes(f.getUnspecifiedType(), pai.getElementSize(), size) and - semBounded(getSemanticExpr(pai.getRight()), any(SemZeroBound b), bound, true, _) and - delta = bound - size and - delta >= 0 and - size != 0 and - size != 1 +bindingset[baseTypeSize] +pragma[inline_late] +predicate arrayTypeHasSizes(ArrayType arr, int baseTypeSize, int size) { + arrayTypeCand(arr) and + arr.getByteSize() / baseTypeSize = size +} + +bindingset[pai] +pragma[inline_late] +predicate constantUpperBounded(PointerArithmeticInstruction pai, int delta) { + semBounded(getSemanticExpr(pai.getRight()), any(SemZeroBound b), delta, true, _) +} + +bindingset[pai, size] +predicate pointerArithOverflow0Impl(PointerArithmeticInstruction pai, int size, int delta) { + exists(int bound | + constantUpperBounded(pai, bound) and + delta = bound - size and + delta >= 0 and + size != 0 and + size != 1 + ) +} + +pragma[nomagic] +predicate pointerArithOverflow0(PointerArithmeticInstruction pai, int delta) { + exists(int size | + arrayTypeHasSizes(_, pai.getElementSize(), size) and + pointerArithOverflow0Impl(pai, size, delta) + ) } module PointerArithmeticToDerefConfig implements DataFlow::ConfigSig { - predicate isSource(DataFlow::Node source) { - pointerArithOverflow0(source.asInstruction(), _, _, _, _) - } + predicate isSource(DataFlow::Node source) { pointerArithOverflow0(source.asInstruction(), _) } predicate isBarrierIn(DataFlow::Node node) { isSource(node) } @@ -110,25 +127,38 @@ module PointerArithmeticToDerefConfig implements DataFlow::ConfigSig { module PointerArithmeticToDerefFlow = DataFlow::Global; -predicate pointerArithOverflow( - PointerArithmeticInstruction pai, Field f, int size, int bound, int delta -) { - pointerArithOverflow0(pai, f, size, bound, delta) and +predicate pointerArithOverflow(PointerArithmeticInstruction pai, int delta) { + pointerArithOverflow0(pai, delta) and PointerArithmeticToDerefFlow::flow(DataFlow::instructionNode(pai), _) } -module FieldAddressToDerefConfig implements DataFlow::StateConfigSig { +bindingset[v] +predicate finalPointerArithOverflow(Variable v, PointerArithmeticInstruction pai, int delta) { + exists(int size | + arrayTypeHasSizes(pragma[only_bind_out](v.getUnspecifiedType()), pai.getElementSize(), size) and + pointerArithOverflow0Impl(pai, size, delta) + ) +} + +predicate isSourceImpl(DataFlow::Node source, Variable v) { + ( + source.asInstruction().(FieldAddressInstruction).getField() = v + or + source.asInstruction().(VariableAddressInstruction).getAstVariable() = v + ) and + arrayTypeCand(v.getUnspecifiedType()) +} + +module ArrayAddressToDerefConfig implements DataFlow::StateConfigSig { newtype FlowState = - additional TArray(Field f) { pointerArithOverflow(_, f, _, _, _) } or + additional TArray() or additional TOverflowArithmetic(PointerArithmeticInstruction pai) { - pointerArithOverflow(pai, _, _, _, _) + pointerArithOverflow(pai, _) } predicate isSource(DataFlow::Node source, FlowState state) { - exists(Field f | - source.asInstruction().(FieldAddressInstruction).getField() = f and - state = TArray(f) - ) + isSourceImpl(source, _) and + state = TArray() } predicate isSink(DataFlow::Node sink, FlowState state) { @@ -147,27 +177,27 @@ module FieldAddressToDerefConfig implements DataFlow::StateConfigSig { predicate isAdditionalFlowStep( DataFlow::Node node1, FlowState state1, DataFlow::Node node2, FlowState state2 ) { - exists(PointerArithmeticInstruction pai, Field f | - state1 = TArray(f) and + exists(PointerArithmeticInstruction pai | + state1 = TArray() and state2 = TOverflowArithmetic(pai) and pai.getLeft() = node1.asInstruction() and node2.asInstruction() = pai and - pointerArithOverflow(pai, f, _, _, _) + pointerArithOverflow(pai, _) ) } } -module FieldAddressToDerefFlow = DataFlow::GlobalWithState; +module ArrayAddressToDerefFlow = DataFlow::GlobalWithState; from - Field f, FieldAddressToDerefFlow::PathNode source, PointerArithmeticInstruction pai, - FieldAddressToDerefFlow::PathNode sink, Instruction deref, string operation, int delta + Variable v, ArrayAddressToDerefFlow::PathNode source, PointerArithmeticInstruction pai, + ArrayAddressToDerefFlow::PathNode sink, Instruction deref, string operation, int delta where - FieldAddressToDerefFlow::flowPath(source, sink) and + ArrayAddressToDerefFlow::flowPath(pragma[only_bind_into](source), pragma[only_bind_into](sink)) and isInvalidPointerDerefSink2(sink.getNode(), deref, operation) and - source.getState() = FieldAddressToDerefConfig::TArray(f) and - sink.getState() = FieldAddressToDerefConfig::TOverflowArithmetic(pai) and - pointerArithOverflow(pai, f, _, _, delta) + pragma[only_bind_out](sink.getState()) = ArrayAddressToDerefConfig::TOverflowArithmetic(pai) and + isSourceImpl(source.getNode(), v) and + finalPointerArithOverflow(v, pai, delta) select pai, source, sink, "This pointer arithmetic may have an off-by-" + (delta + 1) + - " error allowing it to overrun $@ at this $@.", f, f.getName(), deref, operation + " error allowing it to overrun $@ at this $@.", v, v.getName(), deref, operation diff --git a/cpp/ql/src/experimental/Security/CWE/CWE-193/InvalidPointerDeref.ql b/cpp/ql/src/experimental/Security/CWE/CWE-193/InvalidPointerDeref.ql index 478ab2cc92a..c1f7e735636 100644 --- a/cpp/ql/src/experimental/Security/CWE/CWE-193/InvalidPointerDeref.ql +++ b/cpp/ql/src/experimental/Security/CWE/CWE-193/InvalidPointerDeref.ql @@ -19,6 +19,8 @@ import cpp import semmle.code.cpp.ir.dataflow.internal.ProductFlow import semmle.code.cpp.rangeanalysis.new.internal.semantic.analysis.RangeAnalysis import semmle.code.cpp.rangeanalysis.new.internal.semantic.SemanticExprSpecific +import semmle.code.cpp.ir.ValueNumbering +import semmle.code.cpp.controlflow.IRGuards import semmle.code.cpp.ir.IR import codeql.util.Unit @@ -67,6 +69,86 @@ predicate hasSize(HeuristicAllocationExpr alloc, DataFlow::Node n, int state) { ) } +/** + * A module that encapsulates a barrier guard to remove false positives from flow like: + * ```cpp + * char *p = new char[size]; + * // ... + * unsigned n = size; + * // ... + * if(n < size) { + * use(*p[n]); + * } + * ``` + * In this case, the sink pair identified by the product flow library (without any additional barriers) + * would be `(p, n)` (where `n` is the `n` in `p[n]`), because there exists a pointer-arithmetic + * instruction `pai` such that: + * 1. The left-hand of `pai` flows from the allocation, and + * 2. The right-hand of `pai` is non-strictly upper bounded by `n` (where `n` is the `n` in `p[n]`) + * but because there's a strict comparison that compares `n` against the size of the allocation this + * snippet is fine. + */ +module Barrier2 { + private class FlowState2 = AllocToInvalidPointerConfig::FlowState2; + + private module BarrierConfig2 implements DataFlow::ConfigSig { + predicate isSource(DataFlow::Node source) { + // The sources is the same as in the sources for the second + // projection in the `AllocToInvalidPointerConfig` module. + hasSize(_, source, _) + } + + additional predicate isSink( + DataFlow::Node left, DataFlow::Node right, IRGuardCondition g, FlowState2 state, + boolean testIsTrue + ) { + // The sink is any "large" side of a relational comparison. + g.comparesLt(left.asOperand(), right.asOperand(), state, true, testIsTrue) + } + + predicate isSink(DataFlow::Node sink) { isSink(_, sink, _, _, _) } + } + + private import DataFlow::Global + + private FlowState2 getAFlowStateForNode(DataFlow::Node node) { + exists(DataFlow::Node source | + flow(source, node) and + hasSize(_, source, result) + ) + } + + private predicate operandGuardChecks( + IRGuardCondition g, Operand left, Operand right, FlowState2 state, boolean edge + ) { + exists(DataFlow::Node nLeft, DataFlow::Node nRight, FlowState2 state0 | + nRight.asOperand() = right and + nLeft.asOperand() = left and + BarrierConfig2::isSink(nLeft, nRight, g, state0, edge) and + state = getAFlowStateForNode(nRight) and + state0 <= state + ) + } + + Instruction getABarrierInstruction(FlowState2 state) { + exists(IRGuardCondition g, ValueNumber value, Operand use, boolean edge | + use = value.getAUse() and + operandGuardChecks(pragma[only_bind_into](g), pragma[only_bind_into](use), _, + pragma[only_bind_into](state), pragma[only_bind_into](edge)) and + result = value.getAnInstruction() and + g.controls(result.getBlock(), edge) + ) + } + + DataFlow::Node getABarrierNode(FlowState2 state) { + result.asOperand() = getABarrierInstruction(state).getAUse() + } + + IRBlock getABarrierBlock(FlowState2 state) { + result.getAnInstruction() = getABarrierInstruction(state) + } +} + /** * A product-flow configuration for flow from an (allocation, size) pair to a * pointer-arithmetic operation that is non-strictly upper-bounded by `allocation + size`. @@ -111,15 +193,14 @@ module AllocToInvalidPointerConfig implements ProductFlow::StateConfigSig { exists(state1) and // We check that the delta computed by the range analysis matches the // state value that we set in `isSourcePair`. - exists(int delta | - isSinkImpl(_, sink1, sink2, delta) and - state2 = delta - ) + isSinkImpl(_, sink1, sink2, state2) } predicate isBarrier1(DataFlow::Node node, FlowState1 state) { none() } - predicate isBarrier2(DataFlow::Node node, FlowState2 state) { none() } + predicate isBarrier2(DataFlow::Node node, FlowState2 state) { + node = Barrier2::getABarrierNode(state) + } predicate isBarrierIn1(DataFlow::Node node) { isSourcePair(node, _, _, _) } @@ -160,13 +241,40 @@ pragma[nomagic] predicate pointerAddInstructionHasBounds( PointerAddInstruction pai, DataFlow::Node sink1, DataFlow::Node sink2, int delta ) { - exists(Instruction right | + InterestingPointerAddInstruction::isInteresting(pragma[only_bind_into](pai)) and + exists(Instruction right, Instruction instr2 | pai.getRight() = right and pai.getLeft() = sink1.asInstruction() and - bounded1(right, sink2.asInstruction(), delta) + instr2 = sink2.asInstruction() and + bounded1(right, instr2, delta) and + not right = Barrier2::getABarrierInstruction(delta) and + not instr2 = Barrier2::getABarrierInstruction(delta) ) } +module InterestingPointerAddInstruction { + private module PointerAddInstructionConfig implements DataFlow::ConfigSig { + predicate isSource(DataFlow::Node source) { + // The sources is the same as in the sources for the second + // projection in the `AllocToInvalidPointerConfig` module. + hasSize(source.asConvertedExpr(), _, _) + } + + predicate isSink(DataFlow::Node sink) { + sink.asInstruction() = any(PointerAddInstruction pai).getLeft() + } + } + + private import DataFlow::Global + + predicate isInteresting(PointerAddInstruction pai) { + exists(DataFlow::Node n | + n.asInstruction() = pai.getLeft() and + flowTo(n) + ) + } +} + /** * Holds if `pai` is non-strictly upper bounded by `sink2 + delta` and `sink1` is the * left operand of the pointer-arithmetic operation. @@ -204,11 +312,13 @@ Instruction getASuccessor(Instruction instr) { */ pragma[inline] predicate isInvalidPointerDerefSink(DataFlow::Node sink, Instruction i, string operation, int delta) { - exists(AddressOperand addr, Instruction s | + exists(AddressOperand addr, Instruction s, IRBlock b | s = sink.asInstruction() and - bounded1(addr.getDef(), s, delta) and + boundedImpl(addr.getDef(), s, delta) and delta >= 0 and - i.getAnOperand() = addr + i.getAnOperand() = addr and + b = i.getBlock() and + not b = InvalidPointerToDerefBarrier::getABarrierBlock(delta) | i instanceof StoreInstruction and operation = "write" @@ -218,6 +328,60 @@ predicate isInvalidPointerDerefSink(DataFlow::Node sink, Instruction i, string o ) } +module InvalidPointerToDerefBarrier { + private module BarrierConfig implements DataFlow::ConfigSig { + predicate isSource(DataFlow::Node source) { + // The sources is the same as in the sources for `InvalidPointerToDerefConfig`. + invalidPointerToDerefSource(_, source, _) + } + + additional predicate isSink( + DataFlow::Node left, DataFlow::Node right, IRGuardCondition g, int state, boolean testIsTrue + ) { + // The sink is any "large" side of a relational comparison. + g.comparesLt(left.asOperand(), right.asOperand(), state, true, testIsTrue) + } + + predicate isSink(DataFlow::Node sink) { isSink(_, sink, _, _, _) } + } + + private import DataFlow::Global + + private int getInvalidPointerToDerefSourceDelta(DataFlow::Node node) { + exists(DataFlow::Node source | + flow(source, node) and + invalidPointerToDerefSource(_, source, result) + ) + } + + private predicate operandGuardChecks( + IRGuardCondition g, Operand left, Operand right, int state, boolean edge + ) { + exists(DataFlow::Node nLeft, DataFlow::Node nRight, int state0 | + nRight.asOperand() = right and + nLeft.asOperand() = left and + BarrierConfig::isSink(nLeft, nRight, g, state0, edge) and + state = getInvalidPointerToDerefSourceDelta(nRight) and + state0 <= state + ) + } + + Instruction getABarrierInstruction(int state) { + exists(IRGuardCondition g, ValueNumber value, Operand use, boolean edge | + use = value.getAUse() and + operandGuardChecks(pragma[only_bind_into](g), pragma[only_bind_into](use), _, state, + pragma[only_bind_into](edge)) and + result = value.getAnInstruction() and + g.controls(result.getBlock(), edge) + ) + } + + DataFlow::Node getABarrierNode() { result.asOperand() = getABarrierInstruction(_).getAUse() } + + pragma[nomagic] + IRBlock getABarrierBlock(int state) { result.getAnInstruction() = getABarrierInstruction(state) } +} + /** * A configuration to track flow from a pointer-arithmetic operation found * by `AllocToInvalidPointerConfig` to a dereference of the pointer. @@ -230,6 +394,8 @@ module InvalidPointerToDerefConfig implements DataFlow::ConfigSig { predicate isBarrier(DataFlow::Node node) { node = any(DataFlow::SsaPhiNode phi | not phi.isPhiRead()).getAnInput(true) + or + node = InvalidPointerToDerefBarrier::getABarrierNode() } } @@ -246,12 +412,21 @@ module InvalidPointerToDerefFlow = DataFlow::Global predicate invalidPointerToDerefSource( PointerArithmeticInstruction pai, DataFlow::Node source, int delta ) { - exists(AllocToInvalidPointerFlow::PathNode1 p, DataFlow::Node sink1 | - pragma[only_bind_out](p.getNode()) = sink1 and - AllocToInvalidPointerFlow::flowPath(_, _, pragma[only_bind_into](p), _) and - isSinkImpl(pai, sink1, _, _) and + exists( + AllocToInvalidPointerFlow::PathNode1 p1, AllocToInvalidPointerFlow::PathNode2 p2, + DataFlow::Node sink1, DataFlow::Node sink2, int delta0 + | + pragma[only_bind_out](p1.getNode()) = sink1 and + pragma[only_bind_out](p2.getNode()) = sink2 and + AllocToInvalidPointerFlow::flowPath(_, _, pragma[only_bind_into](p1), pragma[only_bind_into](p2)) and + // Note that `delta` is not necessarily equal to `delta0`: + // `delta0` is the constant offset added to the size of the allocation, and + // delta is the constant difference between the pointer-arithmetic instruction + // and the instruction computing the address for which we will search for a dereference. + isSinkImpl(pai, sink1, sink2, delta0) and bounded2(source.asInstruction(), pai, delta) and - delta >= 0 + delta >= 0 and + not source.getBasicBlock() = Barrier2::getABarrierBlock(delta0) ) } @@ -265,7 +440,7 @@ newtype TMergedPathNode = // pointer, but we want to raise an alert at the dereference. TPathNodeSink(Instruction i) { exists(DataFlow::Node n | - InvalidPointerToDerefFlow::flowTo(n) and + InvalidPointerToDerefFlow::flowTo(pragma[only_bind_into](n)) and isInvalidPointerDerefSink(n, i, _, _) and i = getASuccessor(n.asInstruction()) ) diff --git a/cpp/ql/src/qlpack.yml b/cpp/ql/src/qlpack.yml index 46dffc3e763..077b34194fb 100644 --- a/cpp/ql/src/qlpack.yml +++ b/cpp/ql/src/qlpack.yml @@ -1,5 +1,5 @@ name: codeql/cpp-queries -version: 0.6.3-dev +version: 0.6.4-dev groups: - cpp - queries diff --git a/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-193/array-access/ArrayAccessProductFlow.expected b/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-193/array-access/ArrayAccessProductFlow.expected index 820c48447ff..dbd71611d81 100644 --- a/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-193/array-access/ArrayAccessProductFlow.expected +++ b/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-193/array-access/ArrayAccessProductFlow.expected @@ -4,8 +4,9 @@ edges | test.cpp:19:9:19:16 | mk_array indirection [p] | test.cpp:28:19:28:26 | call to mk_array [p] | | test.cpp:19:9:19:16 | mk_array indirection [p] | test.cpp:50:18:50:25 | call to mk_array [p] | | test.cpp:21:5:21:24 | ... = ... | test.cpp:21:9:21:9 | arr indirection [post update] [p] | -| test.cpp:21:9:21:9 | arr indirection [post update] [p] | test.cpp:19:9:19:16 | mk_array indirection [p] | +| test.cpp:21:9:21:9 | arr indirection [post update] [p] | test.cpp:22:5:22:7 | arr indirection [p] | | test.cpp:21:13:21:18 | call to malloc | test.cpp:21:5:21:24 | ... = ... | +| test.cpp:22:5:22:7 | arr indirection [p] | test.cpp:19:9:19:16 | mk_array indirection [p] | | test.cpp:28:19:28:26 | call to mk_array [p] | test.cpp:31:9:31:11 | arr indirection [p] | | test.cpp:28:19:28:26 | call to mk_array [p] | test.cpp:35:9:35:11 | arr indirection [p] | | test.cpp:31:9:31:11 | arr indirection [p] | test.cpp:31:13:31:13 | p indirection | @@ -20,9 +21,10 @@ edges | test.cpp:45:13:45:13 | p indirection | test.cpp:45:13:45:13 | p | | test.cpp:50:18:50:25 | call to mk_array [p] | test.cpp:39:27:39:29 | arr [p] | | test.cpp:55:5:55:24 | ... = ... | test.cpp:55:9:55:9 | arr indirection [post update] [p] | -| test.cpp:55:9:55:9 | arr indirection [post update] [p] | test.cpp:59:9:59:11 | arr indirection [p] | -| test.cpp:55:9:55:9 | arr indirection [post update] [p] | test.cpp:63:9:63:11 | arr indirection [p] | +| test.cpp:55:9:55:9 | arr indirection [post update] [p] | test.cpp:56:5:56:7 | arr indirection [p] | | test.cpp:55:13:55:18 | call to malloc | test.cpp:55:5:55:24 | ... = ... | +| test.cpp:56:5:56:7 | arr indirection [p] | test.cpp:59:9:59:11 | arr indirection [p] | +| test.cpp:56:5:56:7 | arr indirection [p] | test.cpp:63:9:63:11 | arr indirection [p] | | test.cpp:59:9:59:11 | arr indirection [p] | test.cpp:59:13:59:13 | p indirection | | test.cpp:59:13:59:13 | p indirection | test.cpp:59:13:59:13 | p | | test.cpp:63:9:63:11 | arr indirection [p] | test.cpp:63:13:63:13 | p indirection | @@ -30,8 +32,9 @@ edges | test.cpp:67:10:67:19 | mk_array_p indirection [p] | test.cpp:76:20:76:29 | call to mk_array_p indirection [p] | | test.cpp:67:10:67:19 | mk_array_p indirection [p] | test.cpp:98:18:98:27 | call to mk_array_p indirection [p] | | test.cpp:69:5:69:25 | ... = ... | test.cpp:69:10:69:10 | arr indirection [post update] [p] | -| test.cpp:69:10:69:10 | arr indirection [post update] [p] | test.cpp:67:10:67:19 | mk_array_p indirection [p] | +| test.cpp:69:10:69:10 | arr indirection [post update] [p] | test.cpp:70:5:70:7 | arr indirection [p] | | test.cpp:69:14:69:19 | call to malloc | test.cpp:69:5:69:25 | ... = ... | +| test.cpp:70:5:70:7 | arr indirection [p] | test.cpp:67:10:67:19 | mk_array_p indirection [p] | | test.cpp:76:20:76:29 | call to mk_array_p indirection [p] | test.cpp:79:9:79:11 | arr indirection [p] | | test.cpp:76:20:76:29 | call to mk_array_p indirection [p] | test.cpp:83:9:83:11 | arr indirection [p] | | test.cpp:79:9:79:11 | arr indirection [p] | test.cpp:79:14:79:14 | p indirection | @@ -53,6 +56,7 @@ nodes | test.cpp:21:5:21:24 | ... = ... | semmle.label | ... = ... | | test.cpp:21:9:21:9 | arr indirection [post update] [p] | semmle.label | arr indirection [post update] [p] | | test.cpp:21:13:21:18 | call to malloc | semmle.label | call to malloc | +| test.cpp:22:5:22:7 | arr indirection [p] | semmle.label | arr indirection [p] | | test.cpp:28:19:28:26 | call to mk_array [p] | semmle.label | call to mk_array [p] | | test.cpp:31:9:31:11 | arr indirection [p] | semmle.label | arr indirection [p] | | test.cpp:31:13:31:13 | p | semmle.label | p | @@ -71,6 +75,7 @@ nodes | test.cpp:55:5:55:24 | ... = ... | semmle.label | ... = ... | | test.cpp:55:9:55:9 | arr indirection [post update] [p] | semmle.label | arr indirection [post update] [p] | | test.cpp:55:13:55:18 | call to malloc | semmle.label | call to malloc | +| test.cpp:56:5:56:7 | arr indirection [p] | semmle.label | arr indirection [p] | | test.cpp:59:9:59:11 | arr indirection [p] | semmle.label | arr indirection [p] | | test.cpp:59:13:59:13 | p | semmle.label | p | | test.cpp:59:13:59:13 | p indirection | semmle.label | p indirection | @@ -81,6 +86,7 @@ nodes | test.cpp:69:5:69:25 | ... = ... | semmle.label | ... = ... | | test.cpp:69:10:69:10 | arr indirection [post update] [p] | semmle.label | arr indirection [post update] [p] | | test.cpp:69:14:69:19 | call to malloc | semmle.label | call to malloc | +| test.cpp:70:5:70:7 | arr indirection [p] | semmle.label | arr indirection [p] | | test.cpp:76:20:76:29 | call to mk_array_p indirection [p] | semmle.label | call to mk_array_p indirection [p] | | test.cpp:79:9:79:11 | arr indirection [p] | semmle.label | arr indirection [p] | | test.cpp:79:14:79:14 | p | semmle.label | p | diff --git a/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-193/constant-size/ConstantSizeArrayOffByOne.expected b/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-193/constant-size/ConstantSizeArrayOffByOne.expected index 7d3df8cb7cb..ff75c77e702 100644 --- a/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-193/constant-size/ConstantSizeArrayOffByOne.expected +++ b/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-193/constant-size/ConstantSizeArrayOffByOne.expected @@ -1,38 +1,102 @@ edges +| test.cpp:34:10:34:12 | buf | test.cpp:34:5:34:24 | access to array | | test.cpp:35:10:35:12 | buf | test.cpp:35:5:35:22 | access to array | | test.cpp:36:10:36:12 | buf | test.cpp:36:5:36:24 | access to array | +| test.cpp:39:14:39:16 | buf | test.cpp:39:9:39:19 | access to array | | test.cpp:43:14:43:16 | buf | test.cpp:43:9:43:19 | access to array | +| test.cpp:48:10:48:12 | buf | test.cpp:48:5:48:24 | access to array | | test.cpp:49:10:49:12 | buf | test.cpp:49:5:49:22 | access to array | | test.cpp:50:10:50:12 | buf | test.cpp:50:5:50:24 | access to array | +| test.cpp:53:14:53:16 | buf | test.cpp:53:9:53:19 | access to array | | test.cpp:57:14:57:16 | buf | test.cpp:57:9:57:19 | access to array | | test.cpp:61:14:61:16 | buf | test.cpp:61:9:61:19 | access to array | +| test.cpp:70:33:70:33 | p | test.cpp:71:5:71:17 | access to array | | test.cpp:70:33:70:33 | p | test.cpp:72:5:72:15 | access to array | +| test.cpp:76:26:76:46 | & ... | test.cpp:66:32:66:32 | p | +| test.cpp:76:32:76:34 | buf | test.cpp:76:26:76:46 | & ... | | test.cpp:77:26:77:44 | & ... | test.cpp:66:32:66:32 | p | | test.cpp:77:32:77:34 | buf | test.cpp:77:26:77:44 | & ... | | test.cpp:79:27:79:34 | buf | test.cpp:70:33:70:33 | p | | test.cpp:79:32:79:34 | buf | test.cpp:79:27:79:34 | buf | +| test.cpp:85:34:85:36 | buf | test.cpp:87:5:87:31 | access to array | +| test.cpp:85:34:85:36 | buf | test.cpp:88:5:88:27 | access to array | +| test.cpp:96:13:96:15 | arr | test.cpp:96:13:96:18 | access to array | +| test.cpp:111:17:111:19 | arr | test.cpp:111:17:111:22 | access to array | +| test.cpp:111:17:111:19 | arr | test.cpp:115:35:115:40 | access to array | +| test.cpp:111:17:111:19 | arr | test.cpp:119:17:119:22 | access to array | +| test.cpp:115:35:115:37 | arr | test.cpp:111:17:111:22 | access to array | +| test.cpp:115:35:115:37 | arr | test.cpp:115:35:115:40 | access to array | +| test.cpp:115:35:115:37 | arr | test.cpp:119:17:119:22 | access to array | +| test.cpp:119:17:119:19 | arr | test.cpp:111:17:111:22 | access to array | +| test.cpp:119:17:119:19 | arr | test.cpp:115:35:115:40 | access to array | +| test.cpp:119:17:119:19 | arr | test.cpp:119:17:119:22 | access to array | +| test.cpp:128:9:128:11 | arr | test.cpp:128:9:128:14 | access to array | +| test.cpp:134:25:134:27 | arr | test.cpp:136:9:136:16 | ... += ... | +| test.cpp:136:9:136:16 | ... += ... | test.cpp:138:13:138:15 | arr | +| test.cpp:143:18:143:21 | asdf | test.cpp:134:25:134:27 | arr | +| test.cpp:143:18:143:21 | asdf | test.cpp:143:18:143:21 | asdf | +| test.cpp:148:23:148:28 | buffer | test.cpp:150:5:150:11 | access to array | +| test.cpp:148:23:148:28 | buffer | test.cpp:151:5:151:11 | access to array | +| test.cpp:159:25:159:29 | array | test.cpp:161:5:161:10 | access to array | +| test.cpp:159:25:159:29 | array | test.cpp:162:5:162:10 | access to array | nodes +| test.cpp:34:5:34:24 | access to array | semmle.label | access to array | +| test.cpp:34:10:34:12 | buf | semmle.label | buf | | test.cpp:35:5:35:22 | access to array | semmle.label | access to array | | test.cpp:35:10:35:12 | buf | semmle.label | buf | | test.cpp:36:5:36:24 | access to array | semmle.label | access to array | | test.cpp:36:10:36:12 | buf | semmle.label | buf | +| test.cpp:39:9:39:19 | access to array | semmle.label | access to array | +| test.cpp:39:14:39:16 | buf | semmle.label | buf | | test.cpp:43:9:43:19 | access to array | semmle.label | access to array | | test.cpp:43:14:43:16 | buf | semmle.label | buf | +| test.cpp:48:5:48:24 | access to array | semmle.label | access to array | +| test.cpp:48:10:48:12 | buf | semmle.label | buf | | test.cpp:49:5:49:22 | access to array | semmle.label | access to array | | test.cpp:49:10:49:12 | buf | semmle.label | buf | | test.cpp:50:5:50:24 | access to array | semmle.label | access to array | | test.cpp:50:10:50:12 | buf | semmle.label | buf | +| test.cpp:53:9:53:19 | access to array | semmle.label | access to array | +| test.cpp:53:14:53:16 | buf | semmle.label | buf | | test.cpp:57:9:57:19 | access to array | semmle.label | access to array | | test.cpp:57:14:57:16 | buf | semmle.label | buf | | test.cpp:61:9:61:19 | access to array | semmle.label | access to array | | test.cpp:61:14:61:16 | buf | semmle.label | buf | | test.cpp:66:32:66:32 | p | semmle.label | p | +| test.cpp:66:32:66:32 | p | semmle.label | p | | test.cpp:70:33:70:33 | p | semmle.label | p | +| test.cpp:71:5:71:17 | access to array | semmle.label | access to array | | test.cpp:72:5:72:15 | access to array | semmle.label | access to array | +| test.cpp:76:26:76:46 | & ... | semmle.label | & ... | +| test.cpp:76:32:76:34 | buf | semmle.label | buf | | test.cpp:77:26:77:44 | & ... | semmle.label | & ... | | test.cpp:77:32:77:34 | buf | semmle.label | buf | | test.cpp:79:27:79:34 | buf | semmle.label | buf | | test.cpp:79:32:79:34 | buf | semmle.label | buf | +| test.cpp:85:34:85:36 | buf | semmle.label | buf | +| test.cpp:87:5:87:31 | access to array | semmle.label | access to array | +| test.cpp:88:5:88:27 | access to array | semmle.label | access to array | +| test.cpp:96:13:96:15 | arr | semmle.label | arr | +| test.cpp:96:13:96:18 | access to array | semmle.label | access to array | +| test.cpp:111:17:111:19 | arr | semmle.label | arr | +| test.cpp:111:17:111:22 | access to array | semmle.label | access to array | +| test.cpp:115:35:115:37 | arr | semmle.label | arr | +| test.cpp:115:35:115:40 | access to array | semmle.label | access to array | +| test.cpp:119:17:119:19 | arr | semmle.label | arr | +| test.cpp:119:17:119:22 | access to array | semmle.label | access to array | +| test.cpp:128:9:128:11 | arr | semmle.label | arr | +| test.cpp:128:9:128:14 | access to array | semmle.label | access to array | +| test.cpp:134:25:134:27 | arr | semmle.label | arr | +| test.cpp:136:9:136:16 | ... += ... | semmle.label | ... += ... | +| test.cpp:138:13:138:15 | arr | semmle.label | arr | +| test.cpp:143:18:143:21 | asdf | semmle.label | asdf | +| test.cpp:143:18:143:21 | asdf | semmle.label | asdf | +| test.cpp:148:23:148:28 | buffer | semmle.label | buffer | +| test.cpp:150:5:150:11 | access to array | semmle.label | access to array | +| test.cpp:151:5:151:11 | access to array | semmle.label | access to array | +| test.cpp:159:25:159:29 | array | semmle.label | array | +| test.cpp:161:5:161:10 | access to array | semmle.label | access to array | +| test.cpp:162:5:162:10 | access to array | semmle.label | access to array | subpaths #select | test.cpp:35:5:35:22 | PointerAdd: access to array | test.cpp:35:10:35:12 | buf | test.cpp:35:5:35:22 | access to array | This pointer arithmetic may have an off-by-1 error allowing it to overrun $@ at this $@. | test.cpp:15:9:15:11 | buf | buf | test.cpp:35:5:35:26 | Store: ... = ... | write | @@ -44,3 +108,8 @@ subpaths | test.cpp:61:9:61:19 | PointerAdd: access to array | test.cpp:61:14:61:16 | buf | test.cpp:61:9:61:19 | access to array | This pointer arithmetic may have an off-by-2 error allowing it to overrun $@ at this $@. | test.cpp:19:9:19:11 | buf | buf | test.cpp:61:9:61:23 | Store: ... = ... | write | | test.cpp:72:5:72:15 | PointerAdd: access to array | test.cpp:79:32:79:34 | buf | test.cpp:72:5:72:15 | access to array | This pointer arithmetic may have an off-by-1 error allowing it to overrun $@ at this $@. | test.cpp:15:9:15:11 | buf | buf | test.cpp:72:5:72:19 | Store: ... = ... | write | | test.cpp:77:27:77:44 | PointerAdd: access to array | test.cpp:77:32:77:34 | buf | test.cpp:66:32:66:32 | p | This pointer arithmetic may have an off-by-1 error allowing it to overrun $@ at this $@. | test.cpp:15:9:15:11 | buf | buf | test.cpp:67:5:67:10 | Store: ... = ... | write | +| test.cpp:88:5:88:27 | PointerAdd: access to array | test.cpp:85:34:85:36 | buf | test.cpp:88:5:88:27 | access to array | This pointer arithmetic may have an off-by-1 error allowing it to overrun $@ at this $@. | test.cpp:15:9:15:11 | buf | buf | test.cpp:88:5:88:31 | Store: ... = ... | write | +| test.cpp:128:9:128:14 | PointerAdd: access to array | test.cpp:128:9:128:11 | arr | test.cpp:128:9:128:14 | access to array | This pointer arithmetic may have an off-by-1 error allowing it to overrun $@ at this $@. | test.cpp:125:11:125:13 | arr | arr | test.cpp:128:9:128:18 | Store: ... = ... | write | +| test.cpp:136:9:136:16 | PointerAdd: ... += ... | test.cpp:143:18:143:21 | asdf | test.cpp:138:13:138:15 | arr | This pointer arithmetic may have an off-by-2 error allowing it to overrun $@ at this $@. | test.cpp:142:10:142:13 | asdf | asdf | test.cpp:138:12:138:15 | Load: * ... | read | +| test.cpp:151:5:151:11 | PointerAdd: access to array | test.cpp:148:23:148:28 | buffer | test.cpp:151:5:151:11 | access to array | This pointer arithmetic may have an off-by-1 error allowing it to overrun $@ at this $@. | test.cpp:147:19:147:24 | buffer | buffer | test.cpp:151:5:151:15 | Store: ... = ... | write | +| test.cpp:162:5:162:10 | PointerAdd: access to array | test.cpp:159:25:159:29 | array | test.cpp:162:5:162:10 | access to array | This pointer arithmetic may have an off-by-1 error allowing it to overrun $@ at this $@. | test.cpp:158:10:158:14 | array | array | test.cpp:162:5:162:19 | Store: ... = ... | write | diff --git a/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-193/constant-size/test.cpp b/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-193/constant-size/test.cpp index a33f43bfa49..902bf5a2cd9 100644 --- a/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-193/constant-size/test.cpp +++ b/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-193/constant-size/test.cpp @@ -85,7 +85,7 @@ void testCharIndex(BigArray *arr) { char *charBuf = (char*) arr->buf; charBuf[MAX_SIZE_BYTES - 1] = 0; // GOOD - charBuf[MAX_SIZE_BYTES] = 0; // BAD [FALSE NEGATIVE] + charBuf[MAX_SIZE_BYTES] = 0; // BAD } void testEqRefinement() { @@ -120,3 +120,52 @@ void testEqRefinement2() { } } } + +void testStackAllocated() { + char *arr[MAX_SIZE]; + + for(int i = 0; i <= MAX_SIZE; i++) { + arr[i] = 0; // BAD + } +} + +int strncmp(const char*, const char*, int); + +char testStrncmp2(char *arr) { + if(strncmp(arr, "", 6) == 0) { + arr += 6; + } + return *arr; // GOOD [FALSE POSITIVE] +} + +void testStrncmp1() { + char asdf[5]; + testStrncmp2(asdf); +} + +void pointer_size_larger_than_array_element_size() { + unsigned char buffer[100]; // getByteSize() = 100 + int *ptr = (int *)buffer; // pai.getElementSize() will be sizeof(int) = 4 -> size = 25 + + ptr[24] = 0; // GOOD: writes bytes 96, 97, 98, 99 + ptr[25] = 0; // BAD: writes bytes 100, 101, 102, 103 +} + +struct vec2 { int x, y; }; +struct vec3 { int x, y, z; }; + +void pointer_size_smaller_than_array_element_size_but_does_not_divide_it() { + vec3 array[3]; // getByteSize() = 9 * sizeof(int) + vec2 *ptr = (vec2 *)array; // pai.getElementSize() will be 2 * sizeof(int) -> size = 4 + + ptr[3] = vec2{}; // GOOD: writes ints 6, 7 + ptr[4] = vec2{}; // BAD: writes ints 8, 9 +} + +void pointer_size_larger_than_array_element_size_and_does_not_divide_it() { + vec2 array[2]; // getByteSize() = 4 * sizeof(int) = 4 * 4 = 16 + vec3 *ptr = (vec3 *)array; // pai.getElementSize() will be 3 * sizeof(int) -> size = 1 + + ptr[0] = vec3{}; // GOOD: writes ints 0, 1, 2 + ptr[1] = vec3{}; // BAD: writes ints 3, 4, 5 [NOT DETECTED] +} diff --git a/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-193/pointer-deref/InvalidPointerDeref.expected b/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-193/pointer-deref/InvalidPointerDeref.expected index 7c27659de1f..b1ecd38c3c3 100644 --- a/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-193/pointer-deref/InvalidPointerDeref.expected +++ b/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-193/pointer-deref/InvalidPointerDeref.expected @@ -380,9 +380,10 @@ edges | test.cpp:80:9:80:16 | mk_array indirection [end] | test.cpp:89:19:89:26 | call to mk_array [end] | | test.cpp:80:9:80:16 | mk_array indirection [end] | test.cpp:119:18:119:25 | call to mk_array [end] | | test.cpp:82:5:82:28 | ... = ... | test.cpp:82:9:82:13 | arr indirection [post update] [begin] | -| test.cpp:82:9:82:13 | arr indirection [post update] [begin] | test.cpp:80:9:80:16 | mk_array indirection [begin] | +| test.cpp:82:9:82:13 | arr indirection [post update] [begin] | test.cpp:83:5:83:7 | arr indirection [begin] | | test.cpp:82:9:82:13 | arr indirection [post update] [begin] | test.cpp:83:15:83:17 | arr indirection [begin] | | test.cpp:82:17:82:22 | call to malloc | test.cpp:82:5:82:28 | ... = ... | +| test.cpp:83:5:83:7 | arr indirection [begin] | test.cpp:80:9:80:16 | mk_array indirection [begin] | | test.cpp:83:5:83:30 | ... = ... | test.cpp:83:9:83:11 | arr indirection [post update] [end] | | test.cpp:83:9:83:11 | arr indirection [post update] [end] | test.cpp:80:9:80:16 | mk_array indirection [end] | | test.cpp:83:15:83:17 | arr indirection [begin] | test.cpp:83:19:83:23 | begin indirection | @@ -455,9 +456,10 @@ edges | test.cpp:124:15:124:20 | call to malloc | test.cpp:125:5:125:17 | ... = ... | | test.cpp:124:15:124:20 | call to malloc | test.cpp:126:15:126:15 | p | | test.cpp:125:5:125:17 | ... = ... | test.cpp:125:9:125:13 | arr indirection [post update] [begin] | -| test.cpp:125:9:125:13 | arr indirection [post update] [begin] | test.cpp:129:11:129:13 | arr indirection [begin] | -| test.cpp:125:9:125:13 | arr indirection [post update] [begin] | test.cpp:133:11:133:13 | arr indirection [begin] | -| test.cpp:125:9:125:13 | arr indirection [post update] [begin] | test.cpp:137:11:137:13 | arr indirection [begin] | +| test.cpp:125:9:125:13 | arr indirection [post update] [begin] | test.cpp:126:5:126:7 | arr indirection [begin] | +| test.cpp:126:5:126:7 | arr indirection [begin] | test.cpp:129:11:129:13 | arr indirection [begin] | +| test.cpp:126:5:126:7 | arr indirection [begin] | test.cpp:133:11:133:13 | arr indirection [begin] | +| test.cpp:126:5:126:7 | arr indirection [begin] | test.cpp:137:11:137:13 | arr indirection [begin] | | test.cpp:129:11:129:13 | arr indirection [begin] | test.cpp:129:15:129:19 | begin indirection | | test.cpp:129:15:129:19 | begin indirection | test.cpp:129:15:129:19 | begin | | test.cpp:133:11:133:13 | arr indirection [begin] | test.cpp:133:15:133:19 | begin indirection | @@ -469,9 +471,10 @@ edges | test.cpp:141:10:141:19 | mk_array_p indirection [end] | test.cpp:150:20:150:29 | call to mk_array_p indirection [end] | | test.cpp:141:10:141:19 | mk_array_p indirection [end] | test.cpp:180:19:180:28 | call to mk_array_p indirection [end] | | test.cpp:143:5:143:29 | ... = ... | test.cpp:143:10:143:14 | arr indirection [post update] [begin] | -| test.cpp:143:10:143:14 | arr indirection [post update] [begin] | test.cpp:141:10:141:19 | mk_array_p indirection [begin] | +| test.cpp:143:10:143:14 | arr indirection [post update] [begin] | test.cpp:144:5:144:7 | arr indirection [begin] | | test.cpp:143:10:143:14 | arr indirection [post update] [begin] | test.cpp:144:16:144:18 | arr indirection [begin] | | test.cpp:143:18:143:23 | call to malloc | test.cpp:143:5:143:29 | ... = ... | +| test.cpp:144:5:144:7 | arr indirection [begin] | test.cpp:141:10:141:19 | mk_array_p indirection [begin] | | test.cpp:144:5:144:32 | ... = ... | test.cpp:144:10:144:12 | arr indirection [post update] [end] | | test.cpp:144:10:144:12 | arr indirection [post update] [end] | test.cpp:141:10:141:19 | mk_array_p indirection [end] | | test.cpp:144:16:144:18 | arr indirection [begin] | test.cpp:144:21:144:25 | begin indirection | @@ -717,14 +720,6 @@ edges | test.cpp:359:16:359:27 | end_plus_one | test.cpp:359:14:359:32 | Load: * ... | | test.cpp:359:16:359:31 | ... + ... | test.cpp:359:14:359:32 | Load: * ... | | test.cpp:363:14:363:27 | new[] | test.cpp:365:15:365:15 | p | -| test.cpp:365:15:365:15 | p | test.cpp:368:5:368:10 | ... += ... | -| test.cpp:365:15:365:15 | p | test.cpp:368:5:368:10 | ... += ... | -| test.cpp:368:5:368:10 | ... += ... | test.cpp:371:7:371:7 | p | -| test.cpp:368:5:368:10 | ... += ... | test.cpp:371:7:371:7 | p | -| test.cpp:368:5:368:10 | ... += ... | test.cpp:372:16:372:16 | p | -| test.cpp:368:5:368:10 | ... += ... | test.cpp:372:16:372:16 | p | -| test.cpp:371:7:371:7 | p | test.cpp:372:15:372:16 | Load: * ... | -| test.cpp:372:16:372:16 | p | test.cpp:372:15:372:16 | Load: * ... | | test.cpp:377:14:377:27 | new[] | test.cpp:378:15:378:16 | xs | | test.cpp:378:15:378:16 | xs | test.cpp:378:15:378:23 | ... + ... | | test.cpp:378:15:378:16 | xs | test.cpp:378:15:378:23 | ... + ... | @@ -749,45 +744,284 @@ edges | test.cpp:381:5:381:9 | ... ++ | test.cpp:384:14:384:16 | end | | test.cpp:384:14:384:16 | end | test.cpp:384:13:384:16 | Load: * ... | | test.cpp:388:14:388:27 | new[] | test.cpp:389:16:389:17 | xs | -| test.cpp:388:14:388:27 | new[] | test.cpp:392:5:392:6 | xs | -| test.cpp:389:16:389:17 | xs | test.cpp:392:5:392:8 | ... ++ | -| test.cpp:389:16:389:17 | xs | test.cpp:392:5:392:8 | ... ++ | -| test.cpp:389:16:389:17 | xs | test.cpp:392:5:392:8 | ... ++ | -| test.cpp:389:16:389:17 | xs | test.cpp:392:5:392:8 | ... ++ | -| test.cpp:389:16:389:17 | xs | test.cpp:393:9:393:10 | xs | -| test.cpp:389:16:389:17 | xs | test.cpp:393:9:393:10 | xs | -| test.cpp:392:5:392:8 | ... ++ | test.cpp:392:5:392:8 | ... ++ | -| test.cpp:392:5:392:8 | ... ++ | test.cpp:392:5:392:8 | ... ++ | -| test.cpp:392:5:392:8 | ... ++ | test.cpp:393:9:393:10 | xs | -| test.cpp:392:5:392:8 | ... ++ | test.cpp:393:9:393:10 | xs | -| test.cpp:392:5:392:8 | ... ++ | test.cpp:393:9:393:10 | xs | -| test.cpp:392:5:392:8 | ... ++ | test.cpp:393:9:393:10 | xs | -| test.cpp:392:5:392:8 | ... ++ | test.cpp:395:5:395:6 | xs | -| test.cpp:392:5:392:8 | ... ++ | test.cpp:395:5:395:6 | xs | -| test.cpp:392:5:392:8 | ... ++ | test.cpp:395:5:395:13 | Store: ... = ... | -| test.cpp:392:5:392:8 | ... ++ | test.cpp:395:5:395:13 | Store: ... = ... | -| test.cpp:392:5:392:8 | ... ++ | test.cpp:395:5:395:13 | Store: ... = ... | -| test.cpp:392:5:392:8 | ... ++ | test.cpp:395:5:395:13 | Store: ... = ... | -| test.cpp:393:9:393:10 | xs | test.cpp:395:5:395:6 | xs | -| test.cpp:393:9:393:10 | xs | test.cpp:395:5:395:13 | Store: ... = ... | -| test.cpp:393:9:393:10 | xs | test.cpp:395:5:395:13 | Store: ... = ... | -| test.cpp:395:5:395:6 | xs | test.cpp:395:5:395:13 | Store: ... = ... | -| test.cpp:404:3:404:25 | ... = ... | test.cpp:404:7:404:8 | val indirection [post update] [xs] | -| test.cpp:404:7:404:8 | val indirection [post update] [xs] | test.cpp:407:3:407:5 | val indirection [xs] | -| test.cpp:404:12:404:25 | new[] | test.cpp:404:3:404:25 | ... = ... | -| test.cpp:406:3:406:25 | ... = ... | test.cpp:406:7:406:8 | val indirection [post update] [xs] | -| test.cpp:406:7:406:8 | val indirection [post update] [xs] | test.cpp:407:3:407:5 | val indirection [xs] | -| test.cpp:406:12:406:25 | new[] | test.cpp:406:3:406:25 | ... = ... | -| test.cpp:407:3:407:5 | val indirection [xs] | test.cpp:407:7:407:8 | xs indirection | -| test.cpp:407:3:407:18 | access to array | test.cpp:407:3:407:22 | Store: ... = ... | -| test.cpp:407:7:407:8 | xs | test.cpp:407:3:407:18 | access to array | -| test.cpp:407:7:407:8 | xs indirection | test.cpp:407:7:407:8 | xs | -| test.cpp:417:16:417:33 | new[] | test.cpp:419:7:419:8 | xs | -| test.cpp:419:7:419:8 | xs | test.cpp:419:7:419:11 | access to array | -| test.cpp:419:7:419:11 | access to array | test.cpp:419:7:419:15 | Store: ... = ... | -| test.cpp:427:14:427:27 | new[] | test.cpp:433:5:433:6 | xs | -| test.cpp:433:5:433:6 | xs | test.cpp:433:5:433:17 | access to array | -| test.cpp:433:5:433:17 | access to array | test.cpp:433:5:433:21 | Store: ... = ... | +| test.cpp:388:14:388:27 | new[] | test.cpp:392:3:392:4 | xs | +| test.cpp:399:14:399:27 | new[] | test.cpp:400:16:400:17 | xs | +| test.cpp:399:14:399:27 | new[] | test.cpp:402:5:402:6 | xs | +| test.cpp:410:14:410:27 | new[] | test.cpp:411:16:411:17 | xs | +| test.cpp:410:14:410:27 | new[] | test.cpp:413:5:413:6 | xs | +| test.cpp:411:15:411:23 | & ... | test.cpp:411:15:411:23 | & ... | +| test.cpp:411:15:411:23 | & ... | test.cpp:411:15:411:23 | & ... | +| test.cpp:411:15:411:23 | & ... | test.cpp:412:12:412:14 | end | +| test.cpp:411:15:411:23 | & ... | test.cpp:412:12:412:14 | end | +| test.cpp:411:15:411:23 | & ... | test.cpp:412:12:412:14 | end | +| test.cpp:411:15:411:23 | & ... | test.cpp:412:12:412:14 | end | +| test.cpp:411:15:411:23 | & ... | test.cpp:414:14:414:16 | end | +| test.cpp:411:15:411:23 | & ... | test.cpp:414:14:414:16 | end | +| test.cpp:411:15:411:23 | & ... | test.cpp:415:7:415:15 | Store: ... = ... | +| test.cpp:411:15:411:23 | & ... | test.cpp:415:7:415:15 | Store: ... = ... | +| test.cpp:411:15:411:23 | & ... | test.cpp:415:7:415:15 | Store: ... = ... | +| test.cpp:411:15:411:23 | & ... | test.cpp:415:7:415:15 | Store: ... = ... | +| test.cpp:411:16:411:17 | xs | test.cpp:411:15:411:23 | & ... | +| test.cpp:411:16:411:17 | xs | test.cpp:411:15:411:23 | & ... | +| test.cpp:411:16:411:17 | xs | test.cpp:411:15:411:23 | & ... | +| test.cpp:411:16:411:17 | xs | test.cpp:411:15:411:23 | & ... | +| test.cpp:411:16:411:17 | xs | test.cpp:411:16:411:23 | access to array | +| test.cpp:411:16:411:17 | xs | test.cpp:411:16:411:23 | access to array | +| test.cpp:411:16:411:17 | xs | test.cpp:412:12:412:14 | end | +| test.cpp:411:16:411:17 | xs | test.cpp:412:12:412:14 | end | +| test.cpp:411:16:411:17 | xs | test.cpp:413:5:413:8 | ... ++ | +| test.cpp:411:16:411:17 | xs | test.cpp:413:5:413:8 | ... ++ | +| test.cpp:411:16:411:17 | xs | test.cpp:413:5:413:8 | ... ++ | +| test.cpp:411:16:411:17 | xs | test.cpp:413:5:413:8 | ... ++ | +| test.cpp:411:16:411:17 | xs | test.cpp:414:9:414:10 | xs | +| test.cpp:411:16:411:17 | xs | test.cpp:414:14:414:16 | end | +| test.cpp:411:16:411:17 | xs | test.cpp:415:7:415:11 | access to array | +| test.cpp:411:16:411:23 | access to array | test.cpp:411:15:411:23 | & ... | +| test.cpp:411:16:411:23 | access to array | test.cpp:411:15:411:23 | & ... | +| test.cpp:411:16:411:23 | access to array | test.cpp:411:15:411:23 | & ... | +| test.cpp:411:16:411:23 | access to array | test.cpp:411:15:411:23 | & ... | +| test.cpp:411:16:411:23 | access to array | test.cpp:412:12:412:14 | end | +| test.cpp:411:16:411:23 | access to array | test.cpp:412:12:412:14 | end | +| test.cpp:411:16:411:23 | access to array | test.cpp:414:14:414:16 | end | +| test.cpp:411:16:411:23 | access to array | test.cpp:415:7:415:15 | Store: ... = ... | +| test.cpp:411:16:411:23 | access to array | test.cpp:415:7:415:15 | Store: ... = ... | +| test.cpp:412:12:412:14 | end | test.cpp:414:14:414:16 | end | +| test.cpp:412:12:412:14 | end | test.cpp:415:7:415:15 | Store: ... = ... | +| test.cpp:412:12:412:14 | end | test.cpp:415:7:415:15 | Store: ... = ... | +| test.cpp:413:5:413:8 | ... ++ | test.cpp:413:5:413:8 | ... ++ | +| test.cpp:413:5:413:8 | ... ++ | test.cpp:413:5:413:8 | ... ++ | +| test.cpp:413:5:413:8 | ... ++ | test.cpp:414:9:414:10 | xs | +| test.cpp:413:5:413:8 | ... ++ | test.cpp:414:9:414:10 | xs | +| test.cpp:413:5:413:8 | ... ++ | test.cpp:415:7:415:15 | Store: ... = ... | +| test.cpp:413:5:413:8 | ... ++ | test.cpp:415:7:415:15 | Store: ... = ... | +| test.cpp:413:5:413:8 | ... ++ | test.cpp:415:7:415:15 | Store: ... = ... | +| test.cpp:413:5:413:8 | ... ++ | test.cpp:415:7:415:15 | Store: ... = ... | +| test.cpp:414:9:414:10 | xs | test.cpp:415:7:415:15 | Store: ... = ... | +| test.cpp:414:14:414:16 | end | test.cpp:415:7:415:15 | Store: ... = ... | +| test.cpp:415:7:415:11 | access to array | test.cpp:415:7:415:15 | Store: ... = ... | +| test.cpp:421:14:421:27 | new[] | test.cpp:422:16:422:17 | xs | +| test.cpp:421:14:421:27 | new[] | test.cpp:424:5:424:6 | xs | +| test.cpp:422:15:422:23 | & ... | test.cpp:422:15:422:23 | & ... | +| test.cpp:422:15:422:23 | & ... | test.cpp:422:15:422:23 | & ... | +| test.cpp:422:15:422:23 | & ... | test.cpp:423:12:423:14 | end | +| test.cpp:422:15:422:23 | & ... | test.cpp:423:12:423:14 | end | +| test.cpp:422:15:422:23 | & ... | test.cpp:423:12:423:14 | end | +| test.cpp:422:15:422:23 | & ... | test.cpp:423:12:423:14 | end | +| test.cpp:422:15:422:23 | & ... | test.cpp:425:18:425:20 | end | +| test.cpp:422:15:422:23 | & ... | test.cpp:425:18:425:20 | end | +| test.cpp:422:15:422:23 | & ... | test.cpp:426:7:426:15 | Store: ... = ... | +| test.cpp:422:15:422:23 | & ... | test.cpp:426:7:426:15 | Store: ... = ... | +| test.cpp:422:15:422:23 | & ... | test.cpp:426:7:426:15 | Store: ... = ... | +| test.cpp:422:15:422:23 | & ... | test.cpp:426:7:426:15 | Store: ... = ... | +| test.cpp:422:16:422:17 | xs | test.cpp:422:15:422:23 | & ... | +| test.cpp:422:16:422:17 | xs | test.cpp:422:15:422:23 | & ... | +| test.cpp:422:16:422:17 | xs | test.cpp:422:15:422:23 | & ... | +| test.cpp:422:16:422:17 | xs | test.cpp:422:15:422:23 | & ... | +| test.cpp:422:16:422:17 | xs | test.cpp:422:16:422:23 | access to array | +| test.cpp:422:16:422:17 | xs | test.cpp:422:16:422:23 | access to array | +| test.cpp:422:16:422:17 | xs | test.cpp:423:12:423:14 | end | +| test.cpp:422:16:422:17 | xs | test.cpp:423:12:423:14 | end | +| test.cpp:422:16:422:17 | xs | test.cpp:424:5:424:8 | ... ++ | +| test.cpp:422:16:422:17 | xs | test.cpp:424:5:424:8 | ... ++ | +| test.cpp:422:16:422:17 | xs | test.cpp:424:5:424:8 | ... ++ | +| test.cpp:422:16:422:17 | xs | test.cpp:424:5:424:8 | ... ++ | +| test.cpp:422:16:422:17 | xs | test.cpp:425:9:425:10 | xs | +| test.cpp:422:16:422:17 | xs | test.cpp:425:9:425:10 | xs | +| test.cpp:422:16:422:17 | xs | test.cpp:425:18:425:20 | end | +| test.cpp:422:16:422:17 | xs | test.cpp:426:7:426:8 | xs | +| test.cpp:422:16:422:17 | xs | test.cpp:426:7:426:11 | access to array | +| test.cpp:422:16:422:23 | access to array | test.cpp:422:15:422:23 | & ... | +| test.cpp:422:16:422:23 | access to array | test.cpp:422:15:422:23 | & ... | +| test.cpp:422:16:422:23 | access to array | test.cpp:422:15:422:23 | & ... | +| test.cpp:422:16:422:23 | access to array | test.cpp:422:15:422:23 | & ... | +| test.cpp:422:16:422:23 | access to array | test.cpp:423:12:423:14 | end | +| test.cpp:422:16:422:23 | access to array | test.cpp:423:12:423:14 | end | +| test.cpp:422:16:422:23 | access to array | test.cpp:425:18:425:20 | end | +| test.cpp:422:16:422:23 | access to array | test.cpp:426:7:426:15 | Store: ... = ... | +| test.cpp:422:16:422:23 | access to array | test.cpp:426:7:426:15 | Store: ... = ... | +| test.cpp:423:12:423:14 | end | test.cpp:425:18:425:20 | end | +| test.cpp:423:12:423:14 | end | test.cpp:426:7:426:15 | Store: ... = ... | +| test.cpp:423:12:423:14 | end | test.cpp:426:7:426:15 | Store: ... = ... | +| test.cpp:424:5:424:8 | ... ++ | test.cpp:424:5:424:8 | ... ++ | +| test.cpp:424:5:424:8 | ... ++ | test.cpp:424:5:424:8 | ... ++ | +| test.cpp:424:5:424:8 | ... ++ | test.cpp:425:9:425:10 | xs | +| test.cpp:424:5:424:8 | ... ++ | test.cpp:425:9:425:10 | xs | +| test.cpp:424:5:424:8 | ... ++ | test.cpp:425:9:425:10 | xs | +| test.cpp:424:5:424:8 | ... ++ | test.cpp:425:9:425:10 | xs | +| test.cpp:424:5:424:8 | ... ++ | test.cpp:426:7:426:8 | xs | +| test.cpp:424:5:424:8 | ... ++ | test.cpp:426:7:426:8 | xs | +| test.cpp:424:5:424:8 | ... ++ | test.cpp:426:7:426:15 | Store: ... = ... | +| test.cpp:424:5:424:8 | ... ++ | test.cpp:426:7:426:15 | Store: ... = ... | +| test.cpp:424:5:424:8 | ... ++ | test.cpp:426:7:426:15 | Store: ... = ... | +| test.cpp:424:5:424:8 | ... ++ | test.cpp:426:7:426:15 | Store: ... = ... | +| test.cpp:425:9:425:10 | xs | test.cpp:426:7:426:8 | xs | +| test.cpp:425:9:425:10 | xs | test.cpp:426:7:426:15 | Store: ... = ... | +| test.cpp:425:9:425:10 | xs | test.cpp:426:7:426:15 | Store: ... = ... | +| test.cpp:425:18:425:20 | end | test.cpp:426:7:426:15 | Store: ... = ... | +| test.cpp:426:7:426:8 | xs | test.cpp:426:7:426:15 | Store: ... = ... | +| test.cpp:426:7:426:11 | access to array | test.cpp:426:7:426:15 | Store: ... = ... | +| test.cpp:432:14:432:27 | new[] | test.cpp:433:16:433:17 | xs | +| test.cpp:432:14:432:27 | new[] | test.cpp:436:5:436:6 | xs | +| test.cpp:433:15:433:23 | & ... | test.cpp:433:15:433:23 | & ... | +| test.cpp:433:15:433:23 | & ... | test.cpp:433:15:433:23 | & ... | +| test.cpp:433:15:433:23 | & ... | test.cpp:434:12:434:14 | end | +| test.cpp:433:15:433:23 | & ... | test.cpp:434:12:434:14 | end | +| test.cpp:433:15:433:23 | & ... | test.cpp:434:12:434:14 | end | +| test.cpp:433:15:433:23 | & ... | test.cpp:434:12:434:14 | end | +| test.cpp:433:15:433:23 | & ... | test.cpp:435:5:435:7 | end | +| test.cpp:433:15:433:23 | & ... | test.cpp:435:5:435:7 | end | +| test.cpp:433:15:433:23 | & ... | test.cpp:438:7:438:15 | Store: ... = ... | +| test.cpp:433:15:433:23 | & ... | test.cpp:438:7:438:15 | Store: ... = ... | +| test.cpp:433:15:433:23 | & ... | test.cpp:438:7:438:15 | Store: ... = ... | +| test.cpp:433:15:433:23 | & ... | test.cpp:438:7:438:15 | Store: ... = ... | +| test.cpp:433:16:433:17 | xs | test.cpp:433:15:433:23 | & ... | +| test.cpp:433:16:433:17 | xs | test.cpp:433:15:433:23 | & ... | +| test.cpp:433:16:433:17 | xs | test.cpp:433:15:433:23 | & ... | +| test.cpp:433:16:433:17 | xs | test.cpp:433:15:433:23 | & ... | +| test.cpp:433:16:433:17 | xs | test.cpp:433:16:433:23 | access to array | +| test.cpp:433:16:433:17 | xs | test.cpp:433:16:433:23 | access to array | +| test.cpp:433:16:433:17 | xs | test.cpp:434:12:434:14 | end | +| test.cpp:433:16:433:17 | xs | test.cpp:434:12:434:14 | end | +| test.cpp:433:16:433:17 | xs | test.cpp:435:5:435:7 | end | +| test.cpp:433:16:433:17 | xs | test.cpp:436:5:436:8 | ... ++ | +| test.cpp:433:16:433:17 | xs | test.cpp:436:5:436:8 | ... ++ | +| test.cpp:433:16:433:17 | xs | test.cpp:436:5:436:8 | ... ++ | +| test.cpp:433:16:433:17 | xs | test.cpp:436:5:436:8 | ... ++ | +| test.cpp:433:16:433:17 | xs | test.cpp:437:9:437:10 | xs | +| test.cpp:433:16:433:17 | xs | test.cpp:438:7:438:11 | access to array | +| test.cpp:433:16:433:23 | access to array | test.cpp:433:15:433:23 | & ... | +| test.cpp:433:16:433:23 | access to array | test.cpp:433:15:433:23 | & ... | +| test.cpp:433:16:433:23 | access to array | test.cpp:433:15:433:23 | & ... | +| test.cpp:433:16:433:23 | access to array | test.cpp:433:15:433:23 | & ... | +| test.cpp:433:16:433:23 | access to array | test.cpp:434:12:434:14 | end | +| test.cpp:433:16:433:23 | access to array | test.cpp:434:12:434:14 | end | +| test.cpp:433:16:433:23 | access to array | test.cpp:435:5:435:7 | end | +| test.cpp:433:16:433:23 | access to array | test.cpp:438:7:438:15 | Store: ... = ... | +| test.cpp:433:16:433:23 | access to array | test.cpp:438:7:438:15 | Store: ... = ... | +| test.cpp:434:12:434:14 | end | test.cpp:435:5:435:7 | end | +| test.cpp:434:12:434:14 | end | test.cpp:438:7:438:15 | Store: ... = ... | +| test.cpp:434:12:434:14 | end | test.cpp:438:7:438:15 | Store: ... = ... | +| test.cpp:435:5:435:7 | end | test.cpp:438:7:438:15 | Store: ... = ... | +| test.cpp:436:5:436:8 | ... ++ | test.cpp:436:5:436:8 | ... ++ | +| test.cpp:436:5:436:8 | ... ++ | test.cpp:436:5:436:8 | ... ++ | +| test.cpp:436:5:436:8 | ... ++ | test.cpp:437:9:437:10 | xs | +| test.cpp:436:5:436:8 | ... ++ | test.cpp:437:9:437:10 | xs | +| test.cpp:436:5:436:8 | ... ++ | test.cpp:438:7:438:15 | Store: ... = ... | +| test.cpp:436:5:436:8 | ... ++ | test.cpp:438:7:438:15 | Store: ... = ... | +| test.cpp:436:5:436:8 | ... ++ | test.cpp:438:7:438:15 | Store: ... = ... | +| test.cpp:436:5:436:8 | ... ++ | test.cpp:438:7:438:15 | Store: ... = ... | +| test.cpp:437:9:437:10 | xs | test.cpp:438:7:438:15 | Store: ... = ... | +| test.cpp:438:7:438:11 | access to array | test.cpp:438:7:438:15 | Store: ... = ... | +| test.cpp:444:14:444:27 | new[] | test.cpp:445:16:445:17 | xs | +| test.cpp:444:14:444:27 | new[] | test.cpp:448:5:448:6 | xs | +| test.cpp:445:15:445:23 | & ... | test.cpp:445:15:445:23 | & ... | +| test.cpp:445:15:445:23 | & ... | test.cpp:445:15:445:23 | & ... | +| test.cpp:445:15:445:23 | & ... | test.cpp:446:3:446:5 | end | +| test.cpp:445:15:445:23 | & ... | test.cpp:446:3:446:5 | end | +| test.cpp:445:15:445:23 | & ... | test.cpp:450:7:450:15 | Store: ... = ... | +| test.cpp:445:15:445:23 | & ... | test.cpp:450:7:450:15 | Store: ... = ... | +| test.cpp:445:15:445:23 | & ... | test.cpp:450:7:450:15 | Store: ... = ... | +| test.cpp:445:15:445:23 | & ... | test.cpp:450:7:450:15 | Store: ... = ... | +| test.cpp:445:16:445:17 | xs | test.cpp:445:15:445:23 | & ... | +| test.cpp:445:16:445:17 | xs | test.cpp:445:15:445:23 | & ... | +| test.cpp:445:16:445:17 | xs | test.cpp:445:15:445:23 | & ... | +| test.cpp:445:16:445:17 | xs | test.cpp:445:15:445:23 | & ... | +| test.cpp:445:16:445:17 | xs | test.cpp:445:16:445:23 | access to array | +| test.cpp:445:16:445:17 | xs | test.cpp:445:16:445:23 | access to array | +| test.cpp:445:16:445:17 | xs | test.cpp:446:3:446:5 | end | +| test.cpp:445:16:445:17 | xs | test.cpp:448:5:448:8 | ... ++ | +| test.cpp:445:16:445:17 | xs | test.cpp:448:5:448:8 | ... ++ | +| test.cpp:445:16:445:17 | xs | test.cpp:448:5:448:8 | ... ++ | +| test.cpp:445:16:445:17 | xs | test.cpp:448:5:448:8 | ... ++ | +| test.cpp:445:16:445:17 | xs | test.cpp:449:9:449:10 | xs | +| test.cpp:445:16:445:17 | xs | test.cpp:450:7:450:11 | access to array | +| test.cpp:445:16:445:23 | access to array | test.cpp:445:15:445:23 | & ... | +| test.cpp:445:16:445:23 | access to array | test.cpp:445:15:445:23 | & ... | +| test.cpp:445:16:445:23 | access to array | test.cpp:445:15:445:23 | & ... | +| test.cpp:445:16:445:23 | access to array | test.cpp:445:15:445:23 | & ... | +| test.cpp:445:16:445:23 | access to array | test.cpp:446:3:446:5 | end | +| test.cpp:445:16:445:23 | access to array | test.cpp:450:7:450:15 | Store: ... = ... | +| test.cpp:445:16:445:23 | access to array | test.cpp:450:7:450:15 | Store: ... = ... | +| test.cpp:446:3:446:5 | end | test.cpp:450:7:450:15 | Store: ... = ... | +| test.cpp:448:5:448:8 | ... ++ | test.cpp:448:5:448:8 | ... ++ | +| test.cpp:448:5:448:8 | ... ++ | test.cpp:448:5:448:8 | ... ++ | +| test.cpp:448:5:448:8 | ... ++ | test.cpp:449:9:449:10 | xs | +| test.cpp:448:5:448:8 | ... ++ | test.cpp:449:9:449:10 | xs | +| test.cpp:448:5:448:8 | ... ++ | test.cpp:450:7:450:15 | Store: ... = ... | +| test.cpp:448:5:448:8 | ... ++ | test.cpp:450:7:450:15 | Store: ... = ... | +| test.cpp:448:5:448:8 | ... ++ | test.cpp:450:7:450:15 | Store: ... = ... | +| test.cpp:448:5:448:8 | ... ++ | test.cpp:450:7:450:15 | Store: ... = ... | +| test.cpp:449:9:449:10 | xs | test.cpp:450:7:450:15 | Store: ... = ... | +| test.cpp:450:7:450:11 | access to array | test.cpp:450:7:450:15 | Store: ... = ... | +| test.cpp:456:14:456:31 | new[] | test.cpp:457:16:457:17 | xs | +| test.cpp:456:14:456:31 | new[] | test.cpp:460:5:460:6 | xs | +| test.cpp:468:14:468:27 | new[] | test.cpp:469:16:469:17 | xs | +| test.cpp:468:14:468:27 | new[] | test.cpp:472:5:472:6 | xs | +| test.cpp:480:14:480:27 | new[] | test.cpp:481:16:481:17 | xs | +| test.cpp:480:14:480:27 | new[] | test.cpp:484:5:484:6 | xs | +| test.cpp:481:15:481:23 | & ... | test.cpp:481:15:481:23 | & ... | +| test.cpp:481:15:481:23 | & ... | test.cpp:481:15:481:23 | & ... | +| test.cpp:481:15:481:23 | & ... | test.cpp:482:3:482:5 | end | +| test.cpp:481:15:481:23 | & ... | test.cpp:482:3:482:5 | end | +| test.cpp:481:15:481:23 | & ... | test.cpp:486:7:486:15 | Store: ... = ... | +| test.cpp:481:15:481:23 | & ... | test.cpp:486:7:486:15 | Store: ... = ... | +| test.cpp:481:15:481:23 | & ... | test.cpp:486:7:486:15 | Store: ... = ... | +| test.cpp:481:15:481:23 | & ... | test.cpp:486:7:486:15 | Store: ... = ... | +| test.cpp:481:16:481:17 | xs | test.cpp:481:15:481:23 | & ... | +| test.cpp:481:16:481:17 | xs | test.cpp:481:15:481:23 | & ... | +| test.cpp:481:16:481:17 | xs | test.cpp:481:15:481:23 | & ... | +| test.cpp:481:16:481:17 | xs | test.cpp:481:15:481:23 | & ... | +| test.cpp:481:16:481:17 | xs | test.cpp:481:16:481:23 | access to array | +| test.cpp:481:16:481:17 | xs | test.cpp:481:16:481:23 | access to array | +| test.cpp:481:16:481:17 | xs | test.cpp:482:3:482:5 | end | +| test.cpp:481:16:481:17 | xs | test.cpp:484:5:484:8 | ... ++ | +| test.cpp:481:16:481:17 | xs | test.cpp:484:5:484:8 | ... ++ | +| test.cpp:481:16:481:17 | xs | test.cpp:484:5:484:8 | ... ++ | +| test.cpp:481:16:481:17 | xs | test.cpp:484:5:484:8 | ... ++ | +| test.cpp:481:16:481:17 | xs | test.cpp:485:9:485:10 | xs | +| test.cpp:481:16:481:17 | xs | test.cpp:486:7:486:11 | access to array | +| test.cpp:481:16:481:23 | access to array | test.cpp:481:15:481:23 | & ... | +| test.cpp:481:16:481:23 | access to array | test.cpp:481:15:481:23 | & ... | +| test.cpp:481:16:481:23 | access to array | test.cpp:481:15:481:23 | & ... | +| test.cpp:481:16:481:23 | access to array | test.cpp:481:15:481:23 | & ... | +| test.cpp:481:16:481:23 | access to array | test.cpp:482:3:482:5 | end | +| test.cpp:481:16:481:23 | access to array | test.cpp:486:7:486:15 | Store: ... = ... | +| test.cpp:481:16:481:23 | access to array | test.cpp:486:7:486:15 | Store: ... = ... | +| test.cpp:482:3:482:5 | end | test.cpp:486:7:486:15 | Store: ... = ... | +| test.cpp:484:5:484:8 | ... ++ | test.cpp:484:5:484:8 | ... ++ | +| test.cpp:484:5:484:8 | ... ++ | test.cpp:484:5:484:8 | ... ++ | +| test.cpp:484:5:484:8 | ... ++ | test.cpp:485:9:485:10 | xs | +| test.cpp:484:5:484:8 | ... ++ | test.cpp:485:9:485:10 | xs | +| test.cpp:484:5:484:8 | ... ++ | test.cpp:486:7:486:15 | Store: ... = ... | +| test.cpp:484:5:484:8 | ... ++ | test.cpp:486:7:486:15 | Store: ... = ... | +| test.cpp:484:5:484:8 | ... ++ | test.cpp:486:7:486:15 | Store: ... = ... | +| test.cpp:484:5:484:8 | ... ++ | test.cpp:486:7:486:15 | Store: ... = ... | +| test.cpp:485:9:485:10 | xs | test.cpp:486:7:486:15 | Store: ... = ... | +| test.cpp:486:7:486:11 | access to array | test.cpp:486:7:486:15 | Store: ... = ... | +| test.cpp:499:3:499:25 | ... = ... | test.cpp:499:7:499:8 | val indirection [post update] [xs] | +| test.cpp:499:7:499:8 | val indirection [post update] [xs] | test.cpp:500:3:500:5 | val indirection [xs] | +| test.cpp:499:12:499:25 | new[] | test.cpp:499:3:499:25 | ... = ... | +| test.cpp:500:3:500:5 | val indirection [xs] | test.cpp:500:7:500:8 | xs indirection | +| test.cpp:500:7:500:8 | xs indirection | test.cpp:500:7:500:8 | xs | +| test.cpp:510:16:510:33 | new[] | test.cpp:512:7:512:8 | xs | +| test.cpp:520:14:520:27 | new[] | test.cpp:526:5:526:6 | xs | +| test.cpp:532:14:532:27 | new[] | test.cpp:537:5:537:6 | xs | +| test.cpp:543:14:543:27 | new[] | test.cpp:548:5:548:6 | xs | +| test.cpp:548:5:548:6 | xs | test.cpp:548:5:548:15 | access to array | +| test.cpp:548:5:548:15 | access to array | test.cpp:548:5:548:19 | Store: ... = ... | +| test.cpp:554:14:554:27 | new[] | test.cpp:559:5:559:6 | xs | +| test.cpp:559:5:559:6 | xs | test.cpp:559:5:559:15 | access to array | +| test.cpp:559:5:559:15 | access to array | test.cpp:559:5:559:19 | Store: ... = ... | +| test.cpp:565:14:565:27 | new[] | test.cpp:570:5:570:6 | xs | +| test.cpp:576:14:576:27 | new[] | test.cpp:581:5:581:6 | xs | +| test.cpp:587:14:587:31 | new[] | test.cpp:592:5:592:6 | xs | +| test.cpp:598:14:598:31 | new[] | test.cpp:603:5:603:6 | xs | +| test.cpp:609:14:609:31 | new[] | test.cpp:614:5:614:6 | xs | +| test.cpp:620:14:620:31 | new[] | test.cpp:625:5:625:6 | xs | +| test.cpp:631:14:631:31 | new[] | test.cpp:636:5:636:6 | xs | +| test.cpp:642:14:642:31 | new[] | test.cpp:647:5:647:6 | xs | +| test.cpp:647:5:647:6 | xs | test.cpp:647:5:647:15 | access to array | +| test.cpp:647:5:647:15 | access to array | test.cpp:647:5:647:19 | Store: ... = ... | nodes | test.cpp:4:15:4:20 | call to malloc | semmle.label | call to malloc | | test.cpp:5:15:5:15 | p | semmle.label | p | @@ -880,6 +1114,7 @@ nodes | test.cpp:82:5:82:28 | ... = ... | semmle.label | ... = ... | | test.cpp:82:9:82:13 | arr indirection [post update] [begin] | semmle.label | arr indirection [post update] [begin] | | test.cpp:82:17:82:22 | call to malloc | semmle.label | call to malloc | +| test.cpp:83:5:83:7 | arr indirection [begin] | semmle.label | arr indirection [begin] | | test.cpp:83:5:83:30 | ... = ... | semmle.label | ... = ... | | test.cpp:83:9:83:11 | arr indirection [post update] [end] | semmle.label | arr indirection [post update] [end] | | test.cpp:83:15:83:17 | arr indirection [begin] | semmle.label | arr indirection [begin] | @@ -939,6 +1174,7 @@ nodes | test.cpp:124:15:124:20 | call to malloc | semmle.label | call to malloc | | test.cpp:125:5:125:17 | ... = ... | semmle.label | ... = ... | | test.cpp:125:9:125:13 | arr indirection [post update] [begin] | semmle.label | arr indirection [post update] [begin] | +| test.cpp:126:5:126:7 | arr indirection [begin] | semmle.label | arr indirection [begin] | | test.cpp:126:15:126:15 | p | semmle.label | p | | test.cpp:129:11:129:13 | arr indirection [begin] | semmle.label | arr indirection [begin] | | test.cpp:129:15:129:19 | begin | semmle.label | begin | @@ -954,6 +1190,7 @@ nodes | test.cpp:143:5:143:29 | ... = ... | semmle.label | ... = ... | | test.cpp:143:10:143:14 | arr indirection [post update] [begin] | semmle.label | arr indirection [post update] [begin] | | test.cpp:143:18:143:23 | call to malloc | semmle.label | call to malloc | +| test.cpp:144:5:144:7 | arr indirection [begin] | semmle.label | arr indirection [begin] | | test.cpp:144:5:144:32 | ... = ... | semmle.label | ... = ... | | test.cpp:144:10:144:12 | arr indirection [post update] [end] | semmle.label | arr indirection [post update] [end] | | test.cpp:144:16:144:18 | arr indirection [begin] | semmle.label | arr indirection [begin] | @@ -1111,11 +1348,6 @@ nodes | test.cpp:359:16:359:31 | ... + ... | semmle.label | ... + ... | | test.cpp:363:14:363:27 | new[] | semmle.label | new[] | | test.cpp:365:15:365:15 | p | semmle.label | p | -| test.cpp:368:5:368:10 | ... += ... | semmle.label | ... += ... | -| test.cpp:368:5:368:10 | ... += ... | semmle.label | ... += ... | -| test.cpp:371:7:371:7 | p | semmle.label | p | -| test.cpp:372:15:372:16 | Load: * ... | semmle.label | Load: * ... | -| test.cpp:372:16:372:16 | p | semmle.label | p | | test.cpp:377:14:377:27 | new[] | semmle.label | new[] | | test.cpp:378:15:378:16 | xs | semmle.label | xs | | test.cpp:378:15:378:23 | ... + ... | semmle.label | ... + ... | @@ -1129,34 +1361,147 @@ nodes | test.cpp:384:14:384:16 | end | semmle.label | end | | test.cpp:388:14:388:27 | new[] | semmle.label | new[] | | test.cpp:389:16:389:17 | xs | semmle.label | xs | -| test.cpp:392:5:392:6 | xs | semmle.label | xs | -| test.cpp:392:5:392:8 | ... ++ | semmle.label | ... ++ | -| test.cpp:392:5:392:8 | ... ++ | semmle.label | ... ++ | -| test.cpp:392:5:392:8 | ... ++ | semmle.label | ... ++ | -| test.cpp:392:5:392:8 | ... ++ | semmle.label | ... ++ | -| test.cpp:393:9:393:10 | xs | semmle.label | xs | -| test.cpp:393:9:393:10 | xs | semmle.label | xs | -| test.cpp:395:5:395:6 | xs | semmle.label | xs | -| test.cpp:395:5:395:13 | Store: ... = ... | semmle.label | Store: ... = ... | -| test.cpp:404:3:404:25 | ... = ... | semmle.label | ... = ... | -| test.cpp:404:7:404:8 | val indirection [post update] [xs] | semmle.label | val indirection [post update] [xs] | -| test.cpp:404:12:404:25 | new[] | semmle.label | new[] | -| test.cpp:406:3:406:25 | ... = ... | semmle.label | ... = ... | -| test.cpp:406:7:406:8 | val indirection [post update] [xs] | semmle.label | val indirection [post update] [xs] | -| test.cpp:406:12:406:25 | new[] | semmle.label | new[] | -| test.cpp:407:3:407:5 | val indirection [xs] | semmle.label | val indirection [xs] | -| test.cpp:407:3:407:18 | access to array | semmle.label | access to array | -| test.cpp:407:3:407:22 | Store: ... = ... | semmle.label | Store: ... = ... | -| test.cpp:407:7:407:8 | xs | semmle.label | xs | -| test.cpp:407:7:407:8 | xs indirection | semmle.label | xs indirection | -| test.cpp:417:16:417:33 | new[] | semmle.label | new[] | -| test.cpp:419:7:419:8 | xs | semmle.label | xs | -| test.cpp:419:7:419:11 | access to array | semmle.label | access to array | -| test.cpp:419:7:419:15 | Store: ... = ... | semmle.label | Store: ... = ... | -| test.cpp:427:14:427:27 | new[] | semmle.label | new[] | -| test.cpp:433:5:433:6 | xs | semmle.label | xs | -| test.cpp:433:5:433:17 | access to array | semmle.label | access to array | -| test.cpp:433:5:433:21 | Store: ... = ... | semmle.label | Store: ... = ... | +| test.cpp:392:3:392:4 | xs | semmle.label | xs | +| test.cpp:399:14:399:27 | new[] | semmle.label | new[] | +| test.cpp:400:16:400:17 | xs | semmle.label | xs | +| test.cpp:402:5:402:6 | xs | semmle.label | xs | +| test.cpp:410:14:410:27 | new[] | semmle.label | new[] | +| test.cpp:411:15:411:23 | & ... | semmle.label | & ... | +| test.cpp:411:15:411:23 | & ... | semmle.label | & ... | +| test.cpp:411:15:411:23 | & ... | semmle.label | & ... | +| test.cpp:411:15:411:23 | & ... | semmle.label | & ... | +| test.cpp:411:16:411:17 | xs | semmle.label | xs | +| test.cpp:411:16:411:23 | access to array | semmle.label | access to array | +| test.cpp:411:16:411:23 | access to array | semmle.label | access to array | +| test.cpp:412:12:412:14 | end | semmle.label | end | +| test.cpp:412:12:412:14 | end | semmle.label | end | +| test.cpp:413:5:413:6 | xs | semmle.label | xs | +| test.cpp:413:5:413:8 | ... ++ | semmle.label | ... ++ | +| test.cpp:413:5:413:8 | ... ++ | semmle.label | ... ++ | +| test.cpp:413:5:413:8 | ... ++ | semmle.label | ... ++ | +| test.cpp:413:5:413:8 | ... ++ | semmle.label | ... ++ | +| test.cpp:414:9:414:10 | xs | semmle.label | xs | +| test.cpp:414:14:414:16 | end | semmle.label | end | +| test.cpp:415:7:415:11 | access to array | semmle.label | access to array | +| test.cpp:415:7:415:15 | Store: ... = ... | semmle.label | Store: ... = ... | +| test.cpp:421:14:421:27 | new[] | semmle.label | new[] | +| test.cpp:422:15:422:23 | & ... | semmle.label | & ... | +| test.cpp:422:15:422:23 | & ... | semmle.label | & ... | +| test.cpp:422:15:422:23 | & ... | semmle.label | & ... | +| test.cpp:422:15:422:23 | & ... | semmle.label | & ... | +| test.cpp:422:16:422:17 | xs | semmle.label | xs | +| test.cpp:422:16:422:23 | access to array | semmle.label | access to array | +| test.cpp:422:16:422:23 | access to array | semmle.label | access to array | +| test.cpp:423:12:423:14 | end | semmle.label | end | +| test.cpp:423:12:423:14 | end | semmle.label | end | +| test.cpp:424:5:424:6 | xs | semmle.label | xs | +| test.cpp:424:5:424:8 | ... ++ | semmle.label | ... ++ | +| test.cpp:424:5:424:8 | ... ++ | semmle.label | ... ++ | +| test.cpp:424:5:424:8 | ... ++ | semmle.label | ... ++ | +| test.cpp:424:5:424:8 | ... ++ | semmle.label | ... ++ | +| test.cpp:425:9:425:10 | xs | semmle.label | xs | +| test.cpp:425:9:425:10 | xs | semmle.label | xs | +| test.cpp:425:18:425:20 | end | semmle.label | end | +| test.cpp:426:7:426:8 | xs | semmle.label | xs | +| test.cpp:426:7:426:11 | access to array | semmle.label | access to array | +| test.cpp:426:7:426:15 | Store: ... = ... | semmle.label | Store: ... = ... | +| test.cpp:432:14:432:27 | new[] | semmle.label | new[] | +| test.cpp:433:15:433:23 | & ... | semmle.label | & ... | +| test.cpp:433:15:433:23 | & ... | semmle.label | & ... | +| test.cpp:433:15:433:23 | & ... | semmle.label | & ... | +| test.cpp:433:15:433:23 | & ... | semmle.label | & ... | +| test.cpp:433:16:433:17 | xs | semmle.label | xs | +| test.cpp:433:16:433:23 | access to array | semmle.label | access to array | +| test.cpp:433:16:433:23 | access to array | semmle.label | access to array | +| test.cpp:434:12:434:14 | end | semmle.label | end | +| test.cpp:434:12:434:14 | end | semmle.label | end | +| test.cpp:435:5:435:7 | end | semmle.label | end | +| test.cpp:436:5:436:6 | xs | semmle.label | xs | +| test.cpp:436:5:436:8 | ... ++ | semmle.label | ... ++ | +| test.cpp:436:5:436:8 | ... ++ | semmle.label | ... ++ | +| test.cpp:436:5:436:8 | ... ++ | semmle.label | ... ++ | +| test.cpp:436:5:436:8 | ... ++ | semmle.label | ... ++ | +| test.cpp:437:9:437:10 | xs | semmle.label | xs | +| test.cpp:438:7:438:11 | access to array | semmle.label | access to array | +| test.cpp:438:7:438:15 | Store: ... = ... | semmle.label | Store: ... = ... | +| test.cpp:444:14:444:27 | new[] | semmle.label | new[] | +| test.cpp:445:15:445:23 | & ... | semmle.label | & ... | +| test.cpp:445:15:445:23 | & ... | semmle.label | & ... | +| test.cpp:445:15:445:23 | & ... | semmle.label | & ... | +| test.cpp:445:15:445:23 | & ... | semmle.label | & ... | +| test.cpp:445:16:445:17 | xs | semmle.label | xs | +| test.cpp:445:16:445:23 | access to array | semmle.label | access to array | +| test.cpp:445:16:445:23 | access to array | semmle.label | access to array | +| test.cpp:446:3:446:5 | end | semmle.label | end | +| test.cpp:448:5:448:6 | xs | semmle.label | xs | +| test.cpp:448:5:448:8 | ... ++ | semmle.label | ... ++ | +| test.cpp:448:5:448:8 | ... ++ | semmle.label | ... ++ | +| test.cpp:448:5:448:8 | ... ++ | semmle.label | ... ++ | +| test.cpp:448:5:448:8 | ... ++ | semmle.label | ... ++ | +| test.cpp:449:9:449:10 | xs | semmle.label | xs | +| test.cpp:450:7:450:11 | access to array | semmle.label | access to array | +| test.cpp:450:7:450:15 | Store: ... = ... | semmle.label | Store: ... = ... | +| test.cpp:456:14:456:31 | new[] | semmle.label | new[] | +| test.cpp:457:16:457:17 | xs | semmle.label | xs | +| test.cpp:460:5:460:6 | xs | semmle.label | xs | +| test.cpp:468:14:468:27 | new[] | semmle.label | new[] | +| test.cpp:469:16:469:17 | xs | semmle.label | xs | +| test.cpp:472:5:472:6 | xs | semmle.label | xs | +| test.cpp:480:14:480:27 | new[] | semmle.label | new[] | +| test.cpp:481:15:481:23 | & ... | semmle.label | & ... | +| test.cpp:481:15:481:23 | & ... | semmle.label | & ... | +| test.cpp:481:15:481:23 | & ... | semmle.label | & ... | +| test.cpp:481:15:481:23 | & ... | semmle.label | & ... | +| test.cpp:481:16:481:17 | xs | semmle.label | xs | +| test.cpp:481:16:481:23 | access to array | semmle.label | access to array | +| test.cpp:481:16:481:23 | access to array | semmle.label | access to array | +| test.cpp:482:3:482:5 | end | semmle.label | end | +| test.cpp:484:5:484:6 | xs | semmle.label | xs | +| test.cpp:484:5:484:8 | ... ++ | semmle.label | ... ++ | +| test.cpp:484:5:484:8 | ... ++ | semmle.label | ... ++ | +| test.cpp:484:5:484:8 | ... ++ | semmle.label | ... ++ | +| test.cpp:484:5:484:8 | ... ++ | semmle.label | ... ++ | +| test.cpp:485:9:485:10 | xs | semmle.label | xs | +| test.cpp:486:7:486:11 | access to array | semmle.label | access to array | +| test.cpp:486:7:486:15 | Store: ... = ... | semmle.label | Store: ... = ... | +| test.cpp:499:3:499:25 | ... = ... | semmle.label | ... = ... | +| test.cpp:499:7:499:8 | val indirection [post update] [xs] | semmle.label | val indirection [post update] [xs] | +| test.cpp:499:12:499:25 | new[] | semmle.label | new[] | +| test.cpp:500:3:500:5 | val indirection [xs] | semmle.label | val indirection [xs] | +| test.cpp:500:7:500:8 | xs | semmle.label | xs | +| test.cpp:500:7:500:8 | xs indirection | semmle.label | xs indirection | +| test.cpp:510:16:510:33 | new[] | semmle.label | new[] | +| test.cpp:512:7:512:8 | xs | semmle.label | xs | +| test.cpp:520:14:520:27 | new[] | semmle.label | new[] | +| test.cpp:526:5:526:6 | xs | semmle.label | xs | +| test.cpp:532:14:532:27 | new[] | semmle.label | new[] | +| test.cpp:537:5:537:6 | xs | semmle.label | xs | +| test.cpp:543:14:543:27 | new[] | semmle.label | new[] | +| test.cpp:548:5:548:6 | xs | semmle.label | xs | +| test.cpp:548:5:548:15 | access to array | semmle.label | access to array | +| test.cpp:548:5:548:19 | Store: ... = ... | semmle.label | Store: ... = ... | +| test.cpp:554:14:554:27 | new[] | semmle.label | new[] | +| test.cpp:559:5:559:6 | xs | semmle.label | xs | +| test.cpp:559:5:559:15 | access to array | semmle.label | access to array | +| test.cpp:559:5:559:19 | Store: ... = ... | semmle.label | Store: ... = ... | +| test.cpp:565:14:565:27 | new[] | semmle.label | new[] | +| test.cpp:570:5:570:6 | xs | semmle.label | xs | +| test.cpp:576:14:576:27 | new[] | semmle.label | new[] | +| test.cpp:581:5:581:6 | xs | semmle.label | xs | +| test.cpp:587:14:587:31 | new[] | semmle.label | new[] | +| test.cpp:592:5:592:6 | xs | semmle.label | xs | +| test.cpp:598:14:598:31 | new[] | semmle.label | new[] | +| test.cpp:603:5:603:6 | xs | semmle.label | xs | +| test.cpp:609:14:609:31 | new[] | semmle.label | new[] | +| test.cpp:614:5:614:6 | xs | semmle.label | xs | +| test.cpp:620:14:620:31 | new[] | semmle.label | new[] | +| test.cpp:625:5:625:6 | xs | semmle.label | xs | +| test.cpp:631:14:631:31 | new[] | semmle.label | new[] | +| test.cpp:636:5:636:6 | xs | semmle.label | xs | +| test.cpp:642:14:642:31 | new[] | semmle.label | new[] | +| test.cpp:647:5:647:6 | xs | semmle.label | xs | +| test.cpp:647:5:647:15 | access to array | semmle.label | access to array | +| test.cpp:647:5:647:19 | Store: ... = ... | semmle.label | Store: ... = ... | subpaths #select | test.cpp:6:14:6:15 | Load: * ... | test.cpp:4:15:4:20 | call to malloc | test.cpp:6:14:6:15 | Load: * ... | This read might be out of bounds, as the pointer might be equal to $@ + $@. | test.cpp:4:15:4:20 | call to malloc | call to malloc | test.cpp:5:19:5:22 | size | size | @@ -1181,9 +1526,12 @@ subpaths | test.cpp:308:5:308:29 | Store: ... = ... | test.cpp:304:15:304:26 | new[] | test.cpp:308:5:308:29 | Store: ... = ... | This write might be out of bounds, as the pointer might be equal to $@ + $@. | test.cpp:304:15:304:26 | new[] | new[] | test.cpp:308:8:308:10 | ... + ... | ... + ... | | test.cpp:358:14:358:26 | Load: * ... | test.cpp:355:14:355:27 | new[] | test.cpp:358:14:358:26 | Load: * ... | This read might be out of bounds, as the pointer might be equal to $@ + $@ + 1. | test.cpp:355:14:355:27 | new[] | new[] | test.cpp:356:20:356:23 | size | size | | test.cpp:359:14:359:32 | Load: * ... | test.cpp:355:14:355:27 | new[] | test.cpp:359:14:359:32 | Load: * ... | This read might be out of bounds, as the pointer might be equal to $@ + $@ + 2. | test.cpp:355:14:355:27 | new[] | new[] | test.cpp:356:20:356:23 | size | size | -| test.cpp:372:15:372:16 | Load: * ... | test.cpp:363:14:363:27 | new[] | test.cpp:372:15:372:16 | Load: * ... | This read might be out of bounds, as the pointer might be equal to $@ + $@. | test.cpp:363:14:363:27 | new[] | new[] | test.cpp:365:19:365:22 | size | size | | test.cpp:384:13:384:16 | Load: * ... | test.cpp:377:14:377:27 | new[] | test.cpp:384:13:384:16 | Load: * ... | This read might be out of bounds, as the pointer might be equal to $@ + $@. | test.cpp:377:14:377:27 | new[] | new[] | test.cpp:378:20:378:23 | size | size | -| test.cpp:395:5:395:13 | Store: ... = ... | test.cpp:388:14:388:27 | new[] | test.cpp:395:5:395:13 | Store: ... = ... | This write might be out of bounds, as the pointer might be equal to $@ + $@. | test.cpp:388:14:388:27 | new[] | new[] | test.cpp:389:19:389:22 | size | size | -| test.cpp:407:3:407:22 | Store: ... = ... | test.cpp:404:12:404:25 | new[] | test.cpp:407:3:407:22 | Store: ... = ... | This write might be out of bounds, as the pointer might be equal to $@ + $@. | test.cpp:404:12:404:25 | new[] | new[] | test.cpp:407:10:407:17 | ... - ... | ... - ... | -| test.cpp:419:7:419:15 | Store: ... = ... | test.cpp:417:16:417:33 | new[] | test.cpp:419:7:419:15 | Store: ... = ... | This write might be out of bounds, as the pointer might be equal to $@ + $@. | test.cpp:417:16:417:33 | new[] | new[] | test.cpp:419:10:419:10 | i | i | -| test.cpp:433:5:433:21 | Store: ... = ... | test.cpp:427:14:427:27 | new[] | test.cpp:433:5:433:21 | Store: ... = ... | This write might be out of bounds, as the pointer might be equal to $@ + $@. | test.cpp:427:14:427:27 | new[] | new[] | test.cpp:433:8:433:16 | ... ++ | ... ++ | +| test.cpp:415:7:415:15 | Store: ... = ... | test.cpp:410:14:410:27 | new[] | test.cpp:415:7:415:15 | Store: ... = ... | This write might be out of bounds, as the pointer might be equal to $@ + $@. | test.cpp:410:14:410:27 | new[] | new[] | test.cpp:411:19:411:22 | size | size | +| test.cpp:426:7:426:15 | Store: ... = ... | test.cpp:421:14:421:27 | new[] | test.cpp:426:7:426:15 | Store: ... = ... | This write might be out of bounds, as the pointer might be equal to $@ + $@. | test.cpp:421:14:421:27 | new[] | new[] | test.cpp:422:19:422:22 | size | size | +| test.cpp:438:7:438:15 | Store: ... = ... | test.cpp:432:14:432:27 | new[] | test.cpp:438:7:438:15 | Store: ... = ... | This write might be out of bounds, as the pointer might be equal to $@ + $@. | test.cpp:432:14:432:27 | new[] | new[] | test.cpp:433:19:433:22 | size | size | +| test.cpp:450:7:450:15 | Store: ... = ... | test.cpp:444:14:444:27 | new[] | test.cpp:450:7:450:15 | Store: ... = ... | This write might be out of bounds, as the pointer might be equal to $@ + $@. | test.cpp:444:14:444:27 | new[] | new[] | test.cpp:445:19:445:22 | size | size | +| test.cpp:486:7:486:15 | Store: ... = ... | test.cpp:480:14:480:27 | new[] | test.cpp:486:7:486:15 | Store: ... = ... | This write might be out of bounds, as the pointer might be equal to $@ + $@ + 498. | test.cpp:480:14:480:27 | new[] | new[] | test.cpp:481:19:481:22 | size | size | +| test.cpp:548:5:548:19 | Store: ... = ... | test.cpp:543:14:543:27 | new[] | test.cpp:548:5:548:19 | Store: ... = ... | This write might be out of bounds, as the pointer might be equal to $@ + $@. | test.cpp:543:14:543:27 | new[] | new[] | test.cpp:548:8:548:14 | src_pos | src_pos | +| test.cpp:559:5:559:19 | Store: ... = ... | test.cpp:554:14:554:27 | new[] | test.cpp:559:5:559:19 | Store: ... = ... | This write might be out of bounds, as the pointer might be equal to $@ + $@. | test.cpp:554:14:554:27 | new[] | new[] | test.cpp:559:8:559:14 | src_pos | src_pos | +| test.cpp:647:5:647:19 | Store: ... = ... | test.cpp:642:14:642:31 | new[] | test.cpp:647:5:647:19 | Store: ... = ... | This write might be out of bounds, as the pointer might be equal to $@ + $@. | test.cpp:642:14:642:31 | new[] | new[] | test.cpp:647:8:647:14 | src_pos | src_pos | diff --git a/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-193/pointer-deref/test.cpp b/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-193/pointer-deref/test.cpp index 4048c15be8b..b10a2775fb2 100644 --- a/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-193/pointer-deref/test.cpp +++ b/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-193/pointer-deref/test.cpp @@ -369,7 +369,7 @@ void test26(unsigned size) { } if (p < end) { - int val = *p; // GOOD [FALSE POSITIVE] + int val = *p; // GOOD } } @@ -387,12 +387,105 @@ void test27(unsigned size, bool b) { void test28(unsigned size) { char *xs = new char[size]; char *end = &xs[size]; - if (xs >= end) - return; + if (xs >= end) + return; + xs++; + if (xs >= end) + return; + xs[0] = 0; // GOOD +} + +void test28_simple(unsigned size) { + char *xs = new char[size]; + char *end = &xs[size]; + if (xs < end) { xs++; - if (xs >= end) - return; - xs[0] = 0; // GOOD [FALSE POSITIVE] + if (xs < end) { + xs[0] = 0; // GOOD + } + } +} + +void test28_simple2(unsigned size) { + char *xs = new char[size]; + char *end = &xs[size]; + if (xs < end) { + xs++; + if (xs < end + 1) { + xs[0] = 0; // BAD + } + } +} + +void test28_simple3(unsigned size) { + char *xs = new char[size]; + char *end = &xs[size]; + if (xs < end) { + xs++; + if (xs - 1 < end) { + xs[0] = 0; // BAD + } + } +} + +void test28_simple4(unsigned size) { + char *xs = new char[size]; + char *end = &xs[size]; + if (xs < end) { + end++; + xs++; + if (xs < end) { + xs[0] = 0; // BAD + } + } +} + +void test28_simple5(unsigned size) { + char *xs = new char[size]; + char *end = &xs[size]; + end++; + if (xs < end) { + xs++; + if (xs < end) { + xs[0] = 0; // BAD + } + } +} + +void test28_simple6(unsigned size) { + char *xs = new char[size + 1]; + char *end = &xs[size]; + end++; + if (xs < end) { + xs++; + if (xs < end) { + xs[0] = 0; // GOOD + } + } +} + +void test28_simple7(unsigned size) { + char *xs = new char[size]; + char *end = &xs[size]; + end++; + if (xs < end) { + xs++; + if (xs < end - 1) { + xs[0] = 0; // GOOD + } + } +} + +void test28_simple8(unsigned size) { + char *xs = new char[size]; + char *end = &xs[size]; + end += 500; + if (xs < end) { + xs++; + if (xs < end - 1) { + xs[0] = 0; // BAD + } + } } struct test29_struct { @@ -404,7 +497,7 @@ void test29(unsigned size) { val.xs = new char[size]; size++; val.xs = new char[size]; - val.xs[size - 1] = 0; // GOOD [FALSE POSITIVE] + val.xs[size - 1] = 0; // GOOD } void test30(int *size) @@ -416,7 +509,7 @@ void test30(int *size) new_size = tmp_size + 1; char *xs = new char[new_size]; for (int i = 0; i < new_size; i++) { - xs[i] = 0; // GOOD [FALSE POSITIVE] + xs[i] = 0; // GOOD } } *size = new_size; @@ -429,7 +522,128 @@ void test31(unsigned size, unsigned src_pos) src_pos = size; } unsigned dst_pos = src_pos; - if(dst_pos < size - 3) { - xs[dst_pos++] = 0; // GOOD [FALSE POSITIVE] + if (dst_pos < size - 3) { + xs[dst_pos++] = 0; // GOOD + } +} + +void test31_simple1(unsigned size, unsigned src_pos) +{ + char *xs = new char[size]; + if (src_pos > size) { + src_pos = size; + } + if (src_pos < size) { + xs[src_pos] = 0; // GOOD + } +} + +void test31_simple2(unsigned size, unsigned src_pos) +{ + char *xs = new char[size]; + if (src_pos > size) { + src_pos = size; + } + if (src_pos < size + 1) { + xs[src_pos] = 0; // BAD + } +} + +void test31_simple3(unsigned size, unsigned src_pos) +{ + char *xs = new char[size]; + if (src_pos > size) { + src_pos = size; + } + if (src_pos - 1 < size) { + xs[src_pos] = 0; // BAD + } +} + +void test31_simple4(unsigned size, unsigned src_pos) +{ + char *xs = new char[size]; + if (src_pos > size) { + src_pos = size; + } + if (src_pos < size - 1) { + xs[src_pos] = 0; // GOOD + } +} + +void test31_simple5(unsigned size, unsigned src_pos) +{ + char *xs = new char[size]; + if (src_pos > size) { + src_pos = size; + } + if (src_pos + 1 < size) { + xs[src_pos] = 0; // GOOD + } +} + +void test31_simple1_plus1(unsigned size, unsigned src_pos) +{ + char *xs = new char[size + 1]; + if (src_pos > size) { + src_pos = size; + } + if (src_pos < size) { + xs[src_pos] = 0; // GOOD + } +} + +void test31_simple2_plus1(unsigned size, unsigned src_pos) +{ + char *xs = new char[size + 1]; + if (src_pos > size) { + src_pos = size; + } + if (src_pos < size + 1) { + xs[src_pos] = 0; // GOOD + } +} + +void test31_simple3_plus1(unsigned size, unsigned src_pos) +{ + char *xs = new char[size + 1]; + if (src_pos > size) { + src_pos = size; + } + if (src_pos - 1 < size) { + xs[src_pos] = 0; // GOOD + } +} + +void test31_simple4_plus1(unsigned size, unsigned src_pos) +{ + char *xs = new char[size + 1]; + if (src_pos > size) { + src_pos = size; + } + if (src_pos < size - 1) { + xs[src_pos] = 0; // GOOD + } +} + +void test31_simple5_plus1(unsigned size, unsigned src_pos) +{ + char *xs = new char[size + 1]; + if (src_pos > size) { + src_pos = size; + } + if (src_pos + 1 < size) { + xs[src_pos] = 0; // GOOD + } +} + +void test31_simple1_sub1(unsigned size, unsigned src_pos) +{ + char *xs = new char[size - 1]; + if (src_pos > size) { + src_pos = size; + } + if (src_pos < size) { + xs[src_pos] = 0; // BAD } } 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 acf233ed2ee..eb9e8efb1d2 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 @@ -67,6 +67,8 @@ postWithInFlow | ref.cpp:109:9:109:11 | val [post update] | PostUpdateNode should not be the target of local flow. | | ref.cpp:113:11:113:13 | val [post update] | PostUpdateNode should not be the target of local flow. | | ref.cpp:115:11:115:13 | val [post update] | PostUpdateNode should not be the target of local flow. | +| self_parameter_flow.cpp:3:4:3:5 | ps [inner post update] | PostUpdateNode should not be the target of local flow. | +| self_parameter_flow.cpp:8:9:8:9 | s [inner post update] | PostUpdateNode should not be the target of local flow. | | test.cpp:91:3:91:9 | source1 [post update] | PostUpdateNode should not be the target of local flow. | | test.cpp:115:3:115:6 | * ... [post update] | PostUpdateNode should not be the target of local flow. | | test.cpp:115:4:115:6 | out [inner post update] | PostUpdateNode should not be the target of local flow. | @@ -128,6 +130,10 @@ postWithInFlow | test.cpp:690:3:690:3 | s [post update] | PostUpdateNode should not be the target of local flow. | | test.cpp:694:4:694:6 | buf [inner post update] | PostUpdateNode should not be the target of local flow. | | test.cpp:704:23:704:25 | buf [inner post update] | PostUpdateNode should not be the target of local flow. | +| test.cpp:715:25:715:25 | c [inner post update] | PostUpdateNode should not be the target of local flow. | +| test.cpp:728:3:728:4 | * ... [post update] | PostUpdateNode should not be the target of local flow. | +| test.cpp:728:4:728:4 | p [inner post update] | PostUpdateNode should not be the target of local flow. | +| test.cpp:734:41:734:41 | x [inner post update] | PostUpdateNode should not be the target of local flow. | viableImplInCallContextTooLarge uniqueParameterNodeAtPosition uniqueParameterNodePosition diff --git a/cpp/ql/test/library-tests/dataflow/dataflow-tests/self-Iterator.cpp b/cpp/ql/test/library-tests/dataflow/dataflow-tests/self-Iterator.cpp new file mode 100644 index 00000000000..cac7f222c30 --- /dev/null +++ b/cpp/ql/test/library-tests/dataflow/dataflow-tests/self-Iterator.cpp @@ -0,0 +1,21 @@ +#include "../../../include/iterator.h" +int source(); + +template +void sink(T); + +template<> struct std::iterator_traits +{ // get traits from integer type + typedef std::input_iterator_tag iterator_category; + typedef unsigned long value_type; + typedef unsigned long difference_type; + typedef unsigned long distance_type; + typedef unsigned long * pointer; + typedef unsigned long& reference; +}; + + +int test() { + unsigned long x = source(); + sink(x); // $ ast ir +} \ No newline at end of file diff --git a/cpp/ql/test/library-tests/dataflow/dataflow-tests/self_parameter_flow.cpp b/cpp/ql/test/library-tests/dataflow/dataflow-tests/self_parameter_flow.cpp new file mode 100644 index 00000000000..2298e644b05 --- /dev/null +++ b/cpp/ql/test/library-tests/dataflow/dataflow-tests/self_parameter_flow.cpp @@ -0,0 +1,14 @@ +void incr(unsigned char **ps) // $ ast-def=ps ir-def=*ps ir-def=**ps +{ + *ps += 1; +} + +void callincr(unsigned char *s) // $ ast-def=s +{ + incr(&s); +} + +void test(unsigned char *s) // $ ast-def=s +{ + callincr(s); // $ flow +} \ No newline at end of file diff --git a/cpp/ql/test/library-tests/dataflow/dataflow-tests/test.cpp b/cpp/ql/test/library-tests/dataflow/dataflow-tests/test.cpp index 915a8421475..7b6ea0fa718 100644 --- a/cpp/ql/test/library-tests/dataflow/dataflow-tests/test.cpp +++ b/cpp/ql/test/library-tests/dataflow/dataflow-tests/test.cpp @@ -702,4 +702,35 @@ void call_increment_buf(int** buf) { // $ ast-def=buf void test_conflation_regression(int* source) { // $ ast-def=source int* buf = source; call_increment_buf(&buf); -} \ No newline at end of file +} + +void write_to_star_star_p(unsigned char **p) // $ ast-def=p ir-def=**p ir-def=*p +{ + **p = 0; +} + +void write_to_star_buf(unsigned char *buf) // $ ast-def=buf +{ + unsigned char *c = buf; + write_to_star_star_p(&c); +} + +void test_write_to_star_buf(unsigned char *source) // $ ast-def=source +{ + write_to_star_buf(source); + sink(*source); // clean +} + +void does_not_write_source_to_dereference(int *p) // $ ast-def=p ir-def=*p +{ + int x = source(); + p = &x; + *p = 42; +} + +void test_does_not_write_source_to_dereference() +{ + int x; + does_not_write_source_to_dereference(&x); + sink(x); // $ ast,ir=733:7 SPURIOUS: ast,ir=726:11 +} diff --git a/ruby/ql/test/library-tests/dataflow/api-graphs/use.expected b/cpp/ql/test/library-tests/dataflow/dataflow-tests/test_self_parameter_flow.expected similarity index 100% rename from ruby/ql/test/library-tests/dataflow/api-graphs/use.expected rename to cpp/ql/test/library-tests/dataflow/dataflow-tests/test_self_parameter_flow.expected diff --git a/cpp/ql/test/library-tests/dataflow/dataflow-tests/test_self_parameter_flow.ql b/cpp/ql/test/library-tests/dataflow/dataflow-tests/test_self_parameter_flow.ql new file mode 100644 index 00000000000..c6ea9c5c96f --- /dev/null +++ b/cpp/ql/test/library-tests/dataflow/dataflow-tests/test_self_parameter_flow.ql @@ -0,0 +1,34 @@ +import cpp +import semmle.code.cpp.dataflow.new.DataFlow +import TestUtilities.InlineExpectationsTest + +module TestConfig implements DataFlow::ConfigSig { + predicate isSource(DataFlow::Node source) { + source.getLocation().getFile().getBaseName() = "self_parameter_flow.cpp" and + source.asIndirectArgument() = + any(Call call | call.getTarget().hasName("callincr")).getAnArgument() + } + + predicate isSink(DataFlow::Node sink) { + sink.asDefiningArgument() = + any(Call call | call.getTarget().hasName("callincr")).getAnArgument() + } +} + +import DataFlow::Global + +module TestSelfParameterFlow implements TestSig { + string getARelevantTag() { result = "flow" } + + predicate hasActualResult(Location location, string element, string tag, string value) { + exists(DataFlow::Node sink | + flowTo(sink) and + location = sink.getLocation() and + element = sink.toString() and + tag = "flow" and + value = "" + ) + } +} + +import MakeTest diff --git a/cpp/ql/test/library-tests/dataflow/dataflow-tests/uninitialized.expected b/cpp/ql/test/library-tests/dataflow/dataflow-tests/uninitialized.expected index 0a52d928028..b40148f4950 100644 --- a/cpp/ql/test/library-tests/dataflow/dataflow-tests/uninitialized.expected +++ b/cpp/ql/test/library-tests/dataflow/dataflow-tests/uninitialized.expected @@ -42,3 +42,5 @@ | test.cpp:551:9:551:9 | y | test.cpp:552:28:552:28 | y | | test.cpp:595:8:595:9 | xs | test.cpp:596:3:596:4 | xs | | test.cpp:595:8:595:9 | xs | test.cpp:597:9:597:10 | xs | +| test.cpp:733:7:733:7 | x | test.cpp:734:41:734:41 | x | +| test.cpp:733:7:733:7 | x | test.cpp:735:8:735:8 | x | diff --git a/cpp/ql/test/library-tests/dataflow/fields/clearning.cpp b/cpp/ql/test/library-tests/dataflow/fields/clearning.cpp new file mode 100644 index 00000000000..0347c6725d2 --- /dev/null +++ b/cpp/ql/test/library-tests/dataflow/fields/clearning.cpp @@ -0,0 +1,182 @@ +// We want a source of user input that can be both a pointer and a non-pointer. So we +// hack the testing a bit by providing an overload that takes a boolean to distinguish +// between the two while still satisfying the test requirement that the function must +// be named `user_input`. +int user_input(); +int* user_input(bool); +void sink(...); +void argument_source(int*); + +struct S { + int** x; +}; + +void test() +{ + { + S s; + **s.x = user_input(); + *s.x = 0; + sink(**s.x); // clean, as *s.x was overwritten and that contains the tainted **s.x + } + + { + S s; + **s.x = user_input(); + **s.x = 0; + sink(**s.x); // clean, as **s.x was overwritten and tainted + } + + { + S s; + *s.x = user_input(true); + **s.x = 0; + sink(*s.x); // $ ir // not clean, as **s.x was overwritten and is neither equal nor contains the tainted *s.x + } + + { + S s; + *s.x = user_input(true); + s.x = 0; + sink(*s.x); // clean, as s.x was overwritten and contains the tainted *s.x + } + + { + S s; + **s.x = user_input(); + s.x = 0; + sink(*s.x); // clean, as s.x was overwritten and contains the tainted **s.x + } + + { + S s; + *s.x = user_input(true); + s.x++; + sink(s.x); // $ SPURIOUS: ir ast // Cannot tell the difference with the whole array being tainted + } + + { + S s; + **s.x = user_input(); + s.x++; + sink(s.x); // $ SPURIOUS: ir // Cannot tell the difference with the whole array being tainted + } +} + +struct S2 +{ + int* val; +}; + +void test_uncertain_write_is_not_clear() +{ + S2 s; + argument_source(s.val); + s.val[10] = 0; + sink(*s.val); // $ ir MISSING: ast // not clean, as all elements of s.val are tainted and only one is overwitten +} + +void test_indirection_should_not_be_cleared_with_write_1() { + S2 s; + argument_source(s.val); // *s.val is tainted + s.val[0] = 0; + s.val = s.val + 1; + sink(*s.val); // $ ir MISSING: ast // not clean, as all elements of s.val are tainted, only one if overwritten, and the updated pointer still points to tainted elements +} + +void test_indirection_should_not_be_cleared_with_write_2() { + S2 s; + argument_source(s.val); // *s.val is tainted + *s.val++ = 0; + sink(*s.val); // $ ir MISSING: ast // not clean, as all elements of s.val are tainted, only one if overwritten, and the updated pointer still points to tainted elements +} + +void test_indirection_should_not_be_cleared_without_write_1() { + S2 s; + argument_source(s.val); // *s.val is tainted + s.val = s.val + 1; + sink(*s.val); // $ ir MISSING: ast // not clean, as all elements of s.val are tainted and the updated pointer still points to tainted elements +} + +void test_indirection_should_not_be_cleared_without_write_2() { + S2 s; + argument_source(s.val); // *s.val is tainted + s.val++; + sink(*s.val); // $ ir MISSING: ast // not clean, as all elements of s.val are tainted and the updated pointer still points to tainted elements +} + +void test_indirection_should_not_be_cleared_without_write_3() { + S2 s; + argument_source(s.val); // *s.val is tainted + ++s.val; + sink(*s.val); // $ ir MISSING: ast // not clean as the pointer is only moved to the next tainted element +} + +void test_indirection_should_not_be_cleared_without_write_4() { + S2 s; + argument_source(s.val); // *s.val is tainted + s.val += 1; + sink(*s.val); // $ ir MISSING: ast // not clean as the pointer is only moved to the next tainted element +} + +void test_direct_should_be_cleared() { + S2 s; + s.val = user_input(true); // s.val is tainted + s.val += 1; + sink(s.val); // $ SPURIOUS: ast // clean, as s.val was overwritten and tainted +} + +void test_direct_should_be_cleared_post() { + S2 s; + s.val = user_input(true); // s.val is tainted + s.val++; + sink(s.val); // $ SPURIOUS: ast // clean, as s.val was overwritten and tainted +} + +void test_direct_should_be_cleared_pre() { + S2 s; + s.val = user_input(true); // s.val is tainted + ++s.val; + sink(s.val); // $ SPURIOUS: ast // // clean, as s.x was overwritten and tainted +} + +struct S3 +{ + int val; +}; + +void test_direct() { + { + S3 s; + s.val = user_input(); + sink(s.val); // $ ir ast + } + + { + S3 s; + s.val = user_input(); + s.val = 0; + sink(s.val); // $ SPURIOUS: ast // clean + } + + { + S3 s; + s.val = user_input(); + s.val++; + sink(s.val); // $ SPURIOUS: ast // clean + } + + { + S3 s; + s.val = user_input(); + s.val += 1; + sink(s.val); // $ SPURIOUS: ast // clean + } + + { + S3 s; + s.val = user_input(); + s.val = s.val + 1; + sink(s.val); // $ SPURIOUS: ast // clean + } +} 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 71c84a0446d..e8f54d3d7ba 100644 --- a/cpp/ql/test/library-tests/dataflow/fields/dataflow-consistency.expected +++ b/cpp/ql/test/library-tests/dataflow/fields/dataflow-consistency.expected @@ -43,6 +43,9 @@ argHasPostUpdate | arrays.cpp:10:8:10:15 | * ... | ArgumentNode is missing PostUpdateNode. | | arrays.cpp:16:8:16:13 | access to array | ArgumentNode is missing PostUpdateNode. | | arrays.cpp:17:8:17:13 | access to array | ArgumentNode is missing PostUpdateNode. | +| clearning.cpp:34:8:34:11 | * ... | ArgumentNode is missing PostUpdateNode. | +| clearning.cpp:41:8:41:11 | * ... | ArgumentNode is missing PostUpdateNode. | +| clearning.cpp:48:8:48:11 | * ... | ArgumentNode is missing PostUpdateNode. | postWithInFlow | A.cpp:25:13:25:13 | c [post update] | PostUpdateNode should not be the target of local flow. | | A.cpp:27:28:27:28 | c [post update] | PostUpdateNode should not be the target of local flow. | @@ -123,6 +126,32 @@ postWithInFlow | by_reference.cpp:108:24:108:24 | a [inner post update] | PostUpdateNode should not be the target of local flow. | | by_reference.cpp:123:28:123:36 | inner_ptr [inner post update] | PostUpdateNode should not be the target of local flow. | | by_reference.cpp:127:30:127:38 | inner_ptr [inner post update] | PostUpdateNode should not be the target of local flow. | +| clearning.cpp:19:3:19:6 | * ... [post update] | PostUpdateNode should not be the target of local flow. | +| clearning.cpp:19:6:19:6 | x [inner post update] | PostUpdateNode should not be the target of local flow. | +| clearning.cpp:32:3:32:6 | * ... [post update] | PostUpdateNode should not be the target of local flow. | +| clearning.cpp:32:6:32:6 | x [inner post update] | PostUpdateNode should not be the target of local flow. | +| clearning.cpp:39:3:39:6 | * ... [post update] | PostUpdateNode should not be the target of local flow. | +| clearning.cpp:39:6:39:6 | x [inner post update] | PostUpdateNode should not be the target of local flow. | +| clearning.cpp:40:5:40:5 | x [post update] | PostUpdateNode should not be the target of local flow. | +| clearning.cpp:47:5:47:5 | x [post update] | PostUpdateNode should not be the target of local flow. | +| clearning.cpp:53:3:53:6 | * ... [post update] | PostUpdateNode should not be the target of local flow. | +| clearning.cpp:53:6:53:6 | x [inner post update] | PostUpdateNode should not be the target of local flow. | +| clearning.cpp:75:2:75:10 | access to array [post update] | PostUpdateNode should not be the target of local flow. | +| clearning.cpp:75:4:75:6 | val [inner post update] | PostUpdateNode should not be the target of local flow. | +| clearning.cpp:82:2:82:9 | access to array [post update] | PostUpdateNode should not be the target of local flow. | +| clearning.cpp:82:4:82:6 | val [inner post update] | PostUpdateNode should not be the target of local flow. | +| clearning.cpp:83:7:83:9 | val [post update] | PostUpdateNode should not be the target of local flow. | +| clearning.cpp:97:4:97:6 | val [post update] | PostUpdateNode should not be the target of local flow. | +| clearning.cpp:124:4:124:6 | val [post update] | PostUpdateNode should not be the target of local flow. | +| clearning.cpp:131:4:131:6 | val [post update] | PostUpdateNode should not be the target of local flow. | +| clearning.cpp:138:4:138:6 | val [post update] | PostUpdateNode should not be the target of local flow. | +| clearning.cpp:151:5:151:7 | val [post update] | PostUpdateNode should not be the target of local flow. | +| clearning.cpp:157:5:157:7 | val [post update] | PostUpdateNode should not be the target of local flow. | +| clearning.cpp:158:5:158:7 | val [post update] | PostUpdateNode should not be the target of local flow. | +| clearning.cpp:164:5:164:7 | val [post update] | PostUpdateNode should not be the target of local flow. | +| clearning.cpp:171:5:171:7 | val [post update] | PostUpdateNode should not be the target of local flow. | +| clearning.cpp:178:5:178:7 | val [post update] | PostUpdateNode should not be the target of local flow. | +| clearning.cpp:179:5:179:7 | val [post update] | PostUpdateNode should not be the target of local flow. | | complex.cpp:11:22:11:23 | a_ [post update] | PostUpdateNode should not be the target of local flow. | | complex.cpp:12:22:12:23 | b_ [post update] | PostUpdateNode should not be the target of local flow. | | conflated.cpp:10:3:10:7 | * ... [post update] | PostUpdateNode should not be the target of local flow. | 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 ba007019708..b1acebfde5b 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 @@ -19,6 +19,17 @@ uniquePostUpdate | aliasing.cpp:77:11:77:11 | definition of w indirection | Node has multiple PostUpdateNodes. | | aliasing.cpp:84:11:84:11 | definition of w indirection | Node has multiple PostUpdateNodes. | | aliasing.cpp:91:11:91:11 | definition of w indirection | Node has multiple PostUpdateNodes. | +| clearning.cpp:54:3:54:3 | s indirection | Node has multiple PostUpdateNodes. | +| clearning.cpp:61:3:61:3 | s indirection | Node has multiple PostUpdateNodes. | +| clearning.cpp:90:3:90:3 | s indirection | Node has multiple PostUpdateNodes. | +| clearning.cpp:104:2:104:2 | s indirection | Node has multiple PostUpdateNodes. | +| clearning.cpp:111:4:111:4 | s indirection | Node has multiple PostUpdateNodes. | +| clearning.cpp:118:2:118:2 | s indirection | Node has multiple PostUpdateNodes. | +| clearning.cpp:125:2:125:2 | s indirection | Node has multiple PostUpdateNodes. | +| clearning.cpp:132:2:132:2 | s indirection | Node has multiple PostUpdateNodes. | +| clearning.cpp:139:4:139:4 | s indirection | Node has multiple PostUpdateNodes. | +| clearning.cpp:165:3:165:3 | s indirection | Node has multiple PostUpdateNodes. | +| clearning.cpp:172:3:172:3 | s indirection | Node has multiple PostUpdateNodes. | | complex.cpp:22:3:22:5 | this indirection | Node has multiple PostUpdateNodes. | | complex.cpp:25:7:25:7 | this indirection | Node has multiple PostUpdateNodes. | | complex.cpp:42:10:42:14 | inner indirection | Node has multiple PostUpdateNodes. | diff --git a/cpp/ql/test/library-tests/dataflow/fields/ir-path-flow.expected b/cpp/ql/test/library-tests/dataflow/fields/ir-path-flow.expected index ec21a37dd3f..a90f04df3cf 100644 --- a/cpp/ql/test/library-tests/dataflow/fields/ir-path-flow.expected +++ b/cpp/ql/test/library-tests/dataflow/fields/ir-path-flow.expected @@ -572,6 +572,136 @@ edges | by_reference.cpp:136:8:136:13 | pouter indirection [a] | by_reference.cpp:136:16:136:16 | a | | by_reference.cpp:136:8:136:13 | pouter indirection [a] | by_reference.cpp:136:16:136:16 | a indirection | | by_reference.cpp:136:16:136:16 | a indirection | by_reference.cpp:136:16:136:16 | a | +| clearning.cpp:32:3:32:25 | ... = ... | clearning.cpp:32:6:32:6 | s indirection [post update] [x indirection] | +| clearning.cpp:32:6:32:6 | s indirection [post update] [x indirection] | clearning.cpp:33:5:33:5 | s indirection [x indirection] | +| clearning.cpp:32:10:32:19 | call to user_input | clearning.cpp:32:3:32:25 | ... = ... | +| clearning.cpp:33:5:33:5 | s indirection [x indirection] | clearning.cpp:34:9:34:9 | s indirection [x indirection] | +| clearning.cpp:34:9:34:9 | s indirection [x indirection] | clearning.cpp:34:8:34:11 | * ... | +| clearning.cpp:34:9:34:9 | s indirection [x indirection] | clearning.cpp:34:11:34:11 | x indirection | +| clearning.cpp:34:9:34:9 | s indirection [x indirection] | clearning.cpp:34:11:34:11 | x indirection | +| clearning.cpp:34:11:34:11 | x indirection | clearning.cpp:34:8:34:11 | * ... | +| clearning.cpp:34:11:34:11 | x indirection | clearning.cpp:34:8:34:11 | * ... | +| clearning.cpp:53:3:53:25 | ... = ... | clearning.cpp:53:6:53:6 | s indirection [post update] [x indirection] | +| clearning.cpp:53:6:53:6 | s indirection [post update] [x indirection] | clearning.cpp:54:3:54:3 | s indirection [x indirection] | +| clearning.cpp:53:10:53:19 | call to user_input | clearning.cpp:53:3:53:25 | ... = ... | +| clearning.cpp:54:3:54:3 | s indirection [x indirection] | clearning.cpp:54:3:54:7 | ... ++ indirection | +| clearning.cpp:54:3:54:3 | s indirection [x indirection] | clearning.cpp:54:5:54:5 | x indirection | +| clearning.cpp:54:3:54:3 | s indirection [x indirection] | clearning.cpp:55:8:55:8 | s indirection [x indirection] | +| clearning.cpp:54:3:54:7 | ... ++ indirection | clearning.cpp:54:3:54:7 | ... ++ indirection | +| clearning.cpp:54:3:54:7 | ... ++ indirection | clearning.cpp:54:5:54:5 | s indirection [post update] [x indirection] | +| clearning.cpp:54:5:54:5 | s indirection [post update] [x indirection] | clearning.cpp:55:8:55:8 | s indirection [x indirection] | +| clearning.cpp:54:5:54:5 | x indirection | clearning.cpp:54:3:54:7 | ... ++ indirection | +| clearning.cpp:55:8:55:8 | s indirection [x indirection] | clearning.cpp:55:10:55:10 | x indirection | +| clearning.cpp:55:8:55:8 | s indirection [x indirection] | clearning.cpp:55:10:55:10 | x indirection | +| clearning.cpp:55:10:55:10 | x indirection | clearning.cpp:55:10:55:10 | x indirection | +| clearning.cpp:60:3:60:22 | ... = ... | clearning.cpp:60:7:60:7 | s indirection [post update] [x indirection] | +| clearning.cpp:60:7:60:7 | s indirection [post update] [x indirection] | clearning.cpp:61:3:61:3 | s indirection [x indirection] | +| clearning.cpp:60:11:60:20 | call to user_input | clearning.cpp:60:3:60:22 | ... = ... | +| clearning.cpp:61:3:61:3 | s indirection [x indirection] | clearning.cpp:61:3:61:7 | ... ++ indirection | +| clearning.cpp:61:3:61:3 | s indirection [x indirection] | clearning.cpp:61:5:61:5 | x indirection | +| clearning.cpp:61:3:61:3 | s indirection [x indirection] | clearning.cpp:62:8:62:8 | s indirection [x indirection] | +| clearning.cpp:61:3:61:7 | ... ++ indirection | clearning.cpp:61:3:61:7 | ... ++ indirection | +| clearning.cpp:61:3:61:7 | ... ++ indirection | clearning.cpp:61:5:61:5 | s indirection [post update] [x indirection] | +| clearning.cpp:61:5:61:5 | s indirection [post update] [x indirection] | clearning.cpp:62:8:62:8 | s indirection [x indirection] | +| clearning.cpp:61:5:61:5 | x indirection | clearning.cpp:61:3:61:7 | ... ++ indirection | +| clearning.cpp:62:8:62:8 | s indirection [x indirection] | clearning.cpp:62:10:62:10 | x indirection | +| clearning.cpp:62:8:62:8 | s indirection [x indirection] | clearning.cpp:62:10:62:10 | x indirection | +| clearning.cpp:62:10:62:10 | x indirection | clearning.cpp:62:10:62:10 | x indirection | +| clearning.cpp:74:20:74:22 | argument_source output argument | clearning.cpp:74:20:74:22 | s indirection [post update] [val indirection] | +| clearning.cpp:74:20:74:22 | s indirection [post update] [val indirection] | clearning.cpp:76:8:76:8 | s indirection [val indirection] | +| clearning.cpp:76:8:76:8 | s indirection [val indirection] | clearning.cpp:76:7:76:12 | * ... | +| clearning.cpp:76:8:76:8 | s indirection [val indirection] | clearning.cpp:76:10:76:12 | val indirection | +| clearning.cpp:76:8:76:8 | s indirection [val indirection] | clearning.cpp:76:10:76:12 | val indirection | +| clearning.cpp:76:10:76:12 | val indirection | clearning.cpp:76:7:76:12 | * ... | +| clearning.cpp:76:10:76:12 | val indirection | clearning.cpp:76:7:76:12 | * ... | +| clearning.cpp:81:20:81:22 | argument_source output argument | clearning.cpp:81:20:81:22 | s indirection [post update] [val indirection] | +| clearning.cpp:81:20:81:22 | s indirection [post update] [val indirection] | clearning.cpp:83:13:83:13 | s indirection [val indirection] | +| clearning.cpp:83:5:83:21 | ... = ... indirection | clearning.cpp:83:7:83:9 | s indirection [post update] [val indirection] | +| clearning.cpp:83:7:83:9 | s indirection [post update] [val indirection] | clearning.cpp:84:8:84:8 | s indirection [val indirection] | +| clearning.cpp:83:13:83:13 | s indirection [val indirection] | clearning.cpp:83:13:83:21 | ... + ... indirection | +| clearning.cpp:83:13:83:13 | s indirection [val indirection] | clearning.cpp:83:15:83:17 | val indirection | +| clearning.cpp:83:13:83:21 | ... + ... indirection | clearning.cpp:83:5:83:21 | ... = ... indirection | +| clearning.cpp:83:15:83:17 | val indirection | clearning.cpp:83:5:83:21 | ... = ... indirection | +| clearning.cpp:84:8:84:8 | s indirection [val indirection] | clearning.cpp:84:7:84:12 | * ... | +| clearning.cpp:84:8:84:8 | s indirection [val indirection] | clearning.cpp:84:10:84:12 | val indirection | +| clearning.cpp:84:8:84:8 | s indirection [val indirection] | clearning.cpp:84:10:84:12 | val indirection | +| clearning.cpp:84:10:84:12 | val indirection | clearning.cpp:84:7:84:12 | * ... | +| clearning.cpp:84:10:84:12 | val indirection | clearning.cpp:84:7:84:12 | * ... | +| clearning.cpp:89:20:89:22 | argument_source output argument | clearning.cpp:89:20:89:22 | s indirection [post update] [val indirection] | +| clearning.cpp:89:20:89:22 | s indirection [post update] [val indirection] | clearning.cpp:90:3:90:3 | s indirection [val indirection] | +| clearning.cpp:90:3:90:3 | s indirection [val indirection] | clearning.cpp:90:3:90:9 | ... ++ indirection | +| clearning.cpp:90:3:90:3 | s indirection [val indirection] | clearning.cpp:90:5:90:7 | val indirection | +| clearning.cpp:90:3:90:3 | s indirection [val indirection] | clearning.cpp:91:8:91:8 | s indirection [val indirection] | +| clearning.cpp:90:3:90:9 | ... ++ indirection | clearning.cpp:90:3:90:9 | ... ++ indirection | +| clearning.cpp:90:3:90:9 | ... ++ indirection | clearning.cpp:90:5:90:7 | s indirection [post update] [val indirection] | +| clearning.cpp:90:5:90:7 | s indirection [post update] [val indirection] | clearning.cpp:91:8:91:8 | s indirection [val indirection] | +| clearning.cpp:90:5:90:7 | val indirection | clearning.cpp:90:3:90:9 | ... ++ indirection | +| clearning.cpp:91:8:91:8 | s indirection [val indirection] | clearning.cpp:91:7:91:12 | * ... | +| clearning.cpp:91:8:91:8 | s indirection [val indirection] | clearning.cpp:91:10:91:12 | val indirection | +| clearning.cpp:91:8:91:8 | s indirection [val indirection] | clearning.cpp:91:10:91:12 | val indirection | +| clearning.cpp:91:10:91:12 | val indirection | clearning.cpp:91:7:91:12 | * ... | +| clearning.cpp:91:10:91:12 | val indirection | clearning.cpp:91:7:91:12 | * ... | +| clearning.cpp:96:20:96:22 | argument_source output argument | clearning.cpp:96:20:96:22 | s indirection [post update] [val indirection] | +| clearning.cpp:96:20:96:22 | s indirection [post update] [val indirection] | clearning.cpp:97:10:97:10 | s indirection [val indirection] | +| clearning.cpp:97:2:97:18 | ... = ... indirection | clearning.cpp:97:4:97:6 | s indirection [post update] [val indirection] | +| clearning.cpp:97:4:97:6 | s indirection [post update] [val indirection] | clearning.cpp:98:8:98:8 | s indirection [val indirection] | +| clearning.cpp:97:10:97:10 | s indirection [val indirection] | clearning.cpp:97:10:97:18 | ... + ... indirection | +| clearning.cpp:97:10:97:10 | s indirection [val indirection] | clearning.cpp:97:12:97:14 | val indirection | +| clearning.cpp:97:10:97:18 | ... + ... indirection | clearning.cpp:97:2:97:18 | ... = ... indirection | +| clearning.cpp:97:12:97:14 | val indirection | clearning.cpp:97:2:97:18 | ... = ... indirection | +| clearning.cpp:98:8:98:8 | s indirection [val indirection] | clearning.cpp:98:7:98:12 | * ... | +| clearning.cpp:98:8:98:8 | s indirection [val indirection] | clearning.cpp:98:10:98:12 | val indirection | +| clearning.cpp:98:8:98:8 | s indirection [val indirection] | clearning.cpp:98:10:98:12 | val indirection | +| clearning.cpp:98:10:98:12 | val indirection | clearning.cpp:98:7:98:12 | * ... | +| clearning.cpp:98:10:98:12 | val indirection | clearning.cpp:98:7:98:12 | * ... | +| clearning.cpp:103:20:103:22 | argument_source output argument | clearning.cpp:103:20:103:22 | s indirection [post update] [val indirection] | +| clearning.cpp:103:20:103:22 | s indirection [post update] [val indirection] | clearning.cpp:104:2:104:2 | s indirection [val indirection] | +| clearning.cpp:104:2:104:2 | s indirection [val indirection] | clearning.cpp:104:2:104:8 | ... ++ indirection | +| clearning.cpp:104:2:104:2 | s indirection [val indirection] | clearning.cpp:104:4:104:6 | val indirection | +| clearning.cpp:104:2:104:2 | s indirection [val indirection] | clearning.cpp:105:8:105:8 | s indirection [val indirection] | +| clearning.cpp:104:2:104:8 | ... ++ indirection | clearning.cpp:104:2:104:8 | ... ++ indirection | +| clearning.cpp:104:2:104:8 | ... ++ indirection | clearning.cpp:104:4:104:6 | s indirection [post update] [val indirection] | +| clearning.cpp:104:4:104:6 | s indirection [post update] [val indirection] | clearning.cpp:105:8:105:8 | s indirection [val indirection] | +| clearning.cpp:104:4:104:6 | val indirection | clearning.cpp:104:2:104:8 | ... ++ indirection | +| clearning.cpp:105:8:105:8 | s indirection [val indirection] | clearning.cpp:105:7:105:12 | * ... | +| clearning.cpp:105:8:105:8 | s indirection [val indirection] | clearning.cpp:105:10:105:12 | val indirection | +| clearning.cpp:105:8:105:8 | s indirection [val indirection] | clearning.cpp:105:10:105:12 | val indirection | +| clearning.cpp:105:10:105:12 | val indirection | clearning.cpp:105:7:105:12 | * ... | +| clearning.cpp:105:10:105:12 | val indirection | clearning.cpp:105:7:105:12 | * ... | +| clearning.cpp:110:20:110:22 | argument_source output argument | clearning.cpp:110:20:110:22 | s indirection [post update] [val indirection] | +| clearning.cpp:110:20:110:22 | s indirection [post update] [val indirection] | clearning.cpp:111:4:111:4 | s indirection [val indirection] | +| clearning.cpp:111:2:111:8 | ++ ... indirection | clearning.cpp:111:2:111:8 | ++ ... indirection | +| clearning.cpp:111:2:111:8 | ++ ... indirection | clearning.cpp:111:6:111:8 | s indirection [post update] [val indirection] | +| clearning.cpp:111:4:111:4 | s indirection [val indirection] | clearning.cpp:111:2:111:8 | ++ ... indirection | +| clearning.cpp:111:4:111:4 | s indirection [val indirection] | clearning.cpp:111:6:111:8 | val indirection | +| clearning.cpp:111:4:111:4 | s indirection [val indirection] | clearning.cpp:112:8:112:8 | s indirection [val indirection] | +| clearning.cpp:111:6:111:8 | s indirection [post update] [val indirection] | clearning.cpp:112:8:112:8 | s indirection [val indirection] | +| clearning.cpp:111:6:111:8 | val indirection | clearning.cpp:111:2:111:8 | ++ ... indirection | +| clearning.cpp:112:8:112:8 | s indirection [val indirection] | clearning.cpp:112:7:112:12 | * ... | +| clearning.cpp:112:8:112:8 | s indirection [val indirection] | clearning.cpp:112:10:112:12 | val indirection | +| clearning.cpp:112:8:112:8 | s indirection [val indirection] | clearning.cpp:112:10:112:12 | val indirection | +| clearning.cpp:112:10:112:12 | val indirection | clearning.cpp:112:7:112:12 | * ... | +| clearning.cpp:112:10:112:12 | val indirection | clearning.cpp:112:7:112:12 | * ... | +| clearning.cpp:117:20:117:22 | argument_source output argument | clearning.cpp:117:20:117:22 | s indirection [post update] [val indirection] | +| clearning.cpp:117:20:117:22 | s indirection [post update] [val indirection] | clearning.cpp:118:2:118:2 | s indirection [val indirection] | +| clearning.cpp:118:2:118:2 | s indirection [val indirection] | clearning.cpp:118:2:118:11 | ... += ... indirection | +| clearning.cpp:118:2:118:2 | s indirection [val indirection] | clearning.cpp:118:4:118:6 | val indirection | +| clearning.cpp:118:2:118:2 | s indirection [val indirection] | clearning.cpp:119:8:119:8 | s indirection [val indirection] | +| clearning.cpp:118:2:118:11 | ... += ... indirection | clearning.cpp:118:2:118:11 | ... += ... indirection | +| clearning.cpp:118:2:118:11 | ... += ... indirection | clearning.cpp:118:4:118:6 | s indirection [post update] [val indirection] | +| clearning.cpp:118:4:118:6 | s indirection [post update] [val indirection] | clearning.cpp:119:8:119:8 | s indirection [val indirection] | +| clearning.cpp:118:4:118:6 | val indirection | clearning.cpp:118:2:118:11 | ... += ... indirection | +| clearning.cpp:119:8:119:8 | s indirection [val indirection] | clearning.cpp:119:7:119:12 | * ... | +| clearning.cpp:119:8:119:8 | s indirection [val indirection] | clearning.cpp:119:10:119:12 | val indirection | +| clearning.cpp:119:8:119:8 | s indirection [val indirection] | clearning.cpp:119:10:119:12 | val indirection | +| clearning.cpp:119:10:119:12 | val indirection | clearning.cpp:119:7:119:12 | * ... | +| clearning.cpp:119:10:119:12 | val indirection | clearning.cpp:119:7:119:12 | * ... | +| clearning.cpp:151:3:151:22 | ... = ... | clearning.cpp:151:5:151:7 | s indirection [post update] [val] | +| clearning.cpp:151:5:151:7 | s indirection [post update] [val] | clearning.cpp:152:8:152:8 | s indirection [val] | +| clearning.cpp:151:11:151:20 | call to user_input | clearning.cpp:151:3:151:22 | ... = ... | +| clearning.cpp:152:8:152:8 | s indirection [val] | clearning.cpp:152:10:152:12 | val | +| clearning.cpp:152:8:152:8 | s indirection [val] | clearning.cpp:152:10:152:12 | val indirection | +| clearning.cpp:152:10:152:12 | val indirection | clearning.cpp:152:10:152:12 | val | | complex.cpp:9:7:9:7 | this indirection [a_] | complex.cpp:9:20:9:21 | this indirection [a_] | | complex.cpp:9:20:9:21 | a_ | complex.cpp:9:7:9:7 | a indirection | | complex.cpp:9:20:9:21 | a_ indirection | complex.cpp:9:7:9:7 | a indirection | @@ -861,19 +991,20 @@ edges | struct_init.c:15:8:15:9 | ab indirection [a] | struct_init.c:15:12:15:12 | a | | struct_init.c:15:8:15:9 | ab indirection [a] | struct_init.c:15:12:15:12 | a indirection | | struct_init.c:15:12:15:12 | a indirection | struct_init.c:15:12:15:12 | a | -| struct_init.c:20:17:20:36 | definition of ab indirection [post update] [a] | struct_init.c:22:8:22:9 | ab indirection [a] | -| struct_init.c:20:17:20:36 | definition of ab indirection [post update] [a] | struct_init.c:24:10:24:12 | & ... indirection [a] | -| struct_init.c:20:17:20:36 | definition of ab indirection [post update] [a] | struct_init.c:28:5:28:7 | & ... indirection [a] | +| struct_init.c:20:13:20:14 | definition of ab indirection [a] | struct_init.c:22:8:22:9 | ab indirection [a] | +| struct_init.c:20:13:20:14 | definition of ab indirection [a] | struct_init.c:24:10:24:12 | & ... indirection [a] | +| struct_init.c:20:13:20:14 | definition of ab indirection [a] | struct_init.c:28:5:28:7 | & ... indirection [a] | +| struct_init.c:20:17:20:36 | definition of ab indirection [post update] [a] | struct_init.c:20:13:20:14 | definition of ab indirection [a] | | struct_init.c:20:20:20:29 | call to user_input | struct_init.c:20:17:20:36 | definition of ab indirection [post update] [a] | | struct_init.c:20:20:20:29 | call to user_input | struct_init.c:20:20:20:29 | call to user_input | | struct_init.c:22:8:22:9 | ab indirection [a] | struct_init.c:22:11:22:11 | a | | struct_init.c:22:8:22:9 | ab indirection [a] | struct_init.c:22:11:22:11 | a indirection | | struct_init.c:22:11:22:11 | a indirection | struct_init.c:22:11:22:11 | a | | struct_init.c:24:10:24:12 | & ... indirection [a] | struct_init.c:14:24:14:25 | ab indirection [a] | -| struct_init.c:26:23:29:3 | definition of outer indirection [post update] [nestedAB, a] | struct_init.c:31:8:31:12 | outer indirection [nestedAB, a] | -| struct_init.c:26:23:29:3 | definition of outer indirection [post update] [nestedAB, a] | struct_init.c:31:8:31:12 | outer indirection [nestedAB, a] | -| struct_init.c:26:23:29:3 | definition of outer indirection [post update] [nestedAB, a] | struct_init.c:36:11:36:15 | outer indirection [nestedAB, a] | -| struct_init.c:26:23:29:3 | definition of outer indirection [post update] [nestedAB, a] | struct_init.c:36:11:36:15 | outer indirection [nestedAB, a] | +| struct_init.c:26:16:26:20 | definition of outer indirection [nestedAB, a] | struct_init.c:31:8:31:12 | outer indirection [nestedAB, a] | +| struct_init.c:26:16:26:20 | definition of outer indirection [nestedAB, a] | struct_init.c:36:11:36:15 | outer indirection [nestedAB, a] | +| struct_init.c:26:23:29:3 | definition of outer indirection [post update] [nestedAB, a] | struct_init.c:26:16:26:20 | definition of outer indirection [nestedAB, a] | +| struct_init.c:26:23:29:3 | definition of outer indirection [post update] [nestedAB, a] | struct_init.c:26:16:26:20 | definition of outer indirection [nestedAB, a] | | struct_init.c:26:23:29:3 | definition of outer indirection [post update] [pointerAB indirection, a] | struct_init.c:33:8:33:12 | outer indirection [pointerAB indirection, a] | | struct_init.c:27:5:27:23 | {...} indirection [post update] [a] | struct_init.c:26:23:29:3 | definition of outer indirection [post update] [nestedAB, a] | | struct_init.c:27:5:27:23 | {...} indirection [post update] [a] | struct_init.c:26:23:29:3 | definition of outer indirection [post update] [nestedAB, a] | @@ -892,7 +1023,8 @@ edges | struct_init.c:33:25:33:25 | a indirection | struct_init.c:33:25:33:25 | a | | struct_init.c:36:10:36:24 | & ... indirection [a] | struct_init.c:14:24:14:25 | ab indirection [a] | | struct_init.c:36:11:36:15 | outer indirection [nestedAB, a] | struct_init.c:36:10:36:24 | & ... indirection [a] | -| struct_init.c:40:17:40:36 | definition of ab indirection [post update] [a] | struct_init.c:43:5:43:7 | & ... indirection [a] | +| struct_init.c:40:13:40:14 | definition of ab indirection [a] | struct_init.c:43:5:43:7 | & ... indirection [a] | +| struct_init.c:40:17:40:36 | definition of ab indirection [post update] [a] | struct_init.c:40:13:40:14 | definition of ab indirection [a] | | struct_init.c:40:20:40:29 | call to user_input | struct_init.c:40:17:40:36 | definition of ab indirection [post update] [a] | | struct_init.c:40:20:40:29 | call to user_input | struct_init.c:40:20:40:29 | call to user_input | | struct_init.c:41:23:44:3 | definition of outer indirection [post update] [pointerAB indirection, a] | struct_init.c:46:10:46:14 | outer indirection [pointerAB indirection, a] | @@ -1433,6 +1565,114 @@ nodes | by_reference.cpp:136:8:136:13 | pouter indirection [a] | semmle.label | pouter indirection [a] | | by_reference.cpp:136:16:136:16 | a | semmle.label | a | | by_reference.cpp:136:16:136:16 | a indirection | semmle.label | a indirection | +| clearning.cpp:32:3:32:25 | ... = ... | semmle.label | ... = ... | +| clearning.cpp:32:6:32:6 | s indirection [post update] [x indirection] | semmle.label | s indirection [post update] [x indirection] | +| clearning.cpp:32:10:32:19 | call to user_input | semmle.label | call to user_input | +| clearning.cpp:33:5:33:5 | s indirection [x indirection] | semmle.label | s indirection [x indirection] | +| clearning.cpp:34:8:34:11 | * ... | semmle.label | * ... | +| clearning.cpp:34:9:34:9 | s indirection [x indirection] | semmle.label | s indirection [x indirection] | +| clearning.cpp:34:11:34:11 | x indirection | semmle.label | x indirection | +| clearning.cpp:34:11:34:11 | x indirection | semmle.label | x indirection | +| clearning.cpp:53:3:53:25 | ... = ... | semmle.label | ... = ... | +| clearning.cpp:53:6:53:6 | s indirection [post update] [x indirection] | semmle.label | s indirection [post update] [x indirection] | +| clearning.cpp:53:10:53:19 | call to user_input | semmle.label | call to user_input | +| clearning.cpp:54:3:54:3 | s indirection [x indirection] | semmle.label | s indirection [x indirection] | +| clearning.cpp:54:3:54:7 | ... ++ indirection | semmle.label | ... ++ indirection | +| clearning.cpp:54:3:54:7 | ... ++ indirection | semmle.label | ... ++ indirection | +| clearning.cpp:54:5:54:5 | s indirection [post update] [x indirection] | semmle.label | s indirection [post update] [x indirection] | +| clearning.cpp:54:5:54:5 | x indirection | semmle.label | x indirection | +| clearning.cpp:55:8:55:8 | s indirection [x indirection] | semmle.label | s indirection [x indirection] | +| clearning.cpp:55:10:55:10 | x indirection | semmle.label | x indirection | +| clearning.cpp:55:10:55:10 | x indirection | semmle.label | x indirection | +| clearning.cpp:60:3:60:22 | ... = ... | semmle.label | ... = ... | +| clearning.cpp:60:7:60:7 | s indirection [post update] [x indirection] | semmle.label | s indirection [post update] [x indirection] | +| clearning.cpp:60:11:60:20 | call to user_input | semmle.label | call to user_input | +| clearning.cpp:61:3:61:3 | s indirection [x indirection] | semmle.label | s indirection [x indirection] | +| clearning.cpp:61:3:61:7 | ... ++ indirection | semmle.label | ... ++ indirection | +| clearning.cpp:61:3:61:7 | ... ++ indirection | semmle.label | ... ++ indirection | +| clearning.cpp:61:5:61:5 | s indirection [post update] [x indirection] | semmle.label | s indirection [post update] [x indirection] | +| clearning.cpp:61:5:61:5 | x indirection | semmle.label | x indirection | +| clearning.cpp:62:8:62:8 | s indirection [x indirection] | semmle.label | s indirection [x indirection] | +| clearning.cpp:62:10:62:10 | x indirection | semmle.label | x indirection | +| clearning.cpp:62:10:62:10 | x indirection | semmle.label | x indirection | +| clearning.cpp:74:20:74:22 | argument_source output argument | semmle.label | argument_source output argument | +| clearning.cpp:74:20:74:22 | s indirection [post update] [val indirection] | semmle.label | s indirection [post update] [val indirection] | +| clearning.cpp:76:7:76:12 | * ... | semmle.label | * ... | +| clearning.cpp:76:8:76:8 | s indirection [val indirection] | semmle.label | s indirection [val indirection] | +| clearning.cpp:76:10:76:12 | val indirection | semmle.label | val indirection | +| clearning.cpp:76:10:76:12 | val indirection | semmle.label | val indirection | +| clearning.cpp:81:20:81:22 | argument_source output argument | semmle.label | argument_source output argument | +| clearning.cpp:81:20:81:22 | s indirection [post update] [val indirection] | semmle.label | s indirection [post update] [val indirection] | +| clearning.cpp:83:5:83:21 | ... = ... indirection | semmle.label | ... = ... indirection | +| clearning.cpp:83:7:83:9 | s indirection [post update] [val indirection] | semmle.label | s indirection [post update] [val indirection] | +| clearning.cpp:83:13:83:13 | s indirection [val indirection] | semmle.label | s indirection [val indirection] | +| clearning.cpp:83:13:83:21 | ... + ... indirection | semmle.label | ... + ... indirection | +| clearning.cpp:83:15:83:17 | val indirection | semmle.label | val indirection | +| clearning.cpp:84:7:84:12 | * ... | semmle.label | * ... | +| clearning.cpp:84:8:84:8 | s indirection [val indirection] | semmle.label | s indirection [val indirection] | +| clearning.cpp:84:10:84:12 | val indirection | semmle.label | val indirection | +| clearning.cpp:84:10:84:12 | val indirection | semmle.label | val indirection | +| clearning.cpp:89:20:89:22 | argument_source output argument | semmle.label | argument_source output argument | +| clearning.cpp:89:20:89:22 | s indirection [post update] [val indirection] | semmle.label | s indirection [post update] [val indirection] | +| clearning.cpp:90:3:90:3 | s indirection [val indirection] | semmle.label | s indirection [val indirection] | +| clearning.cpp:90:3:90:9 | ... ++ indirection | semmle.label | ... ++ indirection | +| clearning.cpp:90:3:90:9 | ... ++ indirection | semmle.label | ... ++ indirection | +| clearning.cpp:90:5:90:7 | s indirection [post update] [val indirection] | semmle.label | s indirection [post update] [val indirection] | +| clearning.cpp:90:5:90:7 | val indirection | semmle.label | val indirection | +| clearning.cpp:91:7:91:12 | * ... | semmle.label | * ... | +| clearning.cpp:91:8:91:8 | s indirection [val indirection] | semmle.label | s indirection [val indirection] | +| clearning.cpp:91:10:91:12 | val indirection | semmle.label | val indirection | +| clearning.cpp:91:10:91:12 | val indirection | semmle.label | val indirection | +| clearning.cpp:96:20:96:22 | argument_source output argument | semmle.label | argument_source output argument | +| clearning.cpp:96:20:96:22 | s indirection [post update] [val indirection] | semmle.label | s indirection [post update] [val indirection] | +| clearning.cpp:97:2:97:18 | ... = ... indirection | semmle.label | ... = ... indirection | +| clearning.cpp:97:4:97:6 | s indirection [post update] [val indirection] | semmle.label | s indirection [post update] [val indirection] | +| clearning.cpp:97:10:97:10 | s indirection [val indirection] | semmle.label | s indirection [val indirection] | +| clearning.cpp:97:10:97:18 | ... + ... indirection | semmle.label | ... + ... indirection | +| clearning.cpp:97:12:97:14 | val indirection | semmle.label | val indirection | +| clearning.cpp:98:7:98:12 | * ... | semmle.label | * ... | +| clearning.cpp:98:8:98:8 | s indirection [val indirection] | semmle.label | s indirection [val indirection] | +| clearning.cpp:98:10:98:12 | val indirection | semmle.label | val indirection | +| clearning.cpp:98:10:98:12 | val indirection | semmle.label | val indirection | +| clearning.cpp:103:20:103:22 | argument_source output argument | semmle.label | argument_source output argument | +| clearning.cpp:103:20:103:22 | s indirection [post update] [val indirection] | semmle.label | s indirection [post update] [val indirection] | +| clearning.cpp:104:2:104:2 | s indirection [val indirection] | semmle.label | s indirection [val indirection] | +| clearning.cpp:104:2:104:8 | ... ++ indirection | semmle.label | ... ++ indirection | +| clearning.cpp:104:2:104:8 | ... ++ indirection | semmle.label | ... ++ indirection | +| clearning.cpp:104:4:104:6 | s indirection [post update] [val indirection] | semmle.label | s indirection [post update] [val indirection] | +| clearning.cpp:104:4:104:6 | val indirection | semmle.label | val indirection | +| clearning.cpp:105:7:105:12 | * ... | semmle.label | * ... | +| clearning.cpp:105:8:105:8 | s indirection [val indirection] | semmle.label | s indirection [val indirection] | +| clearning.cpp:105:10:105:12 | val indirection | semmle.label | val indirection | +| clearning.cpp:105:10:105:12 | val indirection | semmle.label | val indirection | +| clearning.cpp:110:20:110:22 | argument_source output argument | semmle.label | argument_source output argument | +| clearning.cpp:110:20:110:22 | s indirection [post update] [val indirection] | semmle.label | s indirection [post update] [val indirection] | +| clearning.cpp:111:2:111:8 | ++ ... indirection | semmle.label | ++ ... indirection | +| clearning.cpp:111:2:111:8 | ++ ... indirection | semmle.label | ++ ... indirection | +| clearning.cpp:111:4:111:4 | s indirection [val indirection] | semmle.label | s indirection [val indirection] | +| clearning.cpp:111:6:111:8 | s indirection [post update] [val indirection] | semmle.label | s indirection [post update] [val indirection] | +| clearning.cpp:111:6:111:8 | val indirection | semmle.label | val indirection | +| clearning.cpp:112:7:112:12 | * ... | semmle.label | * ... | +| clearning.cpp:112:8:112:8 | s indirection [val indirection] | semmle.label | s indirection [val indirection] | +| clearning.cpp:112:10:112:12 | val indirection | semmle.label | val indirection | +| clearning.cpp:112:10:112:12 | val indirection | semmle.label | val indirection | +| clearning.cpp:117:20:117:22 | argument_source output argument | semmle.label | argument_source output argument | +| clearning.cpp:117:20:117:22 | s indirection [post update] [val indirection] | semmle.label | s indirection [post update] [val indirection] | +| clearning.cpp:118:2:118:2 | s indirection [val indirection] | semmle.label | s indirection [val indirection] | +| clearning.cpp:118:2:118:11 | ... += ... indirection | semmle.label | ... += ... indirection | +| clearning.cpp:118:2:118:11 | ... += ... indirection | semmle.label | ... += ... indirection | +| clearning.cpp:118:4:118:6 | s indirection [post update] [val indirection] | semmle.label | s indirection [post update] [val indirection] | +| clearning.cpp:118:4:118:6 | val indirection | semmle.label | val indirection | +| clearning.cpp:119:7:119:12 | * ... | semmle.label | * ... | +| clearning.cpp:119:8:119:8 | s indirection [val indirection] | semmle.label | s indirection [val indirection] | +| clearning.cpp:119:10:119:12 | val indirection | semmle.label | val indirection | +| clearning.cpp:119:10:119:12 | val indirection | semmle.label | val indirection | +| clearning.cpp:151:3:151:22 | ... = ... | semmle.label | ... = ... | +| clearning.cpp:151:5:151:7 | s indirection [post update] [val] | semmle.label | s indirection [post update] [val] | +| clearning.cpp:151:11:151:20 | call to user_input | semmle.label | call to user_input | +| clearning.cpp:152:8:152:8 | s indirection [val] | semmle.label | s indirection [val] | +| clearning.cpp:152:10:152:12 | val | semmle.label | val | +| clearning.cpp:152:10:152:12 | val indirection | semmle.label | val indirection | | complex.cpp:9:7:9:7 | a indirection | semmle.label | a indirection | | complex.cpp:9:7:9:7 | this indirection [a_] | semmle.label | this indirection [a_] | | complex.cpp:9:20:9:21 | a_ | semmle.label | a_ | @@ -1699,6 +1939,7 @@ nodes | struct_init.c:15:8:15:9 | ab indirection [a] | semmle.label | ab indirection [a] | | struct_init.c:15:12:15:12 | a | semmle.label | a | | struct_init.c:15:12:15:12 | a indirection | semmle.label | a indirection | +| struct_init.c:20:13:20:14 | definition of ab indirection [a] | semmle.label | definition of ab indirection [a] | | struct_init.c:20:17:20:36 | definition of ab indirection [post update] [a] | semmle.label | definition of ab indirection [post update] [a] | | struct_init.c:20:20:20:29 | call to user_input | semmle.label | call to user_input | | struct_init.c:20:20:20:29 | call to user_input | semmle.label | call to user_input | @@ -1706,6 +1947,7 @@ nodes | struct_init.c:22:11:22:11 | a | semmle.label | a | | struct_init.c:22:11:22:11 | a indirection | semmle.label | a indirection | | struct_init.c:24:10:24:12 | & ... indirection [a] | semmle.label | & ... indirection [a] | +| struct_init.c:26:16:26:20 | definition of outer indirection [nestedAB, a] | semmle.label | definition of outer indirection [nestedAB, a] | | struct_init.c:26:23:29:3 | definition of outer indirection [post update] [nestedAB, a] | semmle.label | definition of outer indirection [post update] [nestedAB, a] | | struct_init.c:26:23:29:3 | definition of outer indirection [post update] [nestedAB, a] | semmle.label | definition of outer indirection [post update] [nestedAB, a] | | struct_init.c:26:23:29:3 | definition of outer indirection [post update] [pointerAB indirection, a] | semmle.label | definition of outer indirection [post update] [pointerAB indirection, a] | @@ -1724,6 +1966,7 @@ nodes | struct_init.c:33:25:33:25 | a indirection | semmle.label | a indirection | | struct_init.c:36:10:36:24 | & ... indirection [a] | semmle.label | & ... indirection [a] | | struct_init.c:36:11:36:15 | outer indirection [nestedAB, a] | semmle.label | outer indirection [nestedAB, a] | +| struct_init.c:40:13:40:14 | definition of ab indirection [a] | semmle.label | definition of ab indirection [a] | | struct_init.c:40:17:40:36 | definition of ab indirection [post update] [a] | semmle.label | definition of ab indirection [post update] [a] | | struct_init.c:40:20:40:29 | call to user_input | semmle.label | call to user_input | | struct_init.c:40:20:40:29 | call to user_input | semmle.label | call to user_input | @@ -1883,6 +2126,17 @@ subpaths | by_reference.cpp:134:29:134:29 | a | by_reference.cpp:88:13:88:22 | call to user_input | by_reference.cpp:134:29:134:29 | a | a flows from $@ | by_reference.cpp:88:13:88:22 | call to user_input | call to user_input | | by_reference.cpp:135:27:135:27 | a | by_reference.cpp:88:13:88:22 | call to user_input | by_reference.cpp:135:27:135:27 | a | a flows from $@ | by_reference.cpp:88:13:88:22 | call to user_input | call to user_input | | by_reference.cpp:136:16:136:16 | a | by_reference.cpp:96:8:96:17 | call to user_input | by_reference.cpp:136:16:136:16 | a | a flows from $@ | by_reference.cpp:96:8:96:17 | call to user_input | call to user_input | +| clearning.cpp:34:8:34:11 | * ... | clearning.cpp:32:10:32:19 | call to user_input | clearning.cpp:34:8:34:11 | * ... | * ... flows from $@ | clearning.cpp:32:10:32:19 | call to user_input | call to user_input | +| clearning.cpp:55:10:55:10 | x indirection | clearning.cpp:53:10:53:19 | call to user_input | clearning.cpp:55:10:55:10 | x indirection | x indirection flows from $@ | clearning.cpp:53:10:53:19 | call to user_input | call to user_input | +| clearning.cpp:62:10:62:10 | x indirection | clearning.cpp:60:11:60:20 | call to user_input | clearning.cpp:62:10:62:10 | x indirection | x indirection flows from $@ | clearning.cpp:60:11:60:20 | call to user_input | call to user_input | +| clearning.cpp:76:7:76:12 | * ... | clearning.cpp:74:20:74:22 | argument_source output argument | clearning.cpp:76:7:76:12 | * ... | * ... flows from $@ | clearning.cpp:74:20:74:22 | argument_source output argument | argument_source output argument | +| clearning.cpp:84:7:84:12 | * ... | clearning.cpp:81:20:81:22 | argument_source output argument | clearning.cpp:84:7:84:12 | * ... | * ... flows from $@ | clearning.cpp:81:20:81:22 | argument_source output argument | argument_source output argument | +| clearning.cpp:91:7:91:12 | * ... | clearning.cpp:89:20:89:22 | argument_source output argument | clearning.cpp:91:7:91:12 | * ... | * ... flows from $@ | clearning.cpp:89:20:89:22 | argument_source output argument | argument_source output argument | +| clearning.cpp:98:7:98:12 | * ... | clearning.cpp:96:20:96:22 | argument_source output argument | clearning.cpp:98:7:98:12 | * ... | * ... flows from $@ | clearning.cpp:96:20:96:22 | argument_source output argument | argument_source output argument | +| clearning.cpp:105:7:105:12 | * ... | clearning.cpp:103:20:103:22 | argument_source output argument | clearning.cpp:105:7:105:12 | * ... | * ... flows from $@ | clearning.cpp:103:20:103:22 | argument_source output argument | argument_source output argument | +| clearning.cpp:112:7:112:12 | * ... | clearning.cpp:110:20:110:22 | argument_source output argument | clearning.cpp:112:7:112:12 | * ... | * ... flows from $@ | clearning.cpp:110:20:110:22 | argument_source output argument | argument_source output argument | +| clearning.cpp:119:7:119:12 | * ... | clearning.cpp:117:20:117:22 | argument_source output argument | clearning.cpp:119:7:119:12 | * ... | * ... flows from $@ | clearning.cpp:117:20:117:22 | argument_source output argument | argument_source output argument | +| clearning.cpp:152:10:152:12 | val | clearning.cpp:151:11:151:20 | call to user_input | clearning.cpp:152:10:152:12 | val | val flows from $@ | clearning.cpp:151:11:151:20 | call to user_input | call to user_input | | complex.cpp:42:18:42:18 | call to a | complex.cpp:53:19:53:28 | call to user_input | complex.cpp:42:18:42:18 | call to a | call to a flows from $@ | complex.cpp:53:19:53:28 | call to user_input | call to user_input | | complex.cpp:42:18:42:18 | call to a | complex.cpp:55:19:55:28 | call to user_input | complex.cpp:42:18:42:18 | call to a | call to a flows from $@ | complex.cpp:55:19:55:28 | call to user_input | call to user_input | | complex.cpp:43:18:43:18 | call to b | complex.cpp:54:19:54:28 | call to user_input | complex.cpp:43:18:43:18 | call to b | call to b flows from $@ | complex.cpp:54:19:54:28 | call to user_input | call to user_input | diff --git a/cpp/ql/test/library-tests/dataflow/fields/partial-definition-diff.expected b/cpp/ql/test/library-tests/dataflow/fields/partial-definition-diff.expected index 6749e278fba..ad76accce67 100644 --- a/cpp/ql/test/library-tests/dataflow/fields/partial-definition-diff.expected +++ b/cpp/ql/test/library-tests/dataflow/fields/partial-definition-diff.expected @@ -167,6 +167,66 @@ | by_reference.cpp:88:9:88:9 | a | AST only | | by_reference.cpp:92:3:92:5 | * ... | AST only | | by_reference.cpp:96:3:96:4 | pa | AST only | +| clearning.cpp:18:7:18:7 | s | IR only | +| clearning.cpp:19:3:19:6 | * ... | AST only | +| clearning.cpp:20:12:20:12 | s | IR only | +| clearning.cpp:25:7:25:7 | s | IR only | +| clearning.cpp:26:7:26:7 | s | IR only | +| clearning.cpp:27:12:27:12 | s | IR only | +| clearning.cpp:32:3:32:6 | * ... | AST only | +| clearning.cpp:33:7:33:7 | s | IR only | +| clearning.cpp:34:8:34:11 | * ... | IR only | +| clearning.cpp:34:11:34:11 | s | IR only | +| clearning.cpp:39:3:39:6 | * ... | AST only | +| clearning.cpp:40:5:40:5 | x | AST only | +| clearning.cpp:41:8:41:11 | * ... | IR only | +| clearning.cpp:41:11:41:11 | s | IR only | +| clearning.cpp:46:7:46:7 | s | IR only | +| clearning.cpp:47:5:47:5 | x | AST only | +| clearning.cpp:48:8:48:11 | * ... | IR only | +| clearning.cpp:48:11:48:11 | s | IR only | +| clearning.cpp:53:3:53:6 | * ... | AST only | +| clearning.cpp:54:5:54:5 | x | AST only | +| clearning.cpp:60:7:60:7 | s | IR only | +| clearning.cpp:61:5:61:5 | x | AST only | +| clearning.cpp:75:2:75:10 | access to array | AST only | +| clearning.cpp:76:10:76:12 | s | IR only | +| clearning.cpp:82:2:82:9 | access to array | AST only | +| clearning.cpp:83:7:83:9 | val | AST only | +| clearning.cpp:83:15:83:17 | s | IR only | +| clearning.cpp:84:10:84:12 | s | IR only | +| clearning.cpp:90:5:90:7 | val | AST only | +| clearning.cpp:91:10:91:12 | s | IR only | +| clearning.cpp:97:4:97:6 | val | AST only | +| clearning.cpp:97:12:97:14 | s | IR only | +| clearning.cpp:98:10:98:12 | s | IR only | +| clearning.cpp:104:4:104:6 | val | AST only | +| clearning.cpp:105:10:105:12 | s | IR only | +| clearning.cpp:111:6:111:8 | val | AST only | +| clearning.cpp:112:10:112:12 | s | IR only | +| clearning.cpp:118:4:118:6 | val | AST only | +| clearning.cpp:119:10:119:12 | s | IR only | +| clearning.cpp:124:4:124:6 | val | AST only | +| clearning.cpp:125:4:125:6 | val | AST only | +| clearning.cpp:131:4:131:6 | val | AST only | +| clearning.cpp:132:4:132:6 | val | AST only | +| clearning.cpp:138:4:138:6 | val | AST only | +| clearning.cpp:139:6:139:8 | val | AST only | +| clearning.cpp:151:5:151:7 | val | AST only | +| clearning.cpp:152:10:152:12 | s | IR only | +| clearning.cpp:157:5:157:7 | val | AST only | +| clearning.cpp:158:5:158:7 | val | AST only | +| clearning.cpp:159:10:159:12 | s | IR only | +| clearning.cpp:164:5:164:7 | val | AST only | +| clearning.cpp:165:5:165:7 | val | AST only | +| clearning.cpp:166:10:166:12 | s | IR only | +| clearning.cpp:171:5:171:7 | val | AST only | +| clearning.cpp:172:5:172:7 | val | AST only | +| clearning.cpp:173:10:173:12 | s | IR only | +| clearning.cpp:178:5:178:7 | val | AST only | +| clearning.cpp:179:5:179:7 | val | AST only | +| clearning.cpp:179:13:179:15 | s | IR only | +| clearning.cpp:180:10:180:12 | s | IR only | | complex.cpp:9:20:9:21 | this | IR only | | complex.cpp:10:20:10:21 | this | IR only | | complex.cpp:11:22:11:23 | a_ | AST only | diff --git a/cpp/ql/test/library-tests/dataflow/fields/partial-definition-ir.expected b/cpp/ql/test/library-tests/dataflow/fields/partial-definition-ir.expected index 1818cea24bb..823997fd7d3 100644 --- a/cpp/ql/test/library-tests/dataflow/fields/partial-definition-ir.expected +++ b/cpp/ql/test/library-tests/dataflow/fields/partial-definition-ir.expected @@ -408,6 +408,90 @@ | by_reference.cpp:135:27:135:27 | a | | by_reference.cpp:136:8:136:13 | pouter | | by_reference.cpp:136:16:136:16 | a | +| clearning.cpp:18:5:18:5 | s | +| clearning.cpp:19:4:19:4 | s | +| clearning.cpp:20:10:20:10 | s | +| clearning.cpp:25:5:25:5 | s | +| clearning.cpp:26:5:26:5 | s | +| clearning.cpp:27:10:27:10 | s | +| clearning.cpp:32:4:32:4 | s | +| clearning.cpp:33:5:33:5 | s | +| clearning.cpp:34:8:34:11 | * ... | +| clearning.cpp:34:9:34:9 | s | +| clearning.cpp:39:4:39:4 | s | +| clearning.cpp:40:3:40:3 | s | +| clearning.cpp:41:8:41:11 | * ... | +| clearning.cpp:41:9:41:9 | s | +| clearning.cpp:46:5:46:5 | s | +| clearning.cpp:47:3:47:3 | s | +| clearning.cpp:48:8:48:11 | * ... | +| clearning.cpp:48:9:48:9 | s | +| clearning.cpp:53:4:53:4 | s | +| clearning.cpp:54:3:54:3 | s | +| clearning.cpp:55:8:55:8 | s | +| clearning.cpp:55:10:55:10 | x | +| clearning.cpp:60:5:60:5 | s | +| clearning.cpp:61:3:61:3 | s | +| clearning.cpp:62:8:62:8 | s | +| clearning.cpp:62:10:62:10 | x | +| clearning.cpp:74:18:74:18 | s | +| clearning.cpp:74:20:74:22 | val | +| clearning.cpp:75:2:75:2 | s | +| clearning.cpp:76:8:76:8 | s | +| clearning.cpp:81:18:81:18 | s | +| clearning.cpp:81:20:81:22 | val | +| clearning.cpp:82:2:82:2 | s | +| clearning.cpp:83:5:83:5 | s | +| clearning.cpp:83:13:83:13 | s | +| clearning.cpp:84:8:84:8 | s | +| clearning.cpp:89:18:89:18 | s | +| clearning.cpp:89:20:89:22 | val | +| clearning.cpp:90:3:90:3 | s | +| clearning.cpp:91:8:91:8 | s | +| clearning.cpp:96:18:96:18 | s | +| clearning.cpp:96:20:96:22 | val | +| clearning.cpp:97:2:97:2 | s | +| clearning.cpp:97:10:97:10 | s | +| clearning.cpp:98:8:98:8 | s | +| clearning.cpp:103:18:103:18 | s | +| clearning.cpp:103:20:103:22 | val | +| clearning.cpp:104:2:104:2 | s | +| clearning.cpp:105:8:105:8 | s | +| clearning.cpp:110:18:110:18 | s | +| clearning.cpp:110:20:110:22 | val | +| clearning.cpp:111:4:111:4 | s | +| clearning.cpp:112:8:112:8 | s | +| clearning.cpp:117:18:117:18 | s | +| clearning.cpp:117:20:117:22 | val | +| clearning.cpp:118:2:118:2 | s | +| clearning.cpp:119:8:119:8 | s | +| clearning.cpp:124:2:124:2 | s | +| clearning.cpp:125:2:125:2 | s | +| clearning.cpp:126:7:126:7 | s | +| clearning.cpp:126:9:126:11 | val | +| clearning.cpp:131:2:131:2 | s | +| clearning.cpp:132:2:132:2 | s | +| clearning.cpp:133:7:133:7 | s | +| clearning.cpp:133:9:133:11 | val | +| clearning.cpp:138:2:138:2 | s | +| clearning.cpp:139:4:139:4 | s | +| clearning.cpp:140:7:140:7 | s | +| clearning.cpp:140:9:140:11 | val | +| clearning.cpp:151:3:151:3 | s | +| clearning.cpp:152:8:152:8 | s | +| clearning.cpp:157:3:157:3 | s | +| clearning.cpp:158:3:158:3 | s | +| clearning.cpp:159:8:159:8 | s | +| clearning.cpp:164:3:164:3 | s | +| clearning.cpp:165:3:165:3 | s | +| clearning.cpp:166:8:166:8 | s | +| clearning.cpp:171:3:171:3 | s | +| clearning.cpp:172:3:172:3 | s | +| clearning.cpp:173:8:173:8 | s | +| clearning.cpp:178:3:178:3 | s | +| clearning.cpp:179:3:179:3 | s | +| clearning.cpp:179:11:179:11 | s | +| clearning.cpp:180:8:180:8 | s | | complex.cpp:9:20:9:21 | this | | complex.cpp:10:20:10:21 | this | | complex.cpp:11:22:11:23 | this | diff --git a/cpp/ql/test/library-tests/dataflow/fields/partial-definition.expected b/cpp/ql/test/library-tests/dataflow/fields/partial-definition.expected index 373e357142a..c37eca67c75 100644 --- a/cpp/ql/test/library-tests/dataflow/fields/partial-definition.expected +++ b/cpp/ql/test/library-tests/dataflow/fields/partial-definition.expected @@ -348,6 +348,92 @@ | by_reference.cpp:135:27:135:27 | a | | by_reference.cpp:136:8:136:13 | pouter | | by_reference.cpp:136:16:136:16 | a | +| clearning.cpp:19:3:19:6 | * ... | +| clearning.cpp:19:4:19:4 | s | +| clearning.cpp:32:3:32:6 | * ... | +| clearning.cpp:32:4:32:4 | s | +| clearning.cpp:39:3:39:6 | * ... | +| clearning.cpp:39:4:39:4 | s | +| clearning.cpp:40:3:40:3 | s | +| clearning.cpp:40:5:40:5 | x | +| clearning.cpp:47:3:47:3 | s | +| clearning.cpp:47:5:47:5 | x | +| clearning.cpp:53:3:53:6 | * ... | +| clearning.cpp:53:4:53:4 | s | +| clearning.cpp:54:3:54:3 | s | +| clearning.cpp:54:5:54:5 | x | +| clearning.cpp:55:8:55:8 | s | +| clearning.cpp:55:10:55:10 | x | +| clearning.cpp:61:3:61:3 | s | +| clearning.cpp:61:5:61:5 | x | +| clearning.cpp:62:8:62:8 | s | +| clearning.cpp:62:10:62:10 | x | +| clearning.cpp:74:18:74:18 | s | +| clearning.cpp:74:20:74:22 | val | +| clearning.cpp:75:2:75:2 | s | +| clearning.cpp:75:2:75:10 | access to array | +| clearning.cpp:81:18:81:18 | s | +| clearning.cpp:81:20:81:22 | val | +| clearning.cpp:82:2:82:2 | s | +| clearning.cpp:82:2:82:9 | access to array | +| clearning.cpp:83:5:83:5 | s | +| clearning.cpp:83:7:83:9 | val | +| clearning.cpp:89:18:89:18 | s | +| clearning.cpp:89:20:89:22 | val | +| clearning.cpp:90:3:90:3 | s | +| clearning.cpp:90:5:90:7 | val | +| clearning.cpp:96:18:96:18 | s | +| clearning.cpp:96:20:96:22 | val | +| clearning.cpp:97:2:97:2 | s | +| clearning.cpp:97:4:97:6 | val | +| clearning.cpp:103:18:103:18 | s | +| clearning.cpp:103:20:103:22 | val | +| clearning.cpp:104:2:104:2 | s | +| clearning.cpp:104:4:104:6 | val | +| clearning.cpp:110:18:110:18 | s | +| clearning.cpp:110:20:110:22 | val | +| clearning.cpp:111:4:111:4 | s | +| clearning.cpp:111:6:111:8 | val | +| clearning.cpp:117:18:117:18 | s | +| clearning.cpp:117:20:117:22 | val | +| clearning.cpp:118:2:118:2 | s | +| clearning.cpp:118:4:118:6 | val | +| clearning.cpp:124:2:124:2 | s | +| clearning.cpp:124:4:124:6 | val | +| clearning.cpp:125:2:125:2 | s | +| clearning.cpp:125:4:125:6 | val | +| clearning.cpp:126:7:126:7 | s | +| clearning.cpp:126:9:126:11 | val | +| clearning.cpp:131:2:131:2 | s | +| clearning.cpp:131:4:131:6 | val | +| clearning.cpp:132:2:132:2 | s | +| clearning.cpp:132:4:132:6 | val | +| clearning.cpp:133:7:133:7 | s | +| clearning.cpp:133:9:133:11 | val | +| clearning.cpp:138:2:138:2 | s | +| clearning.cpp:138:4:138:6 | val | +| clearning.cpp:139:4:139:4 | s | +| clearning.cpp:139:6:139:8 | val | +| clearning.cpp:140:7:140:7 | s | +| clearning.cpp:140:9:140:11 | val | +| clearning.cpp:151:3:151:3 | s | +| clearning.cpp:151:5:151:7 | val | +| clearning.cpp:157:3:157:3 | s | +| clearning.cpp:157:5:157:7 | val | +| clearning.cpp:158:3:158:3 | s | +| clearning.cpp:158:5:158:7 | val | +| clearning.cpp:164:3:164:3 | s | +| clearning.cpp:164:5:164:7 | val | +| clearning.cpp:165:3:165:3 | s | +| clearning.cpp:165:5:165:7 | val | +| clearning.cpp:171:3:171:3 | s | +| clearning.cpp:171:5:171:7 | val | +| clearning.cpp:172:3:172:3 | s | +| clearning.cpp:172:5:172:7 | val | +| clearning.cpp:178:3:178:3 | s | +| clearning.cpp:178:5:178:7 | val | +| clearning.cpp:179:3:179:3 | s | +| clearning.cpp:179:5:179:7 | val | | complex.cpp:11:22:11:23 | a_ | | complex.cpp:11:22:11:23 | this | | complex.cpp:12:22:12:23 | b_ | diff --git a/cpp/ql/test/library-tests/dataflow/fields/path-flow.expected b/cpp/ql/test/library-tests/dataflow/fields/path-flow.expected index 9eef2881545..00a5f1a3f28 100644 --- a/cpp/ql/test/library-tests/dataflow/fields/path-flow.expected +++ b/cpp/ql/test/library-tests/dataflow/fields/path-flow.expected @@ -448,6 +448,42 @@ edges | by_reference.cpp:135:8:135:13 | pouter [inner_ptr, a] | by_reference.cpp:135:16:135:24 | inner_ptr [a] | | by_reference.cpp:135:16:135:24 | inner_ptr [a] | by_reference.cpp:135:27:135:27 | a | | by_reference.cpp:136:8:136:13 | pouter [a] | by_reference.cpp:136:16:136:16 | a | +| clearning.cpp:53:4:53:4 | s [post update] [x] | clearning.cpp:55:8:55:8 | s [x] | +| clearning.cpp:53:6:53:6 | x [inner post update] | clearning.cpp:53:4:53:4 | s [post update] [x] | +| clearning.cpp:53:10:53:19 | call to user_input | clearning.cpp:53:6:53:6 | x [inner post update] | +| clearning.cpp:55:8:55:8 | s [x] | clearning.cpp:55:10:55:10 | x | +| clearning.cpp:124:2:124:2 | s [post update] [val] | clearning.cpp:126:7:126:7 | s [val] | +| clearning.cpp:124:2:124:25 | ... = ... | clearning.cpp:124:2:124:2 | s [post update] [val] | +| clearning.cpp:124:10:124:19 | call to user_input | clearning.cpp:124:2:124:25 | ... = ... | +| clearning.cpp:126:7:126:7 | s [val] | clearning.cpp:126:9:126:11 | val | +| clearning.cpp:131:2:131:2 | s [post update] [val] | clearning.cpp:133:7:133:7 | s [val] | +| clearning.cpp:131:2:131:25 | ... = ... | clearning.cpp:131:2:131:2 | s [post update] [val] | +| clearning.cpp:131:10:131:19 | call to user_input | clearning.cpp:131:2:131:25 | ... = ... | +| clearning.cpp:133:7:133:7 | s [val] | clearning.cpp:133:9:133:11 | val | +| clearning.cpp:138:2:138:2 | s [post update] [val] | clearning.cpp:140:7:140:7 | s [val] | +| clearning.cpp:138:2:138:25 | ... = ... | clearning.cpp:138:2:138:2 | s [post update] [val] | +| clearning.cpp:138:10:138:19 | call to user_input | clearning.cpp:138:2:138:25 | ... = ... | +| clearning.cpp:140:7:140:7 | s [val] | clearning.cpp:140:9:140:11 | val | +| clearning.cpp:151:3:151:3 | s [post update] [val] | clearning.cpp:152:8:152:8 | s [val] | +| clearning.cpp:151:3:151:22 | ... = ... | clearning.cpp:151:3:151:3 | s [post update] [val] | +| clearning.cpp:151:11:151:20 | call to user_input | clearning.cpp:151:3:151:22 | ... = ... | +| clearning.cpp:152:8:152:8 | s [val] | clearning.cpp:152:10:152:12 | val | +| clearning.cpp:157:3:157:3 | s [post update] [val] | clearning.cpp:159:8:159:8 | s [val] | +| clearning.cpp:157:3:157:22 | ... = ... | clearning.cpp:157:3:157:3 | s [post update] [val] | +| clearning.cpp:157:11:157:20 | call to user_input | clearning.cpp:157:3:157:22 | ... = ... | +| clearning.cpp:159:8:159:8 | s [val] | clearning.cpp:159:10:159:12 | val | +| clearning.cpp:164:3:164:3 | s [post update] [val] | clearning.cpp:166:8:166:8 | s [val] | +| clearning.cpp:164:3:164:22 | ... = ... | clearning.cpp:164:3:164:3 | s [post update] [val] | +| clearning.cpp:164:11:164:20 | call to user_input | clearning.cpp:164:3:164:22 | ... = ... | +| clearning.cpp:166:8:166:8 | s [val] | clearning.cpp:166:10:166:12 | val | +| clearning.cpp:171:3:171:3 | s [post update] [val] | clearning.cpp:173:8:173:8 | s [val] | +| clearning.cpp:171:3:171:22 | ... = ... | clearning.cpp:171:3:171:3 | s [post update] [val] | +| clearning.cpp:171:11:171:20 | call to user_input | clearning.cpp:171:3:171:22 | ... = ... | +| clearning.cpp:173:8:173:8 | s [val] | clearning.cpp:173:10:173:12 | val | +| clearning.cpp:178:3:178:3 | s [post update] [val] | clearning.cpp:180:8:180:8 | s [val] | +| clearning.cpp:178:3:178:22 | ... = ... | clearning.cpp:178:3:178:3 | s [post update] [val] | +| clearning.cpp:178:11:178:20 | call to user_input | clearning.cpp:178:3:178:22 | ... = ... | +| clearning.cpp:180:8:180:8 | s [val] | clearning.cpp:180:10:180:12 | val | | complex.cpp:9:7:9:7 | this [a_] | complex.cpp:9:20:9:21 | this [a_] | | complex.cpp:9:20:9:21 | this [a_] | complex.cpp:9:20:9:21 | a_ | | complex.cpp:10:7:10:7 | this [b_] | complex.cpp:10:20:10:21 | this [b_] | @@ -1155,6 +1191,51 @@ nodes | by_reference.cpp:135:27:135:27 | a | semmle.label | a | | by_reference.cpp:136:8:136:13 | pouter [a] | semmle.label | pouter [a] | | by_reference.cpp:136:16:136:16 | a | semmle.label | a | +| clearning.cpp:53:4:53:4 | s [post update] [x] | semmle.label | s [post update] [x] | +| clearning.cpp:53:6:53:6 | x [inner post update] | semmle.label | x [inner post update] | +| clearning.cpp:53:10:53:19 | call to user_input | semmle.label | call to user_input | +| clearning.cpp:55:8:55:8 | s [x] | semmle.label | s [x] | +| clearning.cpp:55:10:55:10 | x | semmle.label | x | +| clearning.cpp:124:2:124:2 | s [post update] [val] | semmle.label | s [post update] [val] | +| clearning.cpp:124:2:124:25 | ... = ... | semmle.label | ... = ... | +| clearning.cpp:124:10:124:19 | call to user_input | semmle.label | call to user_input | +| clearning.cpp:126:7:126:7 | s [val] | semmle.label | s [val] | +| clearning.cpp:126:9:126:11 | val | semmle.label | val | +| clearning.cpp:131:2:131:2 | s [post update] [val] | semmle.label | s [post update] [val] | +| clearning.cpp:131:2:131:25 | ... = ... | semmle.label | ... = ... | +| clearning.cpp:131:10:131:19 | call to user_input | semmle.label | call to user_input | +| clearning.cpp:133:7:133:7 | s [val] | semmle.label | s [val] | +| clearning.cpp:133:9:133:11 | val | semmle.label | val | +| clearning.cpp:138:2:138:2 | s [post update] [val] | semmle.label | s [post update] [val] | +| clearning.cpp:138:2:138:25 | ... = ... | semmle.label | ... = ... | +| clearning.cpp:138:10:138:19 | call to user_input | semmle.label | call to user_input | +| clearning.cpp:140:7:140:7 | s [val] | semmle.label | s [val] | +| clearning.cpp:140:9:140:11 | val | semmle.label | val | +| clearning.cpp:151:3:151:3 | s [post update] [val] | semmle.label | s [post update] [val] | +| clearning.cpp:151:3:151:22 | ... = ... | semmle.label | ... = ... | +| clearning.cpp:151:11:151:20 | call to user_input | semmle.label | call to user_input | +| clearning.cpp:152:8:152:8 | s [val] | semmle.label | s [val] | +| clearning.cpp:152:10:152:12 | val | semmle.label | val | +| clearning.cpp:157:3:157:3 | s [post update] [val] | semmle.label | s [post update] [val] | +| clearning.cpp:157:3:157:22 | ... = ... | semmle.label | ... = ... | +| clearning.cpp:157:11:157:20 | call to user_input | semmle.label | call to user_input | +| clearning.cpp:159:8:159:8 | s [val] | semmle.label | s [val] | +| clearning.cpp:159:10:159:12 | val | semmle.label | val | +| clearning.cpp:164:3:164:3 | s [post update] [val] | semmle.label | s [post update] [val] | +| clearning.cpp:164:3:164:22 | ... = ... | semmle.label | ... = ... | +| clearning.cpp:164:11:164:20 | call to user_input | semmle.label | call to user_input | +| clearning.cpp:166:8:166:8 | s [val] | semmle.label | s [val] | +| clearning.cpp:166:10:166:12 | val | semmle.label | val | +| clearning.cpp:171:3:171:3 | s [post update] [val] | semmle.label | s [post update] [val] | +| clearning.cpp:171:3:171:22 | ... = ... | semmle.label | ... = ... | +| clearning.cpp:171:11:171:20 | call to user_input | semmle.label | call to user_input | +| clearning.cpp:173:8:173:8 | s [val] | semmle.label | s [val] | +| clearning.cpp:173:10:173:12 | val | semmle.label | val | +| clearning.cpp:178:3:178:3 | s [post update] [val] | semmle.label | s [post update] [val] | +| clearning.cpp:178:3:178:22 | ... = ... | semmle.label | ... = ... | +| clearning.cpp:178:11:178:20 | call to user_input | semmle.label | call to user_input | +| clearning.cpp:180:8:180:8 | s [val] | semmle.label | s [val] | +| clearning.cpp:180:10:180:12 | val | semmle.label | val | | complex.cpp:9:7:9:7 | this [a_] | semmle.label | this [a_] | | complex.cpp:9:20:9:21 | a_ | semmle.label | a_ | | complex.cpp:9:20:9:21 | this [a_] | semmle.label | this [a_] | @@ -1551,6 +1632,15 @@ subpaths | by_reference.cpp:134:29:134:29 | a | by_reference.cpp:88:13:88:22 | call to user_input | by_reference.cpp:134:29:134:29 | a | a flows from $@ | by_reference.cpp:88:13:88:22 | call to user_input | call to user_input | | by_reference.cpp:135:27:135:27 | a | by_reference.cpp:88:13:88:22 | call to user_input | by_reference.cpp:135:27:135:27 | a | a flows from $@ | by_reference.cpp:88:13:88:22 | call to user_input | call to user_input | | by_reference.cpp:136:16:136:16 | a | by_reference.cpp:96:8:96:17 | call to user_input | by_reference.cpp:136:16:136:16 | a | a flows from $@ | by_reference.cpp:96:8:96:17 | call to user_input | call to user_input | +| clearning.cpp:55:10:55:10 | x | clearning.cpp:53:10:53:19 | call to user_input | clearning.cpp:55:10:55:10 | x | x flows from $@ | clearning.cpp:53:10:53:19 | call to user_input | call to user_input | +| clearning.cpp:126:9:126:11 | val | clearning.cpp:124:10:124:19 | call to user_input | clearning.cpp:126:9:126:11 | val | val flows from $@ | clearning.cpp:124:10:124:19 | call to user_input | call to user_input | +| clearning.cpp:133:9:133:11 | val | clearning.cpp:131:10:131:19 | call to user_input | clearning.cpp:133:9:133:11 | val | val flows from $@ | clearning.cpp:131:10:131:19 | call to user_input | call to user_input | +| clearning.cpp:140:9:140:11 | val | clearning.cpp:138:10:138:19 | call to user_input | clearning.cpp:140:9:140:11 | val | val flows from $@ | clearning.cpp:138:10:138:19 | call to user_input | call to user_input | +| clearning.cpp:152:10:152:12 | val | clearning.cpp:151:11:151:20 | call to user_input | clearning.cpp:152:10:152:12 | val | val flows from $@ | clearning.cpp:151:11:151:20 | call to user_input | call to user_input | +| clearning.cpp:159:10:159:12 | val | clearning.cpp:157:11:157:20 | call to user_input | clearning.cpp:159:10:159:12 | val | val flows from $@ | clearning.cpp:157:11:157:20 | call to user_input | call to user_input | +| clearning.cpp:166:10:166:12 | val | clearning.cpp:164:11:164:20 | call to user_input | clearning.cpp:166:10:166:12 | val | val flows from $@ | clearning.cpp:164:11:164:20 | call to user_input | call to user_input | +| clearning.cpp:173:10:173:12 | val | clearning.cpp:171:11:171:20 | call to user_input | clearning.cpp:173:10:173:12 | val | val flows from $@ | clearning.cpp:171:11:171:20 | call to user_input | call to user_input | +| clearning.cpp:180:10:180:12 | val | clearning.cpp:178:11:178:20 | call to user_input | clearning.cpp:180:10:180:12 | val | val flows from $@ | clearning.cpp:178:11:178:20 | call to user_input | call to user_input | | complex.cpp:42:18:42:18 | call to a | complex.cpp:53:19:53:28 | call to user_input | complex.cpp:42:18:42:18 | call to a | call to a flows from $@ | complex.cpp:53:19:53:28 | call to user_input | call to user_input | | complex.cpp:42:18:42:18 | call to a | complex.cpp:55:19:55:28 | call to user_input | complex.cpp:42:18:42:18 | call to a | call to a flows from $@ | complex.cpp:55:19:55:28 | call to user_input | call to user_input | | complex.cpp:43:18:43:18 | call to b | complex.cpp:54:19:54:28 | call to user_input | complex.cpp:43:18:43:18 | call to b | call to b flows from $@ | complex.cpp:54:19:54:28 | call to user_input | call to user_input | 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 f6a7625b57a..44965a9f2d9 100644 --- a/cpp/ql/test/library-tests/dataflow/taint-tests/localTaint.expected +++ b/cpp/ql/test/library-tests/dataflow/taint-tests/localTaint.expected @@ -6591,6 +6591,20 @@ | taint.cpp:702:4:702:6 | ... ++ | taint.cpp:703:8:703:8 | p | TAINT | | taint.cpp:702:10:702:11 | * ... | taint.cpp:702:3:702:11 | ... = ... | | | taint.cpp:702:11:702:11 | s | taint.cpp:702:10:702:11 | * ... | TAINT | +| taint.cpp:709:25:709:25 | d | taint.cpp:709:25:709:25 | d | | +| taint.cpp:709:25:709:25 | d | taint.cpp:711:10:711:10 | d | | +| taint.cpp:709:25:709:25 | d | taint.cpp:712:7:712:7 | d | | +| taint.cpp:709:34:709:34 | s | taint.cpp:709:34:709:34 | s | | +| taint.cpp:709:34:709:34 | s | taint.cpp:710:18:710:18 | s | | +| taint.cpp:709:34:709:34 | s | taint.cpp:711:13:711:13 | s | | +| taint.cpp:710:18:710:18 | ref arg s | taint.cpp:709:34:709:34 | s | | +| taint.cpp:710:18:710:18 | ref arg s | taint.cpp:711:13:711:13 | s | | +| taint.cpp:711:10:711:10 | d | taint.cpp:711:2:711:8 | call to strncpy | | +| taint.cpp:711:10:711:10 | ref arg d | taint.cpp:709:25:709:25 | d | | +| taint.cpp:711:10:711:10 | ref arg d | taint.cpp:712:7:712:7 | d | | +| taint.cpp:711:13:711:13 | s | taint.cpp:711:2:711:8 | call to strncpy | TAINT | +| taint.cpp:711:13:711:13 | s | taint.cpp:711:10:711:10 | ref arg d | TAINT | +| taint.cpp:712:7:712:7 | ref arg d | taint.cpp:709:25:709:25 | d | | | vector.cpp:16:43:16:49 | source1 | vector.cpp:17:26:17:32 | source1 | | | vector.cpp:16:43:16:49 | source1 | vector.cpp:31:38:31:44 | source1 | | | vector.cpp:17:21:17:33 | call to vector | vector.cpp:19:14:19:14 | v | | diff --git a/cpp/ql/test/library-tests/dataflow/taint-tests/taint.cpp b/cpp/ql/test/library-tests/dataflow/taint-tests/taint.cpp index fa6074e44f6..9810418a95e 100644 --- a/cpp/ql/test/library-tests/dataflow/taint-tests/taint.cpp +++ b/cpp/ql/test/library-tests/dataflow/taint-tests/taint.cpp @@ -702,4 +702,12 @@ namespace strings { *p++ = *s; sink(p); // $ ast ir } +} + +char * strncpy (char *, const char *, unsigned long); + +void test_strncpy(char* d, char* s) { + argument_source(s); + strncpy(d, s, 16); + sink(d); // $ ast ir } \ No newline at end of file diff --git a/cpp/ql/test/library-tests/ir/range-analysis/test.cpp b/cpp/ql/test/library-tests/ir/range-analysis/test.cpp index 95e6474124a..2271953b7ab 100644 --- a/cpp/ql/test/library-tests/ir/range-analysis/test.cpp +++ b/cpp/ql/test/library-tests/ir/range-analysis/test.cpp @@ -70,3 +70,28 @@ int f4(int x) { } } } + +// No interesting ranges to check here - this irreducible CFG caused an infinite loop due to back edge detection +void gotoLoop(bool b1, bool b2) +{ + int j; + + if (b1) + return; + + if (!b2) + { + for (j = 0; j < 10; ++j) + { + goto main_decode_loop; + } + } + else + { + for (j = 0; j < 10; ++j) + { + int x; + main_decode_loop: + } + } +} diff --git a/cpp/ql/test/query-tests/Security/CWE/CWE-119/SAMATE/OverrunWriteProductFlow.expected b/cpp/ql/test/query-tests/Security/CWE/CWE-119/SAMATE/OverrunWriteProductFlow.expected index 528d164b888..e1665c23315 100644 --- a/cpp/ql/test/query-tests/Security/CWE/CWE-119/SAMATE/OverrunWriteProductFlow.expected +++ b/cpp/ql/test/query-tests/Security/CWE/CWE-119/SAMATE/OverrunWriteProductFlow.expected @@ -1,8 +1,9 @@ edges | test.cpp:16:11:16:21 | mk_string_t indirection [string] | test.cpp:39:21:39:31 | call to mk_string_t indirection [string] | | test.cpp:18:5:18:30 | ... = ... | test.cpp:18:10:18:15 | str indirection [post update] [string] | -| test.cpp:18:10:18:15 | str indirection [post update] [string] | test.cpp:16:11:16:21 | mk_string_t indirection [string] | +| test.cpp:18:10:18:15 | str indirection [post update] [string] | test.cpp:19:5:19:7 | str indirection [string] | | test.cpp:18:19:18:24 | call to malloc | test.cpp:18:5:18:30 | ... = ... | +| test.cpp:19:5:19:7 | str indirection [string] | test.cpp:16:11:16:21 | mk_string_t indirection [string] | | test.cpp:39:21:39:31 | call to mk_string_t indirection [string] | test.cpp:42:13:42:15 | str indirection [string] | | test.cpp:39:21:39:31 | call to mk_string_t indirection [string] | test.cpp:72:17:72:19 | str indirection [string] | | test.cpp:39:21:39:31 | call to mk_string_t indirection [string] | test.cpp:80:17:80:19 | str indirection [string] | @@ -17,8 +18,9 @@ edges | test.cpp:80:22:80:27 | string indirection | test.cpp:80:22:80:27 | string | | test.cpp:88:11:88:30 | mk_string_t_plus_one indirection [string] | test.cpp:96:21:96:40 | call to mk_string_t_plus_one indirection [string] | | test.cpp:90:5:90:34 | ... = ... | test.cpp:90:10:90:15 | str indirection [post update] [string] | -| test.cpp:90:10:90:15 | str indirection [post update] [string] | test.cpp:88:11:88:30 | mk_string_t_plus_one indirection [string] | +| test.cpp:90:10:90:15 | str indirection [post update] [string] | test.cpp:91:5:91:7 | str indirection [string] | | test.cpp:90:19:90:24 | call to malloc | test.cpp:90:5:90:34 | ... = ... | +| test.cpp:91:5:91:7 | str indirection [string] | test.cpp:88:11:88:30 | mk_string_t_plus_one indirection [string] | | test.cpp:96:21:96:40 | call to mk_string_t_plus_one indirection [string] | test.cpp:99:13:99:15 | str indirection [string] | | test.cpp:96:21:96:40 | call to mk_string_t_plus_one indirection [string] | test.cpp:129:17:129:19 | str indirection [string] | | test.cpp:96:21:96:40 | call to mk_string_t_plus_one indirection [string] | test.cpp:137:17:137:19 | str indirection [string] | @@ -32,16 +34,17 @@ edges | test.cpp:137:17:137:19 | str indirection [string] | test.cpp:137:22:137:27 | string indirection | | test.cpp:137:22:137:27 | string indirection | test.cpp:137:22:137:27 | string | | test.cpp:147:5:147:34 | ... = ... | test.cpp:147:10:147:15 | str indirection [post update] [string] | -| test.cpp:147:10:147:15 | str indirection [post update] [string] | test.cpp:152:13:152:15 | str indirection [string] | -| test.cpp:147:10:147:15 | str indirection [post update] [string] | test.cpp:154:13:154:15 | str indirection [string] | -| test.cpp:147:10:147:15 | str indirection [post update] [string] | test.cpp:156:13:156:15 | str indirection [string] | -| test.cpp:147:10:147:15 | str indirection [post update] [string] | test.cpp:175:17:175:19 | str indirection [string] | -| test.cpp:147:10:147:15 | str indirection [post update] [string] | test.cpp:187:17:187:19 | str indirection [string] | -| test.cpp:147:10:147:15 | str indirection [post update] [string] | test.cpp:195:17:195:19 | str indirection [string] | -| test.cpp:147:10:147:15 | str indirection [post update] [string] | test.cpp:199:17:199:19 | str indirection [string] | -| test.cpp:147:10:147:15 | str indirection [post update] [string] | test.cpp:203:17:203:19 | str indirection [string] | -| test.cpp:147:10:147:15 | str indirection [post update] [string] | test.cpp:207:17:207:19 | str indirection [string] | +| test.cpp:147:10:147:15 | str indirection [post update] [string] | test.cpp:148:5:148:7 | str indirection [string] | | test.cpp:147:19:147:24 | call to malloc | test.cpp:147:5:147:34 | ... = ... | +| test.cpp:148:5:148:7 | str indirection [string] | test.cpp:152:13:152:15 | str indirection [string] | +| test.cpp:148:5:148:7 | str indirection [string] | test.cpp:154:13:154:15 | str indirection [string] | +| test.cpp:148:5:148:7 | str indirection [string] | test.cpp:156:13:156:15 | str indirection [string] | +| test.cpp:148:5:148:7 | str indirection [string] | test.cpp:175:17:175:19 | str indirection [string] | +| test.cpp:148:5:148:7 | str indirection [string] | test.cpp:187:17:187:19 | str indirection [string] | +| test.cpp:148:5:148:7 | str indirection [string] | test.cpp:195:17:195:19 | str indirection [string] | +| test.cpp:148:5:148:7 | str indirection [string] | test.cpp:199:17:199:19 | str indirection [string] | +| test.cpp:148:5:148:7 | str indirection [string] | test.cpp:203:17:203:19 | str indirection [string] | +| test.cpp:148:5:148:7 | str indirection [string] | test.cpp:207:17:207:19 | str indirection [string] | | test.cpp:152:13:152:15 | str indirection [string] | test.cpp:152:18:152:23 | string | | test.cpp:152:13:152:15 | str indirection [string] | test.cpp:152:18:152:23 | string indirection | | test.cpp:152:18:152:23 | string indirection | test.cpp:152:18:152:23 | string | @@ -91,6 +94,7 @@ nodes | test.cpp:18:5:18:30 | ... = ... | semmle.label | ... = ... | | test.cpp:18:10:18:15 | str indirection [post update] [string] | semmle.label | str indirection [post update] [string] | | test.cpp:18:19:18:24 | call to malloc | semmle.label | call to malloc | +| test.cpp:19:5:19:7 | str indirection [string] | semmle.label | str indirection [string] | | test.cpp:39:21:39:31 | call to mk_string_t indirection [string] | semmle.label | call to mk_string_t indirection [string] | | test.cpp:42:13:42:15 | str indirection [string] | semmle.label | str indirection [string] | | test.cpp:42:18:42:23 | string | semmle.label | string | @@ -105,6 +109,7 @@ nodes | test.cpp:90:5:90:34 | ... = ... | semmle.label | ... = ... | | test.cpp:90:10:90:15 | str indirection [post update] [string] | semmle.label | str indirection [post update] [string] | | test.cpp:90:19:90:24 | call to malloc | semmle.label | call to malloc | +| test.cpp:91:5:91:7 | str indirection [string] | semmle.label | str indirection [string] | | test.cpp:96:21:96:40 | call to mk_string_t_plus_one indirection [string] | semmle.label | call to mk_string_t_plus_one indirection [string] | | test.cpp:99:13:99:15 | str indirection [string] | semmle.label | str indirection [string] | | test.cpp:99:18:99:23 | string | semmle.label | string | @@ -118,6 +123,7 @@ nodes | test.cpp:147:5:147:34 | ... = ... | semmle.label | ... = ... | | test.cpp:147:10:147:15 | str indirection [post update] [string] | semmle.label | str indirection [post update] [string] | | test.cpp:147:19:147:24 | call to malloc | semmle.label | call to malloc | +| test.cpp:148:5:148:7 | str indirection [string] | semmle.label | str indirection [string] | | test.cpp:152:13:152:15 | str indirection [string] | semmle.label | str indirection [string] | | test.cpp:152:18:152:23 | string | semmle.label | string | | test.cpp:152:18:152:23 | string indirection | semmle.label | string indirection | diff --git a/cpp/ql/test/query-tests/Security/CWE/CWE-119/semmle/tests/OverflowDestination.expected b/cpp/ql/test/query-tests/Security/CWE/CWE-119/semmle/tests/OverflowDestination.expected index 19de8c61578..8d46c8fe99b 100644 --- a/cpp/ql/test/query-tests/Security/CWE/CWE-119/semmle/tests/OverflowDestination.expected +++ b/cpp/ql/test/query-tests/Security/CWE/CWE-119/semmle/tests/OverflowDestination.expected @@ -8,13 +8,19 @@ edges | overflowdestination.cpp:23:45:23:48 | argv indirection | overflowdestination.cpp:30:17:30:20 | arg1 indirection | | overflowdestination.cpp:23:45:23:48 | argv indirection | overflowdestination.cpp:30:17:30:20 | arg1 indirection | | overflowdestination.cpp:43:8:43:10 | fgets output argument | overflowdestination.cpp:46:15:46:17 | src indirection | +| overflowdestination.cpp:50:52:50:54 | src indirection | overflowdestination.cpp:53:9:53:12 | memcpy output argument | | overflowdestination.cpp:50:52:50:54 | src indirection | overflowdestination.cpp:53:15:53:17 | src indirection | | overflowdestination.cpp:50:52:50:54 | src indirection | overflowdestination.cpp:53:15:53:17 | src indirection | +| overflowdestination.cpp:50:52:50:54 | src indirection | overflowdestination.cpp:54:9:54:12 | memcpy output argument | +| overflowdestination.cpp:53:9:53:12 | memcpy output argument | overflowdestination.cpp:54:9:54:12 | memcpy output argument | +| overflowdestination.cpp:54:9:54:12 | memcpy output argument | overflowdestination.cpp:54:9:54:12 | memcpy output argument | | overflowdestination.cpp:57:52:57:54 | src indirection | overflowdestination.cpp:64:16:64:19 | src2 indirection | | overflowdestination.cpp:57:52:57:54 | src indirection | overflowdestination.cpp:64:16:64:19 | src2 indirection | | overflowdestination.cpp:73:8:73:10 | fgets output argument | overflowdestination.cpp:75:30:75:32 | src indirection | | overflowdestination.cpp:73:8:73:10 | fgets output argument | overflowdestination.cpp:76:30:76:32 | src indirection | +| overflowdestination.cpp:75:30:75:32 | overflowdest_test2 output argument | overflowdestination.cpp:76:30:76:32 | src indirection | | overflowdestination.cpp:75:30:75:32 | src indirection | overflowdestination.cpp:50:52:50:54 | src indirection | +| overflowdestination.cpp:75:30:75:32 | src indirection | overflowdestination.cpp:75:30:75:32 | overflowdest_test2 output argument | | overflowdestination.cpp:76:30:76:32 | src indirection | overflowdestination.cpp:57:52:57:54 | src indirection | nodes | main.cpp:6:27:6:30 | argv indirection | semmle.label | argv indirection | @@ -28,15 +34,20 @@ nodes | overflowdestination.cpp:43:8:43:10 | fgets output argument | semmle.label | fgets output argument | | overflowdestination.cpp:46:15:46:17 | src indirection | semmle.label | src indirection | | overflowdestination.cpp:50:52:50:54 | src indirection | semmle.label | src indirection | +| overflowdestination.cpp:53:9:53:12 | memcpy output argument | semmle.label | memcpy output argument | | overflowdestination.cpp:53:15:53:17 | src indirection | semmle.label | src indirection | | overflowdestination.cpp:53:15:53:17 | src indirection | semmle.label | src indirection | +| overflowdestination.cpp:54:9:54:12 | memcpy output argument | semmle.label | memcpy output argument | | overflowdestination.cpp:57:52:57:54 | src indirection | semmle.label | src indirection | | overflowdestination.cpp:64:16:64:19 | src2 indirection | semmle.label | src2 indirection | | overflowdestination.cpp:64:16:64:19 | src2 indirection | semmle.label | src2 indirection | | overflowdestination.cpp:73:8:73:10 | fgets output argument | semmle.label | fgets output argument | +| overflowdestination.cpp:75:30:75:32 | overflowdest_test2 output argument | semmle.label | overflowdest_test2 output argument | | overflowdestination.cpp:75:30:75:32 | src indirection | semmle.label | src indirection | | overflowdestination.cpp:76:30:76:32 | src indirection | semmle.label | src indirection | subpaths +| overflowdestination.cpp:75:30:75:32 | src indirection | overflowdestination.cpp:50:52:50:54 | src indirection | overflowdestination.cpp:53:9:53:12 | memcpy output argument | overflowdestination.cpp:75:30:75:32 | overflowdest_test2 output argument | +| overflowdestination.cpp:75:30:75:32 | src indirection | overflowdestination.cpp:50:52:50:54 | src indirection | overflowdestination.cpp:54:9:54:12 | memcpy output argument | overflowdestination.cpp:75:30:75:32 | overflowdest_test2 output argument | #select | overflowdestination.cpp:30:2:30:8 | call to strncpy | main.cpp:6:27:6:30 | argv indirection | overflowdestination.cpp:30:17:30:20 | arg1 indirection | To avoid overflow, this operation should be bounded by destination-buffer size, not source-buffer size. | | overflowdestination.cpp:30:2:30:8 | call to strncpy | main.cpp:6:27:6:30 | argv indirection | overflowdestination.cpp:30:17:30:20 | arg1 indirection | To avoid overflow, this operation should be bounded by destination-buffer size, not source-buffer size. | diff --git a/cpp/ql/test/query-tests/Security/CWE/CWE-190/semmle/tainted/qlpack.yml b/cpp/ql/test/query-tests/Security/CWE/CWE-190/semmle/tainted/qlpack.yml index 096e9d13dd2..d29089ece4d 100644 --- a/cpp/ql/test/query-tests/Security/CWE/CWE-190/semmle/tainted/qlpack.yml +++ b/cpp/ql/test/query-tests/Security/CWE/CWE-190/semmle/tainted/qlpack.yml @@ -6,3 +6,4 @@ dependencies: codeql/cpp-queries: ${workspace} extractor: cpp tests: . +warnOnImplicitThis: true diff --git a/csharp/downgrades/qlpack.yml b/csharp/downgrades/qlpack.yml index c326f44bb0d..2ffd6b94f29 100644 --- a/csharp/downgrades/qlpack.yml +++ b/csharp/downgrades/qlpack.yml @@ -2,3 +2,4 @@ name: codeql/csharp-downgrades groups: csharp downgrades: . library: true +warnOnImplicitThis: true diff --git a/csharp/ql/campaigns/Solorigate/lib/CHANGELOG.md b/csharp/ql/campaigns/Solorigate/lib/CHANGELOG.md index ad7a007007f..b466881d9d7 100644 --- a/csharp/ql/campaigns/Solorigate/lib/CHANGELOG.md +++ b/csharp/ql/campaigns/Solorigate/lib/CHANGELOG.md @@ -1,3 +1,7 @@ +## 1.5.3 + +No user-facing changes. + ## 1.5.2 No user-facing changes. diff --git a/csharp/ql/campaigns/Solorigate/lib/change-notes/released/1.5.3.md b/csharp/ql/campaigns/Solorigate/lib/change-notes/released/1.5.3.md new file mode 100644 index 00000000000..2e9bcb5e663 --- /dev/null +++ b/csharp/ql/campaigns/Solorigate/lib/change-notes/released/1.5.3.md @@ -0,0 +1,3 @@ +## 1.5.3 + +No user-facing changes. diff --git a/csharp/ql/campaigns/Solorigate/lib/codeql-pack.release.yml b/csharp/ql/campaigns/Solorigate/lib/codeql-pack.release.yml index 7eb901bae56..232224b0e26 100644 --- a/csharp/ql/campaigns/Solorigate/lib/codeql-pack.release.yml +++ b/csharp/ql/campaigns/Solorigate/lib/codeql-pack.release.yml @@ -1,2 +1,2 @@ --- -lastReleaseVersion: 1.5.2 +lastReleaseVersion: 1.5.3 diff --git a/csharp/ql/campaigns/Solorigate/lib/qlpack.yml b/csharp/ql/campaigns/Solorigate/lib/qlpack.yml index 4f2900e0b73..8d19bca1d61 100644 --- a/csharp/ql/campaigns/Solorigate/lib/qlpack.yml +++ b/csharp/ql/campaigns/Solorigate/lib/qlpack.yml @@ -1,8 +1,9 @@ name: codeql/csharp-solorigate-all -version: 1.5.3-dev +version: 1.5.4-dev groups: - csharp - solorigate library: true dependencies: codeql/csharp-all: ${workspace} +warnOnImplicitThis: true diff --git a/csharp/ql/campaigns/Solorigate/src/CHANGELOG.md b/csharp/ql/campaigns/Solorigate/src/CHANGELOG.md index ad7a007007f..b466881d9d7 100644 --- a/csharp/ql/campaigns/Solorigate/src/CHANGELOG.md +++ b/csharp/ql/campaigns/Solorigate/src/CHANGELOG.md @@ -1,3 +1,7 @@ +## 1.5.3 + +No user-facing changes. + ## 1.5.2 No user-facing changes. diff --git a/csharp/ql/campaigns/Solorigate/src/change-notes/released/1.5.3.md b/csharp/ql/campaigns/Solorigate/src/change-notes/released/1.5.3.md new file mode 100644 index 00000000000..2e9bcb5e663 --- /dev/null +++ b/csharp/ql/campaigns/Solorigate/src/change-notes/released/1.5.3.md @@ -0,0 +1,3 @@ +## 1.5.3 + +No user-facing changes. diff --git a/csharp/ql/campaigns/Solorigate/src/codeql-pack.release.yml b/csharp/ql/campaigns/Solorigate/src/codeql-pack.release.yml index 7eb901bae56..232224b0e26 100644 --- a/csharp/ql/campaigns/Solorigate/src/codeql-pack.release.yml +++ b/csharp/ql/campaigns/Solorigate/src/codeql-pack.release.yml @@ -1,2 +1,2 @@ --- -lastReleaseVersion: 1.5.2 +lastReleaseVersion: 1.5.3 diff --git a/csharp/ql/campaigns/Solorigate/src/qlpack.yml b/csharp/ql/campaigns/Solorigate/src/qlpack.yml index 2318576e19e..6bcc2def6f8 100644 --- a/csharp/ql/campaigns/Solorigate/src/qlpack.yml +++ b/csharp/ql/campaigns/Solorigate/src/qlpack.yml @@ -1,5 +1,5 @@ name: codeql/csharp-solorigate-queries -version: 1.5.3-dev +version: 1.5.4-dev groups: - csharp - solorigate @@ -7,3 +7,4 @@ defaultSuiteFile: codeql-suites/solorigate.qls dependencies: codeql/csharp-all: ${workspace} codeql/csharp-solorigate-all: ${workspace} +warnOnImplicitThis: true diff --git a/csharp/ql/campaigns/Solorigate/test/qlpack.yml b/csharp/ql/campaigns/Solorigate/test/qlpack.yml index 7093935d651..dfd335f2726 100644 --- a/csharp/ql/campaigns/Solorigate/test/qlpack.yml +++ b/csharp/ql/campaigns/Solorigate/test/qlpack.yml @@ -10,3 +10,4 @@ dependencies: codeql/csharp-solorigate-queries: ${workspace} extractor: csharp tests: . +warnOnImplicitThis: true diff --git a/csharp/ql/consistency-queries/AstConsistency.qll b/csharp/ql/consistency-queries/AstConsistency.qll index 5373b638db7..3871cdf99f2 100644 --- a/csharp/ql/consistency-queries/AstConsistency.qll +++ b/csharp/ql/consistency-queries/AstConsistency.qll @@ -17,3 +17,8 @@ query predicate missingLocation(Element e) { not exists(TupleType t | e = t or e = t.getAField()) and not exists(e.getLocation()) } + +query predicate multipleToString(Element e, string s) { + s = strictconcat(e.toString(), ",") and + strictcount(e.toString()) > 1 +} diff --git a/csharp/ql/consistency-queries/CfgConsistency.ql b/csharp/ql/consistency-queries/CfgConsistency.ql index fe45e5a96d8..c50d7aaa101 100644 --- a/csharp/ql/consistency-queries/CfgConsistency.ql +++ b/csharp/ql/consistency-queries/CfgConsistency.ql @@ -62,3 +62,8 @@ query predicate preBasicBlockConsistency(ControlFlowElement cfe1, ControlFlowEle bbIntraSuccInconsistency(cfe1, cfe2) and s = "intra succ inconsistency" } + +query predicate multipleToString(Node n, string s) { + s = strictconcat(n.toString(), ",") and + strictcount(n.toString()) > 1 +} diff --git a/csharp/ql/consistency-queries/DataFlowConsistency.ql b/csharp/ql/consistency-queries/DataFlowConsistency.ql index f4c86ae3815..8f099fe6daf 100644 --- a/csharp/ql/consistency-queries/DataFlowConsistency.ql +++ b/csharp/ql/consistency-queries/DataFlowConsistency.ql @@ -74,3 +74,8 @@ private class MyConsistencyConfiguration extends ConsistencyConfiguration { override predicate identityLocalStepExclude(Node n) { none() } } + +query predicate multipleToString(Node n, string s) { + s = strictconcat(n.toString(), ",") and + strictcount(n.toString()) > 1 +} diff --git a/csharp/ql/consistency-queries/TypeConsistency.qll b/csharp/ql/consistency-queries/TypeConsistency.qll new file mode 100644 index 00000000000..37504a2df3f --- /dev/null +++ b/csharp/ql/consistency-queries/TypeConsistency.qll @@ -0,0 +1,13 @@ +import csharp +import semmle.code.csharp.Unification + +query predicate missingGvn(Type t, string cls) { + not exists(Gvn::getGlobalValueNumber(t)) and + cls = t.getPrimaryQlClasses() +} + +query predicate multipleGvn(Type t, Gvn::GvnType g, string cls) { + g = Gvn::getGlobalValueNumber(t) and + strictcount(Gvn::getGlobalValueNumber(t)) > 1 and + cls = t.getPrimaryQlClasses() +} diff --git a/csharp/ql/consistency-queries/qlpack.yml b/csharp/ql/consistency-queries/qlpack.yml index 0d36d5156f5..8afefd18259 100644 --- a/csharp/ql/consistency-queries/qlpack.yml +++ b/csharp/ql/consistency-queries/qlpack.yml @@ -3,3 +3,4 @@ groups: [csharp, test, consistency-queries] dependencies: codeql/csharp-all: ${workspace} extractor: csharp +warnOnImplicitThis: true diff --git a/csharp/ql/examples/qlpack.yml b/csharp/ql/examples/qlpack.yml index ca5ba5a3ab4..8796b678c16 100644 --- a/csharp/ql/examples/qlpack.yml +++ b/csharp/ql/examples/qlpack.yml @@ -4,3 +4,4 @@ groups: - examples dependencies: codeql/csharp-all: ${workspace} +warnOnImplicitThis: true diff --git a/csharp/ql/integration-tests/qlpack.yml b/csharp/ql/integration-tests/qlpack.yml index 972ad51f2cf..e66c1239b04 100644 --- a/csharp/ql/integration-tests/qlpack.yml +++ b/csharp/ql/integration-tests/qlpack.yml @@ -1,2 +1,3 @@ dependencies: codeql/csharp-all: '*' +warnOnImplicitThis: true diff --git a/csharp/ql/lib/CHANGELOG.md b/csharp/ql/lib/CHANGELOG.md index 435255a997a..8fc9f20a131 100644 --- a/csharp/ql/lib/CHANGELOG.md +++ b/csharp/ql/lib/CHANGELOG.md @@ -1,3 +1,24 @@ +## 0.6.3 + +### Major Analysis Improvements + +* The extractor has been changed to run after the traced compiler call. This allows inspecting compiler generated files, such as the output of source generators. With this change, `.cshtml` files and their generated `.cshtml.g.cs` counterparts are extracted on dotnet 6 and above. + +### Minor Analysis Improvements + +* C#: Analysis of the `dotnet test` command supplied with a `dll` or `exe` file as argument no longer fails due to the addition of an erroneous `-p:SharedCompilation=false` argument. +* Deleted the deprecated `WebConfigXML`, `ConfigurationXMLElement`, `LocationXMLElement`, `SystemWebXMLElement`, `SystemWebServerXMLElement`, `CustomErrorsXMLElement`, and `HttpRuntimeXMLElement` classes from `WebConfig.qll`. The non-deprecated names with PascalCased Xml suffixes should be used instead. +* Deleted the deprecated `Record` class from both `Types.qll` and `Type.qll`. +* Deleted the deprecated `StructuralComparisonConfiguration` class from `StructuralComparison.qll`, use `sameGvn` instead. +* Deleted the deprecated `isParameterOf` predicate from the `ParameterNode` class. +* Deleted the deprecated `SafeExternalAPICallable`, `ExternalAPIDataNode`, `UntrustedDataToExternalAPIConfig`, `UntrustedExternalAPIDataNode`, and `ExternalAPIUsedWithUntrustedData` classes from `ExternalAPIsQuery.qll`. The non-deprecated names with PascalCased Api suffixes should be used instead. +* Updated the following C# sink kind names. Any custom data extensions that use these sink kinds will need to be updated accordingly in order to continue working. + * `code` to `code-injection` + * `sql` to `sql-injection` + * `html` to `html-injection` + * `xss` to `js-injection` + * `remote` to `file-content-store` + ## 0.6.2 ### Minor Analysis Improvements diff --git a/csharp/ql/lib/change-notes/2023-05-09-models-as-data.md b/csharp/ql/lib/change-notes/2023-05-09-models-as-data.md new file mode 100644 index 00000000000..c0abd8f06c0 --- /dev/null +++ b/csharp/ql/lib/change-notes/2023-05-09-models-as-data.md @@ -0,0 +1,4 @@ +--- +category: minorAnalysis +--- +* Additional support for `command-injection`, `ldap-injection`, `log-injection`, and `url-redirection` sink kinds for Models as Data. \ No newline at end of file diff --git a/csharp/ql/lib/change-notes/2023-05-17-update-csharp-sink-kinds.md b/csharp/ql/lib/change-notes/2023-05-17-update-csharp-sink-kinds.md deleted file mode 100644 index ce6d618af5e..00000000000 --- a/csharp/ql/lib/change-notes/2023-05-17-update-csharp-sink-kinds.md +++ /dev/null @@ -1,9 +0,0 @@ ---- -category: minorAnalysis ---- -* Updated the following C# sink kind names. Any custom data extensions that use these sink kinds will need to be updated accordingly in order to continue working. - * `code` to `code-injection` - * `sql` to `sql-injection` - * `html` to `html-injection` - * `xss` to `js-injection` - * `remote` to `file-content-store` diff --git a/csharp/ql/lib/change-notes/2023-05-30-source-generators.md b/csharp/ql/lib/change-notes/2023-05-30-source-generators.md deleted file mode 100644 index 5483ce6af35..00000000000 --- a/csharp/ql/lib/change-notes/2023-05-30-source-generators.md +++ /dev/null @@ -1,4 +0,0 @@ ---- -category: majorAnalysis ---- -* The extractor has been changed to run after the traced compiler call. This allows inspecting compiler generated files, such as the output of source generators. With this change, `.cshtml` files and their generated `.cshtml.g.cs` counterparts are extracted on dotnet 6 and above. diff --git a/csharp/ql/lib/change-notes/2023-06-06-dotnettest.md b/csharp/ql/lib/change-notes/2023-06-06-dotnettest.md deleted file mode 100644 index e7179b93189..00000000000 --- a/csharp/ql/lib/change-notes/2023-06-06-dotnettest.md +++ /dev/null @@ -1,4 +0,0 @@ ---- -category: minorAnalysis ---- -* C#: Analysis of the `dotnet test` command supplied with a `dll` or `exe` file as argument no longer fails due to the addition of an erroneous `-p:SharedCompilation=false` argument. \ No newline at end of file diff --git a/csharp/ql/lib/change-notes/2023-06-02-delete-deps.md b/csharp/ql/lib/change-notes/released/0.6.3.md similarity index 50% rename from csharp/ql/lib/change-notes/2023-06-02-delete-deps.md rename to csharp/ql/lib/change-notes/released/0.6.3.md index 13402f08147..51f62426686 100644 --- a/csharp/ql/lib/change-notes/2023-06-02-delete-deps.md +++ b/csharp/ql/lib/change-notes/released/0.6.3.md @@ -1,8 +1,20 @@ ---- -category: minorAnalysis ---- +## 0.6.3 + +### Major Analysis Improvements + +* The extractor has been changed to run after the traced compiler call. This allows inspecting compiler generated files, such as the output of source generators. With this change, `.cshtml` files and their generated `.cshtml.g.cs` counterparts are extracted on dotnet 6 and above. + +### Minor Analysis Improvements + +* C#: Analysis of the `dotnet test` command supplied with a `dll` or `exe` file as argument no longer fails due to the addition of an erroneous `-p:SharedCompilation=false` argument. * Deleted the deprecated `WebConfigXML`, `ConfigurationXMLElement`, `LocationXMLElement`, `SystemWebXMLElement`, `SystemWebServerXMLElement`, `CustomErrorsXMLElement`, and `HttpRuntimeXMLElement` classes from `WebConfig.qll`. The non-deprecated names with PascalCased Xml suffixes should be used instead. * Deleted the deprecated `Record` class from both `Types.qll` and `Type.qll`. * Deleted the deprecated `StructuralComparisonConfiguration` class from `StructuralComparison.qll`, use `sameGvn` instead. * Deleted the deprecated `isParameterOf` predicate from the `ParameterNode` class. * Deleted the deprecated `SafeExternalAPICallable`, `ExternalAPIDataNode`, `UntrustedDataToExternalAPIConfig`, `UntrustedExternalAPIDataNode`, and `ExternalAPIUsedWithUntrustedData` classes from `ExternalAPIsQuery.qll`. The non-deprecated names with PascalCased Api suffixes should be used instead. +* Updated the following C# sink kind names. Any custom data extensions that use these sink kinds will need to be updated accordingly in order to continue working. + * `code` to `code-injection` + * `sql` to `sql-injection` + * `html` to `html-injection` + * `xss` to `js-injection` + * `remote` to `file-content-store` diff --git a/csharp/ql/lib/codeql-pack.release.yml b/csharp/ql/lib/codeql-pack.release.yml index 5501a2a1cc5..b7dafe32c5d 100644 --- a/csharp/ql/lib/codeql-pack.release.yml +++ b/csharp/ql/lib/codeql-pack.release.yml @@ -1,2 +1,2 @@ --- -lastReleaseVersion: 0.6.2 +lastReleaseVersion: 0.6.3 diff --git a/csharp/ql/lib/qlpack.yml b/csharp/ql/lib/qlpack.yml index 17e00fa022c..43f20151fdc 100644 --- a/csharp/ql/lib/qlpack.yml +++ b/csharp/ql/lib/qlpack.yml @@ -1,11 +1,12 @@ name: codeql/csharp-all -version: 0.6.3-dev +version: 0.6.4-dev groups: csharp dbscheme: semmlecode.csharp.dbscheme extractor: csharp library: true upgrades: upgrades dependencies: + codeql/mad: ${workspace} codeql/ssa: ${workspace} codeql/tutorial: ${workspace} codeql/util: ${workspace} diff --git a/csharp/ql/lib/semmle/code/csharp/AnnotatedType.qll b/csharp/ql/lib/semmle/code/csharp/AnnotatedType.qll index 57221e47aa9..83bffc9b2a8 100644 --- a/csharp/ql/lib/semmle/code/csharp/AnnotatedType.qll +++ b/csharp/ql/lib/semmle/code/csharp/AnnotatedType.qll @@ -401,6 +401,8 @@ class AnnotatedArrayType extends AnnotatedType { class AnnotatedConstructedType extends AnnotatedType { override ConstructedType type; + AnnotatedConstructedType() { not type instanceof NullableType } + /** Gets the `i`th type argument of this constructed type. */ AnnotatedType getTypeArgument(int i) { result.getType() = type.getTypeArgument(i) and diff --git a/csharp/ql/lib/semmle/code/csharp/Generics.qll b/csharp/ql/lib/semmle/code/csharp/Generics.qll index ee850f25ea3..51c1dbc19fd 100644 --- a/csharp/ql/lib/semmle/code/csharp/Generics.qll +++ b/csharp/ql/lib/semmle/code/csharp/Generics.qll @@ -26,7 +26,8 @@ private import TypeRef class Generic extends DotNet::Generic, Declaration, @generic { Generic() { type_parameters(_, _, this, _) or - type_arguments(_, _, this) + type_arguments(_, _, this) or + nullable_underlying_type(this, _) } } @@ -39,7 +40,7 @@ class Generic extends DotNet::Generic, Declaration, @generic { class UnboundGeneric extends DotNet::UnboundGeneric, Generic { UnboundGeneric() { type_parameters(_, _, this, _) } - override TypeParameter getTypeParameter(int n) { type_parameters(result, n, this, _) } + final override TypeParameter getTypeParameter(int n) { type_parameters(result, n, this, _) } override ConstructedGeneric getAConstructedGeneric() { result.getUnboundGeneric() = this } @@ -67,7 +68,11 @@ private string getTypeParameterCommas(UnboundGeneric ug) { * generic method (`ConstructedMethod`). */ class ConstructedGeneric extends DotNet::ConstructedGeneric, Generic { - ConstructedGeneric() { type_arguments(_, _, this) } + ConstructedGeneric() { + type_arguments(_, _, this) + or + nullable_underlying_type(this, _) + } override UnboundGeneric getUnboundGeneric() { constructed_generic(this, result) } @@ -75,8 +80,6 @@ class ConstructedGeneric extends DotNet::ConstructedGeneric, Generic { result = this.getUnboundGeneric().getUnboundDeclaration() } - override int getNumberOfTypeArguments() { result = count(int i | type_arguments(_, i, this)) } - override Type getTypeArgument(int i) { none() } override Type getATypeArgument() { result = this.getTypeArgument(_) } @@ -410,13 +413,13 @@ class ConstructedType extends ValueOrRefType, ConstructedGeneric { override Location getALocation() { result = this.getUnboundDeclaration().getALocation() } - override Type getTypeArgument(int n) { type_arguments(getTypeRef(result), n, getTypeRef(this)) } + override Type getTypeArgument(int n) { type_arguments(getTypeRef(result), n, this) } override UnboundGenericType getUnboundGeneric() { constructed_generic(this, getTypeRef(result)) } final override Type getChild(int n) { result = this.getTypeArgument(n) } - final override string toStringWithTypes() { + override string toStringWithTypes() { result = this.getUndecoratedName() + "<" + getTypeArgumentsToString(this) + ">" } @@ -424,7 +427,7 @@ class ConstructedType extends ValueOrRefType, ConstructedGeneric { result = this.getUndecoratedName() + "<" + getTypeArgumentsNames(this) + ">" } - final override predicate hasQualifiedName(string qualifier, string name) { + override predicate hasQualifiedName(string qualifier, string name) { exists(string name0 | name = name0 + "<" + getTypeArgumentsQualifiedNames(this) + ">" | exists(string enclosing | this.getDeclaringType().hasQualifiedName(qualifier, enclosing) and diff --git a/csharp/ql/lib/semmle/code/csharp/Type.qll b/csharp/ql/lib/semmle/code/csharp/Type.qll index 85fde20e07d..0b1e90fa7d6 100644 --- a/csharp/ql/lib/semmle/code/csharp/Type.qll +++ b/csharp/ql/lib/semmle/code/csharp/Type.qll @@ -974,29 +974,27 @@ class NullType extends RefType, @null_type { /** * A nullable type, for example `int?`. */ -class NullableType extends ValueType, DotNet::ConstructedGeneric, @nullable_type { +class NullableType extends ValueType, ConstructedType, @nullable_type { /** * Gets the underlying value type of this nullable type. * For example `int` in `int?`. */ Type getUnderlyingType() { nullable_underlying_type(this, getTypeRef(result)) } + override UnboundGenericStruct getUnboundGeneric() { + result.hasQualifiedName("System", "Nullable<>") + } + override string toStringWithTypes() { result = this.getUnderlyingType().toStringWithTypes() + "?" } - override Type getChild(int n) { result = this.getUnderlyingType() and n = 0 } - override Location getALocation() { result = this.getUnderlyingType().getALocation() } override Type getTypeArgument(int p) { p = 0 and result = this.getUnderlyingType() } override string getAPrimaryQlClass() { result = "NullableType" } - final override string getName() { - result = "Nullable<" + this.getUnderlyingType().getName() + ">" - } - final override predicate hasQualifiedName(string qualifier, string name) { qualifier = "System" and name = "Nullable<" + this.getUnderlyingType().getQualifiedName() + ">" @@ -1126,7 +1124,10 @@ class ArglistType extends Type, @arglist_type { * A type that could not be resolved. This could happen if an indirect reference * is not available at compilation time. */ -class UnknownType extends Type, @unknown_type { } +class UnknownType extends Type, @unknown_type { + /** Holds if this is the canonical unknown type, and not a type that failed to extract properly. */ + predicate isCanonical() { types(this, _, "") } +} /** * A type representing a tuple. For example, `(int, bool, string)`. diff --git a/csharp/ql/lib/semmle/code/csharp/TypeRef.qll b/csharp/ql/lib/semmle/code/csharp/TypeRef.qll index 7a6de44419f..f13168dd20d 100644 --- a/csharp/ql/lib/semmle/code/csharp/TypeRef.qll +++ b/csharp/ql/lib/semmle/code/csharp/TypeRef.qll @@ -16,7 +16,7 @@ private class TypeRef extends @typeref { typeref_type(this, result) or not typeref_type(this, _) and - result instanceof UnknownType + result.(UnknownType).isCanonical() } } diff --git a/csharp/ql/lib/semmle/code/csharp/Unification.qll b/csharp/ql/lib/semmle/code/csharp/Unification.qll index c3cbbd9ce97..7570070078f 100644 --- a/csharp/ql/lib/semmle/code/csharp/Unification.qll +++ b/csharp/ql/lib/semmle/code/csharp/Unification.qll @@ -15,9 +15,11 @@ module Gvn { * but only if the enclosing type is not a `GenericType`. */ string getNameNested(Type t) { - if not t instanceof NestedType or t.(NestedType).getDeclaringType() instanceof GenericType - then result = t.getName() - else result = getNameNested(t.(NestedType).getDeclaringType()) + "+" + t.getName() + exists(string name | name = t.getName() | + if not t instanceof NestedType or t.(NestedType).getDeclaringType() instanceof GenericType + then result = name + else result = getNameNested(t.(NestedType).getDeclaringType()) + "+" + name + ) } /** @@ -47,8 +49,22 @@ module Gvn { not exists(this.getGenericDeclaringType()) and result = 0 } + /** + * Same as `getChild`, but safe-guards against potential extractor issues where + * multiple children exist at the same index, which may result in a combinatorial + * explosion. + */ + private Type getChildUnique(int i) { + result = unique(Type t | t = this.getChild(i) | t) + or + strictcount(this.getChild(i)) > 1 and + result.(UnknownType).isCanonical() + } + /** Gets the number of arguments of this type, not taking nested types into account. */ - int getNumberOfArgumentsSelf() { result = count(int i | exists(this.getChild(i)) and i >= 0) } + int getNumberOfArgumentsSelf() { + result = count(int i | exists(this.getChildUnique(i)) and i >= 0) + } /** Gets the number of arguments of this type, taking nested types into account. */ int getNumberOfArguments() { @@ -61,7 +77,7 @@ module Gvn { or exists(int offset | offset = this.getNumberOfDeclaringArguments() and - result = this.getChild(i - offset) and + result = this.getChildUnique(i - offset) and i >= offset ) } @@ -91,13 +107,9 @@ module Gvn { int getNumberOfTypeParameters() { this = TPointerTypeKind() and result = 1 or - this = TNullableTypeKind() and result = 1 - or this = TArrayTypeKind(_, _) and result = 1 or - exists(GenericType t | this = TConstructedType(t.getUnboundDeclaration()) | - result = t.getNumberOfArguments() - ) + exists(GenericType t | this = TConstructedType(t) | result = t.getNumberOfArguments()) } /** Gets the unbound declaration type that this kind corresponds to, if any. */ @@ -106,15 +118,12 @@ module Gvn { /** * Gets a textual representation of this kind when applied to arguments `args`. * - * This predicate is restricted to built-in generics (pointers, nullables, and - * arrays). + * This predicate is restricted to built-in generics (pointers and arrays). */ bindingset[args] string toStringBuiltin(string args) { this = TPointerTypeKind() and result = args + "*" or - this = TNullableTypeKind() and result = args + "?" - or exists(int rnk | this = TArrayTypeKind(_, rnk) | result = args + "[" + concat(int i | i in [0 .. rnk - 2] | ",") + "]" ) @@ -135,8 +144,6 @@ module Gvn { CompoundTypeKind getTypeKind(Type t) { result = TPointerTypeKind() and t instanceof PointerType or - result = TNullableTypeKind() and t instanceof NullableType - or t = any(ArrayType at | result = TArrayTypeKind(at.getDimension(), at.getRank())) or result = TConstructedType(t.getUnboundDeclaration()) @@ -280,6 +287,7 @@ module Gvn { pragma[noinline] private predicate toStringPart(int i, int j) { + this.isFullyConstructed() and exists(int offset | exists(GenericType t, int children | t = this.getConstructedGenericDeclaringTypeAt(i) and @@ -449,14 +457,12 @@ module Gvn { cached newtype TCompoundTypeKind = TPointerTypeKind() { Stages::UnificationStage::forceCachingInSameStage() } or - TNullableTypeKind() or TArrayTypeKind(int dim, int rnk) { exists(ArrayType at | dim = at.getDimension() and rnk = at.getRank()) } or TConstructedType(GenericType unboundDecl) { unboundDecl = any(GenericType t).getUnboundDeclaration() and not unboundDecl instanceof PointerType and - not unboundDecl instanceof NullableType and not unboundDecl instanceof ArrayType and not unboundDecl instanceof TupleType } diff --git a/csharp/ql/lib/semmle/code/csharp/dataflow/ExternalFlow.qll b/csharp/ql/lib/semmle/code/csharp/dataflow/ExternalFlow.qll index 46a19828a81..755b8023040 100644 --- a/csharp/ql/lib/semmle/code/csharp/dataflow/ExternalFlow.qll +++ b/csharp/ql/lib/semmle/code/csharp/dataflow/ExternalFlow.qll @@ -62,8 +62,8 @@ * in the given range. The range is inclusive at both ends. * - "ReturnValue": Selects the return value of a call to the selected element. * - * For summaries, `input` and `output` may be prefixed by one of the following, - * separated by the "of" keyword: + * For summaries, `input` and `output` may be suffixed by any number of the + * following, separated by ".": * - "Element": Selects an element in a collection. * - "Field[f]": Selects the contents of field `f`. * - "Property[p]": Selects the contents of property `p`. @@ -95,6 +95,7 @@ private import internal.DataFlowPublic private import internal.FlowSummaryImpl::Public private import internal.FlowSummaryImpl::Private::External private import internal.FlowSummaryImplSpecific +private import codeql.mad.ModelValidation as SharedModelVal /** Holds if a source model exists for the given parameters. */ predicate sourceModel = Extensions::sourceModel/9; @@ -204,30 +205,18 @@ module ModelValidation { ) } - private string getInvalidModelKind() { - exists(string kind | summaryModel(_, _, _, _, _, _, _, _, kind, _) | - not kind = ["taint", "value"] and - result = "Invalid kind \"" + kind + "\" in summary model." - ) - or - exists(string kind | sinkModel(_, _, _, _, _, _, _, kind, _) | - not kind = - ["code-injection", "sql-injection", "js-injection", "html-injection", "file-content-store"] and - not kind.matches("encryption-%") and - result = "Invalid kind \"" + kind + "\" in sink model." - ) - or - exists(string kind | sourceModel(_, _, _, _, _, _, _, kind, _) | - not kind = ["local", "remote", "file", "file-write"] and - result = "Invalid kind \"" + kind + "\" in source model." - ) - or - exists(string kind | neutralModel(_, _, _, _, kind, _) | - not kind = ["summary", "source", "sink"] and - result = "Invalid kind \"" + kind + "\" in neutral model." - ) + private module KindValConfig implements SharedModelVal::KindValidationConfigSig { + predicate summaryKind(string kind) { summaryModel(_, _, _, _, _, _, _, _, kind, _) } + + predicate sinkKind(string kind) { sinkModel(_, _, _, _, _, _, _, kind, _) } + + predicate sourceKind(string kind) { sourceModel(_, _, _, _, _, _, _, kind, _) } + + predicate neutralKind(string kind) { neutralModel(_, _, _, _, kind, _) } } + private module KindVal = SharedModelVal::KindValidation; + private string getInvalidModelSignature() { exists( string pred, string namespace, string type, string name, string signature, string ext, @@ -269,7 +258,7 @@ module ModelValidation { msg = [ getInvalidModelSignature(), getInvalidModelInput(), getInvalidModelOutput(), - getInvalidModelKind() + KindVal::getInvalidModelKind() ] } } diff --git a/csharp/ql/lib/semmle/code/csharp/dataflow/internal/DataFlowImpl.qll b/csharp/ql/lib/semmle/code/csharp/dataflow/internal/DataFlowImpl.qll index 984c5ae2018..284fff191ae 100644 --- a/csharp/ql/lib/semmle/code/csharp/dataflow/internal/DataFlowImpl.qll +++ b/csharp/ql/lib/semmle/code/csharp/dataflow/internal/DataFlowImpl.qll @@ -2021,7 +2021,8 @@ module Impl { FlowCheckNode() { castNode(this.asNode()) or clearsContentCached(this.asNode(), _) or - expectsContentCached(this.asNode(), _) + expectsContentCached(this.asNode(), _) or + neverSkipInPathGraph(this.asNode()) } } diff --git a/csharp/ql/lib/semmle/code/csharp/dataflow/internal/DataFlowPrivate.qll b/csharp/ql/lib/semmle/code/csharp/dataflow/internal/DataFlowPrivate.qll index da26fc1de43..872718d93d5 100644 --- a/csharp/ql/lib/semmle/code/csharp/dataflow/internal/DataFlowPrivate.qll +++ b/csharp/ql/lib/semmle/code/csharp/dataflow/internal/DataFlowPrivate.qll @@ -2140,6 +2140,12 @@ class CastNode extends Node { } } +/** + * Holds if `n` should never be skipped over in the `PathGraph` and in path + * explanations. + */ +predicate neverSkipInPathGraph(Node n) { none() } + class DataFlowExpr = DotNet::Expr; /** Holds if `e` is an expression that always has the same Boolean value `val`. */ diff --git a/csharp/ql/lib/semmle/code/csharp/security/dataflow/CommandInjectionQuery.qll b/csharp/ql/lib/semmle/code/csharp/security/dataflow/CommandInjectionQuery.qll index 265cae5f08a..90615faac9f 100644 --- a/csharp/ql/lib/semmle/code/csharp/security/dataflow/CommandInjectionQuery.qll +++ b/csharp/ql/lib/semmle/code/csharp/security/dataflow/CommandInjectionQuery.qll @@ -6,6 +6,7 @@ import csharp private import semmle.code.csharp.security.dataflow.flowsources.Remote private import semmle.code.csharp.frameworks.system.Diagnostics private import semmle.code.csharp.security.Sanitizers +private import semmle.code.csharp.dataflow.ExternalFlow /** * A source specific to command injection vulnerabilities. @@ -66,6 +67,11 @@ module CommandInjection = TaintTracking::Global; /** A source of remote user input. */ class RemoteSource extends Source instanceof RemoteFlowSource { } +/** Command Injection sinks defined through Models as Data. */ +private class ExternalCommandInjectionExprSink extends Sink { + ExternalCommandInjectionExprSink() { sinkNode(this, "command-injection") } +} + /** * A sink in `System.Diagnostic.Process` or its related classes. */ diff --git a/csharp/ql/lib/semmle/code/csharp/security/dataflow/LDAPInjectionQuery.qll b/csharp/ql/lib/semmle/code/csharp/security/dataflow/LDAPInjectionQuery.qll index 3f9c5947b68..c059cb7523c 100644 --- a/csharp/ql/lib/semmle/code/csharp/security/dataflow/LDAPInjectionQuery.qll +++ b/csharp/ql/lib/semmle/code/csharp/security/dataflow/LDAPInjectionQuery.qll @@ -8,6 +8,7 @@ private import semmle.code.csharp.security.dataflow.flowsources.Remote private import semmle.code.csharp.frameworks.system.DirectoryServices private import semmle.code.csharp.frameworks.system.directoryservices.Protocols private import semmle.code.csharp.security.Sanitizers +private import semmle.code.csharp.dataflow.ExternalFlow /** * A data flow source for unvalidated user input that is used to construct LDAP queries. @@ -68,6 +69,11 @@ module LdapInjection = TaintTracking::Global; /** A source of remote user input. */ class RemoteSource extends Source instanceof RemoteFlowSource { } +/** LDAP sinks defined through Models as Data. */ +private class ExternalLdapExprSink extends Sink { + ExternalLdapExprSink() { sinkNode(this, "ldap-injection") } +} + /** * An argument that sets the `Path` property of a `DirectoryEntry` object that is a sink for LDAP * injection. diff --git a/csharp/ql/lib/semmle/code/csharp/security/dataflow/LogForgingQuery.qll b/csharp/ql/lib/semmle/code/csharp/security/dataflow/LogForgingQuery.qll index f145b18dfeb..e219b5db589 100644 --- a/csharp/ql/lib/semmle/code/csharp/security/dataflow/LogForgingQuery.qll +++ b/csharp/ql/lib/semmle/code/csharp/security/dataflow/LogForgingQuery.qll @@ -8,6 +8,7 @@ private import semmle.code.csharp.frameworks.System private import semmle.code.csharp.frameworks.system.text.RegularExpressions private import semmle.code.csharp.security.Sanitizers private import semmle.code.csharp.security.dataflow.flowsinks.ExternalLocationSink +private import semmle.code.csharp.dataflow.ExternalFlow /** * A data flow source for untrusted user input used in log entries. @@ -72,6 +73,11 @@ private class LogForgingLogMessageSink extends Sink, LogMessageSink { } */ private class LogForgingTraceMessageSink extends Sink, TraceMessageSink { } +/** Log Forging sinks defined through Models as Data. */ +private class ExternalLoggingExprSink extends Sink { + ExternalLoggingExprSink() { sinkNode(this, "log-injection") } +} + /** * A call to String replace or remove that is considered to sanitize replaced string. */ diff --git a/csharp/ql/lib/semmle/code/csharp/security/dataflow/UrlRedirectQuery.qll b/csharp/ql/lib/semmle/code/csharp/security/dataflow/UrlRedirectQuery.qll index 44b90cf3096..56c409b38b5 100644 --- a/csharp/ql/lib/semmle/code/csharp/security/dataflow/UrlRedirectQuery.qll +++ b/csharp/ql/lib/semmle/code/csharp/security/dataflow/UrlRedirectQuery.qll @@ -9,6 +9,7 @@ private import semmle.code.csharp.frameworks.system.Web private import semmle.code.csharp.frameworks.system.web.Mvc private import semmle.code.csharp.security.Sanitizers private import semmle.code.csharp.frameworks.microsoft.AspNetCore +private import semmle.code.csharp.dataflow.ExternalFlow /** * A data flow source for unvalidated URL redirect vulnerabilities. @@ -70,6 +71,11 @@ module UrlRedirect = TaintTracking::Global; /** A source of remote user input. */ class RemoteSource extends Source instanceof RemoteFlowSource { } +/** URL Redirection sinks defined through Models as Data. */ +private class ExternalUrlRedirectExprSink extends Sink { + ExternalUrlRedirectExprSink() { sinkNode(this, "url-redirection") } +} + /** * A URL argument to a call to `HttpResponse.Redirect()` or `Controller.Redirect()`, that is a * sink for URL redirects. diff --git a/csharp/ql/lib/semmle/code/dotnet/Generics.qll b/csharp/ql/lib/semmle/code/dotnet/Generics.qll index f84718d4b82..67b8fb2f5d0 100644 --- a/csharp/ql/lib/semmle/code/dotnet/Generics.qll +++ b/csharp/ql/lib/semmle/code/dotnet/Generics.qll @@ -41,7 +41,7 @@ abstract class ConstructedGeneric extends Generic { UnboundGeneric getUnboundGeneric() { none() } /** Gets the total number of type arguments. */ - int getNumberOfTypeArguments() { result = count(int i | exists(this.getTypeArgument(i))) } + final int getNumberOfTypeArguments() { result = count(int i | exists(this.getTypeArgument(i))) } } /** diff --git a/csharp/ql/src/CHANGELOG.md b/csharp/ql/src/CHANGELOG.md index e214ec42a03..8e82ab07313 100644 --- a/csharp/ql/src/CHANGELOG.md +++ b/csharp/ql/src/CHANGELOG.md @@ -1,3 +1,7 @@ +## 0.6.3 + +No user-facing changes. + ## 0.6.2 No user-facing changes. diff --git a/csharp/ql/src/Security Features/CWE-022/ZipSlip.qhelp b/csharp/ql/src/Security Features/CWE-022/ZipSlip.qhelp index bee5d819836..a1f39d27b8c 100644 --- a/csharp/ql/src/Security Features/CWE-022/ZipSlip.qhelp +++ b/csharp/ql/src/Security Features/CWE-022/ZipSlip.qhelp @@ -3,16 +3,15 @@ "qhelp.dtd"> -

Extracting files from a malicious zip archive without validating that the destination file path -is within the destination directory can cause files outside the destination directory to be -overwritten, due to the possible presence of directory traversal elements (..) in -archive paths.

+

Extracting files from a malicious zip file, or similar type of archive, +is at risk of directory traversal attacks if filenames from the archive are +not properly validated.

Zip archives contain archive entries representing each file in the archive. These entries include a file path for the entry, but these file paths are not restricted and may contain unexpected special elements such as the directory traversal element (..). If these -file paths are used to determine an output file to write the contents of the archive item to, then -the file may be written to an unexpected location. This can result in sensitive information being +file paths are used to create a filesystem path, then a file operation may happen in an +unexpected location. This can result in sensitive information being revealed or deleted, or an attacker being able to influence behavior by modifying unexpected files.

diff --git a/csharp/ql/src/Security Features/CWE-022/ZipSlip.ql b/csharp/ql/src/Security Features/CWE-022/ZipSlip.ql index 87850cde7d6..2e4d59aaf7e 100644 --- a/csharp/ql/src/Security Features/CWE-022/ZipSlip.ql +++ b/csharp/ql/src/Security Features/CWE-022/ZipSlip.ql @@ -1,8 +1,8 @@ /** - * @name Arbitrary file write during zip extraction ("Zip Slip") - * @description Extracting files from a malicious zip archive without validating that the - * destination file path is within the destination directory can cause files outside - * the destination directory to be overwritten. + * @name Arbitrary file access during archive extraction ("Zip Slip") + * @description Extracting files from a malicious ZIP file, or similar type of archive, without + * validating that the destination file path is within the destination directory + * can allow an attacker to unexpectedly gain access to resources. * @kind path-problem * @id cs/zipslip * @problem.severity error diff --git a/csharp/ql/src/Telemetry/ExternalApi.qll b/csharp/ql/src/Telemetry/ExternalApi.qll index 9358e9ce8e4..0921bdf7b5c 100644 --- a/csharp/ql/src/Telemetry/ExternalApi.qll +++ b/csharp/ql/src/Telemetry/ExternalApi.qll @@ -50,7 +50,7 @@ class ExternalApi extends DotNet::Callable { bindingset[this] private string getSignature() { result = - this.getDeclaringType().getUnboundDeclaration() + "." + this.getName() + "(" + + nestedName(this.getDeclaringType().getUnboundDeclaration()) + "." + this.getName() + "(" + parameterQualifiedTypeNamesToString(this) + ")" } @@ -118,6 +118,21 @@ class ExternalApi extends DotNet::Callable { } } +/** + * Gets the nested name of the declaration. + * + * If the declaration is not a nested type, the result is the same as \`getName()\`. + * Otherwise the name of the nested type is prefixed with a \`+\` and appended to + * the name of the enclosing type, which might be a nested type as well. + */ +private string nestedName(Declaration declaration) { + not exists(declaration.getDeclaringType().getUnboundDeclaration()) and + result = declaration.getName() + or + nestedName(declaration.getDeclaringType().getUnboundDeclaration()) + "+" + declaration.getName() = + result +} + /** * Gets the limit for the number of results produced by a telemetry query. */ diff --git a/csharp/ql/src/change-notes/2023-06-16-zipslip-rename.md b/csharp/ql/src/change-notes/2023-06-16-zipslip-rename.md new file mode 100644 index 00000000000..3c13e6da67a --- /dev/null +++ b/csharp/ql/src/change-notes/2023-06-16-zipslip-rename.md @@ -0,0 +1,4 @@ +--- +category: fix +--- +* The query "Arbitrary file write during zip extraction ("Zip Slip")" (`cs/zipslip`) has been renamed to "Arbitrary file access during archive extraction ("Zip Slip")." diff --git a/csharp/ql/src/change-notes/released/0.6.3.md b/csharp/ql/src/change-notes/released/0.6.3.md new file mode 100644 index 00000000000..83374bcef56 --- /dev/null +++ b/csharp/ql/src/change-notes/released/0.6.3.md @@ -0,0 +1,3 @@ +## 0.6.3 + +No user-facing changes. diff --git a/csharp/ql/src/codeql-pack.release.yml b/csharp/ql/src/codeql-pack.release.yml index 5501a2a1cc5..b7dafe32c5d 100644 --- a/csharp/ql/src/codeql-pack.release.yml +++ b/csharp/ql/src/codeql-pack.release.yml @@ -1,2 +1,2 @@ --- -lastReleaseVersion: 0.6.2 +lastReleaseVersion: 0.6.3 diff --git a/csharp/ql/src/qlpack.yml b/csharp/ql/src/qlpack.yml index 95506e0f254..91cba09b8ac 100644 --- a/csharp/ql/src/qlpack.yml +++ b/csharp/ql/src/qlpack.yml @@ -1,5 +1,5 @@ name: codeql/csharp-queries -version: 0.6.3-dev +version: 0.6.4-dev groups: - csharp - queries diff --git a/csharp/ql/test/TestUtilities/InlineFlowTest.qll b/csharp/ql/test/TestUtilities/InlineFlowTest.qll index a31d531e1b6..718752a978a 100644 --- a/csharp/ql/test/TestUtilities/InlineFlowTest.qll +++ b/csharp/ql/test/TestUtilities/InlineFlowTest.qll @@ -4,11 +4,12 @@ * Example for a test.ql: * ```ql * import csharp - * import DefaultValueFlow::PathGraph * import TestUtilities.InlineFlowTest + * import DefaultFlowTest + * import PathGraph * - * from DefaultValueFlow::PathNode source, DefaultValueFlow::PathNode sink - * where DefaultValueFlow::flowPath(source, sink) + * from PathNode source, PathNode sink + * where flowPath(source, sink) * select sink, source, sink, "$@", source, source.toString() * * ``` @@ -32,14 +33,10 @@ * } * ``` * - * If you're not interested in a specific flow type, you can disable either value or taint flow expectations as follows: - * ```ql - * class HasFlowTest extends InlineFlowTest { - * override DataFlow::Configuration getTaintFlowConfig() { none() } - * - * override DataFlow::Configuration getValueFlowConfig() { none() } - * } - * ``` + * If you are only interested in value flow, then instead of importing `DefaultFlowTest`, you can import + * `ValueFlowTest`. Similarly, if you are only interested in taint flow, then instead of + * importing `DefaultFlowTest`, you can import `TaintFlowTest`. In both cases + * `DefaultFlowConfig` can be replaced by another implementation of `DataFlow::ConfigSig`. * * If you need more fine-grained tuning, consider implementing a test using `InlineExpectationsTest`. */ @@ -47,8 +44,8 @@ import csharp import TestUtilities.InlineExpectationsTest -private predicate defaultSource(DataFlow::Node src) { - src.asExpr().(MethodCall).getTarget().getUndecoratedName() = ["Source", "Taint"] +private predicate defaultSource(DataFlow::Node source) { + source.asExpr().(MethodCall).getTarget().getUndecoratedName() = ["Source", "Taint"] } private predicate defaultSink(DataFlow::Node sink) { @@ -58,42 +55,66 @@ private predicate defaultSink(DataFlow::Node sink) { } module DefaultFlowConfig implements DataFlow::ConfigSig { - predicate isSource(DataFlow::Node n) { defaultSource(n) } + predicate isSource(DataFlow::Node source) { defaultSource(source) } - predicate isSink(DataFlow::Node n) { defaultSink(n) } + predicate isSink(DataFlow::Node sink) { defaultSink(sink) } int fieldFlowBranchLimit() { result = 1000 } } -module DefaultValueFlow = DataFlow::Global; +private module NoFlowConfig implements DataFlow::ConfigSig { + predicate isSource(DataFlow::Node source) { none() } -module DefaultTaintFlow = TaintTracking::Global; + predicate isSink(DataFlow::Node sink) { none() } +} private string getSourceArgString(DataFlow::Node src) { defaultSource(src) and src.asExpr().(MethodCall).getAnArgument().getValue() = result } -class InlineFlowTest extends InlineExpectationsTest { - InlineFlowTest() { this = "HasFlowTest" } +module FlowTest { + module ValueFlow = DataFlow::Global; - override string getARelevantTag() { result = ["hasValueFlow", "hasTaintFlow"] } + module TaintFlow = TaintTracking::Global; - override predicate hasActualResult(Location location, string element, string tag, string value) { - tag = "hasValueFlow" and - exists(DataFlow::Node src, DataFlow::Node sink | DefaultValueFlow::flow(src, sink) | - sink.getLocation() = location and - element = sink.toString() and - if exists(getSourceArgString(src)) then value = getSourceArgString(src) else value = "" - ) - or - tag = "hasTaintFlow" and - exists(DataFlow::Node src, DataFlow::Node sink | - DefaultTaintFlow::flow(src, sink) and not DefaultValueFlow::flow(src, sink) - | - sink.getLocation() = location and - element = sink.toString() and - if exists(getSourceArgString(src)) then value = getSourceArgString(src) else value = "" - ) + private module InlineTest implements TestSig { + string getARelevantTag() { result = ["hasValueFlow", "hasTaintFlow"] } + + predicate hasActualResult(Location location, string element, string tag, string value) { + tag = "hasValueFlow" and + exists(DataFlow::Node src, DataFlow::Node sink | ValueFlow::flow(src, sink) | + sink.getLocation() = location and + element = sink.toString() and + if exists(getSourceArgString(src)) then value = getSourceArgString(src) else value = "" + ) + or + tag = "hasTaintFlow" and + exists(DataFlow::Node src, DataFlow::Node sink | + TaintFlow::flow(src, sink) and not ValueFlow::flow(src, sink) + | + sink.getLocation() = location and + element = sink.toString() and + if exists(getSourceArgString(src)) then value = getSourceArgString(src) else value = "" + ) + } + } + + import MakeTest + import DataFlow::MergePathGraph + + predicate flowPath(PathNode source, PathNode sink) { + ValueFlow::flowPath(source.asPathNode1(), sink.asPathNode1()) or + TaintFlow::flowPath(source.asPathNode2(), sink.asPathNode2()) } } + +module DefaultFlowTest = FlowTest; + +module ValueFlowTest { + import FlowTest +} + +module TaintFlowTest { + import FlowTest +} diff --git a/csharp/ql/test/library-tests/dataflow/fields/FieldFlow.expected b/csharp/ql/test/library-tests/dataflow/fields/FieldFlow.expected index aa9ac0493aa..7466125341d 100644 --- a/csharp/ql/test/library-tests/dataflow/fields/FieldFlow.expected +++ b/csharp/ql/test/library-tests/dataflow/fields/FieldFlow.expected @@ -1,1100 +1,2196 @@ failures +testFailures edges | A.cs:5:17:5:28 | call to method Source : C | A.cs:6:24:6:24 | access to local variable c : C | +| A.cs:5:17:5:28 | call to method Source : C | A.cs:6:24:6:24 | access to local variable c : C | +| A.cs:6:17:6:25 | call to method Make : B [field c] : C | A.cs:7:14:7:14 | access to local variable b : B [field c] : C | | A.cs:6:17:6:25 | call to method Make : B [field c] : C | A.cs:7:14:7:14 | access to local variable b : B [field c] : C | | A.cs:6:24:6:24 | access to local variable c : C | A.cs:6:17:6:25 | call to method Make : B [field c] : C | +| A.cs:6:24:6:24 | access to local variable c : C | A.cs:6:17:6:25 | call to method Make : B [field c] : C | +| A.cs:6:24:6:24 | access to local variable c : C | A.cs:147:32:147:32 | c : C | | A.cs:6:24:6:24 | access to local variable c : C | A.cs:147:32:147:32 | c : C | | A.cs:7:14:7:14 | access to local variable b : B [field c] : C | A.cs:7:14:7:16 | access to field c | +| A.cs:7:14:7:14 | access to local variable b : B [field c] : C | A.cs:7:14:7:16 | access to field c | +| A.cs:13:9:13:9 | [post] access to local variable b : B [field c] : C1 | A.cs:14:14:14:14 | access to local variable b : B [field c] : C1 | | A.cs:13:9:13:9 | [post] access to local variable b : B [field c] : C1 | A.cs:14:14:14:14 | access to local variable b : B [field c] : C1 | | A.cs:13:15:13:29 | call to method Source : C1 | A.cs:13:9:13:9 | [post] access to local variable b : B [field c] : C1 | +| A.cs:13:15:13:29 | call to method Source : C1 | A.cs:13:9:13:9 | [post] access to local variable b : B [field c] : C1 | +| A.cs:13:15:13:29 | call to method Source : C1 | A.cs:145:27:145:27 | c : C1 | | A.cs:13:15:13:29 | call to method Source : C1 | A.cs:145:27:145:27 | c : C1 | | A.cs:14:14:14:14 | access to local variable b : B [field c] : C1 | A.cs:14:14:14:20 | call to method Get | +| A.cs:14:14:14:14 | access to local variable b : B [field c] : C1 | A.cs:14:14:14:20 | call to method Get | +| A.cs:14:14:14:14 | access to local variable b : B [field c] : C1 | A.cs:146:18:146:20 | this : B [field c] : C1 | | A.cs:14:14:14:14 | access to local variable b : B [field c] : C1 | A.cs:146:18:146:20 | this : B [field c] : C1 | | A.cs:15:15:15:35 | object creation of type B : B [field c] : C | A.cs:15:14:15:42 | call to method Get | +| A.cs:15:15:15:35 | object creation of type B : B [field c] : C | A.cs:15:14:15:42 | call to method Get | +| A.cs:15:15:15:35 | object creation of type B : B [field c] : C | A.cs:146:18:146:20 | this : B [field c] : C | | A.cs:15:15:15:35 | object creation of type B : B [field c] : C | A.cs:146:18:146:20 | this : B [field c] : C | | A.cs:15:21:15:34 | call to method Source : C | A.cs:15:15:15:35 | object creation of type B : B [field c] : C | +| A.cs:15:21:15:34 | call to method Source : C | A.cs:15:15:15:35 | object creation of type B : B [field c] : C | +| A.cs:15:21:15:34 | call to method Source : C | A.cs:141:20:141:20 | c : C | | A.cs:15:21:15:34 | call to method Source : C | A.cs:141:20:141:20 | c : C | | A.cs:22:14:22:38 | call to method SetOnB : B [field c] : C2 | A.cs:24:14:24:15 | access to local variable b2 : B [field c] : C2 | +| A.cs:22:14:22:38 | call to method SetOnB : B [field c] : C2 | A.cs:24:14:24:15 | access to local variable b2 : B [field c] : C2 | +| A.cs:22:25:22:37 | call to method Source : C2 | A.cs:22:14:22:38 | call to method SetOnB : B [field c] : C2 | | A.cs:22:25:22:37 | call to method Source : C2 | A.cs:22:14:22:38 | call to method SetOnB : B [field c] : C2 | | A.cs:22:25:22:37 | call to method Source : C2 | A.cs:42:29:42:29 | c : C2 | +| A.cs:22:25:22:37 | call to method Source : C2 | A.cs:42:29:42:29 | c : C2 | +| A.cs:24:14:24:15 | access to local variable b2 : B [field c] : C2 | A.cs:24:14:24:17 | access to field c | | A.cs:24:14:24:15 | access to local variable b2 : B [field c] : C2 | A.cs:24:14:24:17 | access to field c | | A.cs:31:14:31:42 | call to method SetOnBWrap : B [field c] : C2 | A.cs:33:14:33:15 | access to local variable b2 : B [field c] : C2 | +| A.cs:31:14:31:42 | call to method SetOnBWrap : B [field c] : C2 | A.cs:33:14:33:15 | access to local variable b2 : B [field c] : C2 | +| A.cs:31:29:31:41 | call to method Source : C2 | A.cs:31:14:31:42 | call to method SetOnBWrap : B [field c] : C2 | | A.cs:31:29:31:41 | call to method Source : C2 | A.cs:31:14:31:42 | call to method SetOnBWrap : B [field c] : C2 | | A.cs:31:29:31:41 | call to method Source : C2 | A.cs:36:33:36:33 | c : C2 | +| A.cs:31:29:31:41 | call to method Source : C2 | A.cs:36:33:36:33 | c : C2 | +| A.cs:33:14:33:15 | access to local variable b2 : B [field c] : C2 | A.cs:33:14:33:17 | access to field c | | A.cs:33:14:33:15 | access to local variable b2 : B [field c] : C2 | A.cs:33:14:33:17 | access to field c | | A.cs:36:33:36:33 | c : C2 | A.cs:38:29:38:29 | access to parameter c : C2 | +| A.cs:36:33:36:33 | c : C2 | A.cs:38:29:38:29 | access to parameter c : C2 | +| A.cs:38:18:38:30 | call to method SetOnB : B [field c] : C2 | A.cs:39:16:39:28 | ... ? ... : ... : B [field c] : C2 | | A.cs:38:18:38:30 | call to method SetOnB : B [field c] : C2 | A.cs:39:16:39:28 | ... ? ... : ... : B [field c] : C2 | | A.cs:38:29:38:29 | access to parameter c : C2 | A.cs:38:18:38:30 | call to method SetOnB : B [field c] : C2 | +| A.cs:38:29:38:29 | access to parameter c : C2 | A.cs:38:18:38:30 | call to method SetOnB : B [field c] : C2 | +| A.cs:38:29:38:29 | access to parameter c : C2 | A.cs:42:29:42:29 | c : C2 | | A.cs:38:29:38:29 | access to parameter c : C2 | A.cs:42:29:42:29 | c : C2 | | A.cs:42:29:42:29 | c : C2 | A.cs:47:20:47:20 | access to parameter c : C2 | +| A.cs:42:29:42:29 | c : C2 | A.cs:47:20:47:20 | access to parameter c : C2 | +| A.cs:47:13:47:14 | [post] access to local variable b2 : B [field c] : C2 | A.cs:48:20:48:21 | access to local variable b2 : B [field c] : C2 | | A.cs:47:13:47:14 | [post] access to local variable b2 : B [field c] : C2 | A.cs:48:20:48:21 | access to local variable b2 : B [field c] : C2 | | A.cs:47:20:47:20 | access to parameter c : C2 | A.cs:47:13:47:14 | [post] access to local variable b2 : B [field c] : C2 | +| A.cs:47:20:47:20 | access to parameter c : C2 | A.cs:47:13:47:14 | [post] access to local variable b2 : B [field c] : C2 | +| A.cs:47:20:47:20 | access to parameter c : C2 | A.cs:145:27:145:27 | c : C2 | | A.cs:47:20:47:20 | access to parameter c : C2 | A.cs:145:27:145:27 | c : C2 | | A.cs:55:17:55:28 | call to method Source : A | A.cs:57:16:57:16 | access to local variable a : A | +| A.cs:55:17:55:28 | call to method Source : A | A.cs:57:16:57:16 | access to local variable a : A | +| A.cs:57:9:57:10 | [post] access to local variable c1 : C1 [field a] : A | A.cs:58:12:58:13 | access to local variable c1 : C1 [field a] : A | | A.cs:57:9:57:10 | [post] access to local variable c1 : C1 [field a] : A | A.cs:58:12:58:13 | access to local variable c1 : C1 [field a] : A | | A.cs:57:16:57:16 | access to local variable a : A | A.cs:57:9:57:10 | [post] access to local variable c1 : C1 [field a] : A | +| A.cs:57:16:57:16 | access to local variable a : A | A.cs:57:9:57:10 | [post] access to local variable c1 : C1 [field a] : A | +| A.cs:58:12:58:13 | access to local variable c1 : C1 [field a] : A | A.cs:60:22:60:22 | c : C1 [field a] : A | | A.cs:58:12:58:13 | access to local variable c1 : C1 [field a] : A | A.cs:60:22:60:22 | c : C1 [field a] : A | | A.cs:60:22:60:22 | c : C1 [field a] : A | A.cs:64:19:64:23 | (...) ... : C1 [field a] : A | +| A.cs:60:22:60:22 | c : C1 [field a] : A | A.cs:64:19:64:23 | (...) ... : C1 [field a] : A | +| A.cs:64:19:64:23 | (...) ... : C1 [field a] : A | A.cs:64:18:64:26 | access to field a | | A.cs:64:19:64:23 | (...) ... : C1 [field a] : A | A.cs:64:18:64:26 | access to field a | | A.cs:83:9:83:9 | [post] access to parameter b : B [field c] : C | A.cs:88:12:88:12 | [post] access to local variable b : B [field c] : C | +| A.cs:83:9:83:9 | [post] access to parameter b : B [field c] : C | A.cs:88:12:88:12 | [post] access to local variable b : B [field c] : C | +| A.cs:83:15:83:26 | call to method Source : C | A.cs:83:9:83:9 | [post] access to parameter b : B [field c] : C | | A.cs:83:15:83:26 | call to method Source : C | A.cs:83:9:83:9 | [post] access to parameter b : B [field c] : C | | A.cs:83:15:83:26 | call to method Source : C | A.cs:145:27:145:27 | c : C | +| A.cs:83:15:83:26 | call to method Source : C | A.cs:145:27:145:27 | c : C | +| A.cs:88:12:88:12 | [post] access to local variable b : B [field c] : C | A.cs:89:14:89:14 | access to local variable b : B [field c] : C | | A.cs:88:12:88:12 | [post] access to local variable b : B [field c] : C | A.cs:89:14:89:14 | access to local variable b : B [field c] : C | | A.cs:89:14:89:14 | access to local variable b : B [field c] : C | A.cs:89:14:89:16 | access to field c | +| A.cs:89:14:89:14 | access to local variable b : B [field c] : C | A.cs:89:14:89:16 | access to field c | +| A.cs:95:20:95:20 | b : B | A.cs:97:13:97:13 | access to parameter b : B | | A.cs:95:20:95:20 | b : B | A.cs:97:13:97:13 | access to parameter b : B | | A.cs:97:13:97:13 | [post] access to parameter b : B [field c] : C | A.cs:98:22:98:43 | ... ? ... : ... : B [field c] : C | +| A.cs:97:13:97:13 | [post] access to parameter b : B [field c] : C | A.cs:98:22:98:43 | ... ? ... : ... : B [field c] : C | +| A.cs:97:13:97:13 | [post] access to parameter b : B [field c] : C | A.cs:105:23:105:23 | [post] access to local variable b : B [field c] : C | | A.cs:97:13:97:13 | [post] access to parameter b : B [field c] : C | A.cs:105:23:105:23 | [post] access to local variable b : B [field c] : C | | A.cs:97:13:97:13 | access to parameter b : B | A.cs:98:22:98:43 | ... ? ... : ... : B | +| A.cs:97:13:97:13 | access to parameter b : B | A.cs:98:22:98:43 | ... ? ... : ... : B | +| A.cs:97:19:97:32 | call to method Source : C | A.cs:97:13:97:13 | [post] access to parameter b : B [field c] : C | | A.cs:97:19:97:32 | call to method Source : C | A.cs:97:13:97:13 | [post] access to parameter b : B [field c] : C | | A.cs:98:13:98:16 | [post] this access : D [field b, field c] : C | A.cs:105:17:105:29 | object creation of type D : D [field b, field c] : C | +| A.cs:98:13:98:16 | [post] this access : D [field b, field c] : C | A.cs:105:17:105:29 | object creation of type D : D [field b, field c] : C | +| A.cs:98:13:98:16 | [post] this access : D [field b] : B | A.cs:105:17:105:29 | object creation of type D : D [field b] : B | | A.cs:98:13:98:16 | [post] this access : D [field b] : B | A.cs:105:17:105:29 | object creation of type D : D [field b] : B | | A.cs:98:22:98:43 | ... ? ... : ... : B | A.cs:98:13:98:16 | [post] this access : D [field b] : B | | A.cs:98:22:98:43 | ... ? ... : ... : B | A.cs:98:13:98:16 | [post] this access : D [field b] : B | +| A.cs:98:22:98:43 | ... ? ... : ... : B | A.cs:98:13:98:16 | [post] this access : D [field b] : B | +| A.cs:98:22:98:43 | ... ? ... : ... : B | A.cs:98:13:98:16 | [post] this access : D [field b] : B | +| A.cs:98:22:98:43 | ... ? ... : ... : B [field c] : C | A.cs:98:13:98:16 | [post] this access : D [field b, field c] : C | | A.cs:98:22:98:43 | ... ? ... : ... : B [field c] : C | A.cs:98:13:98:16 | [post] this access : D [field b, field c] : C | | A.cs:98:30:98:43 | call to method Source : B | A.cs:98:22:98:43 | ... ? ... : ... : B | +| A.cs:98:30:98:43 | call to method Source : B | A.cs:98:22:98:43 | ... ? ... : ... : B | +| A.cs:104:17:104:30 | call to method Source : B | A.cs:105:23:105:23 | access to local variable b : B | | A.cs:104:17:104:30 | call to method Source : B | A.cs:105:23:105:23 | access to local variable b : B | | A.cs:105:17:105:29 | object creation of type D : D [field b, field c] : C | A.cs:107:14:107:14 | access to local variable d : D [field b, field c] : C | +| A.cs:105:17:105:29 | object creation of type D : D [field b, field c] : C | A.cs:107:14:107:14 | access to local variable d : D [field b, field c] : C | +| A.cs:105:17:105:29 | object creation of type D : D [field b] : B | A.cs:106:14:106:14 | access to local variable d : D [field b] : B | | A.cs:105:17:105:29 | object creation of type D : D [field b] : B | A.cs:106:14:106:14 | access to local variable d : D [field b] : B | | A.cs:105:23:105:23 | [post] access to local variable b : B [field c] : C | A.cs:108:14:108:14 | access to local variable b : B [field c] : C | +| A.cs:105:23:105:23 | [post] access to local variable b : B [field c] : C | A.cs:108:14:108:14 | access to local variable b : B [field c] : C | +| A.cs:105:23:105:23 | access to local variable b : B | A.cs:95:20:95:20 | b : B | | A.cs:105:23:105:23 | access to local variable b : B | A.cs:95:20:95:20 | b : B | | A.cs:105:23:105:23 | access to local variable b : B | A.cs:105:17:105:29 | object creation of type D : D [field b] : B | +| A.cs:105:23:105:23 | access to local variable b : B | A.cs:105:17:105:29 | object creation of type D : D [field b] : B | +| A.cs:106:14:106:14 | access to local variable d : D [field b] : B | A.cs:106:14:106:16 | access to field b | | A.cs:106:14:106:14 | access to local variable d : D [field b] : B | A.cs:106:14:106:16 | access to field b | | A.cs:107:14:107:14 | access to local variable d : D [field b, field c] : C | A.cs:107:14:107:16 | access to field b : B [field c] : C | +| A.cs:107:14:107:14 | access to local variable d : D [field b, field c] : C | A.cs:107:14:107:16 | access to field b : B [field c] : C | +| A.cs:107:14:107:16 | access to field b : B [field c] : C | A.cs:107:14:107:18 | access to field c | | A.cs:107:14:107:16 | access to field b : B [field c] : C | A.cs:107:14:107:18 | access to field c | | A.cs:108:14:108:14 | access to local variable b : B [field c] : C | A.cs:108:14:108:16 | access to field c | +| A.cs:108:14:108:14 | access to local variable b : B [field c] : C | A.cs:108:14:108:16 | access to field c | +| A.cs:113:17:113:29 | call to method Source : B | A.cs:114:29:114:29 | access to local variable b : B | | A.cs:113:17:113:29 | call to method Source : B | A.cs:114:29:114:29 | access to local variable b : B | | A.cs:114:18:114:54 | object creation of type MyList : MyList [field head] : B | A.cs:115:35:115:36 | access to local variable l1 : MyList [field head] : B | +| A.cs:114:18:114:54 | object creation of type MyList : MyList [field head] : B | A.cs:115:35:115:36 | access to local variable l1 : MyList [field head] : B | +| A.cs:114:29:114:29 | access to local variable b : B | A.cs:114:18:114:54 | object creation of type MyList : MyList [field head] : B | | A.cs:114:29:114:29 | access to local variable b : B | A.cs:114:18:114:54 | object creation of type MyList : MyList [field head] : B | | A.cs:114:29:114:29 | access to local variable b : B | A.cs:157:25:157:28 | head : B | +| A.cs:114:29:114:29 | access to local variable b : B | A.cs:157:25:157:28 | head : B | +| A.cs:115:18:115:37 | object creation of type MyList : MyList [field next, field head] : B | A.cs:116:35:116:36 | access to local variable l2 : MyList [field next, field head] : B | | A.cs:115:18:115:37 | object creation of type MyList : MyList [field next, field head] : B | A.cs:116:35:116:36 | access to local variable l2 : MyList [field next, field head] : B | | A.cs:115:35:115:36 | access to local variable l1 : MyList [field head] : B | A.cs:115:18:115:37 | object creation of type MyList : MyList [field next, field head] : B | +| A.cs:115:35:115:36 | access to local variable l1 : MyList [field head] : B | A.cs:115:18:115:37 | object creation of type MyList : MyList [field next, field head] : B | +| A.cs:115:35:115:36 | access to local variable l1 : MyList [field head] : B | A.cs:157:38:157:41 | next : MyList [field head] : B | | A.cs:115:35:115:36 | access to local variable l1 : MyList [field head] : B | A.cs:157:38:157:41 | next : MyList [field head] : B | | A.cs:116:18:116:37 | object creation of type MyList : MyList [field next, field next, field head] : B | A.cs:119:14:119:15 | access to local variable l3 : MyList [field next, field next, field head] : B | +| A.cs:116:18:116:37 | object creation of type MyList : MyList [field next, field next, field head] : B | A.cs:119:14:119:15 | access to local variable l3 : MyList [field next, field next, field head] : B | +| A.cs:116:18:116:37 | object creation of type MyList : MyList [field next, field next, field head] : B | A.cs:121:41:121:41 | access to local variable l : MyList [field next, field next, field head] : B | | A.cs:116:18:116:37 | object creation of type MyList : MyList [field next, field next, field head] : B | A.cs:121:41:121:41 | access to local variable l : MyList [field next, field next, field head] : B | | A.cs:116:35:116:36 | access to local variable l2 : MyList [field next, field head] : B | A.cs:116:18:116:37 | object creation of type MyList : MyList [field next, field next, field head] : B | +| A.cs:116:35:116:36 | access to local variable l2 : MyList [field next, field head] : B | A.cs:116:18:116:37 | object creation of type MyList : MyList [field next, field next, field head] : B | +| A.cs:116:35:116:36 | access to local variable l2 : MyList [field next, field head] : B | A.cs:157:38:157:41 | next : MyList [field next, field head] : B | | A.cs:116:35:116:36 | access to local variable l2 : MyList [field next, field head] : B | A.cs:157:38:157:41 | next : MyList [field next, field head] : B | | A.cs:119:14:119:15 | access to local variable l3 : MyList [field next, field next, field head] : B | A.cs:119:14:119:20 | access to field next : MyList [field next, field head] : B | +| A.cs:119:14:119:15 | access to local variable l3 : MyList [field next, field next, field head] : B | A.cs:119:14:119:20 | access to field next : MyList [field next, field head] : B | +| A.cs:119:14:119:20 | access to field next : MyList [field next, field head] : B | A.cs:119:14:119:25 | access to field next : MyList [field head] : B | | A.cs:119:14:119:20 | access to field next : MyList [field next, field head] : B | A.cs:119:14:119:25 | access to field next : MyList [field head] : B | | A.cs:119:14:119:25 | access to field next : MyList [field head] : B | A.cs:119:14:119:30 | access to field head | +| A.cs:119:14:119:25 | access to field next : MyList [field head] : B | A.cs:119:14:119:30 | access to field head | +| A.cs:121:41:121:41 | access to local variable l : MyList [field next, field head] : B | A.cs:121:41:121:46 | access to field next : MyList [field head] : B | | A.cs:121:41:121:41 | access to local variable l : MyList [field next, field head] : B | A.cs:121:41:121:46 | access to field next : MyList [field head] : B | | A.cs:121:41:121:41 | access to local variable l : MyList [field next, field next, field head] : B | A.cs:121:41:121:46 | access to field next : MyList [field next, field head] : B | +| A.cs:121:41:121:41 | access to local variable l : MyList [field next, field next, field head] : B | A.cs:121:41:121:46 | access to field next : MyList [field next, field head] : B | +| A.cs:121:41:121:46 | access to field next : MyList [field head] : B | A.cs:123:18:123:18 | access to local variable l : MyList [field head] : B | | A.cs:121:41:121:46 | access to field next : MyList [field head] : B | A.cs:123:18:123:18 | access to local variable l : MyList [field head] : B | | A.cs:121:41:121:46 | access to field next : MyList [field next, field head] : B | A.cs:121:41:121:41 | access to local variable l : MyList [field next, field head] : B | +| A.cs:121:41:121:46 | access to field next : MyList [field next, field head] : B | A.cs:121:41:121:41 | access to local variable l : MyList [field next, field head] : B | +| A.cs:123:18:123:18 | access to local variable l : MyList [field head] : B | A.cs:123:18:123:23 | access to field head | | A.cs:123:18:123:18 | access to local variable l : MyList [field head] : B | A.cs:123:18:123:23 | access to field head | | A.cs:141:20:141:20 | c : C | A.cs:143:22:143:22 | access to parameter c : C | +| A.cs:141:20:141:20 | c : C | A.cs:143:22:143:22 | access to parameter c : C | +| A.cs:143:22:143:22 | access to parameter c : C | A.cs:143:13:143:16 | [post] this access : B [field c] : C | | A.cs:143:22:143:22 | access to parameter c : C | A.cs:143:13:143:16 | [post] this access : B [field c] : C | | A.cs:145:27:145:27 | c : C | A.cs:145:41:145:41 | access to parameter c : C | +| A.cs:145:27:145:27 | c : C | A.cs:145:41:145:41 | access to parameter c : C | +| A.cs:145:27:145:27 | c : C1 | A.cs:145:41:145:41 | access to parameter c : C1 | | A.cs:145:27:145:27 | c : C1 | A.cs:145:41:145:41 | access to parameter c : C1 | | A.cs:145:27:145:27 | c : C2 | A.cs:145:41:145:41 | access to parameter c : C2 | +| A.cs:145:27:145:27 | c : C2 | A.cs:145:41:145:41 | access to parameter c : C2 | +| A.cs:145:41:145:41 | access to parameter c : C | A.cs:145:32:145:35 | [post] this access : B [field c] : C | | A.cs:145:41:145:41 | access to parameter c : C | A.cs:145:32:145:35 | [post] this access : B [field c] : C | | A.cs:145:41:145:41 | access to parameter c : C1 | A.cs:145:32:145:35 | [post] this access : B [field c] : C1 | +| A.cs:145:41:145:41 | access to parameter c : C1 | A.cs:145:32:145:35 | [post] this access : B [field c] : C1 | +| A.cs:145:41:145:41 | access to parameter c : C2 | A.cs:145:32:145:35 | [post] this access : B [field c] : C2 | | A.cs:145:41:145:41 | access to parameter c : C2 | A.cs:145:32:145:35 | [post] this access : B [field c] : C2 | | A.cs:146:18:146:20 | this : B [field c] : C | A.cs:146:33:146:36 | this access : B [field c] : C | +| A.cs:146:18:146:20 | this : B [field c] : C | A.cs:146:33:146:36 | this access : B [field c] : C | +| A.cs:146:18:146:20 | this : B [field c] : C1 | A.cs:146:33:146:36 | this access : B [field c] : C1 | | A.cs:146:18:146:20 | this : B [field c] : C1 | A.cs:146:33:146:36 | this access : B [field c] : C1 | | A.cs:146:33:146:36 | this access : B [field c] : C | A.cs:146:33:146:38 | access to field c : C | +| A.cs:146:33:146:36 | this access : B [field c] : C | A.cs:146:33:146:38 | access to field c : C | +| A.cs:146:33:146:36 | this access : B [field c] : C1 | A.cs:146:33:146:38 | access to field c : C1 | | A.cs:146:33:146:36 | this access : B [field c] : C1 | A.cs:146:33:146:38 | access to field c : C1 | | A.cs:147:32:147:32 | c : C | A.cs:149:26:149:26 | access to parameter c : C | +| A.cs:147:32:147:32 | c : C | A.cs:149:26:149:26 | access to parameter c : C | +| A.cs:149:26:149:26 | access to parameter c : C | A.cs:141:20:141:20 | c : C | | A.cs:149:26:149:26 | access to parameter c : C | A.cs:141:20:141:20 | c : C | | A.cs:149:26:149:26 | access to parameter c : C | A.cs:149:20:149:27 | object creation of type B : B [field c] : C | +| A.cs:149:26:149:26 | access to parameter c : C | A.cs:149:20:149:27 | object creation of type B : B [field c] : C | +| A.cs:157:25:157:28 | head : B | A.cs:159:25:159:28 | access to parameter head : B | | A.cs:157:25:157:28 | head : B | A.cs:159:25:159:28 | access to parameter head : B | | A.cs:157:38:157:41 | next : MyList [field head] : B | A.cs:160:25:160:28 | access to parameter next : MyList [field head] : B | +| A.cs:157:38:157:41 | next : MyList [field head] : B | A.cs:160:25:160:28 | access to parameter next : MyList [field head] : B | +| A.cs:157:38:157:41 | next : MyList [field next, field head] : B | A.cs:160:25:160:28 | access to parameter next : MyList [field next, field head] : B | | A.cs:157:38:157:41 | next : MyList [field next, field head] : B | A.cs:160:25:160:28 | access to parameter next : MyList [field next, field head] : B | | A.cs:159:25:159:28 | access to parameter head : B | A.cs:159:13:159:16 | [post] this access : MyList [field head] : B | +| A.cs:159:25:159:28 | access to parameter head : B | A.cs:159:13:159:16 | [post] this access : MyList [field head] : B | +| A.cs:160:25:160:28 | access to parameter next : MyList [field head] : B | A.cs:160:13:160:16 | [post] this access : MyList [field next, field head] : B | | A.cs:160:25:160:28 | access to parameter next : MyList [field head] : B | A.cs:160:13:160:16 | [post] this access : MyList [field next, field head] : B | | A.cs:160:25:160:28 | access to parameter next : MyList [field next, field head] : B | A.cs:160:13:160:16 | [post] this access : MyList [field next, field next, field head] : B | +| A.cs:160:25:160:28 | access to parameter next : MyList [field next, field head] : B | A.cs:160:13:160:16 | [post] this access : MyList [field next, field next, field head] : B | +| B.cs:5:17:5:31 | call to method Source : Elem | B.cs:6:27:6:27 | access to local variable e : Elem | | B.cs:5:17:5:31 | call to method Source : Elem | B.cs:6:27:6:27 | access to local variable e : Elem | | B.cs:6:18:6:34 | object creation of type Box1 : Box1 [field elem1] : Elem | B.cs:7:27:7:28 | access to local variable b1 : Box1 [field elem1] : Elem | +| B.cs:6:18:6:34 | object creation of type Box1 : Box1 [field elem1] : Elem | B.cs:7:27:7:28 | access to local variable b1 : Box1 [field elem1] : Elem | +| B.cs:6:27:6:27 | access to local variable e : Elem | B.cs:6:18:6:34 | object creation of type Box1 : Box1 [field elem1] : Elem | | B.cs:6:27:6:27 | access to local variable e : Elem | B.cs:6:18:6:34 | object creation of type Box1 : Box1 [field elem1] : Elem | | B.cs:6:27:6:27 | access to local variable e : Elem | B.cs:29:26:29:27 | e1 : Elem | +| B.cs:6:27:6:27 | access to local variable e : Elem | B.cs:29:26:29:27 | e1 : Elem | +| B.cs:7:18:7:29 | object creation of type Box2 : Box2 [field box1, field elem1] : Elem | B.cs:8:14:8:15 | access to local variable b2 : Box2 [field box1, field elem1] : Elem | | B.cs:7:18:7:29 | object creation of type Box2 : Box2 [field box1, field elem1] : Elem | B.cs:8:14:8:15 | access to local variable b2 : Box2 [field box1, field elem1] : Elem | | B.cs:7:27:7:28 | access to local variable b1 : Box1 [field elem1] : Elem | B.cs:7:18:7:29 | object creation of type Box2 : Box2 [field box1, field elem1] : Elem | +| B.cs:7:27:7:28 | access to local variable b1 : Box1 [field elem1] : Elem | B.cs:7:18:7:29 | object creation of type Box2 : Box2 [field box1, field elem1] : Elem | +| B.cs:7:27:7:28 | access to local variable b1 : Box1 [field elem1] : Elem | B.cs:39:26:39:27 | b1 : Box1 [field elem1] : Elem | | B.cs:7:27:7:28 | access to local variable b1 : Box1 [field elem1] : Elem | B.cs:39:26:39:27 | b1 : Box1 [field elem1] : Elem | | B.cs:8:14:8:15 | access to local variable b2 : Box2 [field box1, field elem1] : Elem | B.cs:8:14:8:20 | access to field box1 : Box1 [field elem1] : Elem | +| B.cs:8:14:8:15 | access to local variable b2 : Box2 [field box1, field elem1] : Elem | B.cs:8:14:8:20 | access to field box1 : Box1 [field elem1] : Elem | +| B.cs:8:14:8:20 | access to field box1 : Box1 [field elem1] : Elem | B.cs:8:14:8:26 | access to field elem1 | | B.cs:8:14:8:20 | access to field box1 : Box1 [field elem1] : Elem | B.cs:8:14:8:26 | access to field elem1 | | B.cs:14:17:14:31 | call to method Source : Elem | B.cs:15:33:15:33 | access to local variable e : Elem | +| B.cs:14:17:14:31 | call to method Source : Elem | B.cs:15:33:15:33 | access to local variable e : Elem | +| B.cs:15:18:15:34 | object creation of type Box1 : Box1 [field elem2] : Elem | B.cs:16:27:16:28 | access to local variable b1 : Box1 [field elem2] : Elem | | B.cs:15:18:15:34 | object creation of type Box1 : Box1 [field elem2] : Elem | B.cs:16:27:16:28 | access to local variable b1 : Box1 [field elem2] : Elem | | B.cs:15:33:15:33 | access to local variable e : Elem | B.cs:15:18:15:34 | object creation of type Box1 : Box1 [field elem2] : Elem | +| B.cs:15:33:15:33 | access to local variable e : Elem | B.cs:15:18:15:34 | object creation of type Box1 : Box1 [field elem2] : Elem | +| B.cs:15:33:15:33 | access to local variable e : Elem | B.cs:29:35:29:36 | e2 : Elem | | B.cs:15:33:15:33 | access to local variable e : Elem | B.cs:29:35:29:36 | e2 : Elem | | B.cs:16:18:16:29 | object creation of type Box2 : Box2 [field box1, field elem2] : Elem | B.cs:18:14:18:15 | access to local variable b2 : Box2 [field box1, field elem2] : Elem | +| B.cs:16:18:16:29 | object creation of type Box2 : Box2 [field box1, field elem2] : Elem | B.cs:18:14:18:15 | access to local variable b2 : Box2 [field box1, field elem2] : Elem | +| B.cs:16:27:16:28 | access to local variable b1 : Box1 [field elem2] : Elem | B.cs:16:18:16:29 | object creation of type Box2 : Box2 [field box1, field elem2] : Elem | | B.cs:16:27:16:28 | access to local variable b1 : Box1 [field elem2] : Elem | B.cs:16:18:16:29 | object creation of type Box2 : Box2 [field box1, field elem2] : Elem | | B.cs:16:27:16:28 | access to local variable b1 : Box1 [field elem2] : Elem | B.cs:39:26:39:27 | b1 : Box1 [field elem2] : Elem | +| B.cs:16:27:16:28 | access to local variable b1 : Box1 [field elem2] : Elem | B.cs:39:26:39:27 | b1 : Box1 [field elem2] : Elem | +| B.cs:18:14:18:15 | access to local variable b2 : Box2 [field box1, field elem2] : Elem | B.cs:18:14:18:20 | access to field box1 : Box1 [field elem2] : Elem | | B.cs:18:14:18:15 | access to local variable b2 : Box2 [field box1, field elem2] : Elem | B.cs:18:14:18:20 | access to field box1 : Box1 [field elem2] : Elem | | B.cs:18:14:18:20 | access to field box1 : Box1 [field elem2] : Elem | B.cs:18:14:18:26 | access to field elem2 | +| B.cs:18:14:18:20 | access to field box1 : Box1 [field elem2] : Elem | B.cs:18:14:18:26 | access to field elem2 | +| B.cs:29:26:29:27 | e1 : Elem | B.cs:31:26:31:27 | access to parameter e1 : Elem | | B.cs:29:26:29:27 | e1 : Elem | B.cs:31:26:31:27 | access to parameter e1 : Elem | | B.cs:29:35:29:36 | e2 : Elem | B.cs:32:26:32:27 | access to parameter e2 : Elem | +| B.cs:29:35:29:36 | e2 : Elem | B.cs:32:26:32:27 | access to parameter e2 : Elem | +| B.cs:31:26:31:27 | access to parameter e1 : Elem | B.cs:31:13:31:16 | [post] this access : Box1 [field elem1] : Elem | | B.cs:31:26:31:27 | access to parameter e1 : Elem | B.cs:31:13:31:16 | [post] this access : Box1 [field elem1] : Elem | | B.cs:32:26:32:27 | access to parameter e2 : Elem | B.cs:32:13:32:16 | [post] this access : Box1 [field elem2] : Elem | +| B.cs:32:26:32:27 | access to parameter e2 : Elem | B.cs:32:13:32:16 | [post] this access : Box1 [field elem2] : Elem | +| B.cs:39:26:39:27 | b1 : Box1 [field elem1] : Elem | B.cs:41:25:41:26 | access to parameter b1 : Box1 [field elem1] : Elem | | B.cs:39:26:39:27 | b1 : Box1 [field elem1] : Elem | B.cs:41:25:41:26 | access to parameter b1 : Box1 [field elem1] : Elem | | B.cs:39:26:39:27 | b1 : Box1 [field elem2] : Elem | B.cs:41:25:41:26 | access to parameter b1 : Box1 [field elem2] : Elem | +| B.cs:39:26:39:27 | b1 : Box1 [field elem2] : Elem | B.cs:41:25:41:26 | access to parameter b1 : Box1 [field elem2] : Elem | +| B.cs:41:25:41:26 | access to parameter b1 : Box1 [field elem1] : Elem | B.cs:41:13:41:16 | [post] this access : Box2 [field box1, field elem1] : Elem | | B.cs:41:25:41:26 | access to parameter b1 : Box1 [field elem1] : Elem | B.cs:41:13:41:16 | [post] this access : Box2 [field box1, field elem1] : Elem | | B.cs:41:25:41:26 | access to parameter b1 : Box1 [field elem2] : Elem | B.cs:41:13:41:16 | [post] this access : Box2 [field box1, field elem2] : Elem | +| B.cs:41:25:41:26 | access to parameter b1 : Box1 [field elem2] : Elem | B.cs:41:13:41:16 | [post] this access : Box2 [field box1, field elem2] : Elem | +| C.cs:3:18:3:19 | [post] this access : C [field s1] : Elem | C.cs:12:15:12:21 | object creation of type C : C [field s1] : Elem | | C.cs:3:18:3:19 | [post] this access : C [field s1] : Elem | C.cs:12:15:12:21 | object creation of type C : C [field s1] : Elem | | C.cs:3:23:3:37 | call to method Source : Elem | C.cs:3:18:3:19 | [post] this access : C [field s1] : Elem | +| C.cs:3:23:3:37 | call to method Source : Elem | C.cs:3:18:3:19 | [post] this access : C [field s1] : Elem | +| C.cs:4:27:4:28 | [post] this access : C [field s2] : Elem | C.cs:12:15:12:21 | object creation of type C : C [field s2] : Elem | | C.cs:4:27:4:28 | [post] this access : C [field s2] : Elem | C.cs:12:15:12:21 | object creation of type C : C [field s2] : Elem | | C.cs:4:32:4:46 | call to method Source : Elem | C.cs:4:27:4:28 | [post] this access : C [field s2] : Elem | +| C.cs:4:32:4:46 | call to method Source : Elem | C.cs:4:27:4:28 | [post] this access : C [field s2] : Elem | +| C.cs:6:30:6:44 | call to method Source : Elem | C.cs:26:14:26:15 | access to field s4 | | C.cs:6:30:6:44 | call to method Source : Elem | C.cs:26:14:26:15 | access to field s4 | | C.cs:7:18:7:19 | [post] this access : C [property s5] : Elem | C.cs:12:15:12:21 | object creation of type C : C [property s5] : Elem | +| C.cs:7:18:7:19 | [post] this access : C [property s5] : Elem | C.cs:12:15:12:21 | object creation of type C : C [property s5] : Elem | +| C.cs:7:37:7:51 | call to method Source : Elem | C.cs:7:18:7:19 | [post] this access : C [property s5] : Elem | | C.cs:7:37:7:51 | call to method Source : Elem | C.cs:7:18:7:19 | [post] this access : C [property s5] : Elem | | C.cs:8:30:8:44 | call to method Source : Elem | C.cs:28:14:28:15 | access to property s6 | +| C.cs:8:30:8:44 | call to method Source : Elem | C.cs:28:14:28:15 | access to property s6 | +| C.cs:12:15:12:21 | object creation of type C : C [field s1] : Elem | C.cs:13:9:13:9 | access to local variable c : C [field s1] : Elem | | C.cs:12:15:12:21 | object creation of type C : C [field s1] : Elem | C.cs:13:9:13:9 | access to local variable c : C [field s1] : Elem | | C.cs:12:15:12:21 | object creation of type C : C [field s2] : Elem | C.cs:13:9:13:9 | access to local variable c : C [field s2] : Elem | +| C.cs:12:15:12:21 | object creation of type C : C [field s2] : Elem | C.cs:13:9:13:9 | access to local variable c : C [field s2] : Elem | +| C.cs:12:15:12:21 | object creation of type C : C [field s3] : Elem | C.cs:13:9:13:9 | access to local variable c : C [field s3] : Elem | | C.cs:12:15:12:21 | object creation of type C : C [field s3] : Elem | C.cs:13:9:13:9 | access to local variable c : C [field s3] : Elem | | C.cs:12:15:12:21 | object creation of type C : C [property s5] : Elem | C.cs:13:9:13:9 | access to local variable c : C [property s5] : Elem | +| C.cs:12:15:12:21 | object creation of type C : C [property s5] : Elem | C.cs:13:9:13:9 | access to local variable c : C [property s5] : Elem | +| C.cs:13:9:13:9 | access to local variable c : C [field s1] : Elem | C.cs:21:17:21:18 | this : C [field s1] : Elem | | C.cs:13:9:13:9 | access to local variable c : C [field s1] : Elem | C.cs:21:17:21:18 | this : C [field s1] : Elem | | C.cs:13:9:13:9 | access to local variable c : C [field s2] : Elem | C.cs:21:17:21:18 | this : C [field s2] : Elem | +| C.cs:13:9:13:9 | access to local variable c : C [field s2] : Elem | C.cs:21:17:21:18 | this : C [field s2] : Elem | +| C.cs:13:9:13:9 | access to local variable c : C [field s3] : Elem | C.cs:21:17:21:18 | this : C [field s3] : Elem | | C.cs:13:9:13:9 | access to local variable c : C [field s3] : Elem | C.cs:21:17:21:18 | this : C [field s3] : Elem | | C.cs:13:9:13:9 | access to local variable c : C [property s5] : Elem | C.cs:21:17:21:18 | this : C [property s5] : Elem | +| C.cs:13:9:13:9 | access to local variable c : C [property s5] : Elem | C.cs:21:17:21:18 | this : C [property s5] : Elem | +| C.cs:18:9:18:12 | [post] this access : C [field s3] : Elem | C.cs:12:15:12:21 | object creation of type C : C [field s3] : Elem | | C.cs:18:9:18:12 | [post] this access : C [field s3] : Elem | C.cs:12:15:12:21 | object creation of type C : C [field s3] : Elem | | C.cs:18:19:18:33 | call to method Source : Elem | C.cs:18:9:18:12 | [post] this access : C [field s3] : Elem | +| C.cs:18:19:18:33 | call to method Source : Elem | C.cs:18:9:18:12 | [post] this access : C [field s3] : Elem | +| C.cs:21:17:21:18 | this : C [field s1] : Elem | C.cs:23:14:23:15 | this access : C [field s1] : Elem | | C.cs:21:17:21:18 | this : C [field s1] : Elem | C.cs:23:14:23:15 | this access : C [field s1] : Elem | | C.cs:21:17:21:18 | this : C [field s2] : Elem | C.cs:24:14:24:15 | this access : C [field s2] : Elem | +| C.cs:21:17:21:18 | this : C [field s2] : Elem | C.cs:24:14:24:15 | this access : C [field s2] : Elem | +| C.cs:21:17:21:18 | this : C [field s3] : Elem | C.cs:25:14:25:15 | this access : C [field s3] : Elem | | C.cs:21:17:21:18 | this : C [field s3] : Elem | C.cs:25:14:25:15 | this access : C [field s3] : Elem | | C.cs:21:17:21:18 | this : C [property s5] : Elem | C.cs:27:14:27:15 | this access : C [property s5] : Elem | +| C.cs:21:17:21:18 | this : C [property s5] : Elem | C.cs:27:14:27:15 | this access : C [property s5] : Elem | +| C.cs:23:14:23:15 | this access : C [field s1] : Elem | C.cs:23:14:23:15 | access to field s1 | | C.cs:23:14:23:15 | this access : C [field s1] : Elem | C.cs:23:14:23:15 | access to field s1 | | C.cs:24:14:24:15 | this access : C [field s2] : Elem | C.cs:24:14:24:15 | access to field s2 | +| C.cs:24:14:24:15 | this access : C [field s2] : Elem | C.cs:24:14:24:15 | access to field s2 | +| C.cs:25:14:25:15 | this access : C [field s3] : Elem | C.cs:25:14:25:15 | access to field s3 | | C.cs:25:14:25:15 | this access : C [field s3] : Elem | C.cs:25:14:25:15 | access to field s3 | | C.cs:27:14:27:15 | this access : C [property s5] : Elem | C.cs:27:14:27:15 | access to property s5 | +| C.cs:27:14:27:15 | this access : C [property s5] : Elem | C.cs:27:14:27:15 | access to property s5 | +| C_ctor.cs:3:18:3:19 | [post] this access : C_no_ctor [field s1] : Elem | C_ctor.cs:7:23:7:37 | object creation of type C_no_ctor : C_no_ctor [field s1] : Elem | | C_ctor.cs:3:18:3:19 | [post] this access : C_no_ctor [field s1] : Elem | C_ctor.cs:7:23:7:37 | object creation of type C_no_ctor : C_no_ctor [field s1] : Elem | | C_ctor.cs:3:23:3:42 | call to method Source : Elem | C_ctor.cs:3:18:3:19 | [post] this access : C_no_ctor [field s1] : Elem | +| C_ctor.cs:3:23:3:42 | call to method Source : Elem | C_ctor.cs:3:18:3:19 | [post] this access : C_no_ctor [field s1] : Elem | +| C_ctor.cs:7:23:7:37 | object creation of type C_no_ctor : C_no_ctor [field s1] : Elem | C_ctor.cs:8:9:8:9 | access to local variable c : C_no_ctor [field s1] : Elem | | C_ctor.cs:7:23:7:37 | object creation of type C_no_ctor : C_no_ctor [field s1] : Elem | C_ctor.cs:8:9:8:9 | access to local variable c : C_no_ctor [field s1] : Elem | | C_ctor.cs:8:9:8:9 | access to local variable c : C_no_ctor [field s1] : Elem | C_ctor.cs:11:17:11:18 | this : C_no_ctor [field s1] : Elem | +| C_ctor.cs:8:9:8:9 | access to local variable c : C_no_ctor [field s1] : Elem | C_ctor.cs:11:17:11:18 | this : C_no_ctor [field s1] : Elem | +| C_ctor.cs:11:17:11:18 | this : C_no_ctor [field s1] : Elem | C_ctor.cs:13:19:13:20 | this access : C_no_ctor [field s1] : Elem | | C_ctor.cs:11:17:11:18 | this : C_no_ctor [field s1] : Elem | C_ctor.cs:13:19:13:20 | this access : C_no_ctor [field s1] : Elem | | C_ctor.cs:13:19:13:20 | this access : C_no_ctor [field s1] : Elem | C_ctor.cs:13:19:13:20 | access to field s1 | +| C_ctor.cs:13:19:13:20 | this access : C_no_ctor [field s1] : Elem | C_ctor.cs:13:19:13:20 | access to field s1 | +| C_ctor.cs:19:18:19:19 | [post] this access : C_with_ctor [field s1] : Elem | C_ctor.cs:23:25:23:41 | object creation of type C_with_ctor : C_with_ctor [field s1] : Elem | | C_ctor.cs:19:18:19:19 | [post] this access : C_with_ctor [field s1] : Elem | C_ctor.cs:23:25:23:41 | object creation of type C_with_ctor : C_with_ctor [field s1] : Elem | | C_ctor.cs:19:23:19:42 | call to method Source : Elem | C_ctor.cs:19:18:19:19 | [post] this access : C_with_ctor [field s1] : Elem | +| C_ctor.cs:19:23:19:42 | call to method Source : Elem | C_ctor.cs:19:18:19:19 | [post] this access : C_with_ctor [field s1] : Elem | +| C_ctor.cs:23:25:23:41 | object creation of type C_with_ctor : C_with_ctor [field s1] : Elem | C_ctor.cs:24:9:24:9 | access to local variable c : C_with_ctor [field s1] : Elem | | C_ctor.cs:23:25:23:41 | object creation of type C_with_ctor : C_with_ctor [field s1] : Elem | C_ctor.cs:24:9:24:9 | access to local variable c : C_with_ctor [field s1] : Elem | | C_ctor.cs:24:9:24:9 | access to local variable c : C_with_ctor [field s1] : Elem | C_ctor.cs:29:17:29:18 | this : C_with_ctor [field s1] : Elem | +| C_ctor.cs:24:9:24:9 | access to local variable c : C_with_ctor [field s1] : Elem | C_ctor.cs:29:17:29:18 | this : C_with_ctor [field s1] : Elem | +| C_ctor.cs:29:17:29:18 | this : C_with_ctor [field s1] : Elem | C_ctor.cs:31:19:31:20 | this access : C_with_ctor [field s1] : Elem | | C_ctor.cs:29:17:29:18 | this : C_with_ctor [field s1] : Elem | C_ctor.cs:31:19:31:20 | this access : C_with_ctor [field s1] : Elem | | C_ctor.cs:31:19:31:20 | this access : C_with_ctor [field s1] : Elem | C_ctor.cs:31:19:31:20 | access to field s1 | +| C_ctor.cs:31:19:31:20 | this access : C_with_ctor [field s1] : Elem | C_ctor.cs:31:19:31:20 | access to field s1 | +| D.cs:8:9:8:11 | this : D [field trivialPropField] : Object | D.cs:8:22:8:25 | this access : D [field trivialPropField] : Object | | D.cs:8:9:8:11 | this : D [field trivialPropField] : Object | D.cs:8:22:8:25 | this access : D [field trivialPropField] : Object | | D.cs:8:22:8:25 | this access : D [field trivialPropField] : Object | D.cs:8:22:8:42 | access to field trivialPropField : Object | +| D.cs:8:22:8:25 | this access : D [field trivialPropField] : Object | D.cs:8:22:8:42 | access to field trivialPropField : Object | +| D.cs:9:9:9:11 | value : Object | D.cs:9:39:9:43 | access to parameter value : Object | | D.cs:9:9:9:11 | value : Object | D.cs:9:39:9:43 | access to parameter value : Object | | D.cs:9:39:9:43 | access to parameter value : Object | D.cs:9:15:9:18 | [post] this access : D [field trivialPropField] : Object | +| D.cs:9:39:9:43 | access to parameter value : Object | D.cs:9:15:9:18 | [post] this access : D [field trivialPropField] : Object | +| D.cs:14:9:14:11 | this : D [field trivialPropField] : Object | D.cs:14:22:14:25 | this access : D [field trivialPropField] : Object | | D.cs:14:9:14:11 | this : D [field trivialPropField] : Object | D.cs:14:22:14:25 | this access : D [field trivialPropField] : Object | | D.cs:14:22:14:25 | this access : D [field trivialPropField] : Object | D.cs:14:22:14:42 | access to field trivialPropField : Object | +| D.cs:14:22:14:25 | this access : D [field trivialPropField] : Object | D.cs:14:22:14:42 | access to field trivialPropField : Object | +| D.cs:15:9:15:11 | value : Object | D.cs:15:34:15:38 | access to parameter value : Object | | D.cs:15:9:15:11 | value : Object | D.cs:15:34:15:38 | access to parameter value : Object | | D.cs:15:34:15:38 | access to parameter value : Object | D.cs:9:9:9:11 | value : Object | +| D.cs:15:34:15:38 | access to parameter value : Object | D.cs:9:9:9:11 | value : Object | +| D.cs:15:34:15:38 | access to parameter value : Object | D.cs:15:15:15:18 | [post] this access : D [field trivialPropField] : Object | | D.cs:15:34:15:38 | access to parameter value : Object | D.cs:15:15:15:18 | [post] this access : D [field trivialPropField] : Object | | D.cs:18:28:18:29 | o1 : Object | D.cs:21:24:21:25 | access to parameter o1 : Object | +| D.cs:18:28:18:29 | o1 : Object | D.cs:21:24:21:25 | access to parameter o1 : Object | +| D.cs:18:39:18:40 | o2 : Object | D.cs:22:27:22:28 | access to parameter o2 : Object | | D.cs:18:39:18:40 | o2 : Object | D.cs:22:27:22:28 | access to parameter o2 : Object | | D.cs:18:50:18:51 | o3 : Object | D.cs:23:27:23:28 | access to parameter o3 : Object | +| D.cs:18:50:18:51 | o3 : Object | D.cs:23:27:23:28 | access to parameter o3 : Object | +| D.cs:21:9:21:11 | [post] access to local variable ret : D [property AutoProp] : Object | D.cs:24:16:24:18 | access to local variable ret : D [property AutoProp] : Object | | D.cs:21:9:21:11 | [post] access to local variable ret : D [property AutoProp] : Object | D.cs:24:16:24:18 | access to local variable ret : D [property AutoProp] : Object | | D.cs:21:24:21:25 | access to parameter o1 : Object | D.cs:21:9:21:11 | [post] access to local variable ret : D [property AutoProp] : Object | +| D.cs:21:24:21:25 | access to parameter o1 : Object | D.cs:21:9:21:11 | [post] access to local variable ret : D [property AutoProp] : Object | +| D.cs:22:9:22:11 | [post] access to local variable ret : D [field trivialPropField] : Object | D.cs:24:16:24:18 | access to local variable ret : D [field trivialPropField] : Object | | D.cs:22:9:22:11 | [post] access to local variable ret : D [field trivialPropField] : Object | D.cs:24:16:24:18 | access to local variable ret : D [field trivialPropField] : Object | | D.cs:22:27:22:28 | access to parameter o2 : Object | D.cs:9:9:9:11 | value : Object | +| D.cs:22:27:22:28 | access to parameter o2 : Object | D.cs:9:9:9:11 | value : Object | +| D.cs:22:27:22:28 | access to parameter o2 : Object | D.cs:22:9:22:11 | [post] access to local variable ret : D [field trivialPropField] : Object | | D.cs:22:27:22:28 | access to parameter o2 : Object | D.cs:22:9:22:11 | [post] access to local variable ret : D [field trivialPropField] : Object | | D.cs:23:9:23:11 | [post] access to local variable ret : D [field trivialPropField] : Object | D.cs:24:16:24:18 | access to local variable ret : D [field trivialPropField] : Object | +| D.cs:23:9:23:11 | [post] access to local variable ret : D [field trivialPropField] : Object | D.cs:24:16:24:18 | access to local variable ret : D [field trivialPropField] : Object | +| D.cs:23:27:23:28 | access to parameter o3 : Object | D.cs:15:9:15:11 | value : Object | | D.cs:23:27:23:28 | access to parameter o3 : Object | D.cs:15:9:15:11 | value : Object | | D.cs:23:27:23:28 | access to parameter o3 : Object | D.cs:23:9:23:11 | [post] access to local variable ret : D [field trivialPropField] : Object | +| D.cs:23:27:23:28 | access to parameter o3 : Object | D.cs:23:9:23:11 | [post] access to local variable ret : D [field trivialPropField] : Object | +| D.cs:29:17:29:33 | call to method Source : Object | D.cs:31:24:31:24 | access to local variable o : Object | | D.cs:29:17:29:33 | call to method Source : Object | D.cs:31:24:31:24 | access to local variable o : Object | | D.cs:31:17:31:37 | call to method Create : D [property AutoProp] : Object | D.cs:32:14:32:14 | access to local variable d : D [property AutoProp] : Object | +| D.cs:31:17:31:37 | call to method Create : D [property AutoProp] : Object | D.cs:32:14:32:14 | access to local variable d : D [property AutoProp] : Object | +| D.cs:31:24:31:24 | access to local variable o : Object | D.cs:18:28:18:29 | o1 : Object | | D.cs:31:24:31:24 | access to local variable o : Object | D.cs:18:28:18:29 | o1 : Object | | D.cs:31:24:31:24 | access to local variable o : Object | D.cs:31:17:31:37 | call to method Create : D [property AutoProp] : Object | +| D.cs:31:24:31:24 | access to local variable o : Object | D.cs:31:17:31:37 | call to method Create : D [property AutoProp] : Object | +| D.cs:32:14:32:14 | access to local variable d : D [property AutoProp] : Object | D.cs:32:14:32:23 | access to property AutoProp | | D.cs:32:14:32:14 | access to local variable d : D [property AutoProp] : Object | D.cs:32:14:32:23 | access to property AutoProp | | D.cs:37:13:37:49 | call to method Create : D [field trivialPropField] : Object | D.cs:39:14:39:14 | access to local variable d : D [field trivialPropField] : Object | +| D.cs:37:13:37:49 | call to method Create : D [field trivialPropField] : Object | D.cs:39:14:39:14 | access to local variable d : D [field trivialPropField] : Object | +| D.cs:37:13:37:49 | call to method Create : D [field trivialPropField] : Object | D.cs:40:14:40:14 | access to local variable d : D [field trivialPropField] : Object | | D.cs:37:13:37:49 | call to method Create : D [field trivialPropField] : Object | D.cs:40:14:40:14 | access to local variable d : D [field trivialPropField] : Object | | D.cs:37:13:37:49 | call to method Create : D [field trivialPropField] : Object | D.cs:41:14:41:14 | access to local variable d : D [field trivialPropField] : Object | +| D.cs:37:13:37:49 | call to method Create : D [field trivialPropField] : Object | D.cs:41:14:41:14 | access to local variable d : D [field trivialPropField] : Object | +| D.cs:37:26:37:42 | call to method Source : Object | D.cs:18:39:18:40 | o2 : Object | | D.cs:37:26:37:42 | call to method Source : Object | D.cs:18:39:18:40 | o2 : Object | | D.cs:37:26:37:42 | call to method Source : Object | D.cs:37:13:37:49 | call to method Create : D [field trivialPropField] : Object | +| D.cs:37:26:37:42 | call to method Source : Object | D.cs:37:13:37:49 | call to method Create : D [field trivialPropField] : Object | +| D.cs:39:14:39:14 | access to local variable d : D [field trivialPropField] : Object | D.cs:8:9:8:11 | this : D [field trivialPropField] : Object | | D.cs:39:14:39:14 | access to local variable d : D [field trivialPropField] : Object | D.cs:8:9:8:11 | this : D [field trivialPropField] : Object | | D.cs:39:14:39:14 | access to local variable d : D [field trivialPropField] : Object | D.cs:39:14:39:26 | access to property TrivialProp | +| D.cs:39:14:39:14 | access to local variable d : D [field trivialPropField] : Object | D.cs:39:14:39:26 | access to property TrivialProp | +| D.cs:40:14:40:14 | access to local variable d : D [field trivialPropField] : Object | D.cs:40:14:40:31 | access to field trivialPropField | | D.cs:40:14:40:14 | access to local variable d : D [field trivialPropField] : Object | D.cs:40:14:40:31 | access to field trivialPropField | | D.cs:41:14:41:14 | access to local variable d : D [field trivialPropField] : Object | D.cs:14:9:14:11 | this : D [field trivialPropField] : Object | +| D.cs:41:14:41:14 | access to local variable d : D [field trivialPropField] : Object | D.cs:14:9:14:11 | this : D [field trivialPropField] : Object | +| D.cs:41:14:41:14 | access to local variable d : D [field trivialPropField] : Object | D.cs:41:14:41:26 | access to property ComplexProp | | D.cs:41:14:41:14 | access to local variable d : D [field trivialPropField] : Object | D.cs:41:14:41:26 | access to property ComplexProp | | D.cs:43:13:43:49 | call to method Create : D [field trivialPropField] : Object | D.cs:45:14:45:14 | access to local variable d : D [field trivialPropField] : Object | +| D.cs:43:13:43:49 | call to method Create : D [field trivialPropField] : Object | D.cs:45:14:45:14 | access to local variable d : D [field trivialPropField] : Object | +| D.cs:43:13:43:49 | call to method Create : D [field trivialPropField] : Object | D.cs:46:14:46:14 | access to local variable d : D [field trivialPropField] : Object | | D.cs:43:13:43:49 | call to method Create : D [field trivialPropField] : Object | D.cs:46:14:46:14 | access to local variable d : D [field trivialPropField] : Object | | D.cs:43:13:43:49 | call to method Create : D [field trivialPropField] : Object | D.cs:47:14:47:14 | access to local variable d : D [field trivialPropField] : Object | +| D.cs:43:13:43:49 | call to method Create : D [field trivialPropField] : Object | D.cs:47:14:47:14 | access to local variable d : D [field trivialPropField] : Object | +| D.cs:43:32:43:48 | call to method Source : Object | D.cs:18:50:18:51 | o3 : Object | | D.cs:43:32:43:48 | call to method Source : Object | D.cs:18:50:18:51 | o3 : Object | | D.cs:43:32:43:48 | call to method Source : Object | D.cs:43:13:43:49 | call to method Create : D [field trivialPropField] : Object | +| D.cs:43:32:43:48 | call to method Source : Object | D.cs:43:13:43:49 | call to method Create : D [field trivialPropField] : Object | +| D.cs:45:14:45:14 | access to local variable d : D [field trivialPropField] : Object | D.cs:8:9:8:11 | this : D [field trivialPropField] : Object | | D.cs:45:14:45:14 | access to local variable d : D [field trivialPropField] : Object | D.cs:8:9:8:11 | this : D [field trivialPropField] : Object | | D.cs:45:14:45:14 | access to local variable d : D [field trivialPropField] : Object | D.cs:45:14:45:26 | access to property TrivialProp | +| D.cs:45:14:45:14 | access to local variable d : D [field trivialPropField] : Object | D.cs:45:14:45:26 | access to property TrivialProp | +| D.cs:46:14:46:14 | access to local variable d : D [field trivialPropField] : Object | D.cs:46:14:46:31 | access to field trivialPropField | | D.cs:46:14:46:14 | access to local variable d : D [field trivialPropField] : Object | D.cs:46:14:46:31 | access to field trivialPropField | | D.cs:47:14:47:14 | access to local variable d : D [field trivialPropField] : Object | D.cs:14:9:14:11 | this : D [field trivialPropField] : Object | +| D.cs:47:14:47:14 | access to local variable d : D [field trivialPropField] : Object | D.cs:14:9:14:11 | this : D [field trivialPropField] : Object | +| D.cs:47:14:47:14 | access to local variable d : D [field trivialPropField] : Object | D.cs:47:14:47:26 | access to property ComplexProp | | D.cs:47:14:47:14 | access to local variable d : D [field trivialPropField] : Object | D.cs:47:14:47:26 | access to property ComplexProp | | E.cs:8:29:8:29 | o : Object | E.cs:11:21:11:21 | access to parameter o : Object | +| E.cs:8:29:8:29 | o : Object | E.cs:11:21:11:21 | access to parameter o : Object | +| E.cs:11:9:11:11 | [post] access to local variable ret : S [field Field] : Object | E.cs:12:16:12:18 | access to local variable ret : S [field Field] : Object | | E.cs:11:9:11:11 | [post] access to local variable ret : S [field Field] : Object | E.cs:12:16:12:18 | access to local variable ret : S [field Field] : Object | | E.cs:11:21:11:21 | access to parameter o : Object | E.cs:11:9:11:11 | [post] access to local variable ret : S [field Field] : Object | +| E.cs:11:21:11:21 | access to parameter o : Object | E.cs:11:9:11:11 | [post] access to local variable ret : S [field Field] : Object | +| E.cs:22:17:22:33 | call to method Source : Object | E.cs:23:25:23:25 | access to local variable o : Object | | E.cs:22:17:22:33 | call to method Source : Object | E.cs:23:25:23:25 | access to local variable o : Object | | E.cs:23:17:23:26 | call to method CreateS : S [field Field] : Object | E.cs:24:14:24:14 | access to local variable s : S [field Field] : Object | +| E.cs:23:17:23:26 | call to method CreateS : S [field Field] : Object | E.cs:24:14:24:14 | access to local variable s : S [field Field] : Object | +| E.cs:23:25:23:25 | access to local variable o : Object | E.cs:8:29:8:29 | o : Object | | E.cs:23:25:23:25 | access to local variable o : Object | E.cs:8:29:8:29 | o : Object | | E.cs:23:25:23:25 | access to local variable o : Object | E.cs:23:17:23:26 | call to method CreateS : S [field Field] : Object | +| E.cs:23:25:23:25 | access to local variable o : Object | E.cs:23:17:23:26 | call to method CreateS : S [field Field] : Object | +| E.cs:24:14:24:14 | access to local variable s : S [field Field] : Object | E.cs:24:14:24:20 | access to field Field | | E.cs:24:14:24:14 | access to local variable s : S [field Field] : Object | E.cs:24:14:24:20 | access to field Field | | F.cs:6:28:6:29 | o1 : Object | F.cs:6:65:6:66 | access to parameter o1 : Object | +| F.cs:6:28:6:29 | o1 : Object | F.cs:6:65:6:66 | access to parameter o1 : Object | +| F.cs:6:39:6:40 | o2 : Object | F.cs:6:78:6:79 | access to parameter o2 : Object | | F.cs:6:39:6:40 | o2 : Object | F.cs:6:78:6:79 | access to parameter o2 : Object | | F.cs:6:54:6:81 | { ..., ... } : F [field Field1] : Object | F.cs:6:46:6:81 | object creation of type F : F [field Field1] : Object | +| F.cs:6:54:6:81 | { ..., ... } : F [field Field1] : Object | F.cs:6:46:6:81 | object creation of type F : F [field Field1] : Object | +| F.cs:6:54:6:81 | { ..., ... } : F [field Field2] : Object | F.cs:6:46:6:81 | object creation of type F : F [field Field2] : Object | | F.cs:6:54:6:81 | { ..., ... } : F [field Field2] : Object | F.cs:6:46:6:81 | object creation of type F : F [field Field2] : Object | | F.cs:6:65:6:66 | access to parameter o1 : Object | F.cs:6:54:6:81 | { ..., ... } : F [field Field1] : Object | +| F.cs:6:65:6:66 | access to parameter o1 : Object | F.cs:6:54:6:81 | { ..., ... } : F [field Field1] : Object | +| F.cs:6:78:6:79 | access to parameter o2 : Object | F.cs:6:54:6:81 | { ..., ... } : F [field Field2] : Object | | F.cs:6:78:6:79 | access to parameter o2 : Object | F.cs:6:54:6:81 | { ..., ... } : F [field Field2] : Object | | F.cs:10:17:10:33 | call to method Source : Object | F.cs:11:24:11:24 | access to local variable o : Object | +| F.cs:10:17:10:33 | call to method Source : Object | F.cs:11:24:11:24 | access to local variable o : Object | +| F.cs:11:17:11:31 | call to method Create : F [field Field1] : Object | F.cs:12:14:12:14 | access to local variable f : F [field Field1] : Object | | F.cs:11:17:11:31 | call to method Create : F [field Field1] : Object | F.cs:12:14:12:14 | access to local variable f : F [field Field1] : Object | | F.cs:11:24:11:24 | access to local variable o : Object | F.cs:6:28:6:29 | o1 : Object | +| F.cs:11:24:11:24 | access to local variable o : Object | F.cs:6:28:6:29 | o1 : Object | +| F.cs:11:24:11:24 | access to local variable o : Object | F.cs:11:17:11:31 | call to method Create : F [field Field1] : Object | | F.cs:11:24:11:24 | access to local variable o : Object | F.cs:11:17:11:31 | call to method Create : F [field Field1] : Object | | F.cs:12:14:12:14 | access to local variable f : F [field Field1] : Object | F.cs:12:14:12:21 | access to field Field1 | +| F.cs:12:14:12:14 | access to local variable f : F [field Field1] : Object | F.cs:12:14:12:21 | access to field Field1 | +| F.cs:15:13:15:43 | call to method Create : F [field Field2] : Object | F.cs:17:14:17:14 | access to local variable f : F [field Field2] : Object | | F.cs:15:13:15:43 | call to method Create : F [field Field2] : Object | F.cs:17:14:17:14 | access to local variable f : F [field Field2] : Object | | F.cs:15:26:15:42 | call to method Source : Object | F.cs:6:39:6:40 | o2 : Object | +| F.cs:15:26:15:42 | call to method Source : Object | F.cs:6:39:6:40 | o2 : Object | +| F.cs:15:26:15:42 | call to method Source : Object | F.cs:15:13:15:43 | call to method Create : F [field Field2] : Object | | F.cs:15:26:15:42 | call to method Source : Object | F.cs:15:13:15:43 | call to method Create : F [field Field2] : Object | | F.cs:17:14:17:14 | access to local variable f : F [field Field2] : Object | F.cs:17:14:17:21 | access to field Field2 | +| F.cs:17:14:17:14 | access to local variable f : F [field Field2] : Object | F.cs:17:14:17:21 | access to field Field2 | +| F.cs:19:21:19:50 | { ..., ... } : F [field Field1] : Object | F.cs:20:14:20:14 | access to local variable f : F [field Field1] : Object | | F.cs:19:21:19:50 | { ..., ... } : F [field Field1] : Object | F.cs:20:14:20:14 | access to local variable f : F [field Field1] : Object | | F.cs:19:32:19:48 | call to method Source : Object | F.cs:19:21:19:50 | { ..., ... } : F [field Field1] : Object | +| F.cs:19:32:19:48 | call to method Source : Object | F.cs:19:21:19:50 | { ..., ... } : F [field Field1] : Object | +| F.cs:20:14:20:14 | access to local variable f : F [field Field1] : Object | F.cs:20:14:20:21 | access to field Field1 | | F.cs:20:14:20:14 | access to local variable f : F [field Field1] : Object | F.cs:20:14:20:21 | access to field Field1 | | F.cs:23:21:23:50 | { ..., ... } : F [field Field2] : Object | F.cs:25:14:25:14 | access to local variable f : F [field Field2] : Object | +| F.cs:23:21:23:50 | { ..., ... } : F [field Field2] : Object | F.cs:25:14:25:14 | access to local variable f : F [field Field2] : Object | +| F.cs:23:32:23:48 | call to method Source : Object | F.cs:23:21:23:50 | { ..., ... } : F [field Field2] : Object | | F.cs:23:32:23:48 | call to method Source : Object | F.cs:23:21:23:50 | { ..., ... } : F [field Field2] : Object | | F.cs:25:14:25:14 | access to local variable f : F [field Field2] : Object | F.cs:25:14:25:21 | access to field Field2 | +| F.cs:25:14:25:14 | access to local variable f : F [field Field2] : Object | F.cs:25:14:25:21 | access to field Field2 | +| F.cs:30:17:30:33 | call to method Source : Object | F.cs:32:27:32:27 | access to local variable o : Object | | F.cs:30:17:30:33 | call to method Source : Object | F.cs:32:27:32:27 | access to local variable o : Object | | F.cs:32:17:32:40 | { ..., ... } : <>__AnonType0 [property X] : Object | F.cs:33:14:33:14 | access to local variable a : <>__AnonType0 [property X] : Object | +| F.cs:32:17:32:40 | { ..., ... } : <>__AnonType0 [property X] : Object | F.cs:33:14:33:14 | access to local variable a : <>__AnonType0 [property X] : Object | +| F.cs:32:27:32:27 | access to local variable o : Object | F.cs:32:17:32:40 | { ..., ... } : <>__AnonType0 [property X] : Object | | F.cs:32:27:32:27 | access to local variable o : Object | F.cs:32:17:32:40 | { ..., ... } : <>__AnonType0 [property X] : Object | | F.cs:33:14:33:14 | access to local variable a : <>__AnonType0 [property X] : Object | F.cs:33:14:33:16 | access to property X | +| F.cs:33:14:33:14 | access to local variable a : <>__AnonType0 [property X] : Object | F.cs:33:14:33:16 | access to property X | +| G.cs:7:18:7:32 | call to method Source : Elem | G.cs:9:23:9:23 | access to local variable e : Elem | | G.cs:7:18:7:32 | call to method Source : Elem | G.cs:9:23:9:23 | access to local variable e : Elem | | G.cs:9:9:9:9 | [post] access to local variable b : Box2 [field Box1, field Elem] : Elem | G.cs:10:18:10:18 | access to local variable b : Box2 [field Box1, field Elem] : Elem | +| G.cs:9:9:9:9 | [post] access to local variable b : Box2 [field Box1, field Elem] : Elem | G.cs:10:18:10:18 | access to local variable b : Box2 [field Box1, field Elem] : Elem | +| G.cs:9:9:9:14 | [post] access to field Box1 : Box1 [field Elem] : Elem | G.cs:9:9:9:9 | [post] access to local variable b : Box2 [field Box1, field Elem] : Elem | | G.cs:9:9:9:14 | [post] access to field Box1 : Box1 [field Elem] : Elem | G.cs:9:9:9:9 | [post] access to local variable b : Box2 [field Box1, field Elem] : Elem | | G.cs:9:23:9:23 | access to local variable e : Elem | G.cs:9:9:9:14 | [post] access to field Box1 : Box1 [field Elem] : Elem | +| G.cs:9:23:9:23 | access to local variable e : Elem | G.cs:9:9:9:14 | [post] access to field Box1 : Box1 [field Elem] : Elem | +| G.cs:10:18:10:18 | access to local variable b : Box2 [field Box1, field Elem] : Elem | G.cs:37:38:37:39 | b2 : Box2 [field Box1, field Elem] : Elem | | G.cs:10:18:10:18 | access to local variable b : Box2 [field Box1, field Elem] : Elem | G.cs:37:38:37:39 | b2 : Box2 [field Box1, field Elem] : Elem | | G.cs:15:18:15:32 | call to method Source : Elem | G.cs:17:24:17:24 | access to local variable e : Elem | +| G.cs:15:18:15:32 | call to method Source : Elem | G.cs:17:24:17:24 | access to local variable e : Elem | +| G.cs:17:9:17:9 | [post] access to local variable b : Box2 [field Box1, field Elem] : Elem | G.cs:18:18:18:18 | access to local variable b : Box2 [field Box1, field Elem] : Elem | | G.cs:17:9:17:9 | [post] access to local variable b : Box2 [field Box1, field Elem] : Elem | G.cs:18:18:18:18 | access to local variable b : Box2 [field Box1, field Elem] : Elem | | G.cs:17:9:17:14 | [post] access to field Box1 : Box1 [field Elem] : Elem | G.cs:17:9:17:9 | [post] access to local variable b : Box2 [field Box1, field Elem] : Elem | +| G.cs:17:9:17:14 | [post] access to field Box1 : Box1 [field Elem] : Elem | G.cs:17:9:17:9 | [post] access to local variable b : Box2 [field Box1, field Elem] : Elem | +| G.cs:17:24:17:24 | access to local variable e : Elem | G.cs:17:9:17:14 | [post] access to field Box1 : Box1 [field Elem] : Elem | | G.cs:17:24:17:24 | access to local variable e : Elem | G.cs:17:9:17:14 | [post] access to field Box1 : Box1 [field Elem] : Elem | | G.cs:17:24:17:24 | access to local variable e : Elem | G.cs:64:34:64:34 | e : Elem | +| G.cs:17:24:17:24 | access to local variable e : Elem | G.cs:64:34:64:34 | e : Elem | +| G.cs:18:18:18:18 | access to local variable b : Box2 [field Box1, field Elem] : Elem | G.cs:37:38:37:39 | b2 : Box2 [field Box1, field Elem] : Elem | | G.cs:18:18:18:18 | access to local variable b : Box2 [field Box1, field Elem] : Elem | G.cs:37:38:37:39 | b2 : Box2 [field Box1, field Elem] : Elem | | G.cs:23:18:23:32 | call to method Source : Elem | G.cs:25:28:25:28 | access to local variable e : Elem | +| G.cs:23:18:23:32 | call to method Source : Elem | G.cs:25:28:25:28 | access to local variable e : Elem | +| G.cs:25:9:25:9 | [post] access to local variable b : Box2 [field Box1, field Elem] : Elem | G.cs:26:18:26:18 | access to local variable b : Box2 [field Box1, field Elem] : Elem | | G.cs:25:9:25:9 | [post] access to local variable b : Box2 [field Box1, field Elem] : Elem | G.cs:26:18:26:18 | access to local variable b : Box2 [field Box1, field Elem] : Elem | | G.cs:25:9:25:19 | [post] call to method GetBox1 : Box1 [field Elem] : Elem | G.cs:25:9:25:9 | [post] access to local variable b : Box2 [field Box1, field Elem] : Elem | +| G.cs:25:9:25:19 | [post] call to method GetBox1 : Box1 [field Elem] : Elem | G.cs:25:9:25:9 | [post] access to local variable b : Box2 [field Box1, field Elem] : Elem | +| G.cs:25:28:25:28 | access to local variable e : Elem | G.cs:25:9:25:19 | [post] call to method GetBox1 : Box1 [field Elem] : Elem | | G.cs:25:28:25:28 | access to local variable e : Elem | G.cs:25:9:25:19 | [post] call to method GetBox1 : Box1 [field Elem] : Elem | | G.cs:26:18:26:18 | access to local variable b : Box2 [field Box1, field Elem] : Elem | G.cs:37:38:37:39 | b2 : Box2 [field Box1, field Elem] : Elem | +| G.cs:26:18:26:18 | access to local variable b : Box2 [field Box1, field Elem] : Elem | G.cs:37:38:37:39 | b2 : Box2 [field Box1, field Elem] : Elem | +| G.cs:31:18:31:32 | call to method Source : Elem | G.cs:33:29:33:29 | access to local variable e : Elem | | G.cs:31:18:31:32 | call to method Source : Elem | G.cs:33:29:33:29 | access to local variable e : Elem | | G.cs:33:9:33:9 | [post] access to local variable b : Box2 [field Box1, field Elem] : Elem | G.cs:34:18:34:18 | access to local variable b : Box2 [field Box1, field Elem] : Elem | +| G.cs:33:9:33:9 | [post] access to local variable b : Box2 [field Box1, field Elem] : Elem | G.cs:34:18:34:18 | access to local variable b : Box2 [field Box1, field Elem] : Elem | +| G.cs:33:9:33:19 | [post] call to method GetBox1 : Box1 [field Elem] : Elem | G.cs:33:9:33:9 | [post] access to local variable b : Box2 [field Box1, field Elem] : Elem | | G.cs:33:9:33:19 | [post] call to method GetBox1 : Box1 [field Elem] : Elem | G.cs:33:9:33:9 | [post] access to local variable b : Box2 [field Box1, field Elem] : Elem | | G.cs:33:29:33:29 | access to local variable e : Elem | G.cs:33:9:33:19 | [post] call to method GetBox1 : Box1 [field Elem] : Elem | +| G.cs:33:29:33:29 | access to local variable e : Elem | G.cs:33:9:33:19 | [post] call to method GetBox1 : Box1 [field Elem] : Elem | +| G.cs:33:29:33:29 | access to local variable e : Elem | G.cs:64:34:64:34 | e : Elem | | G.cs:33:29:33:29 | access to local variable e : Elem | G.cs:64:34:64:34 | e : Elem | | G.cs:34:18:34:18 | access to local variable b : Box2 [field Box1, field Elem] : Elem | G.cs:37:38:37:39 | b2 : Box2 [field Box1, field Elem] : Elem | +| G.cs:34:18:34:18 | access to local variable b : Box2 [field Box1, field Elem] : Elem | G.cs:37:38:37:39 | b2 : Box2 [field Box1, field Elem] : Elem | +| G.cs:37:38:37:39 | b2 : Box2 [field Box1, field Elem] : Elem | G.cs:39:14:39:15 | access to parameter b2 : Box2 [field Box1, field Elem] : Elem | | G.cs:37:38:37:39 | b2 : Box2 [field Box1, field Elem] : Elem | G.cs:39:14:39:15 | access to parameter b2 : Box2 [field Box1, field Elem] : Elem | | G.cs:39:14:39:15 | access to parameter b2 : Box2 [field Box1, field Elem] : Elem | G.cs:39:14:39:25 | call to method GetBox1 : Box1 [field Elem] : Elem | +| G.cs:39:14:39:15 | access to parameter b2 : Box2 [field Box1, field Elem] : Elem | G.cs:39:14:39:25 | call to method GetBox1 : Box1 [field Elem] : Elem | +| G.cs:39:14:39:15 | access to parameter b2 : Box2 [field Box1, field Elem] : Elem | G.cs:71:21:71:27 | this : Box2 [field Box1, field Elem] : Elem | | G.cs:39:14:39:15 | access to parameter b2 : Box2 [field Box1, field Elem] : Elem | G.cs:71:21:71:27 | this : Box2 [field Box1, field Elem] : Elem | | G.cs:39:14:39:25 | call to method GetBox1 : Box1 [field Elem] : Elem | G.cs:39:14:39:35 | call to method GetElem | +| G.cs:39:14:39:25 | call to method GetBox1 : Box1 [field Elem] : Elem | G.cs:39:14:39:35 | call to method GetElem | +| G.cs:39:14:39:25 | call to method GetBox1 : Box1 [field Elem] : Elem | G.cs:63:21:63:27 | this : Box1 [field Elem] : Elem | | G.cs:39:14:39:25 | call to method GetBox1 : Box1 [field Elem] : Elem | G.cs:63:21:63:27 | this : Box1 [field Elem] : Elem | | G.cs:44:18:44:32 | call to method Source : Elem | G.cs:46:30:46:30 | access to local variable e : Elem | +| G.cs:44:18:44:32 | call to method Source : Elem | G.cs:46:30:46:30 | access to local variable e : Elem | +| G.cs:46:9:46:16 | [post] access to field boxfield : Box2 [field Box1, field Elem] : Elem | G.cs:46:9:46:16 | [post] this access : G [field boxfield, field Box1, field Elem] : Elem | | G.cs:46:9:46:16 | [post] access to field boxfield : Box2 [field Box1, field Elem] : Elem | G.cs:46:9:46:16 | [post] this access : G [field boxfield, field Box1, field Elem] : Elem | | G.cs:46:9:46:16 | [post] this access : G [field boxfield, field Box1, field Elem] : Elem | G.cs:47:9:47:13 | this access : G [field boxfield, field Box1, field Elem] : Elem | +| G.cs:46:9:46:16 | [post] this access : G [field boxfield, field Box1, field Elem] : Elem | G.cs:47:9:47:13 | this access : G [field boxfield, field Box1, field Elem] : Elem | +| G.cs:46:9:46:21 | [post] access to field Box1 : Box1 [field Elem] : Elem | G.cs:46:9:46:16 | [post] access to field boxfield : Box2 [field Box1, field Elem] : Elem | | G.cs:46:9:46:21 | [post] access to field Box1 : Box1 [field Elem] : Elem | G.cs:46:9:46:16 | [post] access to field boxfield : Box2 [field Box1, field Elem] : Elem | | G.cs:46:30:46:30 | access to local variable e : Elem | G.cs:46:9:46:21 | [post] access to field Box1 : Box1 [field Elem] : Elem | +| G.cs:46:30:46:30 | access to local variable e : Elem | G.cs:46:9:46:21 | [post] access to field Box1 : Box1 [field Elem] : Elem | +| G.cs:47:9:47:13 | this access : G [field boxfield, field Box1, field Elem] : Elem | G.cs:50:18:50:20 | this : G [field boxfield, field Box1, field Elem] : Elem | | G.cs:47:9:47:13 | this access : G [field boxfield, field Box1, field Elem] : Elem | G.cs:50:18:50:20 | this : G [field boxfield, field Box1, field Elem] : Elem | | G.cs:50:18:50:20 | this : G [field boxfield, field Box1, field Elem] : Elem | G.cs:52:14:52:21 | this access : G [field boxfield, field Box1, field Elem] : Elem | +| G.cs:50:18:50:20 | this : G [field boxfield, field Box1, field Elem] : Elem | G.cs:52:14:52:21 | this access : G [field boxfield, field Box1, field Elem] : Elem | +| G.cs:52:14:52:21 | access to field boxfield : Box2 [field Box1, field Elem] : Elem | G.cs:52:14:52:26 | access to field Box1 : Box1 [field Elem] : Elem | | G.cs:52:14:52:21 | access to field boxfield : Box2 [field Box1, field Elem] : Elem | G.cs:52:14:52:26 | access to field Box1 : Box1 [field Elem] : Elem | | G.cs:52:14:52:21 | this access : G [field boxfield, field Box1, field Elem] : Elem | G.cs:52:14:52:21 | access to field boxfield : Box2 [field Box1, field Elem] : Elem | +| G.cs:52:14:52:21 | this access : G [field boxfield, field Box1, field Elem] : Elem | G.cs:52:14:52:21 | access to field boxfield : Box2 [field Box1, field Elem] : Elem | +| G.cs:52:14:52:26 | access to field Box1 : Box1 [field Elem] : Elem | G.cs:52:14:52:31 | access to field Elem | | G.cs:52:14:52:26 | access to field Box1 : Box1 [field Elem] : Elem | G.cs:52:14:52:31 | access to field Elem | | G.cs:63:21:63:27 | this : Box1 [field Elem] : Elem | G.cs:63:34:63:37 | this access : Box1 [field Elem] : Elem | +| G.cs:63:21:63:27 | this : Box1 [field Elem] : Elem | G.cs:63:34:63:37 | this access : Box1 [field Elem] : Elem | +| G.cs:63:34:63:37 | this access : Box1 [field Elem] : Elem | G.cs:63:34:63:37 | access to field Elem : Elem | | G.cs:63:34:63:37 | this access : Box1 [field Elem] : Elem | G.cs:63:34:63:37 | access to field Elem : Elem | | G.cs:64:34:64:34 | e : Elem | G.cs:64:46:64:46 | access to parameter e : Elem | +| G.cs:64:34:64:34 | e : Elem | G.cs:64:46:64:46 | access to parameter e : Elem | +| G.cs:64:46:64:46 | access to parameter e : Elem | G.cs:64:39:64:42 | [post] this access : Box1 [field Elem] : Elem | | G.cs:64:46:64:46 | access to parameter e : Elem | G.cs:64:39:64:42 | [post] this access : Box1 [field Elem] : Elem | | G.cs:71:21:71:27 | this : Box2 [field Box1, field Elem] : Elem | G.cs:71:34:71:37 | this access : Box2 [field Box1, field Elem] : Elem | +| G.cs:71:21:71:27 | this : Box2 [field Box1, field Elem] : Elem | G.cs:71:34:71:37 | this access : Box2 [field Box1, field Elem] : Elem | +| G.cs:71:34:71:37 | this access : Box2 [field Box1, field Elem] : Elem | G.cs:71:34:71:37 | access to field Box1 : Box1 [field Elem] : Elem | | G.cs:71:34:71:37 | this access : Box2 [field Box1, field Elem] : Elem | G.cs:71:34:71:37 | access to field Box1 : Box1 [field Elem] : Elem | | H.cs:13:15:13:15 | a : A [field FieldA] : Object | H.cs:16:22:16:22 | access to parameter a : A [field FieldA] : Object | +| H.cs:13:15:13:15 | a : A [field FieldA] : Object | H.cs:16:22:16:22 | access to parameter a : A [field FieldA] : Object | +| H.cs:16:9:16:11 | [post] access to local variable ret : A [field FieldA] : Object | H.cs:17:16:17:18 | access to local variable ret : A [field FieldA] : Object | | H.cs:16:9:16:11 | [post] access to local variable ret : A [field FieldA] : Object | H.cs:17:16:17:18 | access to local variable ret : A [field FieldA] : Object | | H.cs:16:22:16:22 | access to parameter a : A [field FieldA] : Object | H.cs:16:22:16:29 | access to field FieldA : Object | +| H.cs:16:22:16:22 | access to parameter a : A [field FieldA] : Object | H.cs:16:22:16:29 | access to field FieldA : Object | +| H.cs:16:22:16:29 | access to field FieldA : Object | H.cs:16:9:16:11 | [post] access to local variable ret : A [field FieldA] : Object | | H.cs:16:22:16:29 | access to field FieldA : Object | H.cs:16:9:16:11 | [post] access to local variable ret : A [field FieldA] : Object | | H.cs:23:9:23:9 | [post] access to local variable a : A [field FieldA] : Object | H.cs:24:27:24:27 | access to local variable a : A [field FieldA] : Object | +| H.cs:23:9:23:9 | [post] access to local variable a : A [field FieldA] : Object | H.cs:24:27:24:27 | access to local variable a : A [field FieldA] : Object | +| H.cs:23:20:23:36 | call to method Source : Object | H.cs:23:9:23:9 | [post] access to local variable a : A [field FieldA] : Object | | H.cs:23:20:23:36 | call to method Source : Object | H.cs:23:9:23:9 | [post] access to local variable a : A [field FieldA] : Object | | H.cs:24:21:24:28 | call to method Clone : A [field FieldA] : Object | H.cs:25:14:25:18 | access to local variable clone : A [field FieldA] : Object | +| H.cs:24:21:24:28 | call to method Clone : A [field FieldA] : Object | H.cs:25:14:25:18 | access to local variable clone : A [field FieldA] : Object | +| H.cs:24:27:24:27 | access to local variable a : A [field FieldA] : Object | H.cs:13:15:13:15 | a : A [field FieldA] : Object | | H.cs:24:27:24:27 | access to local variable a : A [field FieldA] : Object | H.cs:13:15:13:15 | a : A [field FieldA] : Object | | H.cs:24:27:24:27 | access to local variable a : A [field FieldA] : Object | H.cs:24:21:24:28 | call to method Clone : A [field FieldA] : Object | +| H.cs:24:27:24:27 | access to local variable a : A [field FieldA] : Object | H.cs:24:21:24:28 | call to method Clone : A [field FieldA] : Object | +| H.cs:25:14:25:18 | access to local variable clone : A [field FieldA] : Object | H.cs:25:14:25:25 | access to field FieldA | | H.cs:25:14:25:18 | access to local variable clone : A [field FieldA] : Object | H.cs:25:14:25:25 | access to field FieldA | | H.cs:33:19:33:19 | a : A [field FieldA] : A | H.cs:36:20:36:20 | access to parameter a : A [field FieldA] : A | +| H.cs:33:19:33:19 | a : A [field FieldA] : A | H.cs:36:20:36:20 | access to parameter a : A [field FieldA] : A | +| H.cs:33:19:33:19 | a : A [field FieldA] : Object | H.cs:36:20:36:20 | access to parameter a : A [field FieldA] : Object | | H.cs:33:19:33:19 | a : A [field FieldA] : Object | H.cs:36:20:36:20 | access to parameter a : A [field FieldA] : Object | | H.cs:36:9:36:9 | [post] access to local variable b : B [field FieldB] : A | H.cs:37:16:37:16 | access to local variable b : B [field FieldB] : A | +| H.cs:36:9:36:9 | [post] access to local variable b : B [field FieldB] : A | H.cs:37:16:37:16 | access to local variable b : B [field FieldB] : A | +| H.cs:36:9:36:9 | [post] access to local variable b : B [field FieldB] : Object | H.cs:37:16:37:16 | access to local variable b : B [field FieldB] : Object | | H.cs:36:9:36:9 | [post] access to local variable b : B [field FieldB] : Object | H.cs:37:16:37:16 | access to local variable b : B [field FieldB] : Object | | H.cs:36:20:36:20 | access to parameter a : A [field FieldA] : A | H.cs:36:20:36:27 | access to field FieldA : A | +| H.cs:36:20:36:20 | access to parameter a : A [field FieldA] : A | H.cs:36:20:36:27 | access to field FieldA : A | +| H.cs:36:20:36:20 | access to parameter a : A [field FieldA] : Object | H.cs:36:20:36:27 | access to field FieldA : Object | | H.cs:36:20:36:20 | access to parameter a : A [field FieldA] : Object | H.cs:36:20:36:27 | access to field FieldA : Object | | H.cs:36:20:36:27 | access to field FieldA : A | H.cs:36:9:36:9 | [post] access to local variable b : B [field FieldB] : A | +| H.cs:36:20:36:27 | access to field FieldA : A | H.cs:36:9:36:9 | [post] access to local variable b : B [field FieldB] : A | +| H.cs:36:20:36:27 | access to field FieldA : Object | H.cs:36:9:36:9 | [post] access to local variable b : B [field FieldB] : Object | | H.cs:36:20:36:27 | access to field FieldA : Object | H.cs:36:9:36:9 | [post] access to local variable b : B [field FieldB] : Object | | H.cs:43:9:43:9 | [post] access to local variable a : A [field FieldA] : Object | H.cs:44:27:44:27 | access to local variable a : A [field FieldA] : Object | +| H.cs:43:9:43:9 | [post] access to local variable a : A [field FieldA] : Object | H.cs:44:27:44:27 | access to local variable a : A [field FieldA] : Object | +| H.cs:43:20:43:36 | call to method Source : Object | H.cs:43:9:43:9 | [post] access to local variable a : A [field FieldA] : Object | | H.cs:43:20:43:36 | call to method Source : Object | H.cs:43:9:43:9 | [post] access to local variable a : A [field FieldA] : Object | | H.cs:44:17:44:28 | call to method Transform : B [field FieldB] : Object | H.cs:45:14:45:14 | access to local variable b : B [field FieldB] : Object | +| H.cs:44:17:44:28 | call to method Transform : B [field FieldB] : Object | H.cs:45:14:45:14 | access to local variable b : B [field FieldB] : Object | +| H.cs:44:27:44:27 | access to local variable a : A [field FieldA] : Object | H.cs:33:19:33:19 | a : A [field FieldA] : Object | | H.cs:44:27:44:27 | access to local variable a : A [field FieldA] : Object | H.cs:33:19:33:19 | a : A [field FieldA] : Object | | H.cs:44:27:44:27 | access to local variable a : A [field FieldA] : Object | H.cs:44:17:44:28 | call to method Transform : B [field FieldB] : Object | +| H.cs:44:27:44:27 | access to local variable a : A [field FieldA] : Object | H.cs:44:17:44:28 | call to method Transform : B [field FieldB] : Object | +| H.cs:45:14:45:14 | access to local variable b : B [field FieldB] : Object | H.cs:45:14:45:21 | access to field FieldB | | H.cs:45:14:45:14 | access to local variable b : B [field FieldB] : Object | H.cs:45:14:45:21 | access to field FieldB | | H.cs:53:25:53:25 | a : A [field FieldA] : Object | H.cs:55:21:55:21 | access to parameter a : A [field FieldA] : Object | +| H.cs:53:25:53:25 | a : A [field FieldA] : Object | H.cs:55:21:55:21 | access to parameter a : A [field FieldA] : Object | +| H.cs:55:21:55:21 | access to parameter a : A [field FieldA] : Object | H.cs:55:21:55:28 | access to field FieldA : Object | | H.cs:55:21:55:21 | access to parameter a : A [field FieldA] : Object | H.cs:55:21:55:28 | access to field FieldA : Object | | H.cs:55:21:55:28 | access to field FieldA : Object | H.cs:55:9:55:10 | [post] access to parameter b1 : B [field FieldB] : Object | +| H.cs:55:21:55:28 | access to field FieldA : Object | H.cs:55:9:55:10 | [post] access to parameter b1 : B [field FieldB] : Object | +| H.cs:63:9:63:9 | [post] access to local variable a : A [field FieldA] : Object | H.cs:64:22:64:22 | access to local variable a : A [field FieldA] : Object | | H.cs:63:9:63:9 | [post] access to local variable a : A [field FieldA] : Object | H.cs:64:22:64:22 | access to local variable a : A [field FieldA] : Object | | H.cs:63:20:63:36 | call to method Source : Object | H.cs:63:9:63:9 | [post] access to local variable a : A [field FieldA] : Object | +| H.cs:63:20:63:36 | call to method Source : Object | H.cs:63:9:63:9 | [post] access to local variable a : A [field FieldA] : Object | +| H.cs:64:22:64:22 | access to local variable a : A [field FieldA] : Object | H.cs:53:25:53:25 | a : A [field FieldA] : Object | | H.cs:64:22:64:22 | access to local variable a : A [field FieldA] : Object | H.cs:53:25:53:25 | a : A [field FieldA] : Object | | H.cs:64:22:64:22 | access to local variable a : A [field FieldA] : Object | H.cs:64:25:64:26 | [post] access to local variable b1 : B [field FieldB] : Object | +| H.cs:64:22:64:22 | access to local variable a : A [field FieldA] : Object | H.cs:64:25:64:26 | [post] access to local variable b1 : B [field FieldB] : Object | +| H.cs:64:25:64:26 | [post] access to local variable b1 : B [field FieldB] : Object | H.cs:65:14:65:15 | access to local variable b1 : B [field FieldB] : Object | | H.cs:64:25:64:26 | [post] access to local variable b1 : B [field FieldB] : Object | H.cs:65:14:65:15 | access to local variable b1 : B [field FieldB] : Object | | H.cs:65:14:65:15 | access to local variable b1 : B [field FieldB] : Object | H.cs:65:14:65:22 | access to field FieldB | +| H.cs:65:14:65:15 | access to local variable b1 : B [field FieldB] : Object | H.cs:65:14:65:22 | access to field FieldB | +| H.cs:77:30:77:30 | o : Object | H.cs:79:20:79:20 | access to parameter o : Object | | H.cs:77:30:77:30 | o : Object | H.cs:79:20:79:20 | access to parameter o : Object | | H.cs:79:9:79:9 | [post] access to parameter a : A [field FieldA] : Object | H.cs:80:22:80:22 | access to parameter a : A [field FieldA] : Object | +| H.cs:79:9:79:9 | [post] access to parameter a : A [field FieldA] : Object | H.cs:80:22:80:22 | access to parameter a : A [field FieldA] : Object | +| H.cs:79:20:79:20 | access to parameter o : Object | H.cs:79:9:79:9 | [post] access to parameter a : A [field FieldA] : Object | | H.cs:79:20:79:20 | access to parameter o : Object | H.cs:79:9:79:9 | [post] access to parameter a : A [field FieldA] : Object | | H.cs:80:22:80:22 | access to parameter a : A [field FieldA] : Object | H.cs:53:25:53:25 | a : A [field FieldA] : Object | +| H.cs:80:22:80:22 | access to parameter a : A [field FieldA] : Object | H.cs:53:25:53:25 | a : A [field FieldA] : Object | +| H.cs:80:22:80:22 | access to parameter a : A [field FieldA] : Object | H.cs:80:25:80:26 | [post] access to parameter b1 : B [field FieldB] : Object | | H.cs:80:22:80:22 | access to parameter a : A [field FieldA] : Object | H.cs:80:25:80:26 | [post] access to parameter b1 : B [field FieldB] : Object | | H.cs:88:17:88:17 | [post] access to local variable a : A [field FieldA] : Object | H.cs:89:14:89:14 | access to local variable a : A [field FieldA] : Object | +| H.cs:88:17:88:17 | [post] access to local variable a : A [field FieldA] : Object | H.cs:89:14:89:14 | access to local variable a : A [field FieldA] : Object | +| H.cs:88:20:88:36 | call to method Source : Object | H.cs:77:30:77:30 | o : Object | | H.cs:88:20:88:36 | call to method Source : Object | H.cs:77:30:77:30 | o : Object | | H.cs:88:20:88:36 | call to method Source : Object | H.cs:88:17:88:17 | [post] access to local variable a : A [field FieldA] : Object | +| H.cs:88:20:88:36 | call to method Source : Object | H.cs:88:17:88:17 | [post] access to local variable a : A [field FieldA] : Object | +| H.cs:88:20:88:36 | call to method Source : Object | H.cs:88:39:88:40 | [post] access to local variable b1 : B [field FieldB] : Object | | H.cs:88:20:88:36 | call to method Source : Object | H.cs:88:39:88:40 | [post] access to local variable b1 : B [field FieldB] : Object | | H.cs:88:39:88:40 | [post] access to local variable b1 : B [field FieldB] : Object | H.cs:90:14:90:15 | access to local variable b1 : B [field FieldB] : Object | +| H.cs:88:39:88:40 | [post] access to local variable b1 : B [field FieldB] : Object | H.cs:90:14:90:15 | access to local variable b1 : B [field FieldB] : Object | +| H.cs:89:14:89:14 | access to local variable a : A [field FieldA] : Object | H.cs:89:14:89:21 | access to field FieldA | | H.cs:89:14:89:14 | access to local variable a : A [field FieldA] : Object | H.cs:89:14:89:21 | access to field FieldA | | H.cs:90:14:90:15 | access to local variable b1 : B [field FieldB] : Object | H.cs:90:14:90:22 | access to field FieldB | +| H.cs:90:14:90:15 | access to local variable b1 : B [field FieldB] : Object | H.cs:90:14:90:22 | access to field FieldB | +| H.cs:102:23:102:23 | a : A [field FieldA] : Object | H.cs:105:23:105:23 | access to parameter a : A [field FieldA] : Object | | H.cs:102:23:102:23 | a : A [field FieldA] : Object | H.cs:105:23:105:23 | access to parameter a : A [field FieldA] : Object | | H.cs:105:9:105:12 | [post] access to local variable temp : B [field FieldB, field FieldA] : Object | H.cs:106:29:106:32 | access to local variable temp : B [field FieldB, field FieldA] : Object | +| H.cs:105:9:105:12 | [post] access to local variable temp : B [field FieldB, field FieldA] : Object | H.cs:106:29:106:32 | access to local variable temp : B [field FieldB, field FieldA] : Object | +| H.cs:105:23:105:23 | access to parameter a : A [field FieldA] : Object | H.cs:105:9:105:12 | [post] access to local variable temp : B [field FieldB, field FieldA] : Object | | H.cs:105:23:105:23 | access to parameter a : A [field FieldA] : Object | H.cs:105:9:105:12 | [post] access to local variable temp : B [field FieldB, field FieldA] : Object | | H.cs:106:26:106:39 | (...) ... : A [field FieldA] : Object | H.cs:33:19:33:19 | a : A [field FieldA] : Object | +| H.cs:106:26:106:39 | (...) ... : A [field FieldA] : Object | H.cs:33:19:33:19 | a : A [field FieldA] : Object | +| H.cs:106:26:106:39 | (...) ... : A [field FieldA] : Object | H.cs:106:16:106:40 | call to method Transform : B [field FieldB] : Object | | H.cs:106:26:106:39 | (...) ... : A [field FieldA] : Object | H.cs:106:16:106:40 | call to method Transform : B [field FieldB] : Object | | H.cs:106:29:106:32 | access to local variable temp : B [field FieldB, field FieldA] : Object | H.cs:106:29:106:39 | access to field FieldB : A [field FieldA] : Object | +| H.cs:106:29:106:32 | access to local variable temp : B [field FieldB, field FieldA] : Object | H.cs:106:29:106:39 | access to field FieldB : A [field FieldA] : Object | +| H.cs:106:29:106:39 | access to field FieldB : A [field FieldA] : Object | H.cs:106:26:106:39 | (...) ... : A [field FieldA] : Object | | H.cs:106:29:106:39 | access to field FieldB : A [field FieldA] : Object | H.cs:106:26:106:39 | (...) ... : A [field FieldA] : Object | | H.cs:112:9:112:9 | [post] access to local variable a : A [field FieldA] : Object | H.cs:113:31:113:31 | access to local variable a : A [field FieldA] : Object | +| H.cs:112:9:112:9 | [post] access to local variable a : A [field FieldA] : Object | H.cs:113:31:113:31 | access to local variable a : A [field FieldA] : Object | +| H.cs:112:20:112:36 | call to method Source : Object | H.cs:112:9:112:9 | [post] access to local variable a : A [field FieldA] : Object | | H.cs:112:20:112:36 | call to method Source : Object | H.cs:112:9:112:9 | [post] access to local variable a : A [field FieldA] : Object | | H.cs:113:17:113:32 | call to method TransformWrap : B [field FieldB] : Object | H.cs:114:14:114:14 | access to local variable b : B [field FieldB] : Object | +| H.cs:113:17:113:32 | call to method TransformWrap : B [field FieldB] : Object | H.cs:114:14:114:14 | access to local variable b : B [field FieldB] : Object | +| H.cs:113:31:113:31 | access to local variable a : A [field FieldA] : Object | H.cs:102:23:102:23 | a : A [field FieldA] : Object | | H.cs:113:31:113:31 | access to local variable a : A [field FieldA] : Object | H.cs:102:23:102:23 | a : A [field FieldA] : Object | | H.cs:113:31:113:31 | access to local variable a : A [field FieldA] : Object | H.cs:113:17:113:32 | call to method TransformWrap : B [field FieldB] : Object | +| H.cs:113:31:113:31 | access to local variable a : A [field FieldA] : Object | H.cs:113:17:113:32 | call to method TransformWrap : B [field FieldB] : Object | +| H.cs:114:14:114:14 | access to local variable b : B [field FieldB] : Object | H.cs:114:14:114:21 | access to field FieldB | | H.cs:114:14:114:14 | access to local variable b : B [field FieldB] : Object | H.cs:114:14:114:21 | access to field FieldB | | H.cs:122:18:122:18 | a : A [field FieldA] : Object | H.cs:124:26:124:26 | access to parameter a : A [field FieldA] : Object | +| H.cs:122:18:122:18 | a : A [field FieldA] : Object | H.cs:124:26:124:26 | access to parameter a : A [field FieldA] : Object | +| H.cs:124:16:124:27 | call to method Transform : B [field FieldB] : Object | H.cs:124:16:124:34 | access to field FieldB : Object | | H.cs:124:16:124:27 | call to method Transform : B [field FieldB] : Object | H.cs:124:16:124:34 | access to field FieldB : Object | | H.cs:124:26:124:26 | access to parameter a : A [field FieldA] : Object | H.cs:33:19:33:19 | a : A [field FieldA] : Object | +| H.cs:124:26:124:26 | access to parameter a : A [field FieldA] : Object | H.cs:33:19:33:19 | a : A [field FieldA] : Object | +| H.cs:124:26:124:26 | access to parameter a : A [field FieldA] : Object | H.cs:124:16:124:27 | call to method Transform : B [field FieldB] : Object | | H.cs:124:26:124:26 | access to parameter a : A [field FieldA] : Object | H.cs:124:16:124:27 | call to method Transform : B [field FieldB] : Object | | H.cs:130:9:130:9 | [post] access to local variable a : A [field FieldA] : Object | H.cs:131:18:131:18 | access to local variable a : A [field FieldA] : Object | +| H.cs:130:9:130:9 | [post] access to local variable a : A [field FieldA] : Object | H.cs:131:18:131:18 | access to local variable a : A [field FieldA] : Object | +| H.cs:130:20:130:36 | call to method Source : Object | H.cs:130:9:130:9 | [post] access to local variable a : A [field FieldA] : Object | | H.cs:130:20:130:36 | call to method Source : Object | H.cs:130:9:130:9 | [post] access to local variable a : A [field FieldA] : Object | | H.cs:131:18:131:18 | access to local variable a : A [field FieldA] : Object | H.cs:122:18:122:18 | a : A [field FieldA] : Object | +| H.cs:131:18:131:18 | access to local variable a : A [field FieldA] : Object | H.cs:122:18:122:18 | a : A [field FieldA] : Object | +| H.cs:131:18:131:18 | access to local variable a : A [field FieldA] : Object | H.cs:131:14:131:19 | call to method Get | | H.cs:131:18:131:18 | access to local variable a : A [field FieldA] : Object | H.cs:131:14:131:19 | call to method Get | | H.cs:138:27:138:27 | o : A | H.cs:141:20:141:25 | ... as ... : A | +| H.cs:138:27:138:27 | o : A | H.cs:141:20:141:25 | ... as ... : A | +| H.cs:141:9:141:9 | [post] access to local variable a : A [field FieldA] : A | H.cs:142:26:142:26 | access to local variable a : A [field FieldA] : A | | H.cs:141:9:141:9 | [post] access to local variable a : A [field FieldA] : A | H.cs:142:26:142:26 | access to local variable a : A [field FieldA] : A | | H.cs:141:20:141:25 | ... as ... : A | H.cs:141:9:141:9 | [post] access to local variable a : A [field FieldA] : A | +| H.cs:141:20:141:25 | ... as ... : A | H.cs:141:9:141:9 | [post] access to local variable a : A [field FieldA] : A | +| H.cs:142:16:142:27 | call to method Transform : B [field FieldB] : A | H.cs:142:16:142:34 | access to field FieldB : A | | H.cs:142:16:142:27 | call to method Transform : B [field FieldB] : A | H.cs:142:16:142:34 | access to field FieldB : A | | H.cs:142:26:142:26 | access to local variable a : A [field FieldA] : A | H.cs:33:19:33:19 | a : A [field FieldA] : A | +| H.cs:142:26:142:26 | access to local variable a : A [field FieldA] : A | H.cs:33:19:33:19 | a : A [field FieldA] : A | +| H.cs:142:26:142:26 | access to local variable a : A [field FieldA] : A | H.cs:142:16:142:27 | call to method Transform : B [field FieldB] : A | | H.cs:142:26:142:26 | access to local variable a : A [field FieldA] : A | H.cs:142:16:142:27 | call to method Transform : B [field FieldB] : A | | H.cs:147:17:147:39 | call to method Through : A | H.cs:148:14:148:14 | access to local variable a | +| H.cs:147:17:147:39 | call to method Through : A | H.cs:148:14:148:14 | access to local variable a | +| H.cs:147:25:147:38 | call to method Source : A | H.cs:138:27:138:27 | o : A | | H.cs:147:25:147:38 | call to method Source : A | H.cs:138:27:138:27 | o : A | | H.cs:147:25:147:38 | call to method Source : A | H.cs:147:17:147:39 | call to method Through : A | +| H.cs:147:25:147:38 | call to method Source : A | H.cs:147:17:147:39 | call to method Through : A | +| H.cs:153:32:153:32 | o : Object | H.cs:156:20:156:20 | access to parameter o : Object | | H.cs:153:32:153:32 | o : Object | H.cs:156:20:156:20 | access to parameter o : Object | | H.cs:155:17:155:30 | call to method Source : B | H.cs:156:9:156:9 | access to local variable b : B | +| H.cs:155:17:155:30 | call to method Source : B | H.cs:156:9:156:9 | access to local variable b : B | +| H.cs:156:9:156:9 | [post] access to local variable b : B [field FieldB] : Object | H.cs:157:20:157:20 | access to local variable b : B [field FieldB] : Object | | H.cs:156:9:156:9 | [post] access to local variable b : B [field FieldB] : Object | H.cs:157:20:157:20 | access to local variable b : B [field FieldB] : Object | | H.cs:156:9:156:9 | access to local variable b : B | H.cs:157:20:157:20 | access to local variable b : B | +| H.cs:156:9:156:9 | access to local variable b : B | H.cs:157:20:157:20 | access to local variable b : B | +| H.cs:156:20:156:20 | access to parameter o : Object | H.cs:156:9:156:9 | [post] access to local variable b : B [field FieldB] : Object | | H.cs:156:20:156:20 | access to parameter o : Object | H.cs:156:9:156:9 | [post] access to local variable b : B [field FieldB] : Object | | H.cs:157:9:157:9 | [post] access to parameter a : A [field FieldA] : B | H.cs:164:19:164:19 | [post] access to local variable a : A [field FieldA] : B | +| H.cs:157:9:157:9 | [post] access to parameter a : A [field FieldA] : B | H.cs:164:19:164:19 | [post] access to local variable a : A [field FieldA] : B | +| H.cs:157:20:157:20 | access to local variable b : B | H.cs:157:9:157:9 | [post] access to parameter a : A [field FieldA] : B | | H.cs:157:20:157:20 | access to local variable b : B | H.cs:157:9:157:9 | [post] access to parameter a : A [field FieldA] : B | | H.cs:157:20:157:20 | access to local variable b : B [field FieldB] : Object | H.cs:157:9:157:9 | [post] access to parameter a : A [field FieldA, field FieldB] : Object | +| H.cs:157:20:157:20 | access to local variable b : B [field FieldB] : Object | H.cs:157:9:157:9 | [post] access to parameter a : A [field FieldA, field FieldB] : Object | +| H.cs:163:17:163:35 | call to method Source : Object | H.cs:164:22:164:22 | access to local variable o : Object | | H.cs:163:17:163:35 | call to method Source : Object | H.cs:164:22:164:22 | access to local variable o : Object | | H.cs:164:19:164:19 | [post] access to local variable a : A [field FieldA, field FieldB] : Object | H.cs:165:20:165:20 | access to local variable a : A [field FieldA, field FieldB] : Object | +| H.cs:164:19:164:19 | [post] access to local variable a : A [field FieldA, field FieldB] : Object | H.cs:165:20:165:20 | access to local variable a : A [field FieldA, field FieldB] : Object | +| H.cs:164:19:164:19 | [post] access to local variable a : A [field FieldA] : B | H.cs:165:20:165:20 | access to local variable a : A [field FieldA] : B | | H.cs:164:19:164:19 | [post] access to local variable a : A [field FieldA] : B | H.cs:165:20:165:20 | access to local variable a : A [field FieldA] : B | | H.cs:164:22:164:22 | access to local variable o : Object | H.cs:153:32:153:32 | o : Object | +| H.cs:164:22:164:22 | access to local variable o : Object | H.cs:153:32:153:32 | o : Object | +| H.cs:164:22:164:22 | access to local variable o : Object | H.cs:164:19:164:19 | [post] access to local variable a : A [field FieldA, field FieldB] : Object | | H.cs:164:22:164:22 | access to local variable o : Object | H.cs:164:19:164:19 | [post] access to local variable a : A [field FieldA, field FieldB] : Object | | H.cs:165:17:165:27 | (...) ... : B | H.cs:166:14:166:14 | access to local variable b | +| H.cs:165:17:165:27 | (...) ... : B | H.cs:166:14:166:14 | access to local variable b | +| H.cs:165:17:165:27 | (...) ... : B [field FieldB] : Object | H.cs:167:14:167:14 | access to local variable b : B [field FieldB] : Object | | H.cs:165:17:165:27 | (...) ... : B [field FieldB] : Object | H.cs:167:14:167:14 | access to local variable b : B [field FieldB] : Object | | H.cs:165:20:165:20 | access to local variable a : A [field FieldA, field FieldB] : Object | H.cs:165:20:165:27 | access to field FieldA : B [field FieldB] : Object | +| H.cs:165:20:165:20 | access to local variable a : A [field FieldA, field FieldB] : Object | H.cs:165:20:165:27 | access to field FieldA : B [field FieldB] : Object | +| H.cs:165:20:165:20 | access to local variable a : A [field FieldA] : B | H.cs:165:20:165:27 | access to field FieldA : B | | H.cs:165:20:165:20 | access to local variable a : A [field FieldA] : B | H.cs:165:20:165:27 | access to field FieldA : B | | H.cs:165:20:165:27 | access to field FieldA : B | H.cs:165:17:165:27 | (...) ... : B | +| H.cs:165:20:165:27 | access to field FieldA : B | H.cs:165:17:165:27 | (...) ... : B | +| H.cs:165:20:165:27 | access to field FieldA : B [field FieldB] : Object | H.cs:165:17:165:27 | (...) ... : B [field FieldB] : Object | | H.cs:165:20:165:27 | access to field FieldA : B [field FieldB] : Object | H.cs:165:17:165:27 | (...) ... : B [field FieldB] : Object | | H.cs:167:14:167:14 | access to local variable b : B [field FieldB] : Object | H.cs:167:14:167:21 | access to field FieldB | +| H.cs:167:14:167:14 | access to local variable b : B [field FieldB] : Object | H.cs:167:14:167:21 | access to field FieldB | +| I.cs:7:9:7:14 | [post] this access : I [field Field1] : Object | I.cs:21:13:21:19 | object creation of type I : I [field Field1] : Object | | I.cs:7:9:7:14 | [post] this access : I [field Field1] : Object | I.cs:21:13:21:19 | object creation of type I : I [field Field1] : Object | | I.cs:7:9:7:14 | [post] this access : I [field Field1] : Object | I.cs:26:13:26:37 | [pre-initializer] object creation of type I : I [field Field1] : Object | +| I.cs:7:9:7:14 | [post] this access : I [field Field1] : Object | I.cs:26:13:26:37 | [pre-initializer] object creation of type I : I [field Field1] : Object | +| I.cs:7:18:7:34 | call to method Source : Object | I.cs:7:9:7:14 | [post] this access : I [field Field1] : Object | | I.cs:7:18:7:34 | call to method Source : Object | I.cs:7:9:7:14 | [post] this access : I [field Field1] : Object | | I.cs:13:17:13:33 | call to method Source : Object | I.cs:15:20:15:20 | access to local variable o : Object | +| I.cs:13:17:13:33 | call to method Source : Object | I.cs:15:20:15:20 | access to local variable o : Object | +| I.cs:15:9:15:9 | [post] access to local variable i : I [field Field1] : Object | I.cs:16:9:16:9 | access to local variable i : I [field Field1] : Object | | I.cs:15:9:15:9 | [post] access to local variable i : I [field Field1] : Object | I.cs:16:9:16:9 | access to local variable i : I [field Field1] : Object | | I.cs:15:20:15:20 | access to local variable o : Object | I.cs:15:9:15:9 | [post] access to local variable i : I [field Field1] : Object | +| I.cs:15:20:15:20 | access to local variable o : Object | I.cs:15:9:15:9 | [post] access to local variable i : I [field Field1] : Object | +| I.cs:16:9:16:9 | access to local variable i : I [field Field1] : Object | I.cs:17:9:17:9 | access to local variable i : I [field Field1] : Object | | I.cs:16:9:16:9 | access to local variable i : I [field Field1] : Object | I.cs:17:9:17:9 | access to local variable i : I [field Field1] : Object | | I.cs:17:9:17:9 | access to local variable i : I [field Field1] : Object | I.cs:18:14:18:14 | access to local variable i : I [field Field1] : Object | +| I.cs:17:9:17:9 | access to local variable i : I [field Field1] : Object | I.cs:18:14:18:14 | access to local variable i : I [field Field1] : Object | +| I.cs:18:14:18:14 | access to local variable i : I [field Field1] : Object | I.cs:18:14:18:21 | access to field Field1 | | I.cs:18:14:18:14 | access to local variable i : I [field Field1] : Object | I.cs:18:14:18:21 | access to field Field1 | | I.cs:21:13:21:19 | object creation of type I : I [field Field1] : Object | I.cs:22:9:22:9 | access to local variable i : I [field Field1] : Object | +| I.cs:21:13:21:19 | object creation of type I : I [field Field1] : Object | I.cs:22:9:22:9 | access to local variable i : I [field Field1] : Object | +| I.cs:22:9:22:9 | access to local variable i : I [field Field1] : Object | I.cs:23:14:23:14 | access to local variable i : I [field Field1] : Object | | I.cs:22:9:22:9 | access to local variable i : I [field Field1] : Object | I.cs:23:14:23:14 | access to local variable i : I [field Field1] : Object | | I.cs:23:14:23:14 | access to local variable i : I [field Field1] : Object | I.cs:23:14:23:21 | access to field Field1 | +| I.cs:23:14:23:14 | access to local variable i : I [field Field1] : Object | I.cs:23:14:23:21 | access to field Field1 | +| I.cs:26:13:26:37 | [pre-initializer] object creation of type I : I [field Field1] : Object | I.cs:27:14:27:14 | access to local variable i : I [field Field1] : Object | | I.cs:26:13:26:37 | [pre-initializer] object creation of type I : I [field Field1] : Object | I.cs:27:14:27:14 | access to local variable i : I [field Field1] : Object | | I.cs:27:14:27:14 | access to local variable i : I [field Field1] : Object | I.cs:27:14:27:21 | access to field Field1 | +| I.cs:27:14:27:14 | access to local variable i : I [field Field1] : Object | I.cs:27:14:27:21 | access to field Field1 | +| I.cs:31:13:31:29 | call to method Source : Object | I.cs:32:20:32:20 | access to local variable o : Object | | I.cs:31:13:31:29 | call to method Source : Object | I.cs:32:20:32:20 | access to local variable o : Object | | I.cs:32:9:32:9 | [post] access to local variable i : I [field Field1] : Object | I.cs:33:9:33:9 | access to local variable i : I [field Field1] : Object | +| I.cs:32:9:32:9 | [post] access to local variable i : I [field Field1] : Object | I.cs:33:9:33:9 | access to local variable i : I [field Field1] : Object | +| I.cs:32:20:32:20 | access to local variable o : Object | I.cs:32:9:32:9 | [post] access to local variable i : I [field Field1] : Object | | I.cs:32:20:32:20 | access to local variable o : Object | I.cs:32:9:32:9 | [post] access to local variable i : I [field Field1] : Object | | I.cs:33:9:33:9 | access to local variable i : I [field Field1] : Object | I.cs:34:12:34:12 | access to local variable i : I [field Field1] : Object | +| I.cs:33:9:33:9 | access to local variable i : I [field Field1] : Object | I.cs:34:12:34:12 | access to local variable i : I [field Field1] : Object | +| I.cs:34:12:34:12 | access to local variable i : I [field Field1] : Object | I.cs:37:23:37:23 | i : I [field Field1] : Object | | I.cs:34:12:34:12 | access to local variable i : I [field Field1] : Object | I.cs:37:23:37:23 | i : I [field Field1] : Object | | I.cs:37:23:37:23 | i : I [field Field1] : Object | I.cs:39:9:39:9 | access to parameter i : I [field Field1] : Object | +| I.cs:37:23:37:23 | i : I [field Field1] : Object | I.cs:39:9:39:9 | access to parameter i : I [field Field1] : Object | +| I.cs:39:9:39:9 | access to parameter i : I [field Field1] : Object | I.cs:40:14:40:14 | access to parameter i : I [field Field1] : Object | | I.cs:39:9:39:9 | access to parameter i : I [field Field1] : Object | I.cs:40:14:40:14 | access to parameter i : I [field Field1] : Object | | I.cs:40:14:40:14 | access to parameter i : I [field Field1] : Object | I.cs:40:14:40:21 | access to field Field1 | +| I.cs:40:14:40:14 | access to parameter i : I [field Field1] : Object | I.cs:40:14:40:21 | access to field Field1 | +| J.cs:14:26:14:30 | field : Object | J.cs:14:66:14:70 | access to parameter field : Object | | J.cs:14:26:14:30 | field : Object | J.cs:14:66:14:70 | access to parameter field : Object | | J.cs:14:40:14:43 | prop : Object | J.cs:14:73:14:76 | access to parameter prop : Object | +| J.cs:14:40:14:43 | prop : Object | J.cs:14:73:14:76 | access to parameter prop : Object | +| J.cs:14:66:14:70 | access to parameter field : Object | J.cs:14:50:14:54 | [post] this access : Struct [field Field] : Object | | J.cs:14:66:14:70 | access to parameter field : Object | J.cs:14:50:14:54 | [post] this access : Struct [field Field] : Object | | J.cs:14:73:14:76 | access to parameter prop : Object | J.cs:14:57:14:60 | [post] this access : Struct [property Prop] : Object | +| J.cs:14:73:14:76 | access to parameter prop : Object | J.cs:14:57:14:60 | [post] this access : Struct [property Prop] : Object | +| J.cs:21:17:21:33 | call to method Source : Object | J.cs:22:34:22:34 | access to local variable o : Object | | J.cs:21:17:21:33 | call to method Source : Object | J.cs:22:34:22:34 | access to local variable o : Object | | J.cs:22:18:22:41 | object creation of type RecordClass : RecordClass [property Prop1] : Object | J.cs:23:14:23:15 | access to local variable r1 : RecordClass [property Prop1] : Object | +| J.cs:22:18:22:41 | object creation of type RecordClass : RecordClass [property Prop1] : Object | J.cs:23:14:23:15 | access to local variable r1 : RecordClass [property Prop1] : Object | +| J.cs:22:18:22:41 | object creation of type RecordClass : RecordClass [property Prop1] : Object | J.cs:27:14:27:15 | access to local variable r2 : RecordClass [property Prop1] : Object | | J.cs:22:18:22:41 | object creation of type RecordClass : RecordClass [property Prop1] : Object | J.cs:27:14:27:15 | access to local variable r2 : RecordClass [property Prop1] : Object | | J.cs:22:18:22:41 | object creation of type RecordClass : RecordClass [property Prop1] : Object | J.cs:31:14:31:15 | access to local variable r3 : RecordClass [property Prop1] : Object | +| J.cs:22:18:22:41 | object creation of type RecordClass : RecordClass [property Prop1] : Object | J.cs:31:14:31:15 | access to local variable r3 : RecordClass [property Prop1] : Object | +| J.cs:22:34:22:34 | access to local variable o : Object | J.cs:22:18:22:41 | object creation of type RecordClass : RecordClass [property Prop1] : Object | | J.cs:22:34:22:34 | access to local variable o : Object | J.cs:22:18:22:41 | object creation of type RecordClass : RecordClass [property Prop1] : Object | | J.cs:23:14:23:15 | access to local variable r1 : RecordClass [property Prop1] : Object | J.cs:23:14:23:21 | access to property Prop1 | +| J.cs:23:14:23:15 | access to local variable r1 : RecordClass [property Prop1] : Object | J.cs:23:14:23:21 | access to property Prop1 | +| J.cs:27:14:27:15 | access to local variable r2 : RecordClass [property Prop1] : Object | J.cs:27:14:27:21 | access to property Prop1 | | J.cs:27:14:27:15 | access to local variable r2 : RecordClass [property Prop1] : Object | J.cs:27:14:27:21 | access to property Prop1 | | J.cs:30:18:30:54 | ... with { ... } : RecordClass [property Prop2] : Object | J.cs:32:14:32:15 | access to local variable r3 : RecordClass [property Prop2] : Object | +| J.cs:30:18:30:54 | ... with { ... } : RecordClass [property Prop2] : Object | J.cs:32:14:32:15 | access to local variable r3 : RecordClass [property Prop2] : Object | +| J.cs:30:36:30:52 | call to method Source : Object | J.cs:30:18:30:54 | ... with { ... } : RecordClass [property Prop2] : Object | | J.cs:30:36:30:52 | call to method Source : Object | J.cs:30:18:30:54 | ... with { ... } : RecordClass [property Prop2] : Object | | J.cs:31:14:31:15 | access to local variable r3 : RecordClass [property Prop1] : Object | J.cs:31:14:31:21 | access to property Prop1 | +| J.cs:31:14:31:15 | access to local variable r3 : RecordClass [property Prop1] : Object | J.cs:31:14:31:21 | access to property Prop1 | +| J.cs:32:14:32:15 | access to local variable r3 : RecordClass [property Prop2] : Object | J.cs:32:14:32:21 | access to property Prop2 | | J.cs:32:14:32:15 | access to local variable r3 : RecordClass [property Prop2] : Object | J.cs:32:14:32:21 | access to property Prop2 | | J.cs:41:17:41:33 | call to method Source : Object | J.cs:42:35:42:35 | access to local variable o : Object | +| J.cs:41:17:41:33 | call to method Source : Object | J.cs:42:35:42:35 | access to local variable o : Object | +| J.cs:42:18:42:42 | object creation of type RecordStruct : RecordStruct [property Prop1] : Object | J.cs:43:14:43:15 | access to local variable r1 : RecordStruct [property Prop1] : Object | | J.cs:42:18:42:42 | object creation of type RecordStruct : RecordStruct [property Prop1] : Object | J.cs:43:14:43:15 | access to local variable r1 : RecordStruct [property Prop1] : Object | | J.cs:42:18:42:42 | object creation of type RecordStruct : RecordStruct [property Prop1] : Object | J.cs:47:14:47:15 | access to local variable r2 : RecordStruct [property Prop1] : Object | +| J.cs:42:18:42:42 | object creation of type RecordStruct : RecordStruct [property Prop1] : Object | J.cs:47:14:47:15 | access to local variable r2 : RecordStruct [property Prop1] : Object | +| J.cs:42:18:42:42 | object creation of type RecordStruct : RecordStruct [property Prop1] : Object | J.cs:51:14:51:15 | access to local variable r3 : RecordStruct [property Prop1] : Object | | J.cs:42:18:42:42 | object creation of type RecordStruct : RecordStruct [property Prop1] : Object | J.cs:51:14:51:15 | access to local variable r3 : RecordStruct [property Prop1] : Object | | J.cs:42:35:42:35 | access to local variable o : Object | J.cs:42:18:42:42 | object creation of type RecordStruct : RecordStruct [property Prop1] : Object | +| J.cs:42:35:42:35 | access to local variable o : Object | J.cs:42:18:42:42 | object creation of type RecordStruct : RecordStruct [property Prop1] : Object | +| J.cs:43:14:43:15 | access to local variable r1 : RecordStruct [property Prop1] : Object | J.cs:43:14:43:21 | access to property Prop1 | | J.cs:43:14:43:15 | access to local variable r1 : RecordStruct [property Prop1] : Object | J.cs:43:14:43:21 | access to property Prop1 | | J.cs:47:14:47:15 | access to local variable r2 : RecordStruct [property Prop1] : Object | J.cs:47:14:47:21 | access to property Prop1 | +| J.cs:47:14:47:15 | access to local variable r2 : RecordStruct [property Prop1] : Object | J.cs:47:14:47:21 | access to property Prop1 | +| J.cs:50:18:50:54 | ... with { ... } : RecordStruct [property Prop2] : Object | J.cs:52:14:52:15 | access to local variable r3 : RecordStruct [property Prop2] : Object | | J.cs:50:18:50:54 | ... with { ... } : RecordStruct [property Prop2] : Object | J.cs:52:14:52:15 | access to local variable r3 : RecordStruct [property Prop2] : Object | | J.cs:50:36:50:52 | call to method Source : Object | J.cs:50:18:50:54 | ... with { ... } : RecordStruct [property Prop2] : Object | +| J.cs:50:36:50:52 | call to method Source : Object | J.cs:50:18:50:54 | ... with { ... } : RecordStruct [property Prop2] : Object | +| J.cs:51:14:51:15 | access to local variable r3 : RecordStruct [property Prop1] : Object | J.cs:51:14:51:21 | access to property Prop1 | | J.cs:51:14:51:15 | access to local variable r3 : RecordStruct [property Prop1] : Object | J.cs:51:14:51:21 | access to property Prop1 | | J.cs:52:14:52:15 | access to local variable r3 : RecordStruct [property Prop2] : Object | J.cs:52:14:52:21 | access to property Prop2 | +| J.cs:52:14:52:15 | access to local variable r3 : RecordStruct [property Prop2] : Object | J.cs:52:14:52:21 | access to property Prop2 | +| J.cs:61:17:61:33 | call to method Source : Object | J.cs:62:29:62:29 | access to local variable o : Object | | J.cs:61:17:61:33 | call to method Source : Object | J.cs:62:29:62:29 | access to local variable o : Object | | J.cs:62:18:62:36 | object creation of type Struct : Struct [field Field] : Object | J.cs:65:14:65:15 | access to local variable s2 : Struct [field Field] : Object | +| J.cs:62:18:62:36 | object creation of type Struct : Struct [field Field] : Object | J.cs:65:14:65:15 | access to local variable s2 : Struct [field Field] : Object | +| J.cs:62:18:62:36 | object creation of type Struct : Struct [field Field] : Object | J.cs:69:14:69:15 | access to local variable s3 : Struct [field Field] : Object | | J.cs:62:18:62:36 | object creation of type Struct : Struct [field Field] : Object | J.cs:69:14:69:15 | access to local variable s3 : Struct [field Field] : Object | | J.cs:62:29:62:29 | access to local variable o : Object | J.cs:14:26:14:30 | field : Object | +| J.cs:62:29:62:29 | access to local variable o : Object | J.cs:14:26:14:30 | field : Object | +| J.cs:62:29:62:29 | access to local variable o : Object | J.cs:62:18:62:36 | object creation of type Struct : Struct [field Field] : Object | | J.cs:62:29:62:29 | access to local variable o : Object | J.cs:62:18:62:36 | object creation of type Struct : Struct [field Field] : Object | | J.cs:65:14:65:15 | access to local variable s2 : Struct [field Field] : Object | J.cs:65:14:65:21 | access to field Field | +| J.cs:65:14:65:15 | access to local variable s2 : Struct [field Field] : Object | J.cs:65:14:65:21 | access to field Field | +| J.cs:68:18:68:53 | ... with { ... } : Struct [property Prop] : Object | J.cs:70:14:70:15 | access to local variable s3 : Struct [property Prop] : Object | | J.cs:68:18:68:53 | ... with { ... } : Struct [property Prop] : Object | J.cs:70:14:70:15 | access to local variable s3 : Struct [property Prop] : Object | | J.cs:68:35:68:51 | call to method Source : Object | J.cs:68:18:68:53 | ... with { ... } : Struct [property Prop] : Object | +| J.cs:68:35:68:51 | call to method Source : Object | J.cs:68:18:68:53 | ... with { ... } : Struct [property Prop] : Object | +| J.cs:69:14:69:15 | access to local variable s3 : Struct [field Field] : Object | J.cs:69:14:69:21 | access to field Field | | J.cs:69:14:69:15 | access to local variable s3 : Struct [field Field] : Object | J.cs:69:14:69:21 | access to field Field | | J.cs:70:14:70:15 | access to local variable s3 : Struct [property Prop] : Object | J.cs:70:14:70:20 | access to property Prop | +| J.cs:70:14:70:15 | access to local variable s3 : Struct [property Prop] : Object | J.cs:70:14:70:20 | access to property Prop | +| J.cs:79:17:79:33 | call to method Source : Object | J.cs:80:35:80:35 | access to local variable o : Object | | J.cs:79:17:79:33 | call to method Source : Object | J.cs:80:35:80:35 | access to local variable o : Object | | J.cs:80:18:80:36 | object creation of type Struct : Struct [property Prop] : Object | J.cs:84:14:84:15 | access to local variable s2 : Struct [property Prop] : Object | +| J.cs:80:18:80:36 | object creation of type Struct : Struct [property Prop] : Object | J.cs:84:14:84:15 | access to local variable s2 : Struct [property Prop] : Object | +| J.cs:80:18:80:36 | object creation of type Struct : Struct [property Prop] : Object | J.cs:88:14:88:15 | access to local variable s3 : Struct [property Prop] : Object | | J.cs:80:18:80:36 | object creation of type Struct : Struct [property Prop] : Object | J.cs:88:14:88:15 | access to local variable s3 : Struct [property Prop] : Object | | J.cs:80:35:80:35 | access to local variable o : Object | J.cs:14:40:14:43 | prop : Object | +| J.cs:80:35:80:35 | access to local variable o : Object | J.cs:14:40:14:43 | prop : Object | +| J.cs:80:35:80:35 | access to local variable o : Object | J.cs:80:18:80:36 | object creation of type Struct : Struct [property Prop] : Object | | J.cs:80:35:80:35 | access to local variable o : Object | J.cs:80:18:80:36 | object creation of type Struct : Struct [property Prop] : Object | | J.cs:84:14:84:15 | access to local variable s2 : Struct [property Prop] : Object | J.cs:84:14:84:20 | access to property Prop | +| J.cs:84:14:84:15 | access to local variable s2 : Struct [property Prop] : Object | J.cs:84:14:84:20 | access to property Prop | +| J.cs:86:18:86:54 | ... with { ... } : Struct [field Field] : Object | J.cs:87:14:87:15 | access to local variable s3 : Struct [field Field] : Object | | J.cs:86:18:86:54 | ... with { ... } : Struct [field Field] : Object | J.cs:87:14:87:15 | access to local variable s3 : Struct [field Field] : Object | | J.cs:86:36:86:52 | call to method Source : Object | J.cs:86:18:86:54 | ... with { ... } : Struct [field Field] : Object | +| J.cs:86:36:86:52 | call to method Source : Object | J.cs:86:18:86:54 | ... with { ... } : Struct [field Field] : Object | +| J.cs:87:14:87:15 | access to local variable s3 : Struct [field Field] : Object | J.cs:87:14:87:21 | access to field Field | | J.cs:87:14:87:15 | access to local variable s3 : Struct [field Field] : Object | J.cs:87:14:87:21 | access to field Field | | J.cs:88:14:88:15 | access to local variable s3 : Struct [property Prop] : Object | J.cs:88:14:88:20 | access to property Prop | +| J.cs:88:14:88:15 | access to local variable s3 : Struct [property Prop] : Object | J.cs:88:14:88:20 | access to property Prop | +| J.cs:97:17:97:33 | call to method Source : Object | J.cs:99:28:99:28 | access to local variable o : Object | | J.cs:97:17:97:33 | call to method Source : Object | J.cs:99:28:99:28 | access to local variable o : Object | | J.cs:99:18:99:41 | { ..., ... } : <>__AnonType0 [property X] : Object | J.cs:102:14:102:15 | access to local variable a2 : <>__AnonType0 [property X] : Object | +| J.cs:99:18:99:41 | { ..., ... } : <>__AnonType0 [property X] : Object | J.cs:102:14:102:15 | access to local variable a2 : <>__AnonType0 [property X] : Object | +| J.cs:99:18:99:41 | { ..., ... } : <>__AnonType0 [property X] : Object | J.cs:106:14:106:15 | access to local variable a3 : <>__AnonType0 [property X] : Object | | J.cs:99:18:99:41 | { ..., ... } : <>__AnonType0 [property X] : Object | J.cs:106:14:106:15 | access to local variable a3 : <>__AnonType0 [property X] : Object | | J.cs:99:28:99:28 | access to local variable o : Object | J.cs:99:18:99:41 | { ..., ... } : <>__AnonType0 [property X] : Object | +| J.cs:99:28:99:28 | access to local variable o : Object | J.cs:99:18:99:41 | { ..., ... } : <>__AnonType0 [property X] : Object | +| J.cs:102:14:102:15 | access to local variable a2 : <>__AnonType0 [property X] : Object | J.cs:102:14:102:17 | access to property X | | J.cs:102:14:102:15 | access to local variable a2 : <>__AnonType0 [property X] : Object | J.cs:102:14:102:17 | access to property X | | J.cs:105:18:105:50 | ... with { ... } : <>__AnonType0 [property Y] : Object | J.cs:107:14:107:15 | access to local variable a3 : <>__AnonType0 [property Y] : Object | +| J.cs:105:18:105:50 | ... with { ... } : <>__AnonType0 [property Y] : Object | J.cs:107:14:107:15 | access to local variable a3 : <>__AnonType0 [property Y] : Object | +| J.cs:105:32:105:48 | call to method Source : Object | J.cs:105:18:105:50 | ... with { ... } : <>__AnonType0 [property Y] : Object | | J.cs:105:32:105:48 | call to method Source : Object | J.cs:105:18:105:50 | ... with { ... } : <>__AnonType0 [property Y] : Object | | J.cs:106:14:106:15 | access to local variable a3 : <>__AnonType0 [property X] : Object | J.cs:106:14:106:17 | access to property X | +| J.cs:106:14:106:15 | access to local variable a3 : <>__AnonType0 [property X] : Object | J.cs:106:14:106:17 | access to property X | +| J.cs:107:14:107:15 | access to local variable a3 : <>__AnonType0 [property Y] : Object | J.cs:107:14:107:17 | access to property Y | | J.cs:107:14:107:15 | access to local variable a3 : <>__AnonType0 [property Y] : Object | J.cs:107:14:107:17 | access to property Y | | J.cs:119:13:119:13 | [post] access to local variable a : Int32[] [element] : Int32 | J.cs:125:14:125:14 | access to local variable a : Int32[] [element] : Int32 | +| J.cs:119:13:119:13 | [post] access to local variable a : Int32[] [element] : Int32 | J.cs:125:14:125:14 | access to local variable a : Int32[] [element] : Int32 | +| J.cs:119:20:119:34 | call to method Source : Int32 | J.cs:119:13:119:13 | [post] access to local variable a : Int32[] [element] : Int32 | | J.cs:119:20:119:34 | call to method Source : Int32 | J.cs:119:13:119:13 | [post] access to local variable a : Int32[] [element] : Int32 | | J.cs:125:14:125:14 | access to local variable a : Int32[] [element] : Int32 | J.cs:125:14:125:17 | access to array element : Int32 | +| J.cs:125:14:125:14 | access to local variable a : Int32[] [element] : Int32 | J.cs:125:14:125:17 | access to array element : Int32 | +| J.cs:125:14:125:17 | access to array element : Int32 | J.cs:125:14:125:17 | (...) ... | | J.cs:125:14:125:17 | access to array element : Int32 | J.cs:125:14:125:17 | (...) ... | nodes | A.cs:5:17:5:28 | call to method Source : C | semmle.label | call to method Source : C | +| A.cs:5:17:5:28 | call to method Source : C | semmle.label | call to method Source : C | +| A.cs:6:17:6:25 | call to method Make : B [field c] : C | semmle.label | call to method Make : B [field c] : C | | A.cs:6:17:6:25 | call to method Make : B [field c] : C | semmle.label | call to method Make : B [field c] : C | | A.cs:6:24:6:24 | access to local variable c : C | semmle.label | access to local variable c : C | +| A.cs:6:24:6:24 | access to local variable c : C | semmle.label | access to local variable c : C | +| A.cs:7:14:7:14 | access to local variable b : B [field c] : C | semmle.label | access to local variable b : B [field c] : C | | A.cs:7:14:7:14 | access to local variable b : B [field c] : C | semmle.label | access to local variable b : B [field c] : C | | A.cs:7:14:7:16 | access to field c | semmle.label | access to field c | +| A.cs:7:14:7:16 | access to field c | semmle.label | access to field c | +| A.cs:13:9:13:9 | [post] access to local variable b : B [field c] : C1 | semmle.label | [post] access to local variable b : B [field c] : C1 | | A.cs:13:9:13:9 | [post] access to local variable b : B [field c] : C1 | semmle.label | [post] access to local variable b : B [field c] : C1 | | A.cs:13:15:13:29 | call to method Source : C1 | semmle.label | call to method Source : C1 | +| A.cs:13:15:13:29 | call to method Source : C1 | semmle.label | call to method Source : C1 | +| A.cs:14:14:14:14 | access to local variable b : B [field c] : C1 | semmle.label | access to local variable b : B [field c] : C1 | | A.cs:14:14:14:14 | access to local variable b : B [field c] : C1 | semmle.label | access to local variable b : B [field c] : C1 | | A.cs:14:14:14:20 | call to method Get | semmle.label | call to method Get | +| A.cs:14:14:14:20 | call to method Get | semmle.label | call to method Get | +| A.cs:15:14:15:42 | call to method Get | semmle.label | call to method Get | | A.cs:15:14:15:42 | call to method Get | semmle.label | call to method Get | | A.cs:15:15:15:35 | object creation of type B : B [field c] : C | semmle.label | object creation of type B : B [field c] : C | +| A.cs:15:15:15:35 | object creation of type B : B [field c] : C | semmle.label | object creation of type B : B [field c] : C | +| A.cs:15:21:15:34 | call to method Source : C | semmle.label | call to method Source : C | | A.cs:15:21:15:34 | call to method Source : C | semmle.label | call to method Source : C | | A.cs:22:14:22:38 | call to method SetOnB : B [field c] : C2 | semmle.label | call to method SetOnB : B [field c] : C2 | +| A.cs:22:14:22:38 | call to method SetOnB : B [field c] : C2 | semmle.label | call to method SetOnB : B [field c] : C2 | +| A.cs:22:25:22:37 | call to method Source : C2 | semmle.label | call to method Source : C2 | | A.cs:22:25:22:37 | call to method Source : C2 | semmle.label | call to method Source : C2 | | A.cs:24:14:24:15 | access to local variable b2 : B [field c] : C2 | semmle.label | access to local variable b2 : B [field c] : C2 | +| A.cs:24:14:24:15 | access to local variable b2 : B [field c] : C2 | semmle.label | access to local variable b2 : B [field c] : C2 | +| A.cs:24:14:24:17 | access to field c | semmle.label | access to field c | | A.cs:24:14:24:17 | access to field c | semmle.label | access to field c | | A.cs:31:14:31:42 | call to method SetOnBWrap : B [field c] : C2 | semmle.label | call to method SetOnBWrap : B [field c] : C2 | +| A.cs:31:14:31:42 | call to method SetOnBWrap : B [field c] : C2 | semmle.label | call to method SetOnBWrap : B [field c] : C2 | +| A.cs:31:29:31:41 | call to method Source : C2 | semmle.label | call to method Source : C2 | | A.cs:31:29:31:41 | call to method Source : C2 | semmle.label | call to method Source : C2 | | A.cs:33:14:33:15 | access to local variable b2 : B [field c] : C2 | semmle.label | access to local variable b2 : B [field c] : C2 | +| A.cs:33:14:33:15 | access to local variable b2 : B [field c] : C2 | semmle.label | access to local variable b2 : B [field c] : C2 | +| A.cs:33:14:33:17 | access to field c | semmle.label | access to field c | | A.cs:33:14:33:17 | access to field c | semmle.label | access to field c | | A.cs:36:33:36:33 | c : C2 | semmle.label | c : C2 | +| A.cs:36:33:36:33 | c : C2 | semmle.label | c : C2 | +| A.cs:38:18:38:30 | call to method SetOnB : B [field c] : C2 | semmle.label | call to method SetOnB : B [field c] : C2 | | A.cs:38:18:38:30 | call to method SetOnB : B [field c] : C2 | semmle.label | call to method SetOnB : B [field c] : C2 | | A.cs:38:29:38:29 | access to parameter c : C2 | semmle.label | access to parameter c : C2 | +| A.cs:38:29:38:29 | access to parameter c : C2 | semmle.label | access to parameter c : C2 | +| A.cs:39:16:39:28 | ... ? ... : ... : B [field c] : C2 | semmle.label | ... ? ... : ... : B [field c] : C2 | | A.cs:39:16:39:28 | ... ? ... : ... : B [field c] : C2 | semmle.label | ... ? ... : ... : B [field c] : C2 | | A.cs:42:29:42:29 | c : C2 | semmle.label | c : C2 | +| A.cs:42:29:42:29 | c : C2 | semmle.label | c : C2 | +| A.cs:47:13:47:14 | [post] access to local variable b2 : B [field c] : C2 | semmle.label | [post] access to local variable b2 : B [field c] : C2 | | A.cs:47:13:47:14 | [post] access to local variable b2 : B [field c] : C2 | semmle.label | [post] access to local variable b2 : B [field c] : C2 | | A.cs:47:20:47:20 | access to parameter c : C2 | semmle.label | access to parameter c : C2 | +| A.cs:47:20:47:20 | access to parameter c : C2 | semmle.label | access to parameter c : C2 | +| A.cs:48:20:48:21 | access to local variable b2 : B [field c] : C2 | semmle.label | access to local variable b2 : B [field c] : C2 | | A.cs:48:20:48:21 | access to local variable b2 : B [field c] : C2 | semmle.label | access to local variable b2 : B [field c] : C2 | | A.cs:55:17:55:28 | call to method Source : A | semmle.label | call to method Source : A | +| A.cs:55:17:55:28 | call to method Source : A | semmle.label | call to method Source : A | +| A.cs:57:9:57:10 | [post] access to local variable c1 : C1 [field a] : A | semmle.label | [post] access to local variable c1 : C1 [field a] : A | | A.cs:57:9:57:10 | [post] access to local variable c1 : C1 [field a] : A | semmle.label | [post] access to local variable c1 : C1 [field a] : A | | A.cs:57:16:57:16 | access to local variable a : A | semmle.label | access to local variable a : A | +| A.cs:57:16:57:16 | access to local variable a : A | semmle.label | access to local variable a : A | +| A.cs:58:12:58:13 | access to local variable c1 : C1 [field a] : A | semmle.label | access to local variable c1 : C1 [field a] : A | | A.cs:58:12:58:13 | access to local variable c1 : C1 [field a] : A | semmle.label | access to local variable c1 : C1 [field a] : A | | A.cs:60:22:60:22 | c : C1 [field a] : A | semmle.label | c : C1 [field a] : A | +| A.cs:60:22:60:22 | c : C1 [field a] : A | semmle.label | c : C1 [field a] : A | +| A.cs:64:18:64:26 | access to field a | semmle.label | access to field a | | A.cs:64:18:64:26 | access to field a | semmle.label | access to field a | | A.cs:64:19:64:23 | (...) ... : C1 [field a] : A | semmle.label | (...) ... : C1 [field a] : A | +| A.cs:64:19:64:23 | (...) ... : C1 [field a] : A | semmle.label | (...) ... : C1 [field a] : A | +| A.cs:83:9:83:9 | [post] access to parameter b : B [field c] : C | semmle.label | [post] access to parameter b : B [field c] : C | | A.cs:83:9:83:9 | [post] access to parameter b : B [field c] : C | semmle.label | [post] access to parameter b : B [field c] : C | | A.cs:83:15:83:26 | call to method Source : C | semmle.label | call to method Source : C | +| A.cs:83:15:83:26 | call to method Source : C | semmle.label | call to method Source : C | +| A.cs:88:12:88:12 | [post] access to local variable b : B [field c] : C | semmle.label | [post] access to local variable b : B [field c] : C | | A.cs:88:12:88:12 | [post] access to local variable b : B [field c] : C | semmle.label | [post] access to local variable b : B [field c] : C | | A.cs:89:14:89:14 | access to local variable b : B [field c] : C | semmle.label | access to local variable b : B [field c] : C | +| A.cs:89:14:89:14 | access to local variable b : B [field c] : C | semmle.label | access to local variable b : B [field c] : C | +| A.cs:89:14:89:16 | access to field c | semmle.label | access to field c | | A.cs:89:14:89:16 | access to field c | semmle.label | access to field c | | A.cs:95:20:95:20 | b : B | semmle.label | b : B | +| A.cs:95:20:95:20 | b : B | semmle.label | b : B | +| A.cs:97:13:97:13 | [post] access to parameter b : B [field c] : C | semmle.label | [post] access to parameter b : B [field c] : C | | A.cs:97:13:97:13 | [post] access to parameter b : B [field c] : C | semmle.label | [post] access to parameter b : B [field c] : C | | A.cs:97:13:97:13 | access to parameter b : B | semmle.label | access to parameter b : B | +| A.cs:97:13:97:13 | access to parameter b : B | semmle.label | access to parameter b : B | | A.cs:97:19:97:32 | call to method Source : C | semmle.label | call to method Source : C | +| A.cs:97:19:97:32 | call to method Source : C | semmle.label | call to method Source : C | +| A.cs:98:13:98:16 | [post] this access : D [field b, field c] : C | semmle.label | [post] this access : D [field b, field c] : C | | A.cs:98:13:98:16 | [post] this access : D [field b, field c] : C | semmle.label | [post] this access : D [field b, field c] : C | | A.cs:98:13:98:16 | [post] this access : D [field b] : B | semmle.label | [post] this access : D [field b] : B | | A.cs:98:13:98:16 | [post] this access : D [field b] : B | semmle.label | [post] this access : D [field b] : B | +| A.cs:98:13:98:16 | [post] this access : D [field b] : B | semmle.label | [post] this access : D [field b] : B | +| A.cs:98:13:98:16 | [post] this access : D [field b] : B | semmle.label | [post] this access : D [field b] : B | +| A.cs:98:22:98:43 | ... ? ... : ... : B | semmle.label | ... ? ... : ... : B | +| A.cs:98:22:98:43 | ... ? ... : ... : B | semmle.label | ... ? ... : ... : B | | A.cs:98:22:98:43 | ... ? ... : ... : B | semmle.label | ... ? ... : ... : B | | A.cs:98:22:98:43 | ... ? ... : ... : B | semmle.label | ... ? ... : ... : B | | A.cs:98:22:98:43 | ... ? ... : ... : B [field c] : C | semmle.label | ... ? ... : ... : B [field c] : C | +| A.cs:98:22:98:43 | ... ? ... : ... : B [field c] : C | semmle.label | ... ? ... : ... : B [field c] : C | +| A.cs:98:30:98:43 | call to method Source : B | semmle.label | call to method Source : B | | A.cs:98:30:98:43 | call to method Source : B | semmle.label | call to method Source : B | | A.cs:104:17:104:30 | call to method Source : B | semmle.label | call to method Source : B | +| A.cs:104:17:104:30 | call to method Source : B | semmle.label | call to method Source : B | +| A.cs:105:17:105:29 | object creation of type D : D [field b, field c] : C | semmle.label | object creation of type D : D [field b, field c] : C | | A.cs:105:17:105:29 | object creation of type D : D [field b, field c] : C | semmle.label | object creation of type D : D [field b, field c] : C | | A.cs:105:17:105:29 | object creation of type D : D [field b] : B | semmle.label | object creation of type D : D [field b] : B | +| A.cs:105:17:105:29 | object creation of type D : D [field b] : B | semmle.label | object creation of type D : D [field b] : B | +| A.cs:105:23:105:23 | [post] access to local variable b : B [field c] : C | semmle.label | [post] access to local variable b : B [field c] : C | | A.cs:105:23:105:23 | [post] access to local variable b : B [field c] : C | semmle.label | [post] access to local variable b : B [field c] : C | | A.cs:105:23:105:23 | access to local variable b : B | semmle.label | access to local variable b : B | +| A.cs:105:23:105:23 | access to local variable b : B | semmle.label | access to local variable b : B | +| A.cs:106:14:106:14 | access to local variable d : D [field b] : B | semmle.label | access to local variable d : D [field b] : B | | A.cs:106:14:106:14 | access to local variable d : D [field b] : B | semmle.label | access to local variable d : D [field b] : B | | A.cs:106:14:106:16 | access to field b | semmle.label | access to field b | +| A.cs:106:14:106:16 | access to field b | semmle.label | access to field b | +| A.cs:107:14:107:14 | access to local variable d : D [field b, field c] : C | semmle.label | access to local variable d : D [field b, field c] : C | | A.cs:107:14:107:14 | access to local variable d : D [field b, field c] : C | semmle.label | access to local variable d : D [field b, field c] : C | | A.cs:107:14:107:16 | access to field b : B [field c] : C | semmle.label | access to field b : B [field c] : C | +| A.cs:107:14:107:16 | access to field b : B [field c] : C | semmle.label | access to field b : B [field c] : C | +| A.cs:107:14:107:18 | access to field c | semmle.label | access to field c | | A.cs:107:14:107:18 | access to field c | semmle.label | access to field c | | A.cs:108:14:108:14 | access to local variable b : B [field c] : C | semmle.label | access to local variable b : B [field c] : C | +| A.cs:108:14:108:14 | access to local variable b : B [field c] : C | semmle.label | access to local variable b : B [field c] : C | +| A.cs:108:14:108:16 | access to field c | semmle.label | access to field c | | A.cs:108:14:108:16 | access to field c | semmle.label | access to field c | | A.cs:113:17:113:29 | call to method Source : B | semmle.label | call to method Source : B | +| A.cs:113:17:113:29 | call to method Source : B | semmle.label | call to method Source : B | +| A.cs:114:18:114:54 | object creation of type MyList : MyList [field head] : B | semmle.label | object creation of type MyList : MyList [field head] : B | | A.cs:114:18:114:54 | object creation of type MyList : MyList [field head] : B | semmle.label | object creation of type MyList : MyList [field head] : B | | A.cs:114:29:114:29 | access to local variable b : B | semmle.label | access to local variable b : B | +| A.cs:114:29:114:29 | access to local variable b : B | semmle.label | access to local variable b : B | +| A.cs:115:18:115:37 | object creation of type MyList : MyList [field next, field head] : B | semmle.label | object creation of type MyList : MyList [field next, field head] : B | | A.cs:115:18:115:37 | object creation of type MyList : MyList [field next, field head] : B | semmle.label | object creation of type MyList : MyList [field next, field head] : B | | A.cs:115:35:115:36 | access to local variable l1 : MyList [field head] : B | semmle.label | access to local variable l1 : MyList [field head] : B | +| A.cs:115:35:115:36 | access to local variable l1 : MyList [field head] : B | semmle.label | access to local variable l1 : MyList [field head] : B | +| A.cs:116:18:116:37 | object creation of type MyList : MyList [field next, field next, field head] : B | semmle.label | object creation of type MyList : MyList [field next, field next, field head] : B | | A.cs:116:18:116:37 | object creation of type MyList : MyList [field next, field next, field head] : B | semmle.label | object creation of type MyList : MyList [field next, field next, field head] : B | | A.cs:116:35:116:36 | access to local variable l2 : MyList [field next, field head] : B | semmle.label | access to local variable l2 : MyList [field next, field head] : B | +| A.cs:116:35:116:36 | access to local variable l2 : MyList [field next, field head] : B | semmle.label | access to local variable l2 : MyList [field next, field head] : B | +| A.cs:119:14:119:15 | access to local variable l3 : MyList [field next, field next, field head] : B | semmle.label | access to local variable l3 : MyList [field next, field next, field head] : B | | A.cs:119:14:119:15 | access to local variable l3 : MyList [field next, field next, field head] : B | semmle.label | access to local variable l3 : MyList [field next, field next, field head] : B | | A.cs:119:14:119:20 | access to field next : MyList [field next, field head] : B | semmle.label | access to field next : MyList [field next, field head] : B | +| A.cs:119:14:119:20 | access to field next : MyList [field next, field head] : B | semmle.label | access to field next : MyList [field next, field head] : B | +| A.cs:119:14:119:25 | access to field next : MyList [field head] : B | semmle.label | access to field next : MyList [field head] : B | | A.cs:119:14:119:25 | access to field next : MyList [field head] : B | semmle.label | access to field next : MyList [field head] : B | | A.cs:119:14:119:30 | access to field head | semmle.label | access to field head | +| A.cs:119:14:119:30 | access to field head | semmle.label | access to field head | +| A.cs:121:41:121:41 | access to local variable l : MyList [field next, field head] : B | semmle.label | access to local variable l : MyList [field next, field head] : B | | A.cs:121:41:121:41 | access to local variable l : MyList [field next, field head] : B | semmle.label | access to local variable l : MyList [field next, field head] : B | | A.cs:121:41:121:41 | access to local variable l : MyList [field next, field next, field head] : B | semmle.label | access to local variable l : MyList [field next, field next, field head] : B | +| A.cs:121:41:121:41 | access to local variable l : MyList [field next, field next, field head] : B | semmle.label | access to local variable l : MyList [field next, field next, field head] : B | +| A.cs:121:41:121:46 | access to field next : MyList [field head] : B | semmle.label | access to field next : MyList [field head] : B | | A.cs:121:41:121:46 | access to field next : MyList [field head] : B | semmle.label | access to field next : MyList [field head] : B | | A.cs:121:41:121:46 | access to field next : MyList [field next, field head] : B | semmle.label | access to field next : MyList [field next, field head] : B | +| A.cs:121:41:121:46 | access to field next : MyList [field next, field head] : B | semmle.label | access to field next : MyList [field next, field head] : B | +| A.cs:123:18:123:18 | access to local variable l : MyList [field head] : B | semmle.label | access to local variable l : MyList [field head] : B | | A.cs:123:18:123:18 | access to local variable l : MyList [field head] : B | semmle.label | access to local variable l : MyList [field head] : B | | A.cs:123:18:123:23 | access to field head | semmle.label | access to field head | +| A.cs:123:18:123:23 | access to field head | semmle.label | access to field head | +| A.cs:141:20:141:20 | c : C | semmle.label | c : C | | A.cs:141:20:141:20 | c : C | semmle.label | c : C | | A.cs:143:13:143:16 | [post] this access : B [field c] : C | semmle.label | [post] this access : B [field c] : C | +| A.cs:143:13:143:16 | [post] this access : B [field c] : C | semmle.label | [post] this access : B [field c] : C | +| A.cs:143:22:143:22 | access to parameter c : C | semmle.label | access to parameter c : C | | A.cs:143:22:143:22 | access to parameter c : C | semmle.label | access to parameter c : C | | A.cs:145:27:145:27 | c : C | semmle.label | c : C | +| A.cs:145:27:145:27 | c : C | semmle.label | c : C | +| A.cs:145:27:145:27 | c : C1 | semmle.label | c : C1 | | A.cs:145:27:145:27 | c : C1 | semmle.label | c : C1 | | A.cs:145:27:145:27 | c : C2 | semmle.label | c : C2 | +| A.cs:145:27:145:27 | c : C2 | semmle.label | c : C2 | +| A.cs:145:32:145:35 | [post] this access : B [field c] : C | semmle.label | [post] this access : B [field c] : C | | A.cs:145:32:145:35 | [post] this access : B [field c] : C | semmle.label | [post] this access : B [field c] : C | | A.cs:145:32:145:35 | [post] this access : B [field c] : C1 | semmle.label | [post] this access : B [field c] : C1 | +| A.cs:145:32:145:35 | [post] this access : B [field c] : C1 | semmle.label | [post] this access : B [field c] : C1 | +| A.cs:145:32:145:35 | [post] this access : B [field c] : C2 | semmle.label | [post] this access : B [field c] : C2 | | A.cs:145:32:145:35 | [post] this access : B [field c] : C2 | semmle.label | [post] this access : B [field c] : C2 | | A.cs:145:41:145:41 | access to parameter c : C | semmle.label | access to parameter c : C | +| A.cs:145:41:145:41 | access to parameter c : C | semmle.label | access to parameter c : C | +| A.cs:145:41:145:41 | access to parameter c : C1 | semmle.label | access to parameter c : C1 | | A.cs:145:41:145:41 | access to parameter c : C1 | semmle.label | access to parameter c : C1 | | A.cs:145:41:145:41 | access to parameter c : C2 | semmle.label | access to parameter c : C2 | +| A.cs:145:41:145:41 | access to parameter c : C2 | semmle.label | access to parameter c : C2 | +| A.cs:146:18:146:20 | this : B [field c] : C | semmle.label | this : B [field c] : C | | A.cs:146:18:146:20 | this : B [field c] : C | semmle.label | this : B [field c] : C | | A.cs:146:18:146:20 | this : B [field c] : C1 | semmle.label | this : B [field c] : C1 | +| A.cs:146:18:146:20 | this : B [field c] : C1 | semmle.label | this : B [field c] : C1 | +| A.cs:146:33:146:36 | this access : B [field c] : C | semmle.label | this access : B [field c] : C | | A.cs:146:33:146:36 | this access : B [field c] : C | semmle.label | this access : B [field c] : C | | A.cs:146:33:146:36 | this access : B [field c] : C1 | semmle.label | this access : B [field c] : C1 | +| A.cs:146:33:146:36 | this access : B [field c] : C1 | semmle.label | this access : B [field c] : C1 | +| A.cs:146:33:146:38 | access to field c : C | semmle.label | access to field c : C | | A.cs:146:33:146:38 | access to field c : C | semmle.label | access to field c : C | | A.cs:146:33:146:38 | access to field c : C1 | semmle.label | access to field c : C1 | +| A.cs:146:33:146:38 | access to field c : C1 | semmle.label | access to field c : C1 | +| A.cs:147:32:147:32 | c : C | semmle.label | c : C | | A.cs:147:32:147:32 | c : C | semmle.label | c : C | | A.cs:149:20:149:27 | object creation of type B : B [field c] : C | semmle.label | object creation of type B : B [field c] : C | +| A.cs:149:20:149:27 | object creation of type B : B [field c] : C | semmle.label | object creation of type B : B [field c] : C | +| A.cs:149:26:149:26 | access to parameter c : C | semmle.label | access to parameter c : C | | A.cs:149:26:149:26 | access to parameter c : C | semmle.label | access to parameter c : C | | A.cs:157:25:157:28 | head : B | semmle.label | head : B | +| A.cs:157:25:157:28 | head : B | semmle.label | head : B | +| A.cs:157:38:157:41 | next : MyList [field head] : B | semmle.label | next : MyList [field head] : B | | A.cs:157:38:157:41 | next : MyList [field head] : B | semmle.label | next : MyList [field head] : B | | A.cs:157:38:157:41 | next : MyList [field next, field head] : B | semmle.label | next : MyList [field next, field head] : B | +| A.cs:157:38:157:41 | next : MyList [field next, field head] : B | semmle.label | next : MyList [field next, field head] : B | +| A.cs:159:13:159:16 | [post] this access : MyList [field head] : B | semmle.label | [post] this access : MyList [field head] : B | | A.cs:159:13:159:16 | [post] this access : MyList [field head] : B | semmle.label | [post] this access : MyList [field head] : B | | A.cs:159:25:159:28 | access to parameter head : B | semmle.label | access to parameter head : B | +| A.cs:159:25:159:28 | access to parameter head : B | semmle.label | access to parameter head : B | +| A.cs:160:13:160:16 | [post] this access : MyList [field next, field head] : B | semmle.label | [post] this access : MyList [field next, field head] : B | | A.cs:160:13:160:16 | [post] this access : MyList [field next, field head] : B | semmle.label | [post] this access : MyList [field next, field head] : B | | A.cs:160:13:160:16 | [post] this access : MyList [field next, field next, field head] : B | semmle.label | [post] this access : MyList [field next, field next, field head] : B | +| A.cs:160:13:160:16 | [post] this access : MyList [field next, field next, field head] : B | semmle.label | [post] this access : MyList [field next, field next, field head] : B | +| A.cs:160:25:160:28 | access to parameter next : MyList [field head] : B | semmle.label | access to parameter next : MyList [field head] : B | | A.cs:160:25:160:28 | access to parameter next : MyList [field head] : B | semmle.label | access to parameter next : MyList [field head] : B | | A.cs:160:25:160:28 | access to parameter next : MyList [field next, field head] : B | semmle.label | access to parameter next : MyList [field next, field head] : B | +| A.cs:160:25:160:28 | access to parameter next : MyList [field next, field head] : B | semmle.label | access to parameter next : MyList [field next, field head] : B | +| B.cs:5:17:5:31 | call to method Source : Elem | semmle.label | call to method Source : Elem | | B.cs:5:17:5:31 | call to method Source : Elem | semmle.label | call to method Source : Elem | | B.cs:6:18:6:34 | object creation of type Box1 : Box1 [field elem1] : Elem | semmle.label | object creation of type Box1 : Box1 [field elem1] : Elem | +| B.cs:6:18:6:34 | object creation of type Box1 : Box1 [field elem1] : Elem | semmle.label | object creation of type Box1 : Box1 [field elem1] : Elem | +| B.cs:6:27:6:27 | access to local variable e : Elem | semmle.label | access to local variable e : Elem | | B.cs:6:27:6:27 | access to local variable e : Elem | semmle.label | access to local variable e : Elem | | B.cs:7:18:7:29 | object creation of type Box2 : Box2 [field box1, field elem1] : Elem | semmle.label | object creation of type Box2 : Box2 [field box1, field elem1] : Elem | +| B.cs:7:18:7:29 | object creation of type Box2 : Box2 [field box1, field elem1] : Elem | semmle.label | object creation of type Box2 : Box2 [field box1, field elem1] : Elem | +| B.cs:7:27:7:28 | access to local variable b1 : Box1 [field elem1] : Elem | semmle.label | access to local variable b1 : Box1 [field elem1] : Elem | | B.cs:7:27:7:28 | access to local variable b1 : Box1 [field elem1] : Elem | semmle.label | access to local variable b1 : Box1 [field elem1] : Elem | | B.cs:8:14:8:15 | access to local variable b2 : Box2 [field box1, field elem1] : Elem | semmle.label | access to local variable b2 : Box2 [field box1, field elem1] : Elem | +| B.cs:8:14:8:15 | access to local variable b2 : Box2 [field box1, field elem1] : Elem | semmle.label | access to local variable b2 : Box2 [field box1, field elem1] : Elem | +| B.cs:8:14:8:20 | access to field box1 : Box1 [field elem1] : Elem | semmle.label | access to field box1 : Box1 [field elem1] : Elem | | B.cs:8:14:8:20 | access to field box1 : Box1 [field elem1] : Elem | semmle.label | access to field box1 : Box1 [field elem1] : Elem | | B.cs:8:14:8:26 | access to field elem1 | semmle.label | access to field elem1 | +| B.cs:8:14:8:26 | access to field elem1 | semmle.label | access to field elem1 | +| B.cs:14:17:14:31 | call to method Source : Elem | semmle.label | call to method Source : Elem | | B.cs:14:17:14:31 | call to method Source : Elem | semmle.label | call to method Source : Elem | | B.cs:15:18:15:34 | object creation of type Box1 : Box1 [field elem2] : Elem | semmle.label | object creation of type Box1 : Box1 [field elem2] : Elem | +| B.cs:15:18:15:34 | object creation of type Box1 : Box1 [field elem2] : Elem | semmle.label | object creation of type Box1 : Box1 [field elem2] : Elem | +| B.cs:15:33:15:33 | access to local variable e : Elem | semmle.label | access to local variable e : Elem | | B.cs:15:33:15:33 | access to local variable e : Elem | semmle.label | access to local variable e : Elem | | B.cs:16:18:16:29 | object creation of type Box2 : Box2 [field box1, field elem2] : Elem | semmle.label | object creation of type Box2 : Box2 [field box1, field elem2] : Elem | +| B.cs:16:18:16:29 | object creation of type Box2 : Box2 [field box1, field elem2] : Elem | semmle.label | object creation of type Box2 : Box2 [field box1, field elem2] : Elem | +| B.cs:16:27:16:28 | access to local variable b1 : Box1 [field elem2] : Elem | semmle.label | access to local variable b1 : Box1 [field elem2] : Elem | | B.cs:16:27:16:28 | access to local variable b1 : Box1 [field elem2] : Elem | semmle.label | access to local variable b1 : Box1 [field elem2] : Elem | | B.cs:18:14:18:15 | access to local variable b2 : Box2 [field box1, field elem2] : Elem | semmle.label | access to local variable b2 : Box2 [field box1, field elem2] : Elem | +| B.cs:18:14:18:15 | access to local variable b2 : Box2 [field box1, field elem2] : Elem | semmle.label | access to local variable b2 : Box2 [field box1, field elem2] : Elem | +| B.cs:18:14:18:20 | access to field box1 : Box1 [field elem2] : Elem | semmle.label | access to field box1 : Box1 [field elem2] : Elem | | B.cs:18:14:18:20 | access to field box1 : Box1 [field elem2] : Elem | semmle.label | access to field box1 : Box1 [field elem2] : Elem | | B.cs:18:14:18:26 | access to field elem2 | semmle.label | access to field elem2 | +| B.cs:18:14:18:26 | access to field elem2 | semmle.label | access to field elem2 | +| B.cs:29:26:29:27 | e1 : Elem | semmle.label | e1 : Elem | | B.cs:29:26:29:27 | e1 : Elem | semmle.label | e1 : Elem | | B.cs:29:35:29:36 | e2 : Elem | semmle.label | e2 : Elem | +| B.cs:29:35:29:36 | e2 : Elem | semmle.label | e2 : Elem | +| B.cs:31:13:31:16 | [post] this access : Box1 [field elem1] : Elem | semmle.label | [post] this access : Box1 [field elem1] : Elem | | B.cs:31:13:31:16 | [post] this access : Box1 [field elem1] : Elem | semmle.label | [post] this access : Box1 [field elem1] : Elem | | B.cs:31:26:31:27 | access to parameter e1 : Elem | semmle.label | access to parameter e1 : Elem | +| B.cs:31:26:31:27 | access to parameter e1 : Elem | semmle.label | access to parameter e1 : Elem | +| B.cs:32:13:32:16 | [post] this access : Box1 [field elem2] : Elem | semmle.label | [post] this access : Box1 [field elem2] : Elem | | B.cs:32:13:32:16 | [post] this access : Box1 [field elem2] : Elem | semmle.label | [post] this access : Box1 [field elem2] : Elem | | B.cs:32:26:32:27 | access to parameter e2 : Elem | semmle.label | access to parameter e2 : Elem | +| B.cs:32:26:32:27 | access to parameter e2 : Elem | semmle.label | access to parameter e2 : Elem | +| B.cs:39:26:39:27 | b1 : Box1 [field elem1] : Elem | semmle.label | b1 : Box1 [field elem1] : Elem | | B.cs:39:26:39:27 | b1 : Box1 [field elem1] : Elem | semmle.label | b1 : Box1 [field elem1] : Elem | | B.cs:39:26:39:27 | b1 : Box1 [field elem2] : Elem | semmle.label | b1 : Box1 [field elem2] : Elem | +| B.cs:39:26:39:27 | b1 : Box1 [field elem2] : Elem | semmle.label | b1 : Box1 [field elem2] : Elem | +| B.cs:41:13:41:16 | [post] this access : Box2 [field box1, field elem1] : Elem | semmle.label | [post] this access : Box2 [field box1, field elem1] : Elem | | B.cs:41:13:41:16 | [post] this access : Box2 [field box1, field elem1] : Elem | semmle.label | [post] this access : Box2 [field box1, field elem1] : Elem | | B.cs:41:13:41:16 | [post] this access : Box2 [field box1, field elem2] : Elem | semmle.label | [post] this access : Box2 [field box1, field elem2] : Elem | +| B.cs:41:13:41:16 | [post] this access : Box2 [field box1, field elem2] : Elem | semmle.label | [post] this access : Box2 [field box1, field elem2] : Elem | +| B.cs:41:25:41:26 | access to parameter b1 : Box1 [field elem1] : Elem | semmle.label | access to parameter b1 : Box1 [field elem1] : Elem | | B.cs:41:25:41:26 | access to parameter b1 : Box1 [field elem1] : Elem | semmle.label | access to parameter b1 : Box1 [field elem1] : Elem | | B.cs:41:25:41:26 | access to parameter b1 : Box1 [field elem2] : Elem | semmle.label | access to parameter b1 : Box1 [field elem2] : Elem | +| B.cs:41:25:41:26 | access to parameter b1 : Box1 [field elem2] : Elem | semmle.label | access to parameter b1 : Box1 [field elem2] : Elem | +| C.cs:3:18:3:19 | [post] this access : C [field s1] : Elem | semmle.label | [post] this access : C [field s1] : Elem | | C.cs:3:18:3:19 | [post] this access : C [field s1] : Elem | semmle.label | [post] this access : C [field s1] : Elem | | C.cs:3:23:3:37 | call to method Source : Elem | semmle.label | call to method Source : Elem | +| C.cs:3:23:3:37 | call to method Source : Elem | semmle.label | call to method Source : Elem | +| C.cs:4:27:4:28 | [post] this access : C [field s2] : Elem | semmle.label | [post] this access : C [field s2] : Elem | | C.cs:4:27:4:28 | [post] this access : C [field s2] : Elem | semmle.label | [post] this access : C [field s2] : Elem | | C.cs:4:32:4:46 | call to method Source : Elem | semmle.label | call to method Source : Elem | +| C.cs:4:32:4:46 | call to method Source : Elem | semmle.label | call to method Source : Elem | +| C.cs:6:30:6:44 | call to method Source : Elem | semmle.label | call to method Source : Elem | | C.cs:6:30:6:44 | call to method Source : Elem | semmle.label | call to method Source : Elem | | C.cs:7:18:7:19 | [post] this access : C [property s5] : Elem | semmle.label | [post] this access : C [property s5] : Elem | +| C.cs:7:18:7:19 | [post] this access : C [property s5] : Elem | semmle.label | [post] this access : C [property s5] : Elem | +| C.cs:7:37:7:51 | call to method Source : Elem | semmle.label | call to method Source : Elem | | C.cs:7:37:7:51 | call to method Source : Elem | semmle.label | call to method Source : Elem | | C.cs:8:30:8:44 | call to method Source : Elem | semmle.label | call to method Source : Elem | +| C.cs:8:30:8:44 | call to method Source : Elem | semmle.label | call to method Source : Elem | +| C.cs:12:15:12:21 | object creation of type C : C [field s1] : Elem | semmle.label | object creation of type C : C [field s1] : Elem | | C.cs:12:15:12:21 | object creation of type C : C [field s1] : Elem | semmle.label | object creation of type C : C [field s1] : Elem | | C.cs:12:15:12:21 | object creation of type C : C [field s2] : Elem | semmle.label | object creation of type C : C [field s2] : Elem | +| C.cs:12:15:12:21 | object creation of type C : C [field s2] : Elem | semmle.label | object creation of type C : C [field s2] : Elem | +| C.cs:12:15:12:21 | object creation of type C : C [field s3] : Elem | semmle.label | object creation of type C : C [field s3] : Elem | | C.cs:12:15:12:21 | object creation of type C : C [field s3] : Elem | semmle.label | object creation of type C : C [field s3] : Elem | | C.cs:12:15:12:21 | object creation of type C : C [property s5] : Elem | semmle.label | object creation of type C : C [property s5] : Elem | +| C.cs:12:15:12:21 | object creation of type C : C [property s5] : Elem | semmle.label | object creation of type C : C [property s5] : Elem | +| C.cs:13:9:13:9 | access to local variable c : C [field s1] : Elem | semmle.label | access to local variable c : C [field s1] : Elem | | C.cs:13:9:13:9 | access to local variable c : C [field s1] : Elem | semmle.label | access to local variable c : C [field s1] : Elem | | C.cs:13:9:13:9 | access to local variable c : C [field s2] : Elem | semmle.label | access to local variable c : C [field s2] : Elem | +| C.cs:13:9:13:9 | access to local variable c : C [field s2] : Elem | semmle.label | access to local variable c : C [field s2] : Elem | +| C.cs:13:9:13:9 | access to local variable c : C [field s3] : Elem | semmle.label | access to local variable c : C [field s3] : Elem | | C.cs:13:9:13:9 | access to local variable c : C [field s3] : Elem | semmle.label | access to local variable c : C [field s3] : Elem | | C.cs:13:9:13:9 | access to local variable c : C [property s5] : Elem | semmle.label | access to local variable c : C [property s5] : Elem | +| C.cs:13:9:13:9 | access to local variable c : C [property s5] : Elem | semmle.label | access to local variable c : C [property s5] : Elem | +| C.cs:18:9:18:12 | [post] this access : C [field s3] : Elem | semmle.label | [post] this access : C [field s3] : Elem | | C.cs:18:9:18:12 | [post] this access : C [field s3] : Elem | semmle.label | [post] this access : C [field s3] : Elem | | C.cs:18:19:18:33 | call to method Source : Elem | semmle.label | call to method Source : Elem | +| C.cs:18:19:18:33 | call to method Source : Elem | semmle.label | call to method Source : Elem | +| C.cs:21:17:21:18 | this : C [field s1] : Elem | semmle.label | this : C [field s1] : Elem | | C.cs:21:17:21:18 | this : C [field s1] : Elem | semmle.label | this : C [field s1] : Elem | | C.cs:21:17:21:18 | this : C [field s2] : Elem | semmle.label | this : C [field s2] : Elem | +| C.cs:21:17:21:18 | this : C [field s2] : Elem | semmle.label | this : C [field s2] : Elem | +| C.cs:21:17:21:18 | this : C [field s3] : Elem | semmle.label | this : C [field s3] : Elem | | C.cs:21:17:21:18 | this : C [field s3] : Elem | semmle.label | this : C [field s3] : Elem | | C.cs:21:17:21:18 | this : C [property s5] : Elem | semmle.label | this : C [property s5] : Elem | +| C.cs:21:17:21:18 | this : C [property s5] : Elem | semmle.label | this : C [property s5] : Elem | +| C.cs:23:14:23:15 | access to field s1 | semmle.label | access to field s1 | | C.cs:23:14:23:15 | access to field s1 | semmle.label | access to field s1 | | C.cs:23:14:23:15 | this access : C [field s1] : Elem | semmle.label | this access : C [field s1] : Elem | +| C.cs:23:14:23:15 | this access : C [field s1] : Elem | semmle.label | this access : C [field s1] : Elem | +| C.cs:24:14:24:15 | access to field s2 | semmle.label | access to field s2 | | C.cs:24:14:24:15 | access to field s2 | semmle.label | access to field s2 | | C.cs:24:14:24:15 | this access : C [field s2] : Elem | semmle.label | this access : C [field s2] : Elem | +| C.cs:24:14:24:15 | this access : C [field s2] : Elem | semmle.label | this access : C [field s2] : Elem | +| C.cs:25:14:25:15 | access to field s3 | semmle.label | access to field s3 | | C.cs:25:14:25:15 | access to field s3 | semmle.label | access to field s3 | | C.cs:25:14:25:15 | this access : C [field s3] : Elem | semmle.label | this access : C [field s3] : Elem | +| C.cs:25:14:25:15 | this access : C [field s3] : Elem | semmle.label | this access : C [field s3] : Elem | +| C.cs:26:14:26:15 | access to field s4 | semmle.label | access to field s4 | | C.cs:26:14:26:15 | access to field s4 | semmle.label | access to field s4 | | C.cs:27:14:27:15 | access to property s5 | semmle.label | access to property s5 | +| C.cs:27:14:27:15 | access to property s5 | semmle.label | access to property s5 | +| C.cs:27:14:27:15 | this access : C [property s5] : Elem | semmle.label | this access : C [property s5] : Elem | | C.cs:27:14:27:15 | this access : C [property s5] : Elem | semmle.label | this access : C [property s5] : Elem | | C.cs:28:14:28:15 | access to property s6 | semmle.label | access to property s6 | +| C.cs:28:14:28:15 | access to property s6 | semmle.label | access to property s6 | +| C_ctor.cs:3:18:3:19 | [post] this access : C_no_ctor [field s1] : Elem | semmle.label | [post] this access : C_no_ctor [field s1] : Elem | | C_ctor.cs:3:18:3:19 | [post] this access : C_no_ctor [field s1] : Elem | semmle.label | [post] this access : C_no_ctor [field s1] : Elem | | C_ctor.cs:3:23:3:42 | call to method Source : Elem | semmle.label | call to method Source : Elem | +| C_ctor.cs:3:23:3:42 | call to method Source : Elem | semmle.label | call to method Source : Elem | +| C_ctor.cs:7:23:7:37 | object creation of type C_no_ctor : C_no_ctor [field s1] : Elem | semmle.label | object creation of type C_no_ctor : C_no_ctor [field s1] : Elem | | C_ctor.cs:7:23:7:37 | object creation of type C_no_ctor : C_no_ctor [field s1] : Elem | semmle.label | object creation of type C_no_ctor : C_no_ctor [field s1] : Elem | | C_ctor.cs:8:9:8:9 | access to local variable c : C_no_ctor [field s1] : Elem | semmle.label | access to local variable c : C_no_ctor [field s1] : Elem | +| C_ctor.cs:8:9:8:9 | access to local variable c : C_no_ctor [field s1] : Elem | semmle.label | access to local variable c : C_no_ctor [field s1] : Elem | +| C_ctor.cs:11:17:11:18 | this : C_no_ctor [field s1] : Elem | semmle.label | this : C_no_ctor [field s1] : Elem | | C_ctor.cs:11:17:11:18 | this : C_no_ctor [field s1] : Elem | semmle.label | this : C_no_ctor [field s1] : Elem | | C_ctor.cs:13:19:13:20 | access to field s1 | semmle.label | access to field s1 | +| C_ctor.cs:13:19:13:20 | access to field s1 | semmle.label | access to field s1 | +| C_ctor.cs:13:19:13:20 | this access : C_no_ctor [field s1] : Elem | semmle.label | this access : C_no_ctor [field s1] : Elem | | C_ctor.cs:13:19:13:20 | this access : C_no_ctor [field s1] : Elem | semmle.label | this access : C_no_ctor [field s1] : Elem | | C_ctor.cs:19:18:19:19 | [post] this access : C_with_ctor [field s1] : Elem | semmle.label | [post] this access : C_with_ctor [field s1] : Elem | +| C_ctor.cs:19:18:19:19 | [post] this access : C_with_ctor [field s1] : Elem | semmle.label | [post] this access : C_with_ctor [field s1] : Elem | +| C_ctor.cs:19:23:19:42 | call to method Source : Elem | semmle.label | call to method Source : Elem | | C_ctor.cs:19:23:19:42 | call to method Source : Elem | semmle.label | call to method Source : Elem | | C_ctor.cs:23:25:23:41 | object creation of type C_with_ctor : C_with_ctor [field s1] : Elem | semmle.label | object creation of type C_with_ctor : C_with_ctor [field s1] : Elem | +| C_ctor.cs:23:25:23:41 | object creation of type C_with_ctor : C_with_ctor [field s1] : Elem | semmle.label | object creation of type C_with_ctor : C_with_ctor [field s1] : Elem | +| C_ctor.cs:24:9:24:9 | access to local variable c : C_with_ctor [field s1] : Elem | semmle.label | access to local variable c : C_with_ctor [field s1] : Elem | | C_ctor.cs:24:9:24:9 | access to local variable c : C_with_ctor [field s1] : Elem | semmle.label | access to local variable c : C_with_ctor [field s1] : Elem | | C_ctor.cs:29:17:29:18 | this : C_with_ctor [field s1] : Elem | semmle.label | this : C_with_ctor [field s1] : Elem | +| C_ctor.cs:29:17:29:18 | this : C_with_ctor [field s1] : Elem | semmle.label | this : C_with_ctor [field s1] : Elem | +| C_ctor.cs:31:19:31:20 | access to field s1 | semmle.label | access to field s1 | | C_ctor.cs:31:19:31:20 | access to field s1 | semmle.label | access to field s1 | | C_ctor.cs:31:19:31:20 | this access : C_with_ctor [field s1] : Elem | semmle.label | this access : C_with_ctor [field s1] : Elem | +| C_ctor.cs:31:19:31:20 | this access : C_with_ctor [field s1] : Elem | semmle.label | this access : C_with_ctor [field s1] : Elem | +| D.cs:8:9:8:11 | this : D [field trivialPropField] : Object | semmle.label | this : D [field trivialPropField] : Object | | D.cs:8:9:8:11 | this : D [field trivialPropField] : Object | semmle.label | this : D [field trivialPropField] : Object | | D.cs:8:22:8:25 | this access : D [field trivialPropField] : Object | semmle.label | this access : D [field trivialPropField] : Object | +| D.cs:8:22:8:25 | this access : D [field trivialPropField] : Object | semmle.label | this access : D [field trivialPropField] : Object | +| D.cs:8:22:8:42 | access to field trivialPropField : Object | semmle.label | access to field trivialPropField : Object | | D.cs:8:22:8:42 | access to field trivialPropField : Object | semmle.label | access to field trivialPropField : Object | | D.cs:9:9:9:11 | value : Object | semmle.label | value : Object | +| D.cs:9:9:9:11 | value : Object | semmle.label | value : Object | +| D.cs:9:15:9:18 | [post] this access : D [field trivialPropField] : Object | semmle.label | [post] this access : D [field trivialPropField] : Object | | D.cs:9:15:9:18 | [post] this access : D [field trivialPropField] : Object | semmle.label | [post] this access : D [field trivialPropField] : Object | | D.cs:9:39:9:43 | access to parameter value : Object | semmle.label | access to parameter value : Object | +| D.cs:9:39:9:43 | access to parameter value : Object | semmle.label | access to parameter value : Object | +| D.cs:14:9:14:11 | this : D [field trivialPropField] : Object | semmle.label | this : D [field trivialPropField] : Object | | D.cs:14:9:14:11 | this : D [field trivialPropField] : Object | semmle.label | this : D [field trivialPropField] : Object | | D.cs:14:22:14:25 | this access : D [field trivialPropField] : Object | semmle.label | this access : D [field trivialPropField] : Object | +| D.cs:14:22:14:25 | this access : D [field trivialPropField] : Object | semmle.label | this access : D [field trivialPropField] : Object | +| D.cs:14:22:14:42 | access to field trivialPropField : Object | semmle.label | access to field trivialPropField : Object | | D.cs:14:22:14:42 | access to field trivialPropField : Object | semmle.label | access to field trivialPropField : Object | | D.cs:15:9:15:11 | value : Object | semmle.label | value : Object | +| D.cs:15:9:15:11 | value : Object | semmle.label | value : Object | +| D.cs:15:15:15:18 | [post] this access : D [field trivialPropField] : Object | semmle.label | [post] this access : D [field trivialPropField] : Object | | D.cs:15:15:15:18 | [post] this access : D [field trivialPropField] : Object | semmle.label | [post] this access : D [field trivialPropField] : Object | | D.cs:15:34:15:38 | access to parameter value : Object | semmle.label | access to parameter value : Object | +| D.cs:15:34:15:38 | access to parameter value : Object | semmle.label | access to parameter value : Object | +| D.cs:18:28:18:29 | o1 : Object | semmle.label | o1 : Object | | D.cs:18:28:18:29 | o1 : Object | semmle.label | o1 : Object | | D.cs:18:39:18:40 | o2 : Object | semmle.label | o2 : Object | +| D.cs:18:39:18:40 | o2 : Object | semmle.label | o2 : Object | +| D.cs:18:50:18:51 | o3 : Object | semmle.label | o3 : Object | | D.cs:18:50:18:51 | o3 : Object | semmle.label | o3 : Object | | D.cs:21:9:21:11 | [post] access to local variable ret : D [property AutoProp] : Object | semmle.label | [post] access to local variable ret : D [property AutoProp] : Object | +| D.cs:21:9:21:11 | [post] access to local variable ret : D [property AutoProp] : Object | semmle.label | [post] access to local variable ret : D [property AutoProp] : Object | +| D.cs:21:24:21:25 | access to parameter o1 : Object | semmle.label | access to parameter o1 : Object | | D.cs:21:24:21:25 | access to parameter o1 : Object | semmle.label | access to parameter o1 : Object | | D.cs:22:9:22:11 | [post] access to local variable ret : D [field trivialPropField] : Object | semmle.label | [post] access to local variable ret : D [field trivialPropField] : Object | +| D.cs:22:9:22:11 | [post] access to local variable ret : D [field trivialPropField] : Object | semmle.label | [post] access to local variable ret : D [field trivialPropField] : Object | +| D.cs:22:27:22:28 | access to parameter o2 : Object | semmle.label | access to parameter o2 : Object | | D.cs:22:27:22:28 | access to parameter o2 : Object | semmle.label | access to parameter o2 : Object | | D.cs:23:9:23:11 | [post] access to local variable ret : D [field trivialPropField] : Object | semmle.label | [post] access to local variable ret : D [field trivialPropField] : Object | +| D.cs:23:9:23:11 | [post] access to local variable ret : D [field trivialPropField] : Object | semmle.label | [post] access to local variable ret : D [field trivialPropField] : Object | +| D.cs:23:27:23:28 | access to parameter o3 : Object | semmle.label | access to parameter o3 : Object | | D.cs:23:27:23:28 | access to parameter o3 : Object | semmle.label | access to parameter o3 : Object | | D.cs:24:16:24:18 | access to local variable ret : D [field trivialPropField] : Object | semmle.label | access to local variable ret : D [field trivialPropField] : Object | | D.cs:24:16:24:18 | access to local variable ret : D [field trivialPropField] : Object | semmle.label | access to local variable ret : D [field trivialPropField] : Object | +| D.cs:24:16:24:18 | access to local variable ret : D [field trivialPropField] : Object | semmle.label | access to local variable ret : D [field trivialPropField] : Object | +| D.cs:24:16:24:18 | access to local variable ret : D [field trivialPropField] : Object | semmle.label | access to local variable ret : D [field trivialPropField] : Object | +| D.cs:24:16:24:18 | access to local variable ret : D [property AutoProp] : Object | semmle.label | access to local variable ret : D [property AutoProp] : Object | | D.cs:24:16:24:18 | access to local variable ret : D [property AutoProp] : Object | semmle.label | access to local variable ret : D [property AutoProp] : Object | | D.cs:29:17:29:33 | call to method Source : Object | semmle.label | call to method Source : Object | +| D.cs:29:17:29:33 | call to method Source : Object | semmle.label | call to method Source : Object | +| D.cs:31:17:31:37 | call to method Create : D [property AutoProp] : Object | semmle.label | call to method Create : D [property AutoProp] : Object | | D.cs:31:17:31:37 | call to method Create : D [property AutoProp] : Object | semmle.label | call to method Create : D [property AutoProp] : Object | | D.cs:31:24:31:24 | access to local variable o : Object | semmle.label | access to local variable o : Object | +| D.cs:31:24:31:24 | access to local variable o : Object | semmle.label | access to local variable o : Object | +| D.cs:32:14:32:14 | access to local variable d : D [property AutoProp] : Object | semmle.label | access to local variable d : D [property AutoProp] : Object | | D.cs:32:14:32:14 | access to local variable d : D [property AutoProp] : Object | semmle.label | access to local variable d : D [property AutoProp] : Object | | D.cs:32:14:32:23 | access to property AutoProp | semmle.label | access to property AutoProp | +| D.cs:32:14:32:23 | access to property AutoProp | semmle.label | access to property AutoProp | +| D.cs:37:13:37:49 | call to method Create : D [field trivialPropField] : Object | semmle.label | call to method Create : D [field trivialPropField] : Object | | D.cs:37:13:37:49 | call to method Create : D [field trivialPropField] : Object | semmle.label | call to method Create : D [field trivialPropField] : Object | | D.cs:37:26:37:42 | call to method Source : Object | semmle.label | call to method Source : Object | +| D.cs:37:26:37:42 | call to method Source : Object | semmle.label | call to method Source : Object | +| D.cs:39:14:39:14 | access to local variable d : D [field trivialPropField] : Object | semmle.label | access to local variable d : D [field trivialPropField] : Object | | D.cs:39:14:39:14 | access to local variable d : D [field trivialPropField] : Object | semmle.label | access to local variable d : D [field trivialPropField] : Object | | D.cs:39:14:39:26 | access to property TrivialProp | semmle.label | access to property TrivialProp | +| D.cs:39:14:39:26 | access to property TrivialProp | semmle.label | access to property TrivialProp | +| D.cs:40:14:40:14 | access to local variable d : D [field trivialPropField] : Object | semmle.label | access to local variable d : D [field trivialPropField] : Object | | D.cs:40:14:40:14 | access to local variable d : D [field trivialPropField] : Object | semmle.label | access to local variable d : D [field trivialPropField] : Object | | D.cs:40:14:40:31 | access to field trivialPropField | semmle.label | access to field trivialPropField | +| D.cs:40:14:40:31 | access to field trivialPropField | semmle.label | access to field trivialPropField | +| D.cs:41:14:41:14 | access to local variable d : D [field trivialPropField] : Object | semmle.label | access to local variable d : D [field trivialPropField] : Object | | D.cs:41:14:41:14 | access to local variable d : D [field trivialPropField] : Object | semmle.label | access to local variable d : D [field trivialPropField] : Object | | D.cs:41:14:41:26 | access to property ComplexProp | semmle.label | access to property ComplexProp | +| D.cs:41:14:41:26 | access to property ComplexProp | semmle.label | access to property ComplexProp | +| D.cs:43:13:43:49 | call to method Create : D [field trivialPropField] : Object | semmle.label | call to method Create : D [field trivialPropField] : Object | | D.cs:43:13:43:49 | call to method Create : D [field trivialPropField] : Object | semmle.label | call to method Create : D [field trivialPropField] : Object | | D.cs:43:32:43:48 | call to method Source : Object | semmle.label | call to method Source : Object | +| D.cs:43:32:43:48 | call to method Source : Object | semmle.label | call to method Source : Object | +| D.cs:45:14:45:14 | access to local variable d : D [field trivialPropField] : Object | semmle.label | access to local variable d : D [field trivialPropField] : Object | | D.cs:45:14:45:14 | access to local variable d : D [field trivialPropField] : Object | semmle.label | access to local variable d : D [field trivialPropField] : Object | | D.cs:45:14:45:26 | access to property TrivialProp | semmle.label | access to property TrivialProp | +| D.cs:45:14:45:26 | access to property TrivialProp | semmle.label | access to property TrivialProp | +| D.cs:46:14:46:14 | access to local variable d : D [field trivialPropField] : Object | semmle.label | access to local variable d : D [field trivialPropField] : Object | | D.cs:46:14:46:14 | access to local variable d : D [field trivialPropField] : Object | semmle.label | access to local variable d : D [field trivialPropField] : Object | | D.cs:46:14:46:31 | access to field trivialPropField | semmle.label | access to field trivialPropField | +| D.cs:46:14:46:31 | access to field trivialPropField | semmle.label | access to field trivialPropField | +| D.cs:47:14:47:14 | access to local variable d : D [field trivialPropField] : Object | semmle.label | access to local variable d : D [field trivialPropField] : Object | | D.cs:47:14:47:14 | access to local variable d : D [field trivialPropField] : Object | semmle.label | access to local variable d : D [field trivialPropField] : Object | | D.cs:47:14:47:26 | access to property ComplexProp | semmle.label | access to property ComplexProp | +| D.cs:47:14:47:26 | access to property ComplexProp | semmle.label | access to property ComplexProp | +| E.cs:8:29:8:29 | o : Object | semmle.label | o : Object | | E.cs:8:29:8:29 | o : Object | semmle.label | o : Object | | E.cs:11:9:11:11 | [post] access to local variable ret : S [field Field] : Object | semmle.label | [post] access to local variable ret : S [field Field] : Object | +| E.cs:11:9:11:11 | [post] access to local variable ret : S [field Field] : Object | semmle.label | [post] access to local variable ret : S [field Field] : Object | +| E.cs:11:21:11:21 | access to parameter o : Object | semmle.label | access to parameter o : Object | | E.cs:11:21:11:21 | access to parameter o : Object | semmle.label | access to parameter o : Object | | E.cs:12:16:12:18 | access to local variable ret : S [field Field] : Object | semmle.label | access to local variable ret : S [field Field] : Object | +| E.cs:12:16:12:18 | access to local variable ret : S [field Field] : Object | semmle.label | access to local variable ret : S [field Field] : Object | +| E.cs:22:17:22:33 | call to method Source : Object | semmle.label | call to method Source : Object | | E.cs:22:17:22:33 | call to method Source : Object | semmle.label | call to method Source : Object | | E.cs:23:17:23:26 | call to method CreateS : S [field Field] : Object | semmle.label | call to method CreateS : S [field Field] : Object | +| E.cs:23:17:23:26 | call to method CreateS : S [field Field] : Object | semmle.label | call to method CreateS : S [field Field] : Object | +| E.cs:23:25:23:25 | access to local variable o : Object | semmle.label | access to local variable o : Object | | E.cs:23:25:23:25 | access to local variable o : Object | semmle.label | access to local variable o : Object | | E.cs:24:14:24:14 | access to local variable s : S [field Field] : Object | semmle.label | access to local variable s : S [field Field] : Object | +| E.cs:24:14:24:14 | access to local variable s : S [field Field] : Object | semmle.label | access to local variable s : S [field Field] : Object | +| E.cs:24:14:24:20 | access to field Field | semmle.label | access to field Field | | E.cs:24:14:24:20 | access to field Field | semmle.label | access to field Field | | F.cs:6:28:6:29 | o1 : Object | semmle.label | o1 : Object | +| F.cs:6:28:6:29 | o1 : Object | semmle.label | o1 : Object | +| F.cs:6:39:6:40 | o2 : Object | semmle.label | o2 : Object | | F.cs:6:39:6:40 | o2 : Object | semmle.label | o2 : Object | | F.cs:6:46:6:81 | object creation of type F : F [field Field1] : Object | semmle.label | object creation of type F : F [field Field1] : Object | +| F.cs:6:46:6:81 | object creation of type F : F [field Field1] : Object | semmle.label | object creation of type F : F [field Field1] : Object | +| F.cs:6:46:6:81 | object creation of type F : F [field Field2] : Object | semmle.label | object creation of type F : F [field Field2] : Object | | F.cs:6:46:6:81 | object creation of type F : F [field Field2] : Object | semmle.label | object creation of type F : F [field Field2] : Object | | F.cs:6:54:6:81 | { ..., ... } : F [field Field1] : Object | semmle.label | { ..., ... } : F [field Field1] : Object | +| F.cs:6:54:6:81 | { ..., ... } : F [field Field1] : Object | semmle.label | { ..., ... } : F [field Field1] : Object | +| F.cs:6:54:6:81 | { ..., ... } : F [field Field2] : Object | semmle.label | { ..., ... } : F [field Field2] : Object | | F.cs:6:54:6:81 | { ..., ... } : F [field Field2] : Object | semmle.label | { ..., ... } : F [field Field2] : Object | | F.cs:6:65:6:66 | access to parameter o1 : Object | semmle.label | access to parameter o1 : Object | +| F.cs:6:65:6:66 | access to parameter o1 : Object | semmle.label | access to parameter o1 : Object | +| F.cs:6:78:6:79 | access to parameter o2 : Object | semmle.label | access to parameter o2 : Object | | F.cs:6:78:6:79 | access to parameter o2 : Object | semmle.label | access to parameter o2 : Object | | F.cs:10:17:10:33 | call to method Source : Object | semmle.label | call to method Source : Object | +| F.cs:10:17:10:33 | call to method Source : Object | semmle.label | call to method Source : Object | +| F.cs:11:17:11:31 | call to method Create : F [field Field1] : Object | semmle.label | call to method Create : F [field Field1] : Object | | F.cs:11:17:11:31 | call to method Create : F [field Field1] : Object | semmle.label | call to method Create : F [field Field1] : Object | | F.cs:11:24:11:24 | access to local variable o : Object | semmle.label | access to local variable o : Object | +| F.cs:11:24:11:24 | access to local variable o : Object | semmle.label | access to local variable o : Object | +| F.cs:12:14:12:14 | access to local variable f : F [field Field1] : Object | semmle.label | access to local variable f : F [field Field1] : Object | | F.cs:12:14:12:14 | access to local variable f : F [field Field1] : Object | semmle.label | access to local variable f : F [field Field1] : Object | | F.cs:12:14:12:21 | access to field Field1 | semmle.label | access to field Field1 | +| F.cs:12:14:12:21 | access to field Field1 | semmle.label | access to field Field1 | +| F.cs:15:13:15:43 | call to method Create : F [field Field2] : Object | semmle.label | call to method Create : F [field Field2] : Object | | F.cs:15:13:15:43 | call to method Create : F [field Field2] : Object | semmle.label | call to method Create : F [field Field2] : Object | | F.cs:15:26:15:42 | call to method Source : Object | semmle.label | call to method Source : Object | +| F.cs:15:26:15:42 | call to method Source : Object | semmle.label | call to method Source : Object | +| F.cs:17:14:17:14 | access to local variable f : F [field Field2] : Object | semmle.label | access to local variable f : F [field Field2] : Object | | F.cs:17:14:17:14 | access to local variable f : F [field Field2] : Object | semmle.label | access to local variable f : F [field Field2] : Object | | F.cs:17:14:17:21 | access to field Field2 | semmle.label | access to field Field2 | +| F.cs:17:14:17:21 | access to field Field2 | semmle.label | access to field Field2 | +| F.cs:19:21:19:50 | { ..., ... } : F [field Field1] : Object | semmle.label | { ..., ... } : F [field Field1] : Object | | F.cs:19:21:19:50 | { ..., ... } : F [field Field1] : Object | semmle.label | { ..., ... } : F [field Field1] : Object | | F.cs:19:32:19:48 | call to method Source : Object | semmle.label | call to method Source : Object | +| F.cs:19:32:19:48 | call to method Source : Object | semmle.label | call to method Source : Object | +| F.cs:20:14:20:14 | access to local variable f : F [field Field1] : Object | semmle.label | access to local variable f : F [field Field1] : Object | | F.cs:20:14:20:14 | access to local variable f : F [field Field1] : Object | semmle.label | access to local variable f : F [field Field1] : Object | | F.cs:20:14:20:21 | access to field Field1 | semmle.label | access to field Field1 | +| F.cs:20:14:20:21 | access to field Field1 | semmle.label | access to field Field1 | +| F.cs:23:21:23:50 | { ..., ... } : F [field Field2] : Object | semmle.label | { ..., ... } : F [field Field2] : Object | | F.cs:23:21:23:50 | { ..., ... } : F [field Field2] : Object | semmle.label | { ..., ... } : F [field Field2] : Object | | F.cs:23:32:23:48 | call to method Source : Object | semmle.label | call to method Source : Object | +| F.cs:23:32:23:48 | call to method Source : Object | semmle.label | call to method Source : Object | +| F.cs:25:14:25:14 | access to local variable f : F [field Field2] : Object | semmle.label | access to local variable f : F [field Field2] : Object | | F.cs:25:14:25:14 | access to local variable f : F [field Field2] : Object | semmle.label | access to local variable f : F [field Field2] : Object | | F.cs:25:14:25:21 | access to field Field2 | semmle.label | access to field Field2 | +| F.cs:25:14:25:21 | access to field Field2 | semmle.label | access to field Field2 | +| F.cs:30:17:30:33 | call to method Source : Object | semmle.label | call to method Source : Object | | F.cs:30:17:30:33 | call to method Source : Object | semmle.label | call to method Source : Object | | F.cs:32:17:32:40 | { ..., ... } : <>__AnonType0 [property X] : Object | semmle.label | { ..., ... } : <>__AnonType0 [property X] : Object | +| F.cs:32:17:32:40 | { ..., ... } : <>__AnonType0 [property X] : Object | semmle.label | { ..., ... } : <>__AnonType0 [property X] : Object | +| F.cs:32:27:32:27 | access to local variable o : Object | semmle.label | access to local variable o : Object | | F.cs:32:27:32:27 | access to local variable o : Object | semmle.label | access to local variable o : Object | | F.cs:33:14:33:14 | access to local variable a : <>__AnonType0 [property X] : Object | semmle.label | access to local variable a : <>__AnonType0 [property X] : Object | +| F.cs:33:14:33:14 | access to local variable a : <>__AnonType0 [property X] : Object | semmle.label | access to local variable a : <>__AnonType0 [property X] : Object | +| F.cs:33:14:33:16 | access to property X | semmle.label | access to property X | | F.cs:33:14:33:16 | access to property X | semmle.label | access to property X | | G.cs:7:18:7:32 | call to method Source : Elem | semmle.label | call to method Source : Elem | +| G.cs:7:18:7:32 | call to method Source : Elem | semmle.label | call to method Source : Elem | +| G.cs:9:9:9:9 | [post] access to local variable b : Box2 [field Box1, field Elem] : Elem | semmle.label | [post] access to local variable b : Box2 [field Box1, field Elem] : Elem | | G.cs:9:9:9:9 | [post] access to local variable b : Box2 [field Box1, field Elem] : Elem | semmle.label | [post] access to local variable b : Box2 [field Box1, field Elem] : Elem | | G.cs:9:9:9:14 | [post] access to field Box1 : Box1 [field Elem] : Elem | semmle.label | [post] access to field Box1 : Box1 [field Elem] : Elem | +| G.cs:9:9:9:14 | [post] access to field Box1 : Box1 [field Elem] : Elem | semmle.label | [post] access to field Box1 : Box1 [field Elem] : Elem | +| G.cs:9:23:9:23 | access to local variable e : Elem | semmle.label | access to local variable e : Elem | | G.cs:9:23:9:23 | access to local variable e : Elem | semmle.label | access to local variable e : Elem | | G.cs:10:18:10:18 | access to local variable b : Box2 [field Box1, field Elem] : Elem | semmle.label | access to local variable b : Box2 [field Box1, field Elem] : Elem | +| G.cs:10:18:10:18 | access to local variable b : Box2 [field Box1, field Elem] : Elem | semmle.label | access to local variable b : Box2 [field Box1, field Elem] : Elem | +| G.cs:15:18:15:32 | call to method Source : Elem | semmle.label | call to method Source : Elem | | G.cs:15:18:15:32 | call to method Source : Elem | semmle.label | call to method Source : Elem | | G.cs:17:9:17:9 | [post] access to local variable b : Box2 [field Box1, field Elem] : Elem | semmle.label | [post] access to local variable b : Box2 [field Box1, field Elem] : Elem | +| G.cs:17:9:17:9 | [post] access to local variable b : Box2 [field Box1, field Elem] : Elem | semmle.label | [post] access to local variable b : Box2 [field Box1, field Elem] : Elem | +| G.cs:17:9:17:14 | [post] access to field Box1 : Box1 [field Elem] : Elem | semmle.label | [post] access to field Box1 : Box1 [field Elem] : Elem | | G.cs:17:9:17:14 | [post] access to field Box1 : Box1 [field Elem] : Elem | semmle.label | [post] access to field Box1 : Box1 [field Elem] : Elem | | G.cs:17:24:17:24 | access to local variable e : Elem | semmle.label | access to local variable e : Elem | +| G.cs:17:24:17:24 | access to local variable e : Elem | semmle.label | access to local variable e : Elem | +| G.cs:18:18:18:18 | access to local variable b : Box2 [field Box1, field Elem] : Elem | semmle.label | access to local variable b : Box2 [field Box1, field Elem] : Elem | | G.cs:18:18:18:18 | access to local variable b : Box2 [field Box1, field Elem] : Elem | semmle.label | access to local variable b : Box2 [field Box1, field Elem] : Elem | | G.cs:23:18:23:32 | call to method Source : Elem | semmle.label | call to method Source : Elem | +| G.cs:23:18:23:32 | call to method Source : Elem | semmle.label | call to method Source : Elem | +| G.cs:25:9:25:9 | [post] access to local variable b : Box2 [field Box1, field Elem] : Elem | semmle.label | [post] access to local variable b : Box2 [field Box1, field Elem] : Elem | | G.cs:25:9:25:9 | [post] access to local variable b : Box2 [field Box1, field Elem] : Elem | semmle.label | [post] access to local variable b : Box2 [field Box1, field Elem] : Elem | | G.cs:25:9:25:19 | [post] call to method GetBox1 : Box1 [field Elem] : Elem | semmle.label | [post] call to method GetBox1 : Box1 [field Elem] : Elem | +| G.cs:25:9:25:19 | [post] call to method GetBox1 : Box1 [field Elem] : Elem | semmle.label | [post] call to method GetBox1 : Box1 [field Elem] : Elem | +| G.cs:25:28:25:28 | access to local variable e : Elem | semmle.label | access to local variable e : Elem | | G.cs:25:28:25:28 | access to local variable e : Elem | semmle.label | access to local variable e : Elem | | G.cs:26:18:26:18 | access to local variable b : Box2 [field Box1, field Elem] : Elem | semmle.label | access to local variable b : Box2 [field Box1, field Elem] : Elem | +| G.cs:26:18:26:18 | access to local variable b : Box2 [field Box1, field Elem] : Elem | semmle.label | access to local variable b : Box2 [field Box1, field Elem] : Elem | +| G.cs:31:18:31:32 | call to method Source : Elem | semmle.label | call to method Source : Elem | | G.cs:31:18:31:32 | call to method Source : Elem | semmle.label | call to method Source : Elem | | G.cs:33:9:33:9 | [post] access to local variable b : Box2 [field Box1, field Elem] : Elem | semmle.label | [post] access to local variable b : Box2 [field Box1, field Elem] : Elem | +| G.cs:33:9:33:9 | [post] access to local variable b : Box2 [field Box1, field Elem] : Elem | semmle.label | [post] access to local variable b : Box2 [field Box1, field Elem] : Elem | +| G.cs:33:9:33:19 | [post] call to method GetBox1 : Box1 [field Elem] : Elem | semmle.label | [post] call to method GetBox1 : Box1 [field Elem] : Elem | | G.cs:33:9:33:19 | [post] call to method GetBox1 : Box1 [field Elem] : Elem | semmle.label | [post] call to method GetBox1 : Box1 [field Elem] : Elem | | G.cs:33:29:33:29 | access to local variable e : Elem | semmle.label | access to local variable e : Elem | +| G.cs:33:29:33:29 | access to local variable e : Elem | semmle.label | access to local variable e : Elem | +| G.cs:34:18:34:18 | access to local variable b : Box2 [field Box1, field Elem] : Elem | semmle.label | access to local variable b : Box2 [field Box1, field Elem] : Elem | | G.cs:34:18:34:18 | access to local variable b : Box2 [field Box1, field Elem] : Elem | semmle.label | access to local variable b : Box2 [field Box1, field Elem] : Elem | | G.cs:37:38:37:39 | b2 : Box2 [field Box1, field Elem] : Elem | semmle.label | b2 : Box2 [field Box1, field Elem] : Elem | +| G.cs:37:38:37:39 | b2 : Box2 [field Box1, field Elem] : Elem | semmle.label | b2 : Box2 [field Box1, field Elem] : Elem | +| G.cs:39:14:39:15 | access to parameter b2 : Box2 [field Box1, field Elem] : Elem | semmle.label | access to parameter b2 : Box2 [field Box1, field Elem] : Elem | | G.cs:39:14:39:15 | access to parameter b2 : Box2 [field Box1, field Elem] : Elem | semmle.label | access to parameter b2 : Box2 [field Box1, field Elem] : Elem | | G.cs:39:14:39:25 | call to method GetBox1 : Box1 [field Elem] : Elem | semmle.label | call to method GetBox1 : Box1 [field Elem] : Elem | +| G.cs:39:14:39:25 | call to method GetBox1 : Box1 [field Elem] : Elem | semmle.label | call to method GetBox1 : Box1 [field Elem] : Elem | +| G.cs:39:14:39:35 | call to method GetElem | semmle.label | call to method GetElem | | G.cs:39:14:39:35 | call to method GetElem | semmle.label | call to method GetElem | | G.cs:44:18:44:32 | call to method Source : Elem | semmle.label | call to method Source : Elem | +| G.cs:44:18:44:32 | call to method Source : Elem | semmle.label | call to method Source : Elem | +| G.cs:46:9:46:16 | [post] access to field boxfield : Box2 [field Box1, field Elem] : Elem | semmle.label | [post] access to field boxfield : Box2 [field Box1, field Elem] : Elem | | G.cs:46:9:46:16 | [post] access to field boxfield : Box2 [field Box1, field Elem] : Elem | semmle.label | [post] access to field boxfield : Box2 [field Box1, field Elem] : Elem | | G.cs:46:9:46:16 | [post] this access : G [field boxfield, field Box1, field Elem] : Elem | semmle.label | [post] this access : G [field boxfield, field Box1, field Elem] : Elem | +| G.cs:46:9:46:16 | [post] this access : G [field boxfield, field Box1, field Elem] : Elem | semmle.label | [post] this access : G [field boxfield, field Box1, field Elem] : Elem | +| G.cs:46:9:46:21 | [post] access to field Box1 : Box1 [field Elem] : Elem | semmle.label | [post] access to field Box1 : Box1 [field Elem] : Elem | | G.cs:46:9:46:21 | [post] access to field Box1 : Box1 [field Elem] : Elem | semmle.label | [post] access to field Box1 : Box1 [field Elem] : Elem | | G.cs:46:30:46:30 | access to local variable e : Elem | semmle.label | access to local variable e : Elem | +| G.cs:46:30:46:30 | access to local variable e : Elem | semmle.label | access to local variable e : Elem | +| G.cs:47:9:47:13 | this access : G [field boxfield, field Box1, field Elem] : Elem | semmle.label | this access : G [field boxfield, field Box1, field Elem] : Elem | | G.cs:47:9:47:13 | this access : G [field boxfield, field Box1, field Elem] : Elem | semmle.label | this access : G [field boxfield, field Box1, field Elem] : Elem | | G.cs:50:18:50:20 | this : G [field boxfield, field Box1, field Elem] : Elem | semmle.label | this : G [field boxfield, field Box1, field Elem] : Elem | +| G.cs:50:18:50:20 | this : G [field boxfield, field Box1, field Elem] : Elem | semmle.label | this : G [field boxfield, field Box1, field Elem] : Elem | +| G.cs:52:14:52:21 | access to field boxfield : Box2 [field Box1, field Elem] : Elem | semmle.label | access to field boxfield : Box2 [field Box1, field Elem] : Elem | | G.cs:52:14:52:21 | access to field boxfield : Box2 [field Box1, field Elem] : Elem | semmle.label | access to field boxfield : Box2 [field Box1, field Elem] : Elem | | G.cs:52:14:52:21 | this access : G [field boxfield, field Box1, field Elem] : Elem | semmle.label | this access : G [field boxfield, field Box1, field Elem] : Elem | +| G.cs:52:14:52:21 | this access : G [field boxfield, field Box1, field Elem] : Elem | semmle.label | this access : G [field boxfield, field Box1, field Elem] : Elem | +| G.cs:52:14:52:26 | access to field Box1 : Box1 [field Elem] : Elem | semmle.label | access to field Box1 : Box1 [field Elem] : Elem | | G.cs:52:14:52:26 | access to field Box1 : Box1 [field Elem] : Elem | semmle.label | access to field Box1 : Box1 [field Elem] : Elem | | G.cs:52:14:52:31 | access to field Elem | semmle.label | access to field Elem | +| G.cs:52:14:52:31 | access to field Elem | semmle.label | access to field Elem | +| G.cs:63:21:63:27 | this : Box1 [field Elem] : Elem | semmle.label | this : Box1 [field Elem] : Elem | | G.cs:63:21:63:27 | this : Box1 [field Elem] : Elem | semmle.label | this : Box1 [field Elem] : Elem | | G.cs:63:34:63:37 | access to field Elem : Elem | semmle.label | access to field Elem : Elem | +| G.cs:63:34:63:37 | access to field Elem : Elem | semmle.label | access to field Elem : Elem | +| G.cs:63:34:63:37 | this access : Box1 [field Elem] : Elem | semmle.label | this access : Box1 [field Elem] : Elem | | G.cs:63:34:63:37 | this access : Box1 [field Elem] : Elem | semmle.label | this access : Box1 [field Elem] : Elem | | G.cs:64:34:64:34 | e : Elem | semmle.label | e : Elem | +| G.cs:64:34:64:34 | e : Elem | semmle.label | e : Elem | +| G.cs:64:39:64:42 | [post] this access : Box1 [field Elem] : Elem | semmle.label | [post] this access : Box1 [field Elem] : Elem | | G.cs:64:39:64:42 | [post] this access : Box1 [field Elem] : Elem | semmle.label | [post] this access : Box1 [field Elem] : Elem | | G.cs:64:46:64:46 | access to parameter e : Elem | semmle.label | access to parameter e : Elem | +| G.cs:64:46:64:46 | access to parameter e : Elem | semmle.label | access to parameter e : Elem | +| G.cs:71:21:71:27 | this : Box2 [field Box1, field Elem] : Elem | semmle.label | this : Box2 [field Box1, field Elem] : Elem | | G.cs:71:21:71:27 | this : Box2 [field Box1, field Elem] : Elem | semmle.label | this : Box2 [field Box1, field Elem] : Elem | | G.cs:71:34:71:37 | access to field Box1 : Box1 [field Elem] : Elem | semmle.label | access to field Box1 : Box1 [field Elem] : Elem | +| G.cs:71:34:71:37 | access to field Box1 : Box1 [field Elem] : Elem | semmle.label | access to field Box1 : Box1 [field Elem] : Elem | +| G.cs:71:34:71:37 | this access : Box2 [field Box1, field Elem] : Elem | semmle.label | this access : Box2 [field Box1, field Elem] : Elem | | G.cs:71:34:71:37 | this access : Box2 [field Box1, field Elem] : Elem | semmle.label | this access : Box2 [field Box1, field Elem] : Elem | | H.cs:13:15:13:15 | a : A [field FieldA] : Object | semmle.label | a : A [field FieldA] : Object | +| H.cs:13:15:13:15 | a : A [field FieldA] : Object | semmle.label | a : A [field FieldA] : Object | +| H.cs:16:9:16:11 | [post] access to local variable ret : A [field FieldA] : Object | semmle.label | [post] access to local variable ret : A [field FieldA] : Object | | H.cs:16:9:16:11 | [post] access to local variable ret : A [field FieldA] : Object | semmle.label | [post] access to local variable ret : A [field FieldA] : Object | | H.cs:16:22:16:22 | access to parameter a : A [field FieldA] : Object | semmle.label | access to parameter a : A [field FieldA] : Object | +| H.cs:16:22:16:22 | access to parameter a : A [field FieldA] : Object | semmle.label | access to parameter a : A [field FieldA] : Object | +| H.cs:16:22:16:29 | access to field FieldA : Object | semmle.label | access to field FieldA : Object | | H.cs:16:22:16:29 | access to field FieldA : Object | semmle.label | access to field FieldA : Object | | H.cs:17:16:17:18 | access to local variable ret : A [field FieldA] : Object | semmle.label | access to local variable ret : A [field FieldA] : Object | +| H.cs:17:16:17:18 | access to local variable ret : A [field FieldA] : Object | semmle.label | access to local variable ret : A [field FieldA] : Object | +| H.cs:23:9:23:9 | [post] access to local variable a : A [field FieldA] : Object | semmle.label | [post] access to local variable a : A [field FieldA] : Object | | H.cs:23:9:23:9 | [post] access to local variable a : A [field FieldA] : Object | semmle.label | [post] access to local variable a : A [field FieldA] : Object | | H.cs:23:20:23:36 | call to method Source : Object | semmle.label | call to method Source : Object | +| H.cs:23:20:23:36 | call to method Source : Object | semmle.label | call to method Source : Object | +| H.cs:24:21:24:28 | call to method Clone : A [field FieldA] : Object | semmle.label | call to method Clone : A [field FieldA] : Object | | H.cs:24:21:24:28 | call to method Clone : A [field FieldA] : Object | semmle.label | call to method Clone : A [field FieldA] : Object | | H.cs:24:27:24:27 | access to local variable a : A [field FieldA] : Object | semmle.label | access to local variable a : A [field FieldA] : Object | +| H.cs:24:27:24:27 | access to local variable a : A [field FieldA] : Object | semmle.label | access to local variable a : A [field FieldA] : Object | +| H.cs:25:14:25:18 | access to local variable clone : A [field FieldA] : Object | semmle.label | access to local variable clone : A [field FieldA] : Object | | H.cs:25:14:25:18 | access to local variable clone : A [field FieldA] : Object | semmle.label | access to local variable clone : A [field FieldA] : Object | | H.cs:25:14:25:25 | access to field FieldA | semmle.label | access to field FieldA | +| H.cs:25:14:25:25 | access to field FieldA | semmle.label | access to field FieldA | +| H.cs:33:19:33:19 | a : A [field FieldA] : A | semmle.label | a : A [field FieldA] : A | | H.cs:33:19:33:19 | a : A [field FieldA] : A | semmle.label | a : A [field FieldA] : A | | H.cs:33:19:33:19 | a : A [field FieldA] : Object | semmle.label | a : A [field FieldA] : Object | +| H.cs:33:19:33:19 | a : A [field FieldA] : Object | semmle.label | a : A [field FieldA] : Object | +| H.cs:36:9:36:9 | [post] access to local variable b : B [field FieldB] : A | semmle.label | [post] access to local variable b : B [field FieldB] : A | | H.cs:36:9:36:9 | [post] access to local variable b : B [field FieldB] : A | semmle.label | [post] access to local variable b : B [field FieldB] : A | | H.cs:36:9:36:9 | [post] access to local variable b : B [field FieldB] : Object | semmle.label | [post] access to local variable b : B [field FieldB] : Object | +| H.cs:36:9:36:9 | [post] access to local variable b : B [field FieldB] : Object | semmle.label | [post] access to local variable b : B [field FieldB] : Object | +| H.cs:36:20:36:20 | access to parameter a : A [field FieldA] : A | semmle.label | access to parameter a : A [field FieldA] : A | | H.cs:36:20:36:20 | access to parameter a : A [field FieldA] : A | semmle.label | access to parameter a : A [field FieldA] : A | | H.cs:36:20:36:20 | access to parameter a : A [field FieldA] : Object | semmle.label | access to parameter a : A [field FieldA] : Object | +| H.cs:36:20:36:20 | access to parameter a : A [field FieldA] : Object | semmle.label | access to parameter a : A [field FieldA] : Object | +| H.cs:36:20:36:27 | access to field FieldA : A | semmle.label | access to field FieldA : A | | H.cs:36:20:36:27 | access to field FieldA : A | semmle.label | access to field FieldA : A | | H.cs:36:20:36:27 | access to field FieldA : Object | semmle.label | access to field FieldA : Object | +| H.cs:36:20:36:27 | access to field FieldA : Object | semmle.label | access to field FieldA : Object | +| H.cs:37:16:37:16 | access to local variable b : B [field FieldB] : A | semmle.label | access to local variable b : B [field FieldB] : A | | H.cs:37:16:37:16 | access to local variable b : B [field FieldB] : A | semmle.label | access to local variable b : B [field FieldB] : A | | H.cs:37:16:37:16 | access to local variable b : B [field FieldB] : Object | semmle.label | access to local variable b : B [field FieldB] : Object | +| H.cs:37:16:37:16 | access to local variable b : B [field FieldB] : Object | semmle.label | access to local variable b : B [field FieldB] : Object | +| H.cs:43:9:43:9 | [post] access to local variable a : A [field FieldA] : Object | semmle.label | [post] access to local variable a : A [field FieldA] : Object | | H.cs:43:9:43:9 | [post] access to local variable a : A [field FieldA] : Object | semmle.label | [post] access to local variable a : A [field FieldA] : Object | | H.cs:43:20:43:36 | call to method Source : Object | semmle.label | call to method Source : Object | +| H.cs:43:20:43:36 | call to method Source : Object | semmle.label | call to method Source : Object | +| H.cs:44:17:44:28 | call to method Transform : B [field FieldB] : Object | semmle.label | call to method Transform : B [field FieldB] : Object | | H.cs:44:17:44:28 | call to method Transform : B [field FieldB] : Object | semmle.label | call to method Transform : B [field FieldB] : Object | | H.cs:44:27:44:27 | access to local variable a : A [field FieldA] : Object | semmle.label | access to local variable a : A [field FieldA] : Object | +| H.cs:44:27:44:27 | access to local variable a : A [field FieldA] : Object | semmle.label | access to local variable a : A [field FieldA] : Object | +| H.cs:45:14:45:14 | access to local variable b : B [field FieldB] : Object | semmle.label | access to local variable b : B [field FieldB] : Object | | H.cs:45:14:45:14 | access to local variable b : B [field FieldB] : Object | semmle.label | access to local variable b : B [field FieldB] : Object | | H.cs:45:14:45:21 | access to field FieldB | semmle.label | access to field FieldB | +| H.cs:45:14:45:21 | access to field FieldB | semmle.label | access to field FieldB | +| H.cs:53:25:53:25 | a : A [field FieldA] : Object | semmle.label | a : A [field FieldA] : Object | | H.cs:53:25:53:25 | a : A [field FieldA] : Object | semmle.label | a : A [field FieldA] : Object | | H.cs:55:9:55:10 | [post] access to parameter b1 : B [field FieldB] : Object | semmle.label | [post] access to parameter b1 : B [field FieldB] : Object | +| H.cs:55:9:55:10 | [post] access to parameter b1 : B [field FieldB] : Object | semmle.label | [post] access to parameter b1 : B [field FieldB] : Object | +| H.cs:55:21:55:21 | access to parameter a : A [field FieldA] : Object | semmle.label | access to parameter a : A [field FieldA] : Object | | H.cs:55:21:55:21 | access to parameter a : A [field FieldA] : Object | semmle.label | access to parameter a : A [field FieldA] : Object | | H.cs:55:21:55:28 | access to field FieldA : Object | semmle.label | access to field FieldA : Object | +| H.cs:55:21:55:28 | access to field FieldA : Object | semmle.label | access to field FieldA : Object | +| H.cs:63:9:63:9 | [post] access to local variable a : A [field FieldA] : Object | semmle.label | [post] access to local variable a : A [field FieldA] : Object | | H.cs:63:9:63:9 | [post] access to local variable a : A [field FieldA] : Object | semmle.label | [post] access to local variable a : A [field FieldA] : Object | | H.cs:63:20:63:36 | call to method Source : Object | semmle.label | call to method Source : Object | +| H.cs:63:20:63:36 | call to method Source : Object | semmle.label | call to method Source : Object | +| H.cs:64:22:64:22 | access to local variable a : A [field FieldA] : Object | semmle.label | access to local variable a : A [field FieldA] : Object | | H.cs:64:22:64:22 | access to local variable a : A [field FieldA] : Object | semmle.label | access to local variable a : A [field FieldA] : Object | | H.cs:64:25:64:26 | [post] access to local variable b1 : B [field FieldB] : Object | semmle.label | [post] access to local variable b1 : B [field FieldB] : Object | +| H.cs:64:25:64:26 | [post] access to local variable b1 : B [field FieldB] : Object | semmle.label | [post] access to local variable b1 : B [field FieldB] : Object | +| H.cs:65:14:65:15 | access to local variable b1 : B [field FieldB] : Object | semmle.label | access to local variable b1 : B [field FieldB] : Object | | H.cs:65:14:65:15 | access to local variable b1 : B [field FieldB] : Object | semmle.label | access to local variable b1 : B [field FieldB] : Object | | H.cs:65:14:65:22 | access to field FieldB | semmle.label | access to field FieldB | +| H.cs:65:14:65:22 | access to field FieldB | semmle.label | access to field FieldB | +| H.cs:77:30:77:30 | o : Object | semmle.label | o : Object | | H.cs:77:30:77:30 | o : Object | semmle.label | o : Object | | H.cs:79:9:79:9 | [post] access to parameter a : A [field FieldA] : Object | semmle.label | [post] access to parameter a : A [field FieldA] : Object | +| H.cs:79:9:79:9 | [post] access to parameter a : A [field FieldA] : Object | semmle.label | [post] access to parameter a : A [field FieldA] : Object | +| H.cs:79:20:79:20 | access to parameter o : Object | semmle.label | access to parameter o : Object | | H.cs:79:20:79:20 | access to parameter o : Object | semmle.label | access to parameter o : Object | | H.cs:80:22:80:22 | access to parameter a : A [field FieldA] : Object | semmle.label | access to parameter a : A [field FieldA] : Object | +| H.cs:80:22:80:22 | access to parameter a : A [field FieldA] : Object | semmle.label | access to parameter a : A [field FieldA] : Object | +| H.cs:80:25:80:26 | [post] access to parameter b1 : B [field FieldB] : Object | semmle.label | [post] access to parameter b1 : B [field FieldB] : Object | | H.cs:80:25:80:26 | [post] access to parameter b1 : B [field FieldB] : Object | semmle.label | [post] access to parameter b1 : B [field FieldB] : Object | | H.cs:88:17:88:17 | [post] access to local variable a : A [field FieldA] : Object | semmle.label | [post] access to local variable a : A [field FieldA] : Object | +| H.cs:88:17:88:17 | [post] access to local variable a : A [field FieldA] : Object | semmle.label | [post] access to local variable a : A [field FieldA] : Object | +| H.cs:88:20:88:36 | call to method Source : Object | semmle.label | call to method Source : Object | | H.cs:88:20:88:36 | call to method Source : Object | semmle.label | call to method Source : Object | | H.cs:88:39:88:40 | [post] access to local variable b1 : B [field FieldB] : Object | semmle.label | [post] access to local variable b1 : B [field FieldB] : Object | +| H.cs:88:39:88:40 | [post] access to local variable b1 : B [field FieldB] : Object | semmle.label | [post] access to local variable b1 : B [field FieldB] : Object | +| H.cs:89:14:89:14 | access to local variable a : A [field FieldA] : Object | semmle.label | access to local variable a : A [field FieldA] : Object | | H.cs:89:14:89:14 | access to local variable a : A [field FieldA] : Object | semmle.label | access to local variable a : A [field FieldA] : Object | | H.cs:89:14:89:21 | access to field FieldA | semmle.label | access to field FieldA | +| H.cs:89:14:89:21 | access to field FieldA | semmle.label | access to field FieldA | +| H.cs:90:14:90:15 | access to local variable b1 : B [field FieldB] : Object | semmle.label | access to local variable b1 : B [field FieldB] : Object | | H.cs:90:14:90:15 | access to local variable b1 : B [field FieldB] : Object | semmle.label | access to local variable b1 : B [field FieldB] : Object | | H.cs:90:14:90:22 | access to field FieldB | semmle.label | access to field FieldB | +| H.cs:90:14:90:22 | access to field FieldB | semmle.label | access to field FieldB | +| H.cs:102:23:102:23 | a : A [field FieldA] : Object | semmle.label | a : A [field FieldA] : Object | | H.cs:102:23:102:23 | a : A [field FieldA] : Object | semmle.label | a : A [field FieldA] : Object | | H.cs:105:9:105:12 | [post] access to local variable temp : B [field FieldB, field FieldA] : Object | semmle.label | [post] access to local variable temp : B [field FieldB, field FieldA] : Object | +| H.cs:105:9:105:12 | [post] access to local variable temp : B [field FieldB, field FieldA] : Object | semmle.label | [post] access to local variable temp : B [field FieldB, field FieldA] : Object | +| H.cs:105:23:105:23 | access to parameter a : A [field FieldA] : Object | semmle.label | access to parameter a : A [field FieldA] : Object | | H.cs:105:23:105:23 | access to parameter a : A [field FieldA] : Object | semmle.label | access to parameter a : A [field FieldA] : Object | | H.cs:106:16:106:40 | call to method Transform : B [field FieldB] : Object | semmle.label | call to method Transform : B [field FieldB] : Object | +| H.cs:106:16:106:40 | call to method Transform : B [field FieldB] : Object | semmle.label | call to method Transform : B [field FieldB] : Object | +| H.cs:106:26:106:39 | (...) ... : A [field FieldA] : Object | semmle.label | (...) ... : A [field FieldA] : Object | | H.cs:106:26:106:39 | (...) ... : A [field FieldA] : Object | semmle.label | (...) ... : A [field FieldA] : Object | | H.cs:106:29:106:32 | access to local variable temp : B [field FieldB, field FieldA] : Object | semmle.label | access to local variable temp : B [field FieldB, field FieldA] : Object | +| H.cs:106:29:106:32 | access to local variable temp : B [field FieldB, field FieldA] : Object | semmle.label | access to local variable temp : B [field FieldB, field FieldA] : Object | +| H.cs:106:29:106:39 | access to field FieldB : A [field FieldA] : Object | semmle.label | access to field FieldB : A [field FieldA] : Object | | H.cs:106:29:106:39 | access to field FieldB : A [field FieldA] : Object | semmle.label | access to field FieldB : A [field FieldA] : Object | | H.cs:112:9:112:9 | [post] access to local variable a : A [field FieldA] : Object | semmle.label | [post] access to local variable a : A [field FieldA] : Object | +| H.cs:112:9:112:9 | [post] access to local variable a : A [field FieldA] : Object | semmle.label | [post] access to local variable a : A [field FieldA] : Object | +| H.cs:112:20:112:36 | call to method Source : Object | semmle.label | call to method Source : Object | | H.cs:112:20:112:36 | call to method Source : Object | semmle.label | call to method Source : Object | | H.cs:113:17:113:32 | call to method TransformWrap : B [field FieldB] : Object | semmle.label | call to method TransformWrap : B [field FieldB] : Object | +| H.cs:113:17:113:32 | call to method TransformWrap : B [field FieldB] : Object | semmle.label | call to method TransformWrap : B [field FieldB] : Object | +| H.cs:113:31:113:31 | access to local variable a : A [field FieldA] : Object | semmle.label | access to local variable a : A [field FieldA] : Object | | H.cs:113:31:113:31 | access to local variable a : A [field FieldA] : Object | semmle.label | access to local variable a : A [field FieldA] : Object | | H.cs:114:14:114:14 | access to local variable b : B [field FieldB] : Object | semmle.label | access to local variable b : B [field FieldB] : Object | +| H.cs:114:14:114:14 | access to local variable b : B [field FieldB] : Object | semmle.label | access to local variable b : B [field FieldB] : Object | +| H.cs:114:14:114:21 | access to field FieldB | semmle.label | access to field FieldB | | H.cs:114:14:114:21 | access to field FieldB | semmle.label | access to field FieldB | | H.cs:122:18:122:18 | a : A [field FieldA] : Object | semmle.label | a : A [field FieldA] : Object | +| H.cs:122:18:122:18 | a : A [field FieldA] : Object | semmle.label | a : A [field FieldA] : Object | +| H.cs:124:16:124:27 | call to method Transform : B [field FieldB] : Object | semmle.label | call to method Transform : B [field FieldB] : Object | | H.cs:124:16:124:27 | call to method Transform : B [field FieldB] : Object | semmle.label | call to method Transform : B [field FieldB] : Object | | H.cs:124:16:124:34 | access to field FieldB : Object | semmle.label | access to field FieldB : Object | +| H.cs:124:16:124:34 | access to field FieldB : Object | semmle.label | access to field FieldB : Object | +| H.cs:124:26:124:26 | access to parameter a : A [field FieldA] : Object | semmle.label | access to parameter a : A [field FieldA] : Object | | H.cs:124:26:124:26 | access to parameter a : A [field FieldA] : Object | semmle.label | access to parameter a : A [field FieldA] : Object | | H.cs:130:9:130:9 | [post] access to local variable a : A [field FieldA] : Object | semmle.label | [post] access to local variable a : A [field FieldA] : Object | +| H.cs:130:9:130:9 | [post] access to local variable a : A [field FieldA] : Object | semmle.label | [post] access to local variable a : A [field FieldA] : Object | +| H.cs:130:20:130:36 | call to method Source : Object | semmle.label | call to method Source : Object | | H.cs:130:20:130:36 | call to method Source : Object | semmle.label | call to method Source : Object | | H.cs:131:14:131:19 | call to method Get | semmle.label | call to method Get | +| H.cs:131:14:131:19 | call to method Get | semmle.label | call to method Get | +| H.cs:131:18:131:18 | access to local variable a : A [field FieldA] : Object | semmle.label | access to local variable a : A [field FieldA] : Object | | H.cs:131:18:131:18 | access to local variable a : A [field FieldA] : Object | semmle.label | access to local variable a : A [field FieldA] : Object | | H.cs:138:27:138:27 | o : A | semmle.label | o : A | +| H.cs:138:27:138:27 | o : A | semmle.label | o : A | +| H.cs:141:9:141:9 | [post] access to local variable a : A [field FieldA] : A | semmle.label | [post] access to local variable a : A [field FieldA] : A | | H.cs:141:9:141:9 | [post] access to local variable a : A [field FieldA] : A | semmle.label | [post] access to local variable a : A [field FieldA] : A | | H.cs:141:20:141:25 | ... as ... : A | semmle.label | ... as ... : A | +| H.cs:141:20:141:25 | ... as ... : A | semmle.label | ... as ... : A | +| H.cs:142:16:142:27 | call to method Transform : B [field FieldB] : A | semmle.label | call to method Transform : B [field FieldB] : A | | H.cs:142:16:142:27 | call to method Transform : B [field FieldB] : A | semmle.label | call to method Transform : B [field FieldB] : A | | H.cs:142:16:142:34 | access to field FieldB : A | semmle.label | access to field FieldB : A | +| H.cs:142:16:142:34 | access to field FieldB : A | semmle.label | access to field FieldB : A | +| H.cs:142:26:142:26 | access to local variable a : A [field FieldA] : A | semmle.label | access to local variable a : A [field FieldA] : A | | H.cs:142:26:142:26 | access to local variable a : A [field FieldA] : A | semmle.label | access to local variable a : A [field FieldA] : A | | H.cs:147:17:147:39 | call to method Through : A | semmle.label | call to method Through : A | +| H.cs:147:17:147:39 | call to method Through : A | semmle.label | call to method Through : A | +| H.cs:147:25:147:38 | call to method Source : A | semmle.label | call to method Source : A | | H.cs:147:25:147:38 | call to method Source : A | semmle.label | call to method Source : A | | H.cs:148:14:148:14 | access to local variable a | semmle.label | access to local variable a | +| H.cs:148:14:148:14 | access to local variable a | semmle.label | access to local variable a | +| H.cs:153:32:153:32 | o : Object | semmle.label | o : Object | | H.cs:153:32:153:32 | o : Object | semmle.label | o : Object | | H.cs:155:17:155:30 | call to method Source : B | semmle.label | call to method Source : B | +| H.cs:155:17:155:30 | call to method Source : B | semmle.label | call to method Source : B | +| H.cs:156:9:156:9 | [post] access to local variable b : B [field FieldB] : Object | semmle.label | [post] access to local variable b : B [field FieldB] : Object | | H.cs:156:9:156:9 | [post] access to local variable b : B [field FieldB] : Object | semmle.label | [post] access to local variable b : B [field FieldB] : Object | | H.cs:156:9:156:9 | access to local variable b : B | semmle.label | access to local variable b : B | +| H.cs:156:9:156:9 | access to local variable b : B | semmle.label | access to local variable b : B | +| H.cs:156:20:156:20 | access to parameter o : Object | semmle.label | access to parameter o : Object | | H.cs:156:20:156:20 | access to parameter o : Object | semmle.label | access to parameter o : Object | | H.cs:157:9:157:9 | [post] access to parameter a : A [field FieldA, field FieldB] : Object | semmle.label | [post] access to parameter a : A [field FieldA, field FieldB] : Object | +| H.cs:157:9:157:9 | [post] access to parameter a : A [field FieldA, field FieldB] : Object | semmle.label | [post] access to parameter a : A [field FieldA, field FieldB] : Object | +| H.cs:157:9:157:9 | [post] access to parameter a : A [field FieldA] : B | semmle.label | [post] access to parameter a : A [field FieldA] : B | | H.cs:157:9:157:9 | [post] access to parameter a : A [field FieldA] : B | semmle.label | [post] access to parameter a : A [field FieldA] : B | | H.cs:157:20:157:20 | access to local variable b : B | semmle.label | access to local variable b : B | +| H.cs:157:20:157:20 | access to local variable b : B | semmle.label | access to local variable b : B | +| H.cs:157:20:157:20 | access to local variable b : B [field FieldB] : Object | semmle.label | access to local variable b : B [field FieldB] : Object | | H.cs:157:20:157:20 | access to local variable b : B [field FieldB] : Object | semmle.label | access to local variable b : B [field FieldB] : Object | | H.cs:163:17:163:35 | call to method Source : Object | semmle.label | call to method Source : Object | +| H.cs:163:17:163:35 | call to method Source : Object | semmle.label | call to method Source : Object | +| H.cs:164:19:164:19 | [post] access to local variable a : A [field FieldA, field FieldB] : Object | semmle.label | [post] access to local variable a : A [field FieldA, field FieldB] : Object | | H.cs:164:19:164:19 | [post] access to local variable a : A [field FieldA, field FieldB] : Object | semmle.label | [post] access to local variable a : A [field FieldA, field FieldB] : Object | | H.cs:164:19:164:19 | [post] access to local variable a : A [field FieldA] : B | semmle.label | [post] access to local variable a : A [field FieldA] : B | +| H.cs:164:19:164:19 | [post] access to local variable a : A [field FieldA] : B | semmle.label | [post] access to local variable a : A [field FieldA] : B | +| H.cs:164:22:164:22 | access to local variable o : Object | semmle.label | access to local variable o : Object | | H.cs:164:22:164:22 | access to local variable o : Object | semmle.label | access to local variable o : Object | | H.cs:165:17:165:27 | (...) ... : B | semmle.label | (...) ... : B | +| H.cs:165:17:165:27 | (...) ... : B | semmle.label | (...) ... : B | +| H.cs:165:17:165:27 | (...) ... : B [field FieldB] : Object | semmle.label | (...) ... : B [field FieldB] : Object | | H.cs:165:17:165:27 | (...) ... : B [field FieldB] : Object | semmle.label | (...) ... : B [field FieldB] : Object | | H.cs:165:20:165:20 | access to local variable a : A [field FieldA, field FieldB] : Object | semmle.label | access to local variable a : A [field FieldA, field FieldB] : Object | +| H.cs:165:20:165:20 | access to local variable a : A [field FieldA, field FieldB] : Object | semmle.label | access to local variable a : A [field FieldA, field FieldB] : Object | +| H.cs:165:20:165:20 | access to local variable a : A [field FieldA] : B | semmle.label | access to local variable a : A [field FieldA] : B | | H.cs:165:20:165:20 | access to local variable a : A [field FieldA] : B | semmle.label | access to local variable a : A [field FieldA] : B | | H.cs:165:20:165:27 | access to field FieldA : B | semmle.label | access to field FieldA : B | +| H.cs:165:20:165:27 | access to field FieldA : B | semmle.label | access to field FieldA : B | +| H.cs:165:20:165:27 | access to field FieldA : B [field FieldB] : Object | semmle.label | access to field FieldA : B [field FieldB] : Object | | H.cs:165:20:165:27 | access to field FieldA : B [field FieldB] : Object | semmle.label | access to field FieldA : B [field FieldB] : Object | | H.cs:166:14:166:14 | access to local variable b | semmle.label | access to local variable b | +| H.cs:166:14:166:14 | access to local variable b | semmle.label | access to local variable b | +| H.cs:167:14:167:14 | access to local variable b : B [field FieldB] : Object | semmle.label | access to local variable b : B [field FieldB] : Object | | H.cs:167:14:167:14 | access to local variable b : B [field FieldB] : Object | semmle.label | access to local variable b : B [field FieldB] : Object | | H.cs:167:14:167:21 | access to field FieldB | semmle.label | access to field FieldB | +| H.cs:167:14:167:21 | access to field FieldB | semmle.label | access to field FieldB | +| I.cs:7:9:7:14 | [post] this access : I [field Field1] : Object | semmle.label | [post] this access : I [field Field1] : Object | | I.cs:7:9:7:14 | [post] this access : I [field Field1] : Object | semmle.label | [post] this access : I [field Field1] : Object | | I.cs:7:18:7:34 | call to method Source : Object | semmle.label | call to method Source : Object | +| I.cs:7:18:7:34 | call to method Source : Object | semmle.label | call to method Source : Object | +| I.cs:13:17:13:33 | call to method Source : Object | semmle.label | call to method Source : Object | | I.cs:13:17:13:33 | call to method Source : Object | semmle.label | call to method Source : Object | | I.cs:15:9:15:9 | [post] access to local variable i : I [field Field1] : Object | semmle.label | [post] access to local variable i : I [field Field1] : Object | +| I.cs:15:9:15:9 | [post] access to local variable i : I [field Field1] : Object | semmle.label | [post] access to local variable i : I [field Field1] : Object | +| I.cs:15:20:15:20 | access to local variable o : Object | semmle.label | access to local variable o : Object | | I.cs:15:20:15:20 | access to local variable o : Object | semmle.label | access to local variable o : Object | | I.cs:16:9:16:9 | access to local variable i : I [field Field1] : Object | semmle.label | access to local variable i : I [field Field1] : Object | +| I.cs:16:9:16:9 | access to local variable i : I [field Field1] : Object | semmle.label | access to local variable i : I [field Field1] : Object | +| I.cs:17:9:17:9 | access to local variable i : I [field Field1] : Object | semmle.label | access to local variable i : I [field Field1] : Object | | I.cs:17:9:17:9 | access to local variable i : I [field Field1] : Object | semmle.label | access to local variable i : I [field Field1] : Object | | I.cs:18:14:18:14 | access to local variable i : I [field Field1] : Object | semmle.label | access to local variable i : I [field Field1] : Object | +| I.cs:18:14:18:14 | access to local variable i : I [field Field1] : Object | semmle.label | access to local variable i : I [field Field1] : Object | +| I.cs:18:14:18:21 | access to field Field1 | semmle.label | access to field Field1 | | I.cs:18:14:18:21 | access to field Field1 | semmle.label | access to field Field1 | | I.cs:21:13:21:19 | object creation of type I : I [field Field1] : Object | semmle.label | object creation of type I : I [field Field1] : Object | +| I.cs:21:13:21:19 | object creation of type I : I [field Field1] : Object | semmle.label | object creation of type I : I [field Field1] : Object | +| I.cs:22:9:22:9 | access to local variable i : I [field Field1] : Object | semmle.label | access to local variable i : I [field Field1] : Object | | I.cs:22:9:22:9 | access to local variable i : I [field Field1] : Object | semmle.label | access to local variable i : I [field Field1] : Object | | I.cs:23:14:23:14 | access to local variable i : I [field Field1] : Object | semmle.label | access to local variable i : I [field Field1] : Object | +| I.cs:23:14:23:14 | access to local variable i : I [field Field1] : Object | semmle.label | access to local variable i : I [field Field1] : Object | +| I.cs:23:14:23:21 | access to field Field1 | semmle.label | access to field Field1 | | I.cs:23:14:23:21 | access to field Field1 | semmle.label | access to field Field1 | | I.cs:26:13:26:37 | [pre-initializer] object creation of type I : I [field Field1] : Object | semmle.label | [pre-initializer] object creation of type I : I [field Field1] : Object | +| I.cs:26:13:26:37 | [pre-initializer] object creation of type I : I [field Field1] : Object | semmle.label | [pre-initializer] object creation of type I : I [field Field1] : Object | +| I.cs:27:14:27:14 | access to local variable i : I [field Field1] : Object | semmle.label | access to local variable i : I [field Field1] : Object | | I.cs:27:14:27:14 | access to local variable i : I [field Field1] : Object | semmle.label | access to local variable i : I [field Field1] : Object | | I.cs:27:14:27:21 | access to field Field1 | semmle.label | access to field Field1 | +| I.cs:27:14:27:21 | access to field Field1 | semmle.label | access to field Field1 | +| I.cs:31:13:31:29 | call to method Source : Object | semmle.label | call to method Source : Object | | I.cs:31:13:31:29 | call to method Source : Object | semmle.label | call to method Source : Object | | I.cs:32:9:32:9 | [post] access to local variable i : I [field Field1] : Object | semmle.label | [post] access to local variable i : I [field Field1] : Object | +| I.cs:32:9:32:9 | [post] access to local variable i : I [field Field1] : Object | semmle.label | [post] access to local variable i : I [field Field1] : Object | +| I.cs:32:20:32:20 | access to local variable o : Object | semmle.label | access to local variable o : Object | | I.cs:32:20:32:20 | access to local variable o : Object | semmle.label | access to local variable o : Object | | I.cs:33:9:33:9 | access to local variable i : I [field Field1] : Object | semmle.label | access to local variable i : I [field Field1] : Object | +| I.cs:33:9:33:9 | access to local variable i : I [field Field1] : Object | semmle.label | access to local variable i : I [field Field1] : Object | +| I.cs:34:12:34:12 | access to local variable i : I [field Field1] : Object | semmle.label | access to local variable i : I [field Field1] : Object | | I.cs:34:12:34:12 | access to local variable i : I [field Field1] : Object | semmle.label | access to local variable i : I [field Field1] : Object | | I.cs:37:23:37:23 | i : I [field Field1] : Object | semmle.label | i : I [field Field1] : Object | +| I.cs:37:23:37:23 | i : I [field Field1] : Object | semmle.label | i : I [field Field1] : Object | +| I.cs:39:9:39:9 | access to parameter i : I [field Field1] : Object | semmle.label | access to parameter i : I [field Field1] : Object | | I.cs:39:9:39:9 | access to parameter i : I [field Field1] : Object | semmle.label | access to parameter i : I [field Field1] : Object | | I.cs:40:14:40:14 | access to parameter i : I [field Field1] : Object | semmle.label | access to parameter i : I [field Field1] : Object | +| I.cs:40:14:40:14 | access to parameter i : I [field Field1] : Object | semmle.label | access to parameter i : I [field Field1] : Object | +| I.cs:40:14:40:21 | access to field Field1 | semmle.label | access to field Field1 | | I.cs:40:14:40:21 | access to field Field1 | semmle.label | access to field Field1 | | J.cs:14:26:14:30 | field : Object | semmle.label | field : Object | +| J.cs:14:26:14:30 | field : Object | semmle.label | field : Object | +| J.cs:14:40:14:43 | prop : Object | semmle.label | prop : Object | | J.cs:14:40:14:43 | prop : Object | semmle.label | prop : Object | | J.cs:14:50:14:54 | [post] this access : Struct [field Field] : Object | semmle.label | [post] this access : Struct [field Field] : Object | +| J.cs:14:50:14:54 | [post] this access : Struct [field Field] : Object | semmle.label | [post] this access : Struct [field Field] : Object | +| J.cs:14:57:14:60 | [post] this access : Struct [property Prop] : Object | semmle.label | [post] this access : Struct [property Prop] : Object | | J.cs:14:57:14:60 | [post] this access : Struct [property Prop] : Object | semmle.label | [post] this access : Struct [property Prop] : Object | | J.cs:14:66:14:70 | access to parameter field : Object | semmle.label | access to parameter field : Object | +| J.cs:14:66:14:70 | access to parameter field : Object | semmle.label | access to parameter field : Object | +| J.cs:14:73:14:76 | access to parameter prop : Object | semmle.label | access to parameter prop : Object | | J.cs:14:73:14:76 | access to parameter prop : Object | semmle.label | access to parameter prop : Object | | J.cs:21:17:21:33 | call to method Source : Object | semmle.label | call to method Source : Object | +| J.cs:21:17:21:33 | call to method Source : Object | semmle.label | call to method Source : Object | +| J.cs:22:18:22:41 | object creation of type RecordClass : RecordClass [property Prop1] : Object | semmle.label | object creation of type RecordClass : RecordClass [property Prop1] : Object | | J.cs:22:18:22:41 | object creation of type RecordClass : RecordClass [property Prop1] : Object | semmle.label | object creation of type RecordClass : RecordClass [property Prop1] : Object | | J.cs:22:34:22:34 | access to local variable o : Object | semmle.label | access to local variable o : Object | +| J.cs:22:34:22:34 | access to local variable o : Object | semmle.label | access to local variable o : Object | +| J.cs:23:14:23:15 | access to local variable r1 : RecordClass [property Prop1] : Object | semmle.label | access to local variable r1 : RecordClass [property Prop1] : Object | | J.cs:23:14:23:15 | access to local variable r1 : RecordClass [property Prop1] : Object | semmle.label | access to local variable r1 : RecordClass [property Prop1] : Object | | J.cs:23:14:23:21 | access to property Prop1 | semmle.label | access to property Prop1 | +| J.cs:23:14:23:21 | access to property Prop1 | semmle.label | access to property Prop1 | +| J.cs:27:14:27:15 | access to local variable r2 : RecordClass [property Prop1] : Object | semmle.label | access to local variable r2 : RecordClass [property Prop1] : Object | | J.cs:27:14:27:15 | access to local variable r2 : RecordClass [property Prop1] : Object | semmle.label | access to local variable r2 : RecordClass [property Prop1] : Object | | J.cs:27:14:27:21 | access to property Prop1 | semmle.label | access to property Prop1 | +| J.cs:27:14:27:21 | access to property Prop1 | semmle.label | access to property Prop1 | +| J.cs:30:18:30:54 | ... with { ... } : RecordClass [property Prop2] : Object | semmle.label | ... with { ... } : RecordClass [property Prop2] : Object | | J.cs:30:18:30:54 | ... with { ... } : RecordClass [property Prop2] : Object | semmle.label | ... with { ... } : RecordClass [property Prop2] : Object | | J.cs:30:36:30:52 | call to method Source : Object | semmle.label | call to method Source : Object | +| J.cs:30:36:30:52 | call to method Source : Object | semmle.label | call to method Source : Object | +| J.cs:31:14:31:15 | access to local variable r3 : RecordClass [property Prop1] : Object | semmle.label | access to local variable r3 : RecordClass [property Prop1] : Object | | J.cs:31:14:31:15 | access to local variable r3 : RecordClass [property Prop1] : Object | semmle.label | access to local variable r3 : RecordClass [property Prop1] : Object | | J.cs:31:14:31:21 | access to property Prop1 | semmle.label | access to property Prop1 | +| J.cs:31:14:31:21 | access to property Prop1 | semmle.label | access to property Prop1 | +| J.cs:32:14:32:15 | access to local variable r3 : RecordClass [property Prop2] : Object | semmle.label | access to local variable r3 : RecordClass [property Prop2] : Object | | J.cs:32:14:32:15 | access to local variable r3 : RecordClass [property Prop2] : Object | semmle.label | access to local variable r3 : RecordClass [property Prop2] : Object | | J.cs:32:14:32:21 | access to property Prop2 | semmle.label | access to property Prop2 | +| J.cs:32:14:32:21 | access to property Prop2 | semmle.label | access to property Prop2 | +| J.cs:41:17:41:33 | call to method Source : Object | semmle.label | call to method Source : Object | | J.cs:41:17:41:33 | call to method Source : Object | semmle.label | call to method Source : Object | | J.cs:42:18:42:42 | object creation of type RecordStruct : RecordStruct [property Prop1] : Object | semmle.label | object creation of type RecordStruct : RecordStruct [property Prop1] : Object | +| J.cs:42:18:42:42 | object creation of type RecordStruct : RecordStruct [property Prop1] : Object | semmle.label | object creation of type RecordStruct : RecordStruct [property Prop1] : Object | +| J.cs:42:35:42:35 | access to local variable o : Object | semmle.label | access to local variable o : Object | | J.cs:42:35:42:35 | access to local variable o : Object | semmle.label | access to local variable o : Object | | J.cs:43:14:43:15 | access to local variable r1 : RecordStruct [property Prop1] : Object | semmle.label | access to local variable r1 : RecordStruct [property Prop1] : Object | +| J.cs:43:14:43:15 | access to local variable r1 : RecordStruct [property Prop1] : Object | semmle.label | access to local variable r1 : RecordStruct [property Prop1] : Object | +| J.cs:43:14:43:21 | access to property Prop1 | semmle.label | access to property Prop1 | | J.cs:43:14:43:21 | access to property Prop1 | semmle.label | access to property Prop1 | | J.cs:47:14:47:15 | access to local variable r2 : RecordStruct [property Prop1] : Object | semmle.label | access to local variable r2 : RecordStruct [property Prop1] : Object | +| J.cs:47:14:47:15 | access to local variable r2 : RecordStruct [property Prop1] : Object | semmle.label | access to local variable r2 : RecordStruct [property Prop1] : Object | +| J.cs:47:14:47:21 | access to property Prop1 | semmle.label | access to property Prop1 | | J.cs:47:14:47:21 | access to property Prop1 | semmle.label | access to property Prop1 | | J.cs:50:18:50:54 | ... with { ... } : RecordStruct [property Prop2] : Object | semmle.label | ... with { ... } : RecordStruct [property Prop2] : Object | +| J.cs:50:18:50:54 | ... with { ... } : RecordStruct [property Prop2] : Object | semmle.label | ... with { ... } : RecordStruct [property Prop2] : Object | +| J.cs:50:36:50:52 | call to method Source : Object | semmle.label | call to method Source : Object | | J.cs:50:36:50:52 | call to method Source : Object | semmle.label | call to method Source : Object | | J.cs:51:14:51:15 | access to local variable r3 : RecordStruct [property Prop1] : Object | semmle.label | access to local variable r3 : RecordStruct [property Prop1] : Object | +| J.cs:51:14:51:15 | access to local variable r3 : RecordStruct [property Prop1] : Object | semmle.label | access to local variable r3 : RecordStruct [property Prop1] : Object | +| J.cs:51:14:51:21 | access to property Prop1 | semmle.label | access to property Prop1 | | J.cs:51:14:51:21 | access to property Prop1 | semmle.label | access to property Prop1 | | J.cs:52:14:52:15 | access to local variable r3 : RecordStruct [property Prop2] : Object | semmle.label | access to local variable r3 : RecordStruct [property Prop2] : Object | +| J.cs:52:14:52:15 | access to local variable r3 : RecordStruct [property Prop2] : Object | semmle.label | access to local variable r3 : RecordStruct [property Prop2] : Object | +| J.cs:52:14:52:21 | access to property Prop2 | semmle.label | access to property Prop2 | | J.cs:52:14:52:21 | access to property Prop2 | semmle.label | access to property Prop2 | | J.cs:61:17:61:33 | call to method Source : Object | semmle.label | call to method Source : Object | +| J.cs:61:17:61:33 | call to method Source : Object | semmle.label | call to method Source : Object | +| J.cs:62:18:62:36 | object creation of type Struct : Struct [field Field] : Object | semmle.label | object creation of type Struct : Struct [field Field] : Object | | J.cs:62:18:62:36 | object creation of type Struct : Struct [field Field] : Object | semmle.label | object creation of type Struct : Struct [field Field] : Object | | J.cs:62:29:62:29 | access to local variable o : Object | semmle.label | access to local variable o : Object | +| J.cs:62:29:62:29 | access to local variable o : Object | semmle.label | access to local variable o : Object | +| J.cs:65:14:65:15 | access to local variable s2 : Struct [field Field] : Object | semmle.label | access to local variable s2 : Struct [field Field] : Object | | J.cs:65:14:65:15 | access to local variable s2 : Struct [field Field] : Object | semmle.label | access to local variable s2 : Struct [field Field] : Object | | J.cs:65:14:65:21 | access to field Field | semmle.label | access to field Field | +| J.cs:65:14:65:21 | access to field Field | semmle.label | access to field Field | +| J.cs:68:18:68:53 | ... with { ... } : Struct [property Prop] : Object | semmle.label | ... with { ... } : Struct [property Prop] : Object | | J.cs:68:18:68:53 | ... with { ... } : Struct [property Prop] : Object | semmle.label | ... with { ... } : Struct [property Prop] : Object | | J.cs:68:35:68:51 | call to method Source : Object | semmle.label | call to method Source : Object | +| J.cs:68:35:68:51 | call to method Source : Object | semmle.label | call to method Source : Object | +| J.cs:69:14:69:15 | access to local variable s3 : Struct [field Field] : Object | semmle.label | access to local variable s3 : Struct [field Field] : Object | | J.cs:69:14:69:15 | access to local variable s3 : Struct [field Field] : Object | semmle.label | access to local variable s3 : Struct [field Field] : Object | | J.cs:69:14:69:21 | access to field Field | semmle.label | access to field Field | +| J.cs:69:14:69:21 | access to field Field | semmle.label | access to field Field | +| J.cs:70:14:70:15 | access to local variable s3 : Struct [property Prop] : Object | semmle.label | access to local variable s3 : Struct [property Prop] : Object | | J.cs:70:14:70:15 | access to local variable s3 : Struct [property Prop] : Object | semmle.label | access to local variable s3 : Struct [property Prop] : Object | | J.cs:70:14:70:20 | access to property Prop | semmle.label | access to property Prop | +| J.cs:70:14:70:20 | access to property Prop | semmle.label | access to property Prop | +| J.cs:79:17:79:33 | call to method Source : Object | semmle.label | call to method Source : Object | | J.cs:79:17:79:33 | call to method Source : Object | semmle.label | call to method Source : Object | | J.cs:80:18:80:36 | object creation of type Struct : Struct [property Prop] : Object | semmle.label | object creation of type Struct : Struct [property Prop] : Object | +| J.cs:80:18:80:36 | object creation of type Struct : Struct [property Prop] : Object | semmle.label | object creation of type Struct : Struct [property Prop] : Object | +| J.cs:80:35:80:35 | access to local variable o : Object | semmle.label | access to local variable o : Object | | J.cs:80:35:80:35 | access to local variable o : Object | semmle.label | access to local variable o : Object | | J.cs:84:14:84:15 | access to local variable s2 : Struct [property Prop] : Object | semmle.label | access to local variable s2 : Struct [property Prop] : Object | +| J.cs:84:14:84:15 | access to local variable s2 : Struct [property Prop] : Object | semmle.label | access to local variable s2 : Struct [property Prop] : Object | +| J.cs:84:14:84:20 | access to property Prop | semmle.label | access to property Prop | | J.cs:84:14:84:20 | access to property Prop | semmle.label | access to property Prop | | J.cs:86:18:86:54 | ... with { ... } : Struct [field Field] : Object | semmle.label | ... with { ... } : Struct [field Field] : Object | +| J.cs:86:18:86:54 | ... with { ... } : Struct [field Field] : Object | semmle.label | ... with { ... } : Struct [field Field] : Object | +| J.cs:86:36:86:52 | call to method Source : Object | semmle.label | call to method Source : Object | | J.cs:86:36:86:52 | call to method Source : Object | semmle.label | call to method Source : Object | | J.cs:87:14:87:15 | access to local variable s3 : Struct [field Field] : Object | semmle.label | access to local variable s3 : Struct [field Field] : Object | +| J.cs:87:14:87:15 | access to local variable s3 : Struct [field Field] : Object | semmle.label | access to local variable s3 : Struct [field Field] : Object | +| J.cs:87:14:87:21 | access to field Field | semmle.label | access to field Field | | J.cs:87:14:87:21 | access to field Field | semmle.label | access to field Field | | J.cs:88:14:88:15 | access to local variable s3 : Struct [property Prop] : Object | semmle.label | access to local variable s3 : Struct [property Prop] : Object | +| J.cs:88:14:88:15 | access to local variable s3 : Struct [property Prop] : Object | semmle.label | access to local variable s3 : Struct [property Prop] : Object | +| J.cs:88:14:88:20 | access to property Prop | semmle.label | access to property Prop | | J.cs:88:14:88:20 | access to property Prop | semmle.label | access to property Prop | | J.cs:97:17:97:33 | call to method Source : Object | semmle.label | call to method Source : Object | +| J.cs:97:17:97:33 | call to method Source : Object | semmle.label | call to method Source : Object | +| J.cs:99:18:99:41 | { ..., ... } : <>__AnonType0 [property X] : Object | semmle.label | { ..., ... } : <>__AnonType0 [property X] : Object | | J.cs:99:18:99:41 | { ..., ... } : <>__AnonType0 [property X] : Object | semmle.label | { ..., ... } : <>__AnonType0 [property X] : Object | | J.cs:99:28:99:28 | access to local variable o : Object | semmle.label | access to local variable o : Object | +| J.cs:99:28:99:28 | access to local variable o : Object | semmle.label | access to local variable o : Object | +| J.cs:102:14:102:15 | access to local variable a2 : <>__AnonType0 [property X] : Object | semmle.label | access to local variable a2 : <>__AnonType0 [property X] : Object | | J.cs:102:14:102:15 | access to local variable a2 : <>__AnonType0 [property X] : Object | semmle.label | access to local variable a2 : <>__AnonType0 [property X] : Object | | J.cs:102:14:102:17 | access to property X | semmle.label | access to property X | +| J.cs:102:14:102:17 | access to property X | semmle.label | access to property X | +| J.cs:105:18:105:50 | ... with { ... } : <>__AnonType0 [property Y] : Object | semmle.label | ... with { ... } : <>__AnonType0 [property Y] : Object | | J.cs:105:18:105:50 | ... with { ... } : <>__AnonType0 [property Y] : Object | semmle.label | ... with { ... } : <>__AnonType0 [property Y] : Object | | J.cs:105:32:105:48 | call to method Source : Object | semmle.label | call to method Source : Object | +| J.cs:105:32:105:48 | call to method Source : Object | semmle.label | call to method Source : Object | +| J.cs:106:14:106:15 | access to local variable a3 : <>__AnonType0 [property X] : Object | semmle.label | access to local variable a3 : <>__AnonType0 [property X] : Object | | J.cs:106:14:106:15 | access to local variable a3 : <>__AnonType0 [property X] : Object | semmle.label | access to local variable a3 : <>__AnonType0 [property X] : Object | | J.cs:106:14:106:17 | access to property X | semmle.label | access to property X | +| J.cs:106:14:106:17 | access to property X | semmle.label | access to property X | +| J.cs:107:14:107:15 | access to local variable a3 : <>__AnonType0 [property Y] : Object | semmle.label | access to local variable a3 : <>__AnonType0 [property Y] : Object | | J.cs:107:14:107:15 | access to local variable a3 : <>__AnonType0 [property Y] : Object | semmle.label | access to local variable a3 : <>__AnonType0 [property Y] : Object | | J.cs:107:14:107:17 | access to property Y | semmle.label | access to property Y | +| J.cs:107:14:107:17 | access to property Y | semmle.label | access to property Y | +| J.cs:119:13:119:13 | [post] access to local variable a : Int32[] [element] : Int32 | semmle.label | [post] access to local variable a : Int32[] [element] : Int32 | | J.cs:119:13:119:13 | [post] access to local variable a : Int32[] [element] : Int32 | semmle.label | [post] access to local variable a : Int32[] [element] : Int32 | | J.cs:119:20:119:34 | call to method Source : Int32 | semmle.label | call to method Source : Int32 | +| J.cs:119:20:119:34 | call to method Source : Int32 | semmle.label | call to method Source : Int32 | +| J.cs:125:14:125:14 | access to local variable a : Int32[] [element] : Int32 | semmle.label | access to local variable a : Int32[] [element] : Int32 | | J.cs:125:14:125:14 | access to local variable a : Int32[] [element] : Int32 | semmle.label | access to local variable a : Int32[] [element] : Int32 | | J.cs:125:14:125:17 | (...) ... | semmle.label | (...) ... | +| J.cs:125:14:125:17 | (...) ... | semmle.label | (...) ... | +| J.cs:125:14:125:17 | access to array element : Int32 | semmle.label | access to array element : Int32 | | J.cs:125:14:125:17 | access to array element : Int32 | semmle.label | access to array element : Int32 | subpaths | A.cs:6:24:6:24 | access to local variable c : C | A.cs:147:32:147:32 | c : C | A.cs:149:20:149:27 | object creation of type B : B [field c] : C | A.cs:6:17:6:25 | call to method Make : B [field c] : C | +| A.cs:6:24:6:24 | access to local variable c : C | A.cs:147:32:147:32 | c : C | A.cs:149:20:149:27 | object creation of type B : B [field c] : C | A.cs:6:17:6:25 | call to method Make : B [field c] : C | +| A.cs:13:15:13:29 | call to method Source : C1 | A.cs:145:27:145:27 | c : C1 | A.cs:145:32:145:35 | [post] this access : B [field c] : C1 | A.cs:13:9:13:9 | [post] access to local variable b : B [field c] : C1 | | A.cs:13:15:13:29 | call to method Source : C1 | A.cs:145:27:145:27 | c : C1 | A.cs:145:32:145:35 | [post] this access : B [field c] : C1 | A.cs:13:9:13:9 | [post] access to local variable b : B [field c] : C1 | | A.cs:14:14:14:14 | access to local variable b : B [field c] : C1 | A.cs:146:18:146:20 | this : B [field c] : C1 | A.cs:146:33:146:38 | access to field c : C1 | A.cs:14:14:14:20 | call to method Get | +| A.cs:14:14:14:14 | access to local variable b : B [field c] : C1 | A.cs:146:18:146:20 | this : B [field c] : C1 | A.cs:146:33:146:38 | access to field c : C1 | A.cs:14:14:14:20 | call to method Get | +| A.cs:15:15:15:35 | object creation of type B : B [field c] : C | A.cs:146:18:146:20 | this : B [field c] : C | A.cs:146:33:146:38 | access to field c : C | A.cs:15:14:15:42 | call to method Get | | A.cs:15:15:15:35 | object creation of type B : B [field c] : C | A.cs:146:18:146:20 | this : B [field c] : C | A.cs:146:33:146:38 | access to field c : C | A.cs:15:14:15:42 | call to method Get | | A.cs:15:21:15:34 | call to method Source : C | A.cs:141:20:141:20 | c : C | A.cs:143:13:143:16 | [post] this access : B [field c] : C | A.cs:15:15:15:35 | object creation of type B : B [field c] : C | +| A.cs:15:21:15:34 | call to method Source : C | A.cs:141:20:141:20 | c : C | A.cs:143:13:143:16 | [post] this access : B [field c] : C | A.cs:15:15:15:35 | object creation of type B : B [field c] : C | +| A.cs:22:25:22:37 | call to method Source : C2 | A.cs:42:29:42:29 | c : C2 | A.cs:48:20:48:21 | access to local variable b2 : B [field c] : C2 | A.cs:22:14:22:38 | call to method SetOnB : B [field c] : C2 | | A.cs:22:25:22:37 | call to method Source : C2 | A.cs:42:29:42:29 | c : C2 | A.cs:48:20:48:21 | access to local variable b2 : B [field c] : C2 | A.cs:22:14:22:38 | call to method SetOnB : B [field c] : C2 | | A.cs:31:29:31:41 | call to method Source : C2 | A.cs:36:33:36:33 | c : C2 | A.cs:39:16:39:28 | ... ? ... : ... : B [field c] : C2 | A.cs:31:14:31:42 | call to method SetOnBWrap : B [field c] : C2 | +| A.cs:31:29:31:41 | call to method Source : C2 | A.cs:36:33:36:33 | c : C2 | A.cs:39:16:39:28 | ... ? ... : ... : B [field c] : C2 | A.cs:31:14:31:42 | call to method SetOnBWrap : B [field c] : C2 | +| A.cs:38:29:38:29 | access to parameter c : C2 | A.cs:42:29:42:29 | c : C2 | A.cs:48:20:48:21 | access to local variable b2 : B [field c] : C2 | A.cs:38:18:38:30 | call to method SetOnB : B [field c] : C2 | | A.cs:38:29:38:29 | access to parameter c : C2 | A.cs:42:29:42:29 | c : C2 | A.cs:48:20:48:21 | access to local variable b2 : B [field c] : C2 | A.cs:38:18:38:30 | call to method SetOnB : B [field c] : C2 | | A.cs:47:20:47:20 | access to parameter c : C2 | A.cs:145:27:145:27 | c : C2 | A.cs:145:32:145:35 | [post] this access : B [field c] : C2 | A.cs:47:13:47:14 | [post] access to local variable b2 : B [field c] : C2 | +| A.cs:47:20:47:20 | access to parameter c : C2 | A.cs:145:27:145:27 | c : C2 | A.cs:145:32:145:35 | [post] this access : B [field c] : C2 | A.cs:47:13:47:14 | [post] access to local variable b2 : B [field c] : C2 | +| A.cs:83:15:83:26 | call to method Source : C | A.cs:145:27:145:27 | c : C | A.cs:145:32:145:35 | [post] this access : B [field c] : C | A.cs:83:9:83:9 | [post] access to parameter b : B [field c] : C | | A.cs:83:15:83:26 | call to method Source : C | A.cs:145:27:145:27 | c : C | A.cs:145:32:145:35 | [post] this access : B [field c] : C | A.cs:83:9:83:9 | [post] access to parameter b : B [field c] : C | | A.cs:105:23:105:23 | access to local variable b : B | A.cs:95:20:95:20 | b : B | A.cs:98:13:98:16 | [post] this access : D [field b] : B | A.cs:105:17:105:29 | object creation of type D : D [field b] : B | +| A.cs:105:23:105:23 | access to local variable b : B | A.cs:95:20:95:20 | b : B | A.cs:98:13:98:16 | [post] this access : D [field b] : B | A.cs:105:17:105:29 | object creation of type D : D [field b] : B | +| A.cs:114:29:114:29 | access to local variable b : B | A.cs:157:25:157:28 | head : B | A.cs:159:13:159:16 | [post] this access : MyList [field head] : B | A.cs:114:18:114:54 | object creation of type MyList : MyList [field head] : B | | A.cs:114:29:114:29 | access to local variable b : B | A.cs:157:25:157:28 | head : B | A.cs:159:13:159:16 | [post] this access : MyList [field head] : B | A.cs:114:18:114:54 | object creation of type MyList : MyList [field head] : B | | A.cs:115:35:115:36 | access to local variable l1 : MyList [field head] : B | A.cs:157:38:157:41 | next : MyList [field head] : B | A.cs:160:13:160:16 | [post] this access : MyList [field next, field head] : B | A.cs:115:18:115:37 | object creation of type MyList : MyList [field next, field head] : B | +| A.cs:115:35:115:36 | access to local variable l1 : MyList [field head] : B | A.cs:157:38:157:41 | next : MyList [field head] : B | A.cs:160:13:160:16 | [post] this access : MyList [field next, field head] : B | A.cs:115:18:115:37 | object creation of type MyList : MyList [field next, field head] : B | +| A.cs:116:35:116:36 | access to local variable l2 : MyList [field next, field head] : B | A.cs:157:38:157:41 | next : MyList [field next, field head] : B | A.cs:160:13:160:16 | [post] this access : MyList [field next, field next, field head] : B | A.cs:116:18:116:37 | object creation of type MyList : MyList [field next, field next, field head] : B | | A.cs:116:35:116:36 | access to local variable l2 : MyList [field next, field head] : B | A.cs:157:38:157:41 | next : MyList [field next, field head] : B | A.cs:160:13:160:16 | [post] this access : MyList [field next, field next, field head] : B | A.cs:116:18:116:37 | object creation of type MyList : MyList [field next, field next, field head] : B | | A.cs:149:26:149:26 | access to parameter c : C | A.cs:141:20:141:20 | c : C | A.cs:143:13:143:16 | [post] this access : B [field c] : C | A.cs:149:20:149:27 | object creation of type B : B [field c] : C | +| A.cs:149:26:149:26 | access to parameter c : C | A.cs:141:20:141:20 | c : C | A.cs:143:13:143:16 | [post] this access : B [field c] : C | A.cs:149:20:149:27 | object creation of type B : B [field c] : C | +| B.cs:6:27:6:27 | access to local variable e : Elem | B.cs:29:26:29:27 | e1 : Elem | B.cs:31:13:31:16 | [post] this access : Box1 [field elem1] : Elem | B.cs:6:18:6:34 | object creation of type Box1 : Box1 [field elem1] : Elem | | B.cs:6:27:6:27 | access to local variable e : Elem | B.cs:29:26:29:27 | e1 : Elem | B.cs:31:13:31:16 | [post] this access : Box1 [field elem1] : Elem | B.cs:6:18:6:34 | object creation of type Box1 : Box1 [field elem1] : Elem | | B.cs:7:27:7:28 | access to local variable b1 : Box1 [field elem1] : Elem | B.cs:39:26:39:27 | b1 : Box1 [field elem1] : Elem | B.cs:41:13:41:16 | [post] this access : Box2 [field box1, field elem1] : Elem | B.cs:7:18:7:29 | object creation of type Box2 : Box2 [field box1, field elem1] : Elem | +| B.cs:7:27:7:28 | access to local variable b1 : Box1 [field elem1] : Elem | B.cs:39:26:39:27 | b1 : Box1 [field elem1] : Elem | B.cs:41:13:41:16 | [post] this access : Box2 [field box1, field elem1] : Elem | B.cs:7:18:7:29 | object creation of type Box2 : Box2 [field box1, field elem1] : Elem | +| B.cs:15:33:15:33 | access to local variable e : Elem | B.cs:29:35:29:36 | e2 : Elem | B.cs:32:13:32:16 | [post] this access : Box1 [field elem2] : Elem | B.cs:15:18:15:34 | object creation of type Box1 : Box1 [field elem2] : Elem | | B.cs:15:33:15:33 | access to local variable e : Elem | B.cs:29:35:29:36 | e2 : Elem | B.cs:32:13:32:16 | [post] this access : Box1 [field elem2] : Elem | B.cs:15:18:15:34 | object creation of type Box1 : Box1 [field elem2] : Elem | | B.cs:16:27:16:28 | access to local variable b1 : Box1 [field elem2] : Elem | B.cs:39:26:39:27 | b1 : Box1 [field elem2] : Elem | B.cs:41:13:41:16 | [post] this access : Box2 [field box1, field elem2] : Elem | B.cs:16:18:16:29 | object creation of type Box2 : Box2 [field box1, field elem2] : Elem | +| B.cs:16:27:16:28 | access to local variable b1 : Box1 [field elem2] : Elem | B.cs:39:26:39:27 | b1 : Box1 [field elem2] : Elem | B.cs:41:13:41:16 | [post] this access : Box2 [field box1, field elem2] : Elem | B.cs:16:18:16:29 | object creation of type Box2 : Box2 [field box1, field elem2] : Elem | +| D.cs:15:34:15:38 | access to parameter value : Object | D.cs:9:9:9:11 | value : Object | D.cs:9:15:9:18 | [post] this access : D [field trivialPropField] : Object | D.cs:15:15:15:18 | [post] this access : D [field trivialPropField] : Object | | D.cs:15:34:15:38 | access to parameter value : Object | D.cs:9:9:9:11 | value : Object | D.cs:9:15:9:18 | [post] this access : D [field trivialPropField] : Object | D.cs:15:15:15:18 | [post] this access : D [field trivialPropField] : Object | | D.cs:22:27:22:28 | access to parameter o2 : Object | D.cs:9:9:9:11 | value : Object | D.cs:9:15:9:18 | [post] this access : D [field trivialPropField] : Object | D.cs:22:9:22:11 | [post] access to local variable ret : D [field trivialPropField] : Object | +| D.cs:22:27:22:28 | access to parameter o2 : Object | D.cs:9:9:9:11 | value : Object | D.cs:9:15:9:18 | [post] this access : D [field trivialPropField] : Object | D.cs:22:9:22:11 | [post] access to local variable ret : D [field trivialPropField] : Object | +| D.cs:23:27:23:28 | access to parameter o3 : Object | D.cs:15:9:15:11 | value : Object | D.cs:15:15:15:18 | [post] this access : D [field trivialPropField] : Object | D.cs:23:9:23:11 | [post] access to local variable ret : D [field trivialPropField] : Object | | D.cs:23:27:23:28 | access to parameter o3 : Object | D.cs:15:9:15:11 | value : Object | D.cs:15:15:15:18 | [post] this access : D [field trivialPropField] : Object | D.cs:23:9:23:11 | [post] access to local variable ret : D [field trivialPropField] : Object | | D.cs:31:24:31:24 | access to local variable o : Object | D.cs:18:28:18:29 | o1 : Object | D.cs:24:16:24:18 | access to local variable ret : D [property AutoProp] : Object | D.cs:31:17:31:37 | call to method Create : D [property AutoProp] : Object | +| D.cs:31:24:31:24 | access to local variable o : Object | D.cs:18:28:18:29 | o1 : Object | D.cs:24:16:24:18 | access to local variable ret : D [property AutoProp] : Object | D.cs:31:17:31:37 | call to method Create : D [property AutoProp] : Object | +| D.cs:37:26:37:42 | call to method Source : Object | D.cs:18:39:18:40 | o2 : Object | D.cs:24:16:24:18 | access to local variable ret : D [field trivialPropField] : Object | D.cs:37:13:37:49 | call to method Create : D [field trivialPropField] : Object | | D.cs:37:26:37:42 | call to method Source : Object | D.cs:18:39:18:40 | o2 : Object | D.cs:24:16:24:18 | access to local variable ret : D [field trivialPropField] : Object | D.cs:37:13:37:49 | call to method Create : D [field trivialPropField] : Object | | D.cs:39:14:39:14 | access to local variable d : D [field trivialPropField] : Object | D.cs:8:9:8:11 | this : D [field trivialPropField] : Object | D.cs:8:22:8:42 | access to field trivialPropField : Object | D.cs:39:14:39:26 | access to property TrivialProp | +| D.cs:39:14:39:14 | access to local variable d : D [field trivialPropField] : Object | D.cs:8:9:8:11 | this : D [field trivialPropField] : Object | D.cs:8:22:8:42 | access to field trivialPropField : Object | D.cs:39:14:39:26 | access to property TrivialProp | +| D.cs:41:14:41:14 | access to local variable d : D [field trivialPropField] : Object | D.cs:14:9:14:11 | this : D [field trivialPropField] : Object | D.cs:14:22:14:42 | access to field trivialPropField : Object | D.cs:41:14:41:26 | access to property ComplexProp | | D.cs:41:14:41:14 | access to local variable d : D [field trivialPropField] : Object | D.cs:14:9:14:11 | this : D [field trivialPropField] : Object | D.cs:14:22:14:42 | access to field trivialPropField : Object | D.cs:41:14:41:26 | access to property ComplexProp | | D.cs:43:32:43:48 | call to method Source : Object | D.cs:18:50:18:51 | o3 : Object | D.cs:24:16:24:18 | access to local variable ret : D [field trivialPropField] : Object | D.cs:43:13:43:49 | call to method Create : D [field trivialPropField] : Object | +| D.cs:43:32:43:48 | call to method Source : Object | D.cs:18:50:18:51 | o3 : Object | D.cs:24:16:24:18 | access to local variable ret : D [field trivialPropField] : Object | D.cs:43:13:43:49 | call to method Create : D [field trivialPropField] : Object | +| D.cs:45:14:45:14 | access to local variable d : D [field trivialPropField] : Object | D.cs:8:9:8:11 | this : D [field trivialPropField] : Object | D.cs:8:22:8:42 | access to field trivialPropField : Object | D.cs:45:14:45:26 | access to property TrivialProp | | D.cs:45:14:45:14 | access to local variable d : D [field trivialPropField] : Object | D.cs:8:9:8:11 | this : D [field trivialPropField] : Object | D.cs:8:22:8:42 | access to field trivialPropField : Object | D.cs:45:14:45:26 | access to property TrivialProp | | D.cs:47:14:47:14 | access to local variable d : D [field trivialPropField] : Object | D.cs:14:9:14:11 | this : D [field trivialPropField] : Object | D.cs:14:22:14:42 | access to field trivialPropField : Object | D.cs:47:14:47:26 | access to property ComplexProp | +| D.cs:47:14:47:14 | access to local variable d : D [field trivialPropField] : Object | D.cs:14:9:14:11 | this : D [field trivialPropField] : Object | D.cs:14:22:14:42 | access to field trivialPropField : Object | D.cs:47:14:47:26 | access to property ComplexProp | +| E.cs:23:25:23:25 | access to local variable o : Object | E.cs:8:29:8:29 | o : Object | E.cs:12:16:12:18 | access to local variable ret : S [field Field] : Object | E.cs:23:17:23:26 | call to method CreateS : S [field Field] : Object | | E.cs:23:25:23:25 | access to local variable o : Object | E.cs:8:29:8:29 | o : Object | E.cs:12:16:12:18 | access to local variable ret : S [field Field] : Object | E.cs:23:17:23:26 | call to method CreateS : S [field Field] : Object | | F.cs:11:24:11:24 | access to local variable o : Object | F.cs:6:28:6:29 | o1 : Object | F.cs:6:46:6:81 | object creation of type F : F [field Field1] : Object | F.cs:11:17:11:31 | call to method Create : F [field Field1] : Object | +| F.cs:11:24:11:24 | access to local variable o : Object | F.cs:6:28:6:29 | o1 : Object | F.cs:6:46:6:81 | object creation of type F : F [field Field1] : Object | F.cs:11:17:11:31 | call to method Create : F [field Field1] : Object | +| F.cs:15:26:15:42 | call to method Source : Object | F.cs:6:39:6:40 | o2 : Object | F.cs:6:46:6:81 | object creation of type F : F [field Field2] : Object | F.cs:15:13:15:43 | call to method Create : F [field Field2] : Object | | F.cs:15:26:15:42 | call to method Source : Object | F.cs:6:39:6:40 | o2 : Object | F.cs:6:46:6:81 | object creation of type F : F [field Field2] : Object | F.cs:15:13:15:43 | call to method Create : F [field Field2] : Object | | G.cs:17:24:17:24 | access to local variable e : Elem | G.cs:64:34:64:34 | e : Elem | G.cs:64:39:64:42 | [post] this access : Box1 [field Elem] : Elem | G.cs:17:9:17:14 | [post] access to field Box1 : Box1 [field Elem] : Elem | +| G.cs:17:24:17:24 | access to local variable e : Elem | G.cs:64:34:64:34 | e : Elem | G.cs:64:39:64:42 | [post] this access : Box1 [field Elem] : Elem | G.cs:17:9:17:14 | [post] access to field Box1 : Box1 [field Elem] : Elem | +| G.cs:33:29:33:29 | access to local variable e : Elem | G.cs:64:34:64:34 | e : Elem | G.cs:64:39:64:42 | [post] this access : Box1 [field Elem] : Elem | G.cs:33:9:33:19 | [post] call to method GetBox1 : Box1 [field Elem] : Elem | | G.cs:33:29:33:29 | access to local variable e : Elem | G.cs:64:34:64:34 | e : Elem | G.cs:64:39:64:42 | [post] this access : Box1 [field Elem] : Elem | G.cs:33:9:33:19 | [post] call to method GetBox1 : Box1 [field Elem] : Elem | | G.cs:39:14:39:15 | access to parameter b2 : Box2 [field Box1, field Elem] : Elem | G.cs:71:21:71:27 | this : Box2 [field Box1, field Elem] : Elem | G.cs:71:34:71:37 | access to field Box1 : Box1 [field Elem] : Elem | G.cs:39:14:39:25 | call to method GetBox1 : Box1 [field Elem] : Elem | +| G.cs:39:14:39:15 | access to parameter b2 : Box2 [field Box1, field Elem] : Elem | G.cs:71:21:71:27 | this : Box2 [field Box1, field Elem] : Elem | G.cs:71:34:71:37 | access to field Box1 : Box1 [field Elem] : Elem | G.cs:39:14:39:25 | call to method GetBox1 : Box1 [field Elem] : Elem | +| G.cs:39:14:39:25 | call to method GetBox1 : Box1 [field Elem] : Elem | G.cs:63:21:63:27 | this : Box1 [field Elem] : Elem | G.cs:63:34:63:37 | access to field Elem : Elem | G.cs:39:14:39:35 | call to method GetElem | | G.cs:39:14:39:25 | call to method GetBox1 : Box1 [field Elem] : Elem | G.cs:63:21:63:27 | this : Box1 [field Elem] : Elem | G.cs:63:34:63:37 | access to field Elem : Elem | G.cs:39:14:39:35 | call to method GetElem | | H.cs:24:27:24:27 | access to local variable a : A [field FieldA] : Object | H.cs:13:15:13:15 | a : A [field FieldA] : Object | H.cs:17:16:17:18 | access to local variable ret : A [field FieldA] : Object | H.cs:24:21:24:28 | call to method Clone : A [field FieldA] : Object | +| H.cs:24:27:24:27 | access to local variable a : A [field FieldA] : Object | H.cs:13:15:13:15 | a : A [field FieldA] : Object | H.cs:17:16:17:18 | access to local variable ret : A [field FieldA] : Object | H.cs:24:21:24:28 | call to method Clone : A [field FieldA] : Object | +| H.cs:44:27:44:27 | access to local variable a : A [field FieldA] : Object | H.cs:33:19:33:19 | a : A [field FieldA] : Object | H.cs:37:16:37:16 | access to local variable b : B [field FieldB] : Object | H.cs:44:17:44:28 | call to method Transform : B [field FieldB] : Object | | H.cs:44:27:44:27 | access to local variable a : A [field FieldA] : Object | H.cs:33:19:33:19 | a : A [field FieldA] : Object | H.cs:37:16:37:16 | access to local variable b : B [field FieldB] : Object | H.cs:44:17:44:28 | call to method Transform : B [field FieldB] : Object | | H.cs:64:22:64:22 | access to local variable a : A [field FieldA] : Object | H.cs:53:25:53:25 | a : A [field FieldA] : Object | H.cs:55:9:55:10 | [post] access to parameter b1 : B [field FieldB] : Object | H.cs:64:25:64:26 | [post] access to local variable b1 : B [field FieldB] : Object | +| H.cs:64:22:64:22 | access to local variable a : A [field FieldA] : Object | H.cs:53:25:53:25 | a : A [field FieldA] : Object | H.cs:55:9:55:10 | [post] access to parameter b1 : B [field FieldB] : Object | H.cs:64:25:64:26 | [post] access to local variable b1 : B [field FieldB] : Object | +| H.cs:80:22:80:22 | access to parameter a : A [field FieldA] : Object | H.cs:53:25:53:25 | a : A [field FieldA] : Object | H.cs:55:9:55:10 | [post] access to parameter b1 : B [field FieldB] : Object | H.cs:80:25:80:26 | [post] access to parameter b1 : B [field FieldB] : Object | | H.cs:80:22:80:22 | access to parameter a : A [field FieldA] : Object | H.cs:53:25:53:25 | a : A [field FieldA] : Object | H.cs:55:9:55:10 | [post] access to parameter b1 : B [field FieldB] : Object | H.cs:80:25:80:26 | [post] access to parameter b1 : B [field FieldB] : Object | | H.cs:88:20:88:36 | call to method Source : Object | H.cs:77:30:77:30 | o : Object | H.cs:79:9:79:9 | [post] access to parameter a : A [field FieldA] : Object | H.cs:88:17:88:17 | [post] access to local variable a : A [field FieldA] : Object | +| H.cs:88:20:88:36 | call to method Source : Object | H.cs:77:30:77:30 | o : Object | H.cs:79:9:79:9 | [post] access to parameter a : A [field FieldA] : Object | H.cs:88:17:88:17 | [post] access to local variable a : A [field FieldA] : Object | +| H.cs:88:20:88:36 | call to method Source : Object | H.cs:77:30:77:30 | o : Object | H.cs:80:25:80:26 | [post] access to parameter b1 : B [field FieldB] : Object | H.cs:88:39:88:40 | [post] access to local variable b1 : B [field FieldB] : Object | | H.cs:88:20:88:36 | call to method Source : Object | H.cs:77:30:77:30 | o : Object | H.cs:80:25:80:26 | [post] access to parameter b1 : B [field FieldB] : Object | H.cs:88:39:88:40 | [post] access to local variable b1 : B [field FieldB] : Object | | H.cs:106:26:106:39 | (...) ... : A [field FieldA] : Object | H.cs:33:19:33:19 | a : A [field FieldA] : Object | H.cs:37:16:37:16 | access to local variable b : B [field FieldB] : Object | H.cs:106:16:106:40 | call to method Transform : B [field FieldB] : Object | +| H.cs:106:26:106:39 | (...) ... : A [field FieldA] : Object | H.cs:33:19:33:19 | a : A [field FieldA] : Object | H.cs:37:16:37:16 | access to local variable b : B [field FieldB] : Object | H.cs:106:16:106:40 | call to method Transform : B [field FieldB] : Object | +| H.cs:113:31:113:31 | access to local variable a : A [field FieldA] : Object | H.cs:102:23:102:23 | a : A [field FieldA] : Object | H.cs:106:16:106:40 | call to method Transform : B [field FieldB] : Object | H.cs:113:17:113:32 | call to method TransformWrap : B [field FieldB] : Object | | H.cs:113:31:113:31 | access to local variable a : A [field FieldA] : Object | H.cs:102:23:102:23 | a : A [field FieldA] : Object | H.cs:106:16:106:40 | call to method Transform : B [field FieldB] : Object | H.cs:113:17:113:32 | call to method TransformWrap : B [field FieldB] : Object | | H.cs:124:26:124:26 | access to parameter a : A [field FieldA] : Object | H.cs:33:19:33:19 | a : A [field FieldA] : Object | H.cs:37:16:37:16 | access to local variable b : B [field FieldB] : Object | H.cs:124:16:124:27 | call to method Transform : B [field FieldB] : Object | +| H.cs:124:26:124:26 | access to parameter a : A [field FieldA] : Object | H.cs:33:19:33:19 | a : A [field FieldA] : Object | H.cs:37:16:37:16 | access to local variable b : B [field FieldB] : Object | H.cs:124:16:124:27 | call to method Transform : B [field FieldB] : Object | +| H.cs:131:18:131:18 | access to local variable a : A [field FieldA] : Object | H.cs:122:18:122:18 | a : A [field FieldA] : Object | H.cs:124:16:124:34 | access to field FieldB : Object | H.cs:131:14:131:19 | call to method Get | | H.cs:131:18:131:18 | access to local variable a : A [field FieldA] : Object | H.cs:122:18:122:18 | a : A [field FieldA] : Object | H.cs:124:16:124:34 | access to field FieldB : Object | H.cs:131:14:131:19 | call to method Get | | H.cs:142:26:142:26 | access to local variable a : A [field FieldA] : A | H.cs:33:19:33:19 | a : A [field FieldA] : A | H.cs:37:16:37:16 | access to local variable b : B [field FieldB] : A | H.cs:142:16:142:27 | call to method Transform : B [field FieldB] : A | +| H.cs:142:26:142:26 | access to local variable a : A [field FieldA] : A | H.cs:33:19:33:19 | a : A [field FieldA] : A | H.cs:37:16:37:16 | access to local variable b : B [field FieldB] : A | H.cs:142:16:142:27 | call to method Transform : B [field FieldB] : A | +| H.cs:147:25:147:38 | call to method Source : A | H.cs:138:27:138:27 | o : A | H.cs:142:16:142:34 | access to field FieldB : A | H.cs:147:17:147:39 | call to method Through : A | | H.cs:147:25:147:38 | call to method Source : A | H.cs:138:27:138:27 | o : A | H.cs:142:16:142:34 | access to field FieldB : A | H.cs:147:17:147:39 | call to method Through : A | | H.cs:164:22:164:22 | access to local variable o : Object | H.cs:153:32:153:32 | o : Object | H.cs:157:9:157:9 | [post] access to parameter a : A [field FieldA, field FieldB] : Object | H.cs:164:19:164:19 | [post] access to local variable a : A [field FieldA, field FieldB] : Object | +| H.cs:164:22:164:22 | access to local variable o : Object | H.cs:153:32:153:32 | o : Object | H.cs:157:9:157:9 | [post] access to parameter a : A [field FieldA, field FieldB] : Object | H.cs:164:19:164:19 | [post] access to local variable a : A [field FieldA, field FieldB] : Object | | J.cs:62:29:62:29 | access to local variable o : Object | J.cs:14:26:14:30 | field : Object | J.cs:14:50:14:54 | [post] this access : Struct [field Field] : Object | J.cs:62:18:62:36 | object creation of type Struct : Struct [field Field] : Object | +| J.cs:62:29:62:29 | access to local variable o : Object | J.cs:14:26:14:30 | field : Object | J.cs:14:50:14:54 | [post] this access : Struct [field Field] : Object | J.cs:62:18:62:36 | object creation of type Struct : Struct [field Field] : Object | +| J.cs:80:35:80:35 | access to local variable o : Object | J.cs:14:40:14:43 | prop : Object | J.cs:14:57:14:60 | [post] this access : Struct [property Prop] : Object | J.cs:80:18:80:36 | object creation of type Struct : Struct [property Prop] : Object | | J.cs:80:35:80:35 | access to local variable o : Object | J.cs:14:40:14:43 | prop : Object | J.cs:14:57:14:60 | [post] this access : Struct [property Prop] : Object | J.cs:80:18:80:36 | object creation of type Struct : Struct [property Prop] : Object | #select | A.cs:7:14:7:16 | access to field c | A.cs:5:17:5:28 | call to method Source : C | A.cs:7:14:7:16 | access to field c | $@ | A.cs:5:17:5:28 | call to method Source : C | call to method Source : C | +| A.cs:7:14:7:16 | access to field c | A.cs:5:17:5:28 | call to method Source : C | A.cs:7:14:7:16 | access to field c | $@ | A.cs:5:17:5:28 | call to method Source : C | call to method Source : C | +| A.cs:14:14:14:20 | call to method Get | A.cs:13:15:13:29 | call to method Source : C1 | A.cs:14:14:14:20 | call to method Get | $@ | A.cs:13:15:13:29 | call to method Source : C1 | call to method Source : C1 | | A.cs:14:14:14:20 | call to method Get | A.cs:13:15:13:29 | call to method Source : C1 | A.cs:14:14:14:20 | call to method Get | $@ | A.cs:13:15:13:29 | call to method Source : C1 | call to method Source : C1 | | A.cs:15:14:15:42 | call to method Get | A.cs:15:21:15:34 | call to method Source : C | A.cs:15:14:15:42 | call to method Get | $@ | A.cs:15:21:15:34 | call to method Source : C | call to method Source : C | +| A.cs:15:14:15:42 | call to method Get | A.cs:15:21:15:34 | call to method Source : C | A.cs:15:14:15:42 | call to method Get | $@ | A.cs:15:21:15:34 | call to method Source : C | call to method Source : C | +| A.cs:24:14:24:17 | access to field c | A.cs:22:25:22:37 | call to method Source : C2 | A.cs:24:14:24:17 | access to field c | $@ | A.cs:22:25:22:37 | call to method Source : C2 | call to method Source : C2 | | A.cs:24:14:24:17 | access to field c | A.cs:22:25:22:37 | call to method Source : C2 | A.cs:24:14:24:17 | access to field c | $@ | A.cs:22:25:22:37 | call to method Source : C2 | call to method Source : C2 | | A.cs:33:14:33:17 | access to field c | A.cs:31:29:31:41 | call to method Source : C2 | A.cs:33:14:33:17 | access to field c | $@ | A.cs:31:29:31:41 | call to method Source : C2 | call to method Source : C2 | +| A.cs:33:14:33:17 | access to field c | A.cs:31:29:31:41 | call to method Source : C2 | A.cs:33:14:33:17 | access to field c | $@ | A.cs:31:29:31:41 | call to method Source : C2 | call to method Source : C2 | +| A.cs:64:18:64:26 | access to field a | A.cs:55:17:55:28 | call to method Source : A | A.cs:64:18:64:26 | access to field a | $@ | A.cs:55:17:55:28 | call to method Source : A | call to method Source : A | | A.cs:64:18:64:26 | access to field a | A.cs:55:17:55:28 | call to method Source : A | A.cs:64:18:64:26 | access to field a | $@ | A.cs:55:17:55:28 | call to method Source : A | call to method Source : A | | A.cs:89:14:89:16 | access to field c | A.cs:83:15:83:26 | call to method Source : C | A.cs:89:14:89:16 | access to field c | $@ | A.cs:83:15:83:26 | call to method Source : C | call to method Source : C | +| A.cs:89:14:89:16 | access to field c | A.cs:83:15:83:26 | call to method Source : C | A.cs:89:14:89:16 | access to field c | $@ | A.cs:83:15:83:26 | call to method Source : C | call to method Source : C | +| A.cs:106:14:106:16 | access to field b | A.cs:98:30:98:43 | call to method Source : B | A.cs:106:14:106:16 | access to field b | $@ | A.cs:98:30:98:43 | call to method Source : B | call to method Source : B | | A.cs:106:14:106:16 | access to field b | A.cs:98:30:98:43 | call to method Source : B | A.cs:106:14:106:16 | access to field b | $@ | A.cs:98:30:98:43 | call to method Source : B | call to method Source : B | | A.cs:106:14:106:16 | access to field b | A.cs:104:17:104:30 | call to method Source : B | A.cs:106:14:106:16 | access to field b | $@ | A.cs:104:17:104:30 | call to method Source : B | call to method Source : B | +| A.cs:106:14:106:16 | access to field b | A.cs:104:17:104:30 | call to method Source : B | A.cs:106:14:106:16 | access to field b | $@ | A.cs:104:17:104:30 | call to method Source : B | call to method Source : B | +| A.cs:107:14:107:18 | access to field c | A.cs:97:19:97:32 | call to method Source : C | A.cs:107:14:107:18 | access to field c | $@ | A.cs:97:19:97:32 | call to method Source : C | call to method Source : C | | A.cs:107:14:107:18 | access to field c | A.cs:97:19:97:32 | call to method Source : C | A.cs:107:14:107:18 | access to field c | $@ | A.cs:97:19:97:32 | call to method Source : C | call to method Source : C | | A.cs:108:14:108:16 | access to field c | A.cs:97:19:97:32 | call to method Source : C | A.cs:108:14:108:16 | access to field c | $@ | A.cs:97:19:97:32 | call to method Source : C | call to method Source : C | +| A.cs:108:14:108:16 | access to field c | A.cs:97:19:97:32 | call to method Source : C | A.cs:108:14:108:16 | access to field c | $@ | A.cs:97:19:97:32 | call to method Source : C | call to method Source : C | +| A.cs:119:14:119:30 | access to field head | A.cs:113:17:113:29 | call to method Source : B | A.cs:119:14:119:30 | access to field head | $@ | A.cs:113:17:113:29 | call to method Source : B | call to method Source : B | | A.cs:119:14:119:30 | access to field head | A.cs:113:17:113:29 | call to method Source : B | A.cs:119:14:119:30 | access to field head | $@ | A.cs:113:17:113:29 | call to method Source : B | call to method Source : B | | A.cs:123:18:123:23 | access to field head | A.cs:113:17:113:29 | call to method Source : B | A.cs:123:18:123:23 | access to field head | $@ | A.cs:113:17:113:29 | call to method Source : B | call to method Source : B | +| A.cs:123:18:123:23 | access to field head | A.cs:113:17:113:29 | call to method Source : B | A.cs:123:18:123:23 | access to field head | $@ | A.cs:113:17:113:29 | call to method Source : B | call to method Source : B | +| B.cs:8:14:8:26 | access to field elem1 | B.cs:5:17:5:31 | call to method Source : Elem | B.cs:8:14:8:26 | access to field elem1 | $@ | B.cs:5:17:5:31 | call to method Source : Elem | call to method Source : Elem | | B.cs:8:14:8:26 | access to field elem1 | B.cs:5:17:5:31 | call to method Source : Elem | B.cs:8:14:8:26 | access to field elem1 | $@ | B.cs:5:17:5:31 | call to method Source : Elem | call to method Source : Elem | | B.cs:18:14:18:26 | access to field elem2 | B.cs:14:17:14:31 | call to method Source : Elem | B.cs:18:14:18:26 | access to field elem2 | $@ | B.cs:14:17:14:31 | call to method Source : Elem | call to method Source : Elem | +| B.cs:18:14:18:26 | access to field elem2 | B.cs:14:17:14:31 | call to method Source : Elem | B.cs:18:14:18:26 | access to field elem2 | $@ | B.cs:14:17:14:31 | call to method Source : Elem | call to method Source : Elem | +| C.cs:23:14:23:15 | access to field s1 | C.cs:3:23:3:37 | call to method Source : Elem | C.cs:23:14:23:15 | access to field s1 | $@ | C.cs:3:23:3:37 | call to method Source : Elem | call to method Source : Elem | | C.cs:23:14:23:15 | access to field s1 | C.cs:3:23:3:37 | call to method Source : Elem | C.cs:23:14:23:15 | access to field s1 | $@ | C.cs:3:23:3:37 | call to method Source : Elem | call to method Source : Elem | | C.cs:24:14:24:15 | access to field s2 | C.cs:4:32:4:46 | call to method Source : Elem | C.cs:24:14:24:15 | access to field s2 | $@ | C.cs:4:32:4:46 | call to method Source : Elem | call to method Source : Elem | +| C.cs:24:14:24:15 | access to field s2 | C.cs:4:32:4:46 | call to method Source : Elem | C.cs:24:14:24:15 | access to field s2 | $@ | C.cs:4:32:4:46 | call to method Source : Elem | call to method Source : Elem | +| C.cs:25:14:25:15 | access to field s3 | C.cs:18:19:18:33 | call to method Source : Elem | C.cs:25:14:25:15 | access to field s3 | $@ | C.cs:18:19:18:33 | call to method Source : Elem | call to method Source : Elem | | C.cs:25:14:25:15 | access to field s3 | C.cs:18:19:18:33 | call to method Source : Elem | C.cs:25:14:25:15 | access to field s3 | $@ | C.cs:18:19:18:33 | call to method Source : Elem | call to method Source : Elem | | C.cs:26:14:26:15 | access to field s4 | C.cs:6:30:6:44 | call to method Source : Elem | C.cs:26:14:26:15 | access to field s4 | $@ | C.cs:6:30:6:44 | call to method Source : Elem | call to method Source : Elem | +| C.cs:26:14:26:15 | access to field s4 | C.cs:6:30:6:44 | call to method Source : Elem | C.cs:26:14:26:15 | access to field s4 | $@ | C.cs:6:30:6:44 | call to method Source : Elem | call to method Source : Elem | +| C.cs:27:14:27:15 | access to property s5 | C.cs:7:37:7:51 | call to method Source : Elem | C.cs:27:14:27:15 | access to property s5 | $@ | C.cs:7:37:7:51 | call to method Source : Elem | call to method Source : Elem | | C.cs:27:14:27:15 | access to property s5 | C.cs:7:37:7:51 | call to method Source : Elem | C.cs:27:14:27:15 | access to property s5 | $@ | C.cs:7:37:7:51 | call to method Source : Elem | call to method Source : Elem | | C.cs:28:14:28:15 | access to property s6 | C.cs:8:30:8:44 | call to method Source : Elem | C.cs:28:14:28:15 | access to property s6 | $@ | C.cs:8:30:8:44 | call to method Source : Elem | call to method Source : Elem | +| C.cs:28:14:28:15 | access to property s6 | C.cs:8:30:8:44 | call to method Source : Elem | C.cs:28:14:28:15 | access to property s6 | $@ | C.cs:8:30:8:44 | call to method Source : Elem | call to method Source : Elem | +| C_ctor.cs:13:19:13:20 | access to field s1 | C_ctor.cs:3:23:3:42 | call to method Source : Elem | C_ctor.cs:13:19:13:20 | access to field s1 | $@ | C_ctor.cs:3:23:3:42 | call to method Source : Elem | call to method Source : Elem | | C_ctor.cs:13:19:13:20 | access to field s1 | C_ctor.cs:3:23:3:42 | call to method Source : Elem | C_ctor.cs:13:19:13:20 | access to field s1 | $@ | C_ctor.cs:3:23:3:42 | call to method Source : Elem | call to method Source : Elem | | C_ctor.cs:31:19:31:20 | access to field s1 | C_ctor.cs:19:23:19:42 | call to method Source : Elem | C_ctor.cs:31:19:31:20 | access to field s1 | $@ | C_ctor.cs:19:23:19:42 | call to method Source : Elem | call to method Source : Elem | +| C_ctor.cs:31:19:31:20 | access to field s1 | C_ctor.cs:19:23:19:42 | call to method Source : Elem | C_ctor.cs:31:19:31:20 | access to field s1 | $@ | C_ctor.cs:19:23:19:42 | call to method Source : Elem | call to method Source : Elem | +| D.cs:32:14:32:23 | access to property AutoProp | D.cs:29:17:29:33 | call to method Source : Object | D.cs:32:14:32:23 | access to property AutoProp | $@ | D.cs:29:17:29:33 | call to method Source : Object | call to method Source : Object | | D.cs:32:14:32:23 | access to property AutoProp | D.cs:29:17:29:33 | call to method Source : Object | D.cs:32:14:32:23 | access to property AutoProp | $@ | D.cs:29:17:29:33 | call to method Source : Object | call to method Source : Object | | D.cs:39:14:39:26 | access to property TrivialProp | D.cs:37:26:37:42 | call to method Source : Object | D.cs:39:14:39:26 | access to property TrivialProp | $@ | D.cs:37:26:37:42 | call to method Source : Object | call to method Source : Object | +| D.cs:39:14:39:26 | access to property TrivialProp | D.cs:37:26:37:42 | call to method Source : Object | D.cs:39:14:39:26 | access to property TrivialProp | $@ | D.cs:37:26:37:42 | call to method Source : Object | call to method Source : Object | +| D.cs:40:14:40:31 | access to field trivialPropField | D.cs:37:26:37:42 | call to method Source : Object | D.cs:40:14:40:31 | access to field trivialPropField | $@ | D.cs:37:26:37:42 | call to method Source : Object | call to method Source : Object | | D.cs:40:14:40:31 | access to field trivialPropField | D.cs:37:26:37:42 | call to method Source : Object | D.cs:40:14:40:31 | access to field trivialPropField | $@ | D.cs:37:26:37:42 | call to method Source : Object | call to method Source : Object | | D.cs:41:14:41:26 | access to property ComplexProp | D.cs:37:26:37:42 | call to method Source : Object | D.cs:41:14:41:26 | access to property ComplexProp | $@ | D.cs:37:26:37:42 | call to method Source : Object | call to method Source : Object | +| D.cs:41:14:41:26 | access to property ComplexProp | D.cs:37:26:37:42 | call to method Source : Object | D.cs:41:14:41:26 | access to property ComplexProp | $@ | D.cs:37:26:37:42 | call to method Source : Object | call to method Source : Object | +| D.cs:45:14:45:26 | access to property TrivialProp | D.cs:43:32:43:48 | call to method Source : Object | D.cs:45:14:45:26 | access to property TrivialProp | $@ | D.cs:43:32:43:48 | call to method Source : Object | call to method Source : Object | | D.cs:45:14:45:26 | access to property TrivialProp | D.cs:43:32:43:48 | call to method Source : Object | D.cs:45:14:45:26 | access to property TrivialProp | $@ | D.cs:43:32:43:48 | call to method Source : Object | call to method Source : Object | | D.cs:46:14:46:31 | access to field trivialPropField | D.cs:43:32:43:48 | call to method Source : Object | D.cs:46:14:46:31 | access to field trivialPropField | $@ | D.cs:43:32:43:48 | call to method Source : Object | call to method Source : Object | +| D.cs:46:14:46:31 | access to field trivialPropField | D.cs:43:32:43:48 | call to method Source : Object | D.cs:46:14:46:31 | access to field trivialPropField | $@ | D.cs:43:32:43:48 | call to method Source : Object | call to method Source : Object | +| D.cs:47:14:47:26 | access to property ComplexProp | D.cs:43:32:43:48 | call to method Source : Object | D.cs:47:14:47:26 | access to property ComplexProp | $@ | D.cs:43:32:43:48 | call to method Source : Object | call to method Source : Object | | D.cs:47:14:47:26 | access to property ComplexProp | D.cs:43:32:43:48 | call to method Source : Object | D.cs:47:14:47:26 | access to property ComplexProp | $@ | D.cs:43:32:43:48 | call to method Source : Object | call to method Source : Object | | E.cs:24:14:24:20 | access to field Field | E.cs:22:17:22:33 | call to method Source : Object | E.cs:24:14:24:20 | access to field Field | $@ | E.cs:22:17:22:33 | call to method Source : Object | call to method Source : Object | +| E.cs:24:14:24:20 | access to field Field | E.cs:22:17:22:33 | call to method Source : Object | E.cs:24:14:24:20 | access to field Field | $@ | E.cs:22:17:22:33 | call to method Source : Object | call to method Source : Object | +| F.cs:12:14:12:21 | access to field Field1 | F.cs:10:17:10:33 | call to method Source : Object | F.cs:12:14:12:21 | access to field Field1 | $@ | F.cs:10:17:10:33 | call to method Source : Object | call to method Source : Object | | F.cs:12:14:12:21 | access to field Field1 | F.cs:10:17:10:33 | call to method Source : Object | F.cs:12:14:12:21 | access to field Field1 | $@ | F.cs:10:17:10:33 | call to method Source : Object | call to method Source : Object | | F.cs:17:14:17:21 | access to field Field2 | F.cs:15:26:15:42 | call to method Source : Object | F.cs:17:14:17:21 | access to field Field2 | $@ | F.cs:15:26:15:42 | call to method Source : Object | call to method Source : Object | +| F.cs:17:14:17:21 | access to field Field2 | F.cs:15:26:15:42 | call to method Source : Object | F.cs:17:14:17:21 | access to field Field2 | $@ | F.cs:15:26:15:42 | call to method Source : Object | call to method Source : Object | +| F.cs:20:14:20:21 | access to field Field1 | F.cs:19:32:19:48 | call to method Source : Object | F.cs:20:14:20:21 | access to field Field1 | $@ | F.cs:19:32:19:48 | call to method Source : Object | call to method Source : Object | | F.cs:20:14:20:21 | access to field Field1 | F.cs:19:32:19:48 | call to method Source : Object | F.cs:20:14:20:21 | access to field Field1 | $@ | F.cs:19:32:19:48 | call to method Source : Object | call to method Source : Object | | F.cs:25:14:25:21 | access to field Field2 | F.cs:23:32:23:48 | call to method Source : Object | F.cs:25:14:25:21 | access to field Field2 | $@ | F.cs:23:32:23:48 | call to method Source : Object | call to method Source : Object | +| F.cs:25:14:25:21 | access to field Field2 | F.cs:23:32:23:48 | call to method Source : Object | F.cs:25:14:25:21 | access to field Field2 | $@ | F.cs:23:32:23:48 | call to method Source : Object | call to method Source : Object | +| F.cs:33:14:33:16 | access to property X | F.cs:30:17:30:33 | call to method Source : Object | F.cs:33:14:33:16 | access to property X | $@ | F.cs:30:17:30:33 | call to method Source : Object | call to method Source : Object | | F.cs:33:14:33:16 | access to property X | F.cs:30:17:30:33 | call to method Source : Object | F.cs:33:14:33:16 | access to property X | $@ | F.cs:30:17:30:33 | call to method Source : Object | call to method Source : Object | | G.cs:39:14:39:35 | call to method GetElem | G.cs:7:18:7:32 | call to method Source : Elem | G.cs:39:14:39:35 | call to method GetElem | $@ | G.cs:7:18:7:32 | call to method Source : Elem | call to method Source : Elem | +| G.cs:39:14:39:35 | call to method GetElem | G.cs:7:18:7:32 | call to method Source : Elem | G.cs:39:14:39:35 | call to method GetElem | $@ | G.cs:7:18:7:32 | call to method Source : Elem | call to method Source : Elem | +| G.cs:39:14:39:35 | call to method GetElem | G.cs:15:18:15:32 | call to method Source : Elem | G.cs:39:14:39:35 | call to method GetElem | $@ | G.cs:15:18:15:32 | call to method Source : Elem | call to method Source : Elem | | G.cs:39:14:39:35 | call to method GetElem | G.cs:15:18:15:32 | call to method Source : Elem | G.cs:39:14:39:35 | call to method GetElem | $@ | G.cs:15:18:15:32 | call to method Source : Elem | call to method Source : Elem | | G.cs:39:14:39:35 | call to method GetElem | G.cs:23:18:23:32 | call to method Source : Elem | G.cs:39:14:39:35 | call to method GetElem | $@ | G.cs:23:18:23:32 | call to method Source : Elem | call to method Source : Elem | +| G.cs:39:14:39:35 | call to method GetElem | G.cs:23:18:23:32 | call to method Source : Elem | G.cs:39:14:39:35 | call to method GetElem | $@ | G.cs:23:18:23:32 | call to method Source : Elem | call to method Source : Elem | +| G.cs:39:14:39:35 | call to method GetElem | G.cs:31:18:31:32 | call to method Source : Elem | G.cs:39:14:39:35 | call to method GetElem | $@ | G.cs:31:18:31:32 | call to method Source : Elem | call to method Source : Elem | | G.cs:39:14:39:35 | call to method GetElem | G.cs:31:18:31:32 | call to method Source : Elem | G.cs:39:14:39:35 | call to method GetElem | $@ | G.cs:31:18:31:32 | call to method Source : Elem | call to method Source : Elem | | G.cs:52:14:52:31 | access to field Elem | G.cs:44:18:44:32 | call to method Source : Elem | G.cs:52:14:52:31 | access to field Elem | $@ | G.cs:44:18:44:32 | call to method Source : Elem | call to method Source : Elem | +| G.cs:52:14:52:31 | access to field Elem | G.cs:44:18:44:32 | call to method Source : Elem | G.cs:52:14:52:31 | access to field Elem | $@ | G.cs:44:18:44:32 | call to method Source : Elem | call to method Source : Elem | +| H.cs:25:14:25:25 | access to field FieldA | H.cs:23:20:23:36 | call to method Source : Object | H.cs:25:14:25:25 | access to field FieldA | $@ | H.cs:23:20:23:36 | call to method Source : Object | call to method Source : Object | | H.cs:25:14:25:25 | access to field FieldA | H.cs:23:20:23:36 | call to method Source : Object | H.cs:25:14:25:25 | access to field FieldA | $@ | H.cs:23:20:23:36 | call to method Source : Object | call to method Source : Object | | H.cs:45:14:45:21 | access to field FieldB | H.cs:43:20:43:36 | call to method Source : Object | H.cs:45:14:45:21 | access to field FieldB | $@ | H.cs:43:20:43:36 | call to method Source : Object | call to method Source : Object | +| H.cs:45:14:45:21 | access to field FieldB | H.cs:43:20:43:36 | call to method Source : Object | H.cs:45:14:45:21 | access to field FieldB | $@ | H.cs:43:20:43:36 | call to method Source : Object | call to method Source : Object | +| H.cs:65:14:65:22 | access to field FieldB | H.cs:63:20:63:36 | call to method Source : Object | H.cs:65:14:65:22 | access to field FieldB | $@ | H.cs:63:20:63:36 | call to method Source : Object | call to method Source : Object | | H.cs:65:14:65:22 | access to field FieldB | H.cs:63:20:63:36 | call to method Source : Object | H.cs:65:14:65:22 | access to field FieldB | $@ | H.cs:63:20:63:36 | call to method Source : Object | call to method Source : Object | | H.cs:89:14:89:21 | access to field FieldA | H.cs:88:20:88:36 | call to method Source : Object | H.cs:89:14:89:21 | access to field FieldA | $@ | H.cs:88:20:88:36 | call to method Source : Object | call to method Source : Object | +| H.cs:89:14:89:21 | access to field FieldA | H.cs:88:20:88:36 | call to method Source : Object | H.cs:89:14:89:21 | access to field FieldA | $@ | H.cs:88:20:88:36 | call to method Source : Object | call to method Source : Object | +| H.cs:90:14:90:22 | access to field FieldB | H.cs:88:20:88:36 | call to method Source : Object | H.cs:90:14:90:22 | access to field FieldB | $@ | H.cs:88:20:88:36 | call to method Source : Object | call to method Source : Object | | H.cs:90:14:90:22 | access to field FieldB | H.cs:88:20:88:36 | call to method Source : Object | H.cs:90:14:90:22 | access to field FieldB | $@ | H.cs:88:20:88:36 | call to method Source : Object | call to method Source : Object | | H.cs:114:14:114:21 | access to field FieldB | H.cs:112:20:112:36 | call to method Source : Object | H.cs:114:14:114:21 | access to field FieldB | $@ | H.cs:112:20:112:36 | call to method Source : Object | call to method Source : Object | +| H.cs:114:14:114:21 | access to field FieldB | H.cs:112:20:112:36 | call to method Source : Object | H.cs:114:14:114:21 | access to field FieldB | $@ | H.cs:112:20:112:36 | call to method Source : Object | call to method Source : Object | +| H.cs:131:14:131:19 | call to method Get | H.cs:130:20:130:36 | call to method Source : Object | H.cs:131:14:131:19 | call to method Get | $@ | H.cs:130:20:130:36 | call to method Source : Object | call to method Source : Object | | H.cs:131:14:131:19 | call to method Get | H.cs:130:20:130:36 | call to method Source : Object | H.cs:131:14:131:19 | call to method Get | $@ | H.cs:130:20:130:36 | call to method Source : Object | call to method Source : Object | | H.cs:148:14:148:14 | access to local variable a | H.cs:147:25:147:38 | call to method Source : A | H.cs:148:14:148:14 | access to local variable a | $@ | H.cs:147:25:147:38 | call to method Source : A | call to method Source : A | +| H.cs:148:14:148:14 | access to local variable a | H.cs:147:25:147:38 | call to method Source : A | H.cs:148:14:148:14 | access to local variable a | $@ | H.cs:147:25:147:38 | call to method Source : A | call to method Source : A | +| H.cs:166:14:166:14 | access to local variable b | H.cs:155:17:155:30 | call to method Source : B | H.cs:166:14:166:14 | access to local variable b | $@ | H.cs:155:17:155:30 | call to method Source : B | call to method Source : B | | H.cs:166:14:166:14 | access to local variable b | H.cs:155:17:155:30 | call to method Source : B | H.cs:166:14:166:14 | access to local variable b | $@ | H.cs:155:17:155:30 | call to method Source : B | call to method Source : B | | H.cs:167:14:167:21 | access to field FieldB | H.cs:163:17:163:35 | call to method Source : Object | H.cs:167:14:167:21 | access to field FieldB | $@ | H.cs:163:17:163:35 | call to method Source : Object | call to method Source : Object | +| H.cs:167:14:167:21 | access to field FieldB | H.cs:163:17:163:35 | call to method Source : Object | H.cs:167:14:167:21 | access to field FieldB | $@ | H.cs:163:17:163:35 | call to method Source : Object | call to method Source : Object | +| I.cs:18:14:18:21 | access to field Field1 | I.cs:13:17:13:33 | call to method Source : Object | I.cs:18:14:18:21 | access to field Field1 | $@ | I.cs:13:17:13:33 | call to method Source : Object | call to method Source : Object | | I.cs:18:14:18:21 | access to field Field1 | I.cs:13:17:13:33 | call to method Source : Object | I.cs:18:14:18:21 | access to field Field1 | $@ | I.cs:13:17:13:33 | call to method Source : Object | call to method Source : Object | | I.cs:23:14:23:21 | access to field Field1 | I.cs:7:18:7:34 | call to method Source : Object | I.cs:23:14:23:21 | access to field Field1 | $@ | I.cs:7:18:7:34 | call to method Source : Object | call to method Source : Object | +| I.cs:23:14:23:21 | access to field Field1 | I.cs:7:18:7:34 | call to method Source : Object | I.cs:23:14:23:21 | access to field Field1 | $@ | I.cs:7:18:7:34 | call to method Source : Object | call to method Source : Object | +| I.cs:27:14:27:21 | access to field Field1 | I.cs:7:18:7:34 | call to method Source : Object | I.cs:27:14:27:21 | access to field Field1 | $@ | I.cs:7:18:7:34 | call to method Source : Object | call to method Source : Object | | I.cs:27:14:27:21 | access to field Field1 | I.cs:7:18:7:34 | call to method Source : Object | I.cs:27:14:27:21 | access to field Field1 | $@ | I.cs:7:18:7:34 | call to method Source : Object | call to method Source : Object | | I.cs:40:14:40:21 | access to field Field1 | I.cs:31:13:31:29 | call to method Source : Object | I.cs:40:14:40:21 | access to field Field1 | $@ | I.cs:31:13:31:29 | call to method Source : Object | call to method Source : Object | +| I.cs:40:14:40:21 | access to field Field1 | I.cs:31:13:31:29 | call to method Source : Object | I.cs:40:14:40:21 | access to field Field1 | $@ | I.cs:31:13:31:29 | call to method Source : Object | call to method Source : Object | +| J.cs:23:14:23:21 | access to property Prop1 | J.cs:21:17:21:33 | call to method Source : Object | J.cs:23:14:23:21 | access to property Prop1 | $@ | J.cs:21:17:21:33 | call to method Source : Object | call to method Source : Object | | J.cs:23:14:23:21 | access to property Prop1 | J.cs:21:17:21:33 | call to method Source : Object | J.cs:23:14:23:21 | access to property Prop1 | $@ | J.cs:21:17:21:33 | call to method Source : Object | call to method Source : Object | | J.cs:27:14:27:21 | access to property Prop1 | J.cs:21:17:21:33 | call to method Source : Object | J.cs:27:14:27:21 | access to property Prop1 | $@ | J.cs:21:17:21:33 | call to method Source : Object | call to method Source : Object | +| J.cs:27:14:27:21 | access to property Prop1 | J.cs:21:17:21:33 | call to method Source : Object | J.cs:27:14:27:21 | access to property Prop1 | $@ | J.cs:21:17:21:33 | call to method Source : Object | call to method Source : Object | +| J.cs:31:14:31:21 | access to property Prop1 | J.cs:21:17:21:33 | call to method Source : Object | J.cs:31:14:31:21 | access to property Prop1 | $@ | J.cs:21:17:21:33 | call to method Source : Object | call to method Source : Object | | J.cs:31:14:31:21 | access to property Prop1 | J.cs:21:17:21:33 | call to method Source : Object | J.cs:31:14:31:21 | access to property Prop1 | $@ | J.cs:21:17:21:33 | call to method Source : Object | call to method Source : Object | | J.cs:32:14:32:21 | access to property Prop2 | J.cs:30:36:30:52 | call to method Source : Object | J.cs:32:14:32:21 | access to property Prop2 | $@ | J.cs:30:36:30:52 | call to method Source : Object | call to method Source : Object | +| J.cs:32:14:32:21 | access to property Prop2 | J.cs:30:36:30:52 | call to method Source : Object | J.cs:32:14:32:21 | access to property Prop2 | $@ | J.cs:30:36:30:52 | call to method Source : Object | call to method Source : Object | +| J.cs:43:14:43:21 | access to property Prop1 | J.cs:41:17:41:33 | call to method Source : Object | J.cs:43:14:43:21 | access to property Prop1 | $@ | J.cs:41:17:41:33 | call to method Source : Object | call to method Source : Object | | J.cs:43:14:43:21 | access to property Prop1 | J.cs:41:17:41:33 | call to method Source : Object | J.cs:43:14:43:21 | access to property Prop1 | $@ | J.cs:41:17:41:33 | call to method Source : Object | call to method Source : Object | | J.cs:47:14:47:21 | access to property Prop1 | J.cs:41:17:41:33 | call to method Source : Object | J.cs:47:14:47:21 | access to property Prop1 | $@ | J.cs:41:17:41:33 | call to method Source : Object | call to method Source : Object | +| J.cs:47:14:47:21 | access to property Prop1 | J.cs:41:17:41:33 | call to method Source : Object | J.cs:47:14:47:21 | access to property Prop1 | $@ | J.cs:41:17:41:33 | call to method Source : Object | call to method Source : Object | +| J.cs:51:14:51:21 | access to property Prop1 | J.cs:41:17:41:33 | call to method Source : Object | J.cs:51:14:51:21 | access to property Prop1 | $@ | J.cs:41:17:41:33 | call to method Source : Object | call to method Source : Object | | J.cs:51:14:51:21 | access to property Prop1 | J.cs:41:17:41:33 | call to method Source : Object | J.cs:51:14:51:21 | access to property Prop1 | $@ | J.cs:41:17:41:33 | call to method Source : Object | call to method Source : Object | | J.cs:52:14:52:21 | access to property Prop2 | J.cs:50:36:50:52 | call to method Source : Object | J.cs:52:14:52:21 | access to property Prop2 | $@ | J.cs:50:36:50:52 | call to method Source : Object | call to method Source : Object | +| J.cs:52:14:52:21 | access to property Prop2 | J.cs:50:36:50:52 | call to method Source : Object | J.cs:52:14:52:21 | access to property Prop2 | $@ | J.cs:50:36:50:52 | call to method Source : Object | call to method Source : Object | +| J.cs:65:14:65:21 | access to field Field | J.cs:61:17:61:33 | call to method Source : Object | J.cs:65:14:65:21 | access to field Field | $@ | J.cs:61:17:61:33 | call to method Source : Object | call to method Source : Object | | J.cs:65:14:65:21 | access to field Field | J.cs:61:17:61:33 | call to method Source : Object | J.cs:65:14:65:21 | access to field Field | $@ | J.cs:61:17:61:33 | call to method Source : Object | call to method Source : Object | | J.cs:69:14:69:21 | access to field Field | J.cs:61:17:61:33 | call to method Source : Object | J.cs:69:14:69:21 | access to field Field | $@ | J.cs:61:17:61:33 | call to method Source : Object | call to method Source : Object | +| J.cs:69:14:69:21 | access to field Field | J.cs:61:17:61:33 | call to method Source : Object | J.cs:69:14:69:21 | access to field Field | $@ | J.cs:61:17:61:33 | call to method Source : Object | call to method Source : Object | +| J.cs:70:14:70:20 | access to property Prop | J.cs:68:35:68:51 | call to method Source : Object | J.cs:70:14:70:20 | access to property Prop | $@ | J.cs:68:35:68:51 | call to method Source : Object | call to method Source : Object | | J.cs:70:14:70:20 | access to property Prop | J.cs:68:35:68:51 | call to method Source : Object | J.cs:70:14:70:20 | access to property Prop | $@ | J.cs:68:35:68:51 | call to method Source : Object | call to method Source : Object | | J.cs:84:14:84:20 | access to property Prop | J.cs:79:17:79:33 | call to method Source : Object | J.cs:84:14:84:20 | access to property Prop | $@ | J.cs:79:17:79:33 | call to method Source : Object | call to method Source : Object | +| J.cs:84:14:84:20 | access to property Prop | J.cs:79:17:79:33 | call to method Source : Object | J.cs:84:14:84:20 | access to property Prop | $@ | J.cs:79:17:79:33 | call to method Source : Object | call to method Source : Object | +| J.cs:87:14:87:21 | access to field Field | J.cs:86:36:86:52 | call to method Source : Object | J.cs:87:14:87:21 | access to field Field | $@ | J.cs:86:36:86:52 | call to method Source : Object | call to method Source : Object | | J.cs:87:14:87:21 | access to field Field | J.cs:86:36:86:52 | call to method Source : Object | J.cs:87:14:87:21 | access to field Field | $@ | J.cs:86:36:86:52 | call to method Source : Object | call to method Source : Object | | J.cs:88:14:88:20 | access to property Prop | J.cs:79:17:79:33 | call to method Source : Object | J.cs:88:14:88:20 | access to property Prop | $@ | J.cs:79:17:79:33 | call to method Source : Object | call to method Source : Object | +| J.cs:88:14:88:20 | access to property Prop | J.cs:79:17:79:33 | call to method Source : Object | J.cs:88:14:88:20 | access to property Prop | $@ | J.cs:79:17:79:33 | call to method Source : Object | call to method Source : Object | +| J.cs:102:14:102:17 | access to property X | J.cs:97:17:97:33 | call to method Source : Object | J.cs:102:14:102:17 | access to property X | $@ | J.cs:97:17:97:33 | call to method Source : Object | call to method Source : Object | | J.cs:102:14:102:17 | access to property X | J.cs:97:17:97:33 | call to method Source : Object | J.cs:102:14:102:17 | access to property X | $@ | J.cs:97:17:97:33 | call to method Source : Object | call to method Source : Object | | J.cs:106:14:106:17 | access to property X | J.cs:97:17:97:33 | call to method Source : Object | J.cs:106:14:106:17 | access to property X | $@ | J.cs:97:17:97:33 | call to method Source : Object | call to method Source : Object | +| J.cs:106:14:106:17 | access to property X | J.cs:97:17:97:33 | call to method Source : Object | J.cs:106:14:106:17 | access to property X | $@ | J.cs:97:17:97:33 | call to method Source : Object | call to method Source : Object | +| J.cs:107:14:107:17 | access to property Y | J.cs:105:32:105:48 | call to method Source : Object | J.cs:107:14:107:17 | access to property Y | $@ | J.cs:105:32:105:48 | call to method Source : Object | call to method Source : Object | | J.cs:107:14:107:17 | access to property Y | J.cs:105:32:105:48 | call to method Source : Object | J.cs:107:14:107:17 | access to property Y | $@ | J.cs:105:32:105:48 | call to method Source : Object | call to method Source : Object | | J.cs:125:14:125:17 | (...) ... | J.cs:119:20:119:34 | call to method Source : Int32 | J.cs:125:14:125:17 | (...) ... | $@ | J.cs:119:20:119:34 | call to method Source : Int32 | call to method Source : Int32 | +| J.cs:125:14:125:17 | (...) ... | J.cs:119:20:119:34 | call to method Source : Int32 | J.cs:125:14:125:17 | (...) ... | $@ | J.cs:119:20:119:34 | call to method Source : Int32 | call to method Source : Int32 | diff --git a/csharp/ql/test/library-tests/dataflow/fields/FieldFlow.ql b/csharp/ql/test/library-tests/dataflow/fields/FieldFlow.ql index b01c0f7fcaf..9336e1b28be 100644 --- a/csharp/ql/test/library-tests/dataflow/fields/FieldFlow.ql +++ b/csharp/ql/test/library-tests/dataflow/fields/FieldFlow.ql @@ -3,9 +3,10 @@ */ import csharp -import DefaultValueFlow::PathGraph import TestUtilities.InlineFlowTest +import DefaultFlowTest +import PathGraph -from DefaultValueFlow::PathNode source, DefaultValueFlow::PathNode sink -where DefaultValueFlow::flowPath(source, sink) +from PathNode source, PathNode sink +where flowPath(source, sink) select sink, source, sink, "$@", source, source.toString() diff --git a/csharp/ql/test/library-tests/dataflow/operators/operatorFlow.expected b/csharp/ql/test/library-tests/dataflow/operators/operatorFlow.expected index 7b173a98e41..2bddf573b6a 100644 --- a/csharp/ql/test/library-tests/dataflow/operators/operatorFlow.expected +++ b/csharp/ql/test/library-tests/dataflow/operators/operatorFlow.expected @@ -1,95 +1,186 @@ failures +testFailures edges | Operator.cs:9:39:9:39 | x : C | Operator.cs:9:50:9:50 | access to parameter x : C | +| Operator.cs:9:39:9:39 | x : C | Operator.cs:9:50:9:50 | access to parameter x : C | +| Operator.cs:16:38:16:38 | x : C | Operator.cs:16:49:16:49 | access to parameter x : C | | Operator.cs:16:38:16:38 | x : C | Operator.cs:16:49:16:49 | access to parameter x : C | | Operator.cs:18:51:18:51 | y : C | Operator.cs:18:57:18:57 | access to parameter y : C | +| Operator.cs:18:51:18:51 | y : C | Operator.cs:18:57:18:57 | access to parameter y : C | +| Operator.cs:19:38:19:38 | x : C | Operator.cs:19:49:19:49 | access to parameter x : C | | Operator.cs:19:38:19:38 | x : C | Operator.cs:19:49:19:49 | access to parameter x : C | | Operator.cs:21:43:21:43 | y : C | Operator.cs:21:49:21:49 | access to parameter y : C | +| Operator.cs:21:43:21:43 | y : C | Operator.cs:21:49:21:49 | access to parameter y : C | +| Operator.cs:22:51:22:51 | y : C | Operator.cs:22:57:22:57 | access to parameter y : C | | Operator.cs:22:51:22:51 | y : C | Operator.cs:22:57:22:57 | access to parameter y : C | | Operator.cs:27:17:27:28 | call to method Source : C | Operator.cs:29:17:29:17 | access to local variable x : C | +| Operator.cs:27:17:27:28 | call to method Source : C | Operator.cs:29:17:29:17 | access to local variable x : C | +| Operator.cs:29:17:29:17 | access to local variable x : C | Operator.cs:16:38:16:38 | x : C | | Operator.cs:29:17:29:17 | access to local variable x : C | Operator.cs:16:38:16:38 | x : C | | Operator.cs:29:17:29:17 | access to local variable x : C | Operator.cs:29:17:29:21 | call to operator + : C | +| Operator.cs:29:17:29:17 | access to local variable x : C | Operator.cs:29:17:29:21 | call to operator + : C | +| Operator.cs:29:17:29:21 | call to operator + : C | Operator.cs:30:14:30:14 | access to local variable z | | Operator.cs:29:17:29:21 | call to operator + : C | Operator.cs:30:14:30:14 | access to local variable z | | Operator.cs:35:17:35:28 | call to method Source : C | Operator.cs:37:27:37:27 | access to local variable x : C | +| Operator.cs:35:17:35:28 | call to method Source : C | Operator.cs:37:27:37:27 | access to local variable x : C | +| Operator.cs:37:27:37:27 | access to local variable x : C | Operator.cs:19:38:19:38 | x : C | | Operator.cs:37:27:37:27 | access to local variable x : C | Operator.cs:19:38:19:38 | x : C | | Operator.cs:37:27:37:27 | access to local variable x : C | Operator.cs:37:27:37:31 | call to operator - : C | +| Operator.cs:37:27:37:27 | access to local variable x : C | Operator.cs:37:27:37:31 | call to operator - : C | +| Operator.cs:37:27:37:31 | call to operator - : C | Operator.cs:38:14:38:14 | access to local variable z | | Operator.cs:37:27:37:31 | call to operator - : C | Operator.cs:38:14:38:14 | access to local variable z | | Operator.cs:44:17:44:28 | call to method Source : C | Operator.cs:45:29:45:29 | access to local variable y : C | +| Operator.cs:44:17:44:28 | call to method Source : C | Operator.cs:45:29:45:29 | access to local variable y : C | +| Operator.cs:45:25:45:29 | call to operator checked - : C | Operator.cs:46:14:46:14 | access to local variable z | | Operator.cs:45:25:45:29 | call to operator checked - : C | Operator.cs:46:14:46:14 | access to local variable z | | Operator.cs:45:29:45:29 | access to local variable y : C | Operator.cs:18:51:18:51 | y : C | +| Operator.cs:45:29:45:29 | access to local variable y : C | Operator.cs:18:51:18:51 | y : C | +| Operator.cs:45:29:45:29 | access to local variable y : C | Operator.cs:45:25:45:29 | call to operator checked - : C | | Operator.cs:45:29:45:29 | access to local variable y : C | Operator.cs:45:25:45:29 | call to operator checked - : C | | Operator.cs:49:28:49:28 | x : C | Operator.cs:51:17:51:17 | access to parameter x : C | +| Operator.cs:49:28:49:28 | x : C | Operator.cs:51:17:51:17 | access to parameter x : C | +| Operator.cs:51:17:51:17 | access to parameter x : C | Operator.cs:9:39:9:39 | x : C | | Operator.cs:51:17:51:17 | access to parameter x : C | Operator.cs:9:39:9:39 | x : C | | Operator.cs:51:17:51:17 | access to parameter x : C | Operator.cs:51:17:51:21 | call to operator * : C | +| Operator.cs:51:17:51:17 | access to parameter x : C | Operator.cs:51:17:51:21 | call to operator * : C | +| Operator.cs:51:17:51:21 | call to operator * : C | Operator.cs:52:14:52:14 | (...) ... | | Operator.cs:51:17:51:21 | call to operator * : C | Operator.cs:52:14:52:14 | (...) ... | | Operator.cs:57:17:57:28 | call to method Source : C | Operator.cs:59:15:59:15 | access to local variable x : C | +| Operator.cs:57:17:57:28 | call to method Source : C | Operator.cs:59:15:59:15 | access to local variable x : C | +| Operator.cs:59:15:59:15 | access to local variable x : C | Operator.cs:49:28:49:28 | x : C | | Operator.cs:59:15:59:15 | access to local variable x : C | Operator.cs:49:28:49:28 | x : C | | Operator.cs:62:33:62:33 | y : C | Operator.cs:64:21:64:21 | access to parameter y : C | +| Operator.cs:62:33:62:33 | y : C | Operator.cs:64:21:64:21 | access to parameter y : C | +| Operator.cs:64:17:64:21 | call to operator / : C | Operator.cs:65:14:65:14 | (...) ... | | Operator.cs:64:17:64:21 | call to operator / : C | Operator.cs:65:14:65:14 | (...) ... | | Operator.cs:64:21:64:21 | access to parameter y : C | Operator.cs:21:43:21:43 | y : C | +| Operator.cs:64:21:64:21 | access to parameter y : C | Operator.cs:21:43:21:43 | y : C | +| Operator.cs:64:21:64:21 | access to parameter y : C | Operator.cs:64:17:64:21 | call to operator / : C | | Operator.cs:64:21:64:21 | access to parameter y : C | Operator.cs:64:17:64:21 | call to operator / : C | | Operator.cs:71:17:71:29 | call to method Source : C | Operator.cs:72:18:72:18 | access to local variable y : C | +| Operator.cs:71:17:71:29 | call to method Source : C | Operator.cs:72:18:72:18 | access to local variable y : C | +| Operator.cs:72:18:72:18 | access to local variable y : C | Operator.cs:62:33:62:33 | y : C | | Operator.cs:72:18:72:18 | access to local variable y : C | Operator.cs:62:33:62:33 | y : C | | Operator.cs:75:33:75:33 | y : C | Operator.cs:77:29:77:29 | access to parameter y : C | +| Operator.cs:75:33:75:33 | y : C | Operator.cs:77:29:77:29 | access to parameter y : C | +| Operator.cs:77:25:77:29 | call to operator checked / : C | Operator.cs:78:14:78:14 | (...) ... | | Operator.cs:77:25:77:29 | call to operator checked / : C | Operator.cs:78:14:78:14 | (...) ... | | Operator.cs:77:29:77:29 | access to parameter y : C | Operator.cs:22:51:22:51 | y : C | +| Operator.cs:77:29:77:29 | access to parameter y : C | Operator.cs:22:51:22:51 | y : C | +| Operator.cs:77:29:77:29 | access to parameter y : C | Operator.cs:77:25:77:29 | call to operator checked / : C | | Operator.cs:77:29:77:29 | access to parameter y : C | Operator.cs:77:25:77:29 | call to operator checked / : C | | Operator.cs:84:17:84:29 | call to method Source : C | Operator.cs:85:18:85:18 | access to local variable y : C | +| Operator.cs:84:17:84:29 | call to method Source : C | Operator.cs:85:18:85:18 | access to local variable y : C | +| Operator.cs:85:18:85:18 | access to local variable y : C | Operator.cs:75:33:75:33 | y : C | | Operator.cs:85:18:85:18 | access to local variable y : C | Operator.cs:75:33:75:33 | y : C | nodes | Operator.cs:9:39:9:39 | x : C | semmle.label | x : C | +| Operator.cs:9:39:9:39 | x : C | semmle.label | x : C | +| Operator.cs:9:50:9:50 | access to parameter x : C | semmle.label | access to parameter x : C | | Operator.cs:9:50:9:50 | access to parameter x : C | semmle.label | access to parameter x : C | | Operator.cs:16:38:16:38 | x : C | semmle.label | x : C | +| Operator.cs:16:38:16:38 | x : C | semmle.label | x : C | +| Operator.cs:16:49:16:49 | access to parameter x : C | semmle.label | access to parameter x : C | | Operator.cs:16:49:16:49 | access to parameter x : C | semmle.label | access to parameter x : C | | Operator.cs:18:51:18:51 | y : C | semmle.label | y : C | +| Operator.cs:18:51:18:51 | y : C | semmle.label | y : C | +| Operator.cs:18:57:18:57 | access to parameter y : C | semmle.label | access to parameter y : C | | Operator.cs:18:57:18:57 | access to parameter y : C | semmle.label | access to parameter y : C | | Operator.cs:19:38:19:38 | x : C | semmle.label | x : C | +| Operator.cs:19:38:19:38 | x : C | semmle.label | x : C | +| Operator.cs:19:49:19:49 | access to parameter x : C | semmle.label | access to parameter x : C | | Operator.cs:19:49:19:49 | access to parameter x : C | semmle.label | access to parameter x : C | | Operator.cs:21:43:21:43 | y : C | semmle.label | y : C | +| Operator.cs:21:43:21:43 | y : C | semmle.label | y : C | +| Operator.cs:21:49:21:49 | access to parameter y : C | semmle.label | access to parameter y : C | | Operator.cs:21:49:21:49 | access to parameter y : C | semmle.label | access to parameter y : C | | Operator.cs:22:51:22:51 | y : C | semmle.label | y : C | +| Operator.cs:22:51:22:51 | y : C | semmle.label | y : C | +| Operator.cs:22:57:22:57 | access to parameter y : C | semmle.label | access to parameter y : C | | Operator.cs:22:57:22:57 | access to parameter y : C | semmle.label | access to parameter y : C | | Operator.cs:27:17:27:28 | call to method Source : C | semmle.label | call to method Source : C | +| Operator.cs:27:17:27:28 | call to method Source : C | semmle.label | call to method Source : C | +| Operator.cs:29:17:29:17 | access to local variable x : C | semmle.label | access to local variable x : C | | Operator.cs:29:17:29:17 | access to local variable x : C | semmle.label | access to local variable x : C | | Operator.cs:29:17:29:21 | call to operator + : C | semmle.label | call to operator + : C | +| Operator.cs:29:17:29:21 | call to operator + : C | semmle.label | call to operator + : C | +| Operator.cs:30:14:30:14 | access to local variable z | semmle.label | access to local variable z | | Operator.cs:30:14:30:14 | access to local variable z | semmle.label | access to local variable z | | Operator.cs:35:17:35:28 | call to method Source : C | semmle.label | call to method Source : C | +| Operator.cs:35:17:35:28 | call to method Source : C | semmle.label | call to method Source : C | +| Operator.cs:37:27:37:27 | access to local variable x : C | semmle.label | access to local variable x : C | | Operator.cs:37:27:37:27 | access to local variable x : C | semmle.label | access to local variable x : C | | Operator.cs:37:27:37:31 | call to operator - : C | semmle.label | call to operator - : C | +| Operator.cs:37:27:37:31 | call to operator - : C | semmle.label | call to operator - : C | +| Operator.cs:38:14:38:14 | access to local variable z | semmle.label | access to local variable z | | Operator.cs:38:14:38:14 | access to local variable z | semmle.label | access to local variable z | | Operator.cs:44:17:44:28 | call to method Source : C | semmle.label | call to method Source : C | +| Operator.cs:44:17:44:28 | call to method Source : C | semmle.label | call to method Source : C | +| Operator.cs:45:25:45:29 | call to operator checked - : C | semmle.label | call to operator checked - : C | | Operator.cs:45:25:45:29 | call to operator checked - : C | semmle.label | call to operator checked - : C | | Operator.cs:45:29:45:29 | access to local variable y : C | semmle.label | access to local variable y : C | +| Operator.cs:45:29:45:29 | access to local variable y : C | semmle.label | access to local variable y : C | +| Operator.cs:46:14:46:14 | access to local variable z | semmle.label | access to local variable z | | Operator.cs:46:14:46:14 | access to local variable z | semmle.label | access to local variable z | | Operator.cs:49:28:49:28 | x : C | semmle.label | x : C | +| Operator.cs:49:28:49:28 | x : C | semmle.label | x : C | +| Operator.cs:51:17:51:17 | access to parameter x : C | semmle.label | access to parameter x : C | | Operator.cs:51:17:51:17 | access to parameter x : C | semmle.label | access to parameter x : C | | Operator.cs:51:17:51:21 | call to operator * : C | semmle.label | call to operator * : C | +| Operator.cs:51:17:51:21 | call to operator * : C | semmle.label | call to operator * : C | +| Operator.cs:52:14:52:14 | (...) ... | semmle.label | (...) ... | | Operator.cs:52:14:52:14 | (...) ... | semmle.label | (...) ... | | Operator.cs:57:17:57:28 | call to method Source : C | semmle.label | call to method Source : C | +| Operator.cs:57:17:57:28 | call to method Source : C | semmle.label | call to method Source : C | +| Operator.cs:59:15:59:15 | access to local variable x : C | semmle.label | access to local variable x : C | | Operator.cs:59:15:59:15 | access to local variable x : C | semmle.label | access to local variable x : C | | Operator.cs:62:33:62:33 | y : C | semmle.label | y : C | +| Operator.cs:62:33:62:33 | y : C | semmle.label | y : C | +| Operator.cs:64:17:64:21 | call to operator / : C | semmle.label | call to operator / : C | | Operator.cs:64:17:64:21 | call to operator / : C | semmle.label | call to operator / : C | | Operator.cs:64:21:64:21 | access to parameter y : C | semmle.label | access to parameter y : C | +| Operator.cs:64:21:64:21 | access to parameter y : C | semmle.label | access to parameter y : C | +| Operator.cs:65:14:65:14 | (...) ... | semmle.label | (...) ... | | Operator.cs:65:14:65:14 | (...) ... | semmle.label | (...) ... | | Operator.cs:71:17:71:29 | call to method Source : C | semmle.label | call to method Source : C | +| Operator.cs:71:17:71:29 | call to method Source : C | semmle.label | call to method Source : C | +| Operator.cs:72:18:72:18 | access to local variable y : C | semmle.label | access to local variable y : C | | Operator.cs:72:18:72:18 | access to local variable y : C | semmle.label | access to local variable y : C | | Operator.cs:75:33:75:33 | y : C | semmle.label | y : C | +| Operator.cs:75:33:75:33 | y : C | semmle.label | y : C | +| Operator.cs:77:25:77:29 | call to operator checked / : C | semmle.label | call to operator checked / : C | | Operator.cs:77:25:77:29 | call to operator checked / : C | semmle.label | call to operator checked / : C | | Operator.cs:77:29:77:29 | access to parameter y : C | semmle.label | access to parameter y : C | +| Operator.cs:77:29:77:29 | access to parameter y : C | semmle.label | access to parameter y : C | +| Operator.cs:78:14:78:14 | (...) ... | semmle.label | (...) ... | | Operator.cs:78:14:78:14 | (...) ... | semmle.label | (...) ... | | Operator.cs:84:17:84:29 | call to method Source : C | semmle.label | call to method Source : C | +| Operator.cs:84:17:84:29 | call to method Source : C | semmle.label | call to method Source : C | +| Operator.cs:85:18:85:18 | access to local variable y : C | semmle.label | access to local variable y : C | | Operator.cs:85:18:85:18 | access to local variable y : C | semmle.label | access to local variable y : C | subpaths | Operator.cs:29:17:29:17 | access to local variable x : C | Operator.cs:16:38:16:38 | x : C | Operator.cs:16:49:16:49 | access to parameter x : C | Operator.cs:29:17:29:21 | call to operator + : C | +| Operator.cs:29:17:29:17 | access to local variable x : C | Operator.cs:16:38:16:38 | x : C | Operator.cs:16:49:16:49 | access to parameter x : C | Operator.cs:29:17:29:21 | call to operator + : C | +| Operator.cs:37:27:37:27 | access to local variable x : C | Operator.cs:19:38:19:38 | x : C | Operator.cs:19:49:19:49 | access to parameter x : C | Operator.cs:37:27:37:31 | call to operator - : C | | Operator.cs:37:27:37:27 | access to local variable x : C | Operator.cs:19:38:19:38 | x : C | Operator.cs:19:49:19:49 | access to parameter x : C | Operator.cs:37:27:37:31 | call to operator - : C | | Operator.cs:45:29:45:29 | access to local variable y : C | Operator.cs:18:51:18:51 | y : C | Operator.cs:18:57:18:57 | access to parameter y : C | Operator.cs:45:25:45:29 | call to operator checked - : C | +| Operator.cs:45:29:45:29 | access to local variable y : C | Operator.cs:18:51:18:51 | y : C | Operator.cs:18:57:18:57 | access to parameter y : C | Operator.cs:45:25:45:29 | call to operator checked - : C | +| Operator.cs:51:17:51:17 | access to parameter x : C | Operator.cs:9:39:9:39 | x : C | Operator.cs:9:50:9:50 | access to parameter x : C | Operator.cs:51:17:51:21 | call to operator * : C | | Operator.cs:51:17:51:17 | access to parameter x : C | Operator.cs:9:39:9:39 | x : C | Operator.cs:9:50:9:50 | access to parameter x : C | Operator.cs:51:17:51:21 | call to operator * : C | | Operator.cs:64:21:64:21 | access to parameter y : C | Operator.cs:21:43:21:43 | y : C | Operator.cs:21:49:21:49 | access to parameter y : C | Operator.cs:64:17:64:21 | call to operator / : C | +| Operator.cs:64:21:64:21 | access to parameter y : C | Operator.cs:21:43:21:43 | y : C | Operator.cs:21:49:21:49 | access to parameter y : C | Operator.cs:64:17:64:21 | call to operator / : C | +| Operator.cs:77:29:77:29 | access to parameter y : C | Operator.cs:22:51:22:51 | y : C | Operator.cs:22:57:22:57 | access to parameter y : C | Operator.cs:77:25:77:29 | call to operator checked / : C | | Operator.cs:77:29:77:29 | access to parameter y : C | Operator.cs:22:51:22:51 | y : C | Operator.cs:22:57:22:57 | access to parameter y : C | Operator.cs:77:25:77:29 | call to operator checked / : C | #select | Operator.cs:30:14:30:14 | access to local variable z | Operator.cs:27:17:27:28 | call to method Source : C | Operator.cs:30:14:30:14 | access to local variable z | $@ | Operator.cs:27:17:27:28 | call to method Source : C | call to method Source : C | +| Operator.cs:30:14:30:14 | access to local variable z | Operator.cs:27:17:27:28 | call to method Source : C | Operator.cs:30:14:30:14 | access to local variable z | $@ | Operator.cs:27:17:27:28 | call to method Source : C | call to method Source : C | +| Operator.cs:38:14:38:14 | access to local variable z | Operator.cs:35:17:35:28 | call to method Source : C | Operator.cs:38:14:38:14 | access to local variable z | $@ | Operator.cs:35:17:35:28 | call to method Source : C | call to method Source : C | | Operator.cs:38:14:38:14 | access to local variable z | Operator.cs:35:17:35:28 | call to method Source : C | Operator.cs:38:14:38:14 | access to local variable z | $@ | Operator.cs:35:17:35:28 | call to method Source : C | call to method Source : C | | Operator.cs:46:14:46:14 | access to local variable z | Operator.cs:44:17:44:28 | call to method Source : C | Operator.cs:46:14:46:14 | access to local variable z | $@ | Operator.cs:44:17:44:28 | call to method Source : C | call to method Source : C | +| Operator.cs:46:14:46:14 | access to local variable z | Operator.cs:44:17:44:28 | call to method Source : C | Operator.cs:46:14:46:14 | access to local variable z | $@ | Operator.cs:44:17:44:28 | call to method Source : C | call to method Source : C | +| Operator.cs:52:14:52:14 | (...) ... | Operator.cs:57:17:57:28 | call to method Source : C | Operator.cs:52:14:52:14 | (...) ... | $@ | Operator.cs:57:17:57:28 | call to method Source : C | call to method Source : C | | Operator.cs:52:14:52:14 | (...) ... | Operator.cs:57:17:57:28 | call to method Source : C | Operator.cs:52:14:52:14 | (...) ... | $@ | Operator.cs:57:17:57:28 | call to method Source : C | call to method Source : C | | Operator.cs:65:14:65:14 | (...) ... | Operator.cs:71:17:71:29 | call to method Source : C | Operator.cs:65:14:65:14 | (...) ... | $@ | Operator.cs:71:17:71:29 | call to method Source : C | call to method Source : C | +| Operator.cs:65:14:65:14 | (...) ... | Operator.cs:71:17:71:29 | call to method Source : C | Operator.cs:65:14:65:14 | (...) ... | $@ | Operator.cs:71:17:71:29 | call to method Source : C | call to method Source : C | +| Operator.cs:78:14:78:14 | (...) ... | Operator.cs:84:17:84:29 | call to method Source : C | Operator.cs:78:14:78:14 | (...) ... | $@ | Operator.cs:84:17:84:29 | call to method Source : C | call to method Source : C | | Operator.cs:78:14:78:14 | (...) ... | Operator.cs:84:17:84:29 | call to method Source : C | Operator.cs:78:14:78:14 | (...) ... | $@ | Operator.cs:84:17:84:29 | call to method Source : C | call to method Source : C | diff --git a/csharp/ql/test/library-tests/dataflow/operators/operatorFlow.ql b/csharp/ql/test/library-tests/dataflow/operators/operatorFlow.ql index b01c0f7fcaf..9336e1b28be 100644 --- a/csharp/ql/test/library-tests/dataflow/operators/operatorFlow.ql +++ b/csharp/ql/test/library-tests/dataflow/operators/operatorFlow.ql @@ -3,9 +3,10 @@ */ import csharp -import DefaultValueFlow::PathGraph import TestUtilities.InlineFlowTest +import DefaultFlowTest +import PathGraph -from DefaultValueFlow::PathNode source, DefaultValueFlow::PathNode sink -where DefaultValueFlow::flowPath(source, sink) +from PathNode source, PathNode sink +where flowPath(source, sink) select sink, source, sink, "$@", source, source.toString() diff --git a/csharp/ql/test/library-tests/dataflow/patterns/PatternFlow.expected b/csharp/ql/test/library-tests/dataflow/patterns/PatternFlow.expected index f965891244c..f2ab31c12b3 100644 --- a/csharp/ql/test/library-tests/dataflow/patterns/PatternFlow.expected +++ b/csharp/ql/test/library-tests/dataflow/patterns/PatternFlow.expected @@ -1,4 +1,5 @@ failures +testFailures edges nodes subpaths diff --git a/csharp/ql/test/library-tests/dataflow/patterns/PatternFlow.ql b/csharp/ql/test/library-tests/dataflow/patterns/PatternFlow.ql index b01c0f7fcaf..9336e1b28be 100644 --- a/csharp/ql/test/library-tests/dataflow/patterns/PatternFlow.ql +++ b/csharp/ql/test/library-tests/dataflow/patterns/PatternFlow.ql @@ -3,9 +3,10 @@ */ import csharp -import DefaultValueFlow::PathGraph import TestUtilities.InlineFlowTest +import DefaultFlowTest +import PathGraph -from DefaultValueFlow::PathNode source, DefaultValueFlow::PathNode sink -where DefaultValueFlow::flowPath(source, sink) +from PathNode source, PathNode sink +where flowPath(source, sink) select sink, source, sink, "$@", source, source.toString() diff --git a/csharp/ql/test/library-tests/dataflow/tuples/Tuples.expected b/csharp/ql/test/library-tests/dataflow/tuples/Tuples.expected index dae85aa45aa..10b9a4b66ac 100644 --- a/csharp/ql/test/library-tests/dataflow/tuples/Tuples.expected +++ b/csharp/ql/test/library-tests/dataflow/tuples/Tuples.expected @@ -1,233 +1,462 @@ failures +testFailures edges | Tuples.cs:7:18:7:34 | call to method Source : Object | Tuples.cs:10:21:10:22 | access to local variable o1 : Object | +| Tuples.cs:7:18:7:34 | call to method Source : Object | Tuples.cs:10:21:10:22 | access to local variable o1 : Object | +| Tuples.cs:8:18:8:34 | call to method Source : Object | Tuples.cs:10:29:10:30 | access to local variable o2 : Object | | Tuples.cs:8:18:8:34 | call to method Source : Object | Tuples.cs:10:29:10:30 | access to local variable o2 : Object | | Tuples.cs:10:17:10:32 | (..., ...) : ValueTuple> [field Item1] : Object | Tuples.cs:11:9:11:23 | (..., ...) : ValueTuple> [field Item1] : Object | +| Tuples.cs:10:17:10:32 | (..., ...) : ValueTuple> [field Item1] : Object | Tuples.cs:11:9:11:23 | (..., ...) : ValueTuple> [field Item1] : Object | +| Tuples.cs:10:17:10:32 | (..., ...) : ValueTuple> [field Item1] : Object | Tuples.cs:16:9:16:19 | (..., ...) : ValueTuple> [field Item1] : Object | | Tuples.cs:10:17:10:32 | (..., ...) : ValueTuple> [field Item1] : Object | Tuples.cs:16:9:16:19 | (..., ...) : ValueTuple> [field Item1] : Object | | Tuples.cs:10:17:10:32 | (..., ...) : ValueTuple> [field Item1] : Object | Tuples.cs:21:9:21:22 | (..., ...) : ValueTuple> [field Item1] : Object | +| Tuples.cs:10:17:10:32 | (..., ...) : ValueTuple> [field Item1] : Object | Tuples.cs:21:9:21:22 | (..., ...) : ValueTuple> [field Item1] : Object | +| Tuples.cs:10:17:10:32 | (..., ...) : ValueTuple> [field Item1] : Object | Tuples.cs:26:14:26:14 | access to local variable x : ValueTuple> [field Item1] : Object | | Tuples.cs:10:17:10:32 | (..., ...) : ValueTuple> [field Item1] : Object | Tuples.cs:26:14:26:14 | access to local variable x : ValueTuple> [field Item1] : Object | | Tuples.cs:10:17:10:32 | (..., ...) : ValueTuple> [field Item1] : Object | Tuples.cs:27:14:27:14 | access to local variable x : ValueTuple> [field Item1] : Object | +| Tuples.cs:10:17:10:32 | (..., ...) : ValueTuple> [field Item1] : Object | Tuples.cs:27:14:27:14 | access to local variable x : ValueTuple> [field Item1] : Object | +| Tuples.cs:10:17:10:32 | (..., ...) : ValueTuple> [field Item2, field Item2] : Object | Tuples.cs:11:9:11:23 | (..., ...) : ValueTuple> [field Item2, field Item2] : Object | | Tuples.cs:10:17:10:32 | (..., ...) : ValueTuple> [field Item2, field Item2] : Object | Tuples.cs:11:9:11:23 | (..., ...) : ValueTuple> [field Item2, field Item2] : Object | | Tuples.cs:10:17:10:32 | (..., ...) : ValueTuple> [field Item2, field Item2] : Object | Tuples.cs:16:9:16:19 | (..., ...) : ValueTuple> [field Item2, field Item2] : Object | +| Tuples.cs:10:17:10:32 | (..., ...) : ValueTuple> [field Item2, field Item2] : Object | Tuples.cs:16:9:16:19 | (..., ...) : ValueTuple> [field Item2, field Item2] : Object | +| Tuples.cs:10:17:10:32 | (..., ...) : ValueTuple> [field Item2, field Item2] : Object | Tuples.cs:21:9:21:22 | (..., ...) : ValueTuple> [field Item2, field Item2] : Object | | Tuples.cs:10:17:10:32 | (..., ...) : ValueTuple> [field Item2, field Item2] : Object | Tuples.cs:21:9:21:22 | (..., ...) : ValueTuple> [field Item2, field Item2] : Object | | Tuples.cs:10:17:10:32 | (..., ...) : ValueTuple> [field Item2, field Item2] : Object | Tuples.cs:29:14:29:14 | access to local variable x : ValueTuple> [field Item2, field Item2] : Object | +| Tuples.cs:10:17:10:32 | (..., ...) : ValueTuple> [field Item2, field Item2] : Object | Tuples.cs:29:14:29:14 | access to local variable x : ValueTuple> [field Item2, field Item2] : Object | +| Tuples.cs:10:21:10:22 | access to local variable o1 : Object | Tuples.cs:10:17:10:32 | (..., ...) : ValueTuple> [field Item1] : Object | | Tuples.cs:10:21:10:22 | access to local variable o1 : Object | Tuples.cs:10:17:10:32 | (..., ...) : ValueTuple> [field Item1] : Object | | Tuples.cs:10:25:10:31 | (..., ...) : ValueTuple [field Item2] : Object | Tuples.cs:10:17:10:32 | (..., ...) : ValueTuple> [field Item2, field Item2] : Object | +| Tuples.cs:10:25:10:31 | (..., ...) : ValueTuple [field Item2] : Object | Tuples.cs:10:17:10:32 | (..., ...) : ValueTuple> [field Item2, field Item2] : Object | +| Tuples.cs:10:29:10:30 | access to local variable o2 : Object | Tuples.cs:10:25:10:31 | (..., ...) : ValueTuple [field Item2] : Object | | Tuples.cs:10:29:10:30 | access to local variable o2 : Object | Tuples.cs:10:25:10:31 | (..., ...) : ValueTuple [field Item2] : Object | | Tuples.cs:11:9:11:23 | (..., ...) : ValueTuple [field Item2] : Object | Tuples.cs:11:9:11:27 | SSA def(c) : Object | +| Tuples.cs:11:9:11:23 | (..., ...) : ValueTuple [field Item2] : Object | Tuples.cs:11:9:11:27 | SSA def(c) : Object | +| Tuples.cs:11:9:11:23 | (..., ...) : ValueTuple> [field Item1] : Object | Tuples.cs:11:9:11:27 | SSA def(a) : Object | | Tuples.cs:11:9:11:23 | (..., ...) : ValueTuple> [field Item1] : Object | Tuples.cs:11:9:11:27 | SSA def(a) : Object | | Tuples.cs:11:9:11:23 | (..., ...) : ValueTuple> [field Item2, field Item2] : Object | Tuples.cs:11:9:11:23 | (..., ...) : ValueTuple [field Item2] : Object | +| Tuples.cs:11:9:11:23 | (..., ...) : ValueTuple> [field Item2, field Item2] : Object | Tuples.cs:11:9:11:23 | (..., ...) : ValueTuple [field Item2] : Object | +| Tuples.cs:11:9:11:27 | SSA def(a) : Object | Tuples.cs:12:14:12:14 | access to local variable a | | Tuples.cs:11:9:11:27 | SSA def(a) : Object | Tuples.cs:12:14:12:14 | access to local variable a | | Tuples.cs:11:9:11:27 | SSA def(c) : Object | Tuples.cs:14:14:14:14 | access to local variable c | +| Tuples.cs:11:9:11:27 | SSA def(c) : Object | Tuples.cs:14:14:14:14 | access to local variable c | +| Tuples.cs:16:9:16:19 | (..., ...) : ValueTuple> [field Item1] : Object | Tuples.cs:16:9:16:23 | SSA def(a) : Object | | Tuples.cs:16:9:16:19 | (..., ...) : ValueTuple> [field Item1] : Object | Tuples.cs:16:9:16:23 | SSA def(a) : Object | | Tuples.cs:16:9:16:19 | (..., ...) : ValueTuple> [field Item2, field Item2] : Object | Tuples.cs:16:13:16:18 | (..., ...) : ValueTuple [field Item2] : Object | +| Tuples.cs:16:9:16:19 | (..., ...) : ValueTuple> [field Item2, field Item2] : Object | Tuples.cs:16:13:16:18 | (..., ...) : ValueTuple [field Item2] : Object | +| Tuples.cs:16:9:16:23 | SSA def(a) : Object | Tuples.cs:17:14:17:14 | access to local variable a | | Tuples.cs:16:9:16:23 | SSA def(a) : Object | Tuples.cs:17:14:17:14 | access to local variable a | | Tuples.cs:16:9:16:23 | SSA def(c) : Object | Tuples.cs:19:14:19:14 | access to local variable c | +| Tuples.cs:16:9:16:23 | SSA def(c) : Object | Tuples.cs:19:14:19:14 | access to local variable c | +| Tuples.cs:16:13:16:18 | (..., ...) : ValueTuple [field Item2] : Object | Tuples.cs:16:9:16:23 | SSA def(c) : Object | | Tuples.cs:16:13:16:18 | (..., ...) : ValueTuple [field Item2] : Object | Tuples.cs:16:9:16:23 | SSA def(c) : Object | | Tuples.cs:21:9:21:22 | (..., ...) : ValueTuple> [field Item1] : Object | Tuples.cs:21:9:21:26 | SSA def(p) : Object | +| Tuples.cs:21:9:21:22 | (..., ...) : ValueTuple> [field Item1] : Object | Tuples.cs:21:9:21:26 | SSA def(p) : Object | +| Tuples.cs:21:9:21:22 | (..., ...) : ValueTuple> [field Item2, field Item2] : Object | Tuples.cs:21:9:21:26 | SSA def(q) : ValueTuple [field Item2] : Object | | Tuples.cs:21:9:21:22 | (..., ...) : ValueTuple> [field Item2, field Item2] : Object | Tuples.cs:21:9:21:26 | SSA def(q) : ValueTuple [field Item2] : Object | | Tuples.cs:21:9:21:26 | SSA def(p) : Object | Tuples.cs:22:14:22:14 | access to local variable p | +| Tuples.cs:21:9:21:26 | SSA def(p) : Object | Tuples.cs:22:14:22:14 | access to local variable p | +| Tuples.cs:21:9:21:26 | SSA def(q) : ValueTuple [field Item2] : Object | Tuples.cs:24:14:24:14 | access to local variable q : ValueTuple [field Item2] : Object | | Tuples.cs:21:9:21:26 | SSA def(q) : ValueTuple [field Item2] : Object | Tuples.cs:24:14:24:14 | access to local variable q : ValueTuple [field Item2] : Object | | Tuples.cs:24:14:24:14 | access to local variable q : ValueTuple [field Item2] : Object | Tuples.cs:24:14:24:20 | access to field Item2 | +| Tuples.cs:24:14:24:14 | access to local variable q : ValueTuple [field Item2] : Object | Tuples.cs:24:14:24:20 | access to field Item2 | +| Tuples.cs:26:14:26:14 | access to local variable x : ValueTuple> [field Item1] : Object | Tuples.cs:26:14:26:20 | access to field Item1 | | Tuples.cs:26:14:26:14 | access to local variable x : ValueTuple> [field Item1] : Object | Tuples.cs:26:14:26:20 | access to field Item1 | | Tuples.cs:27:14:27:14 | access to local variable x : ValueTuple> [field Item1] : Object | Tuples.cs:27:14:27:16 | access to field Item1 | +| Tuples.cs:27:14:27:14 | access to local variable x : ValueTuple> [field Item1] : Object | Tuples.cs:27:14:27:16 | access to field Item1 | +| Tuples.cs:29:14:29:14 | access to local variable x : ValueTuple> [field Item2, field Item2] : Object | Tuples.cs:29:14:29:20 | access to field Item2 : ValueTuple [field Item2] : Object | | Tuples.cs:29:14:29:14 | access to local variable x : ValueTuple> [field Item2, field Item2] : Object | Tuples.cs:29:14:29:20 | access to field Item2 : ValueTuple [field Item2] : Object | | Tuples.cs:29:14:29:20 | access to field Item2 : ValueTuple [field Item2] : Object | Tuples.cs:29:14:29:26 | access to field Item2 | +| Tuples.cs:29:14:29:20 | access to field Item2 : ValueTuple [field Item2] : Object | Tuples.cs:29:14:29:26 | access to field Item2 | +| Tuples.cs:34:18:34:34 | call to method Source : Object | Tuples.cs:37:18:37:19 | access to local variable o1 : Object | | Tuples.cs:34:18:34:34 | call to method Source : Object | Tuples.cs:37:18:37:19 | access to local variable o1 : Object | | Tuples.cs:35:18:35:34 | call to method Source : Object | Tuples.cs:37:46:37:47 | access to local variable o2 : Object | +| Tuples.cs:35:18:35:34 | call to method Source : Object | Tuples.cs:37:46:37:47 | access to local variable o2 : Object | +| Tuples.cs:37:17:37:48 | (..., ...) : ValueTuple> [field Item1] : Object | Tuples.cs:38:14:38:14 | access to local variable x : ValueTuple> [field Item1] : Object | | Tuples.cs:37:17:37:48 | (..., ...) : ValueTuple> [field Item1] : Object | Tuples.cs:38:14:38:14 | access to local variable x : ValueTuple> [field Item1] : Object | | Tuples.cs:37:17:37:48 | (..., ...) : ValueTuple> [field Item10] : Object | Tuples.cs:40:14:40:14 | access to local variable x : ValueTuple> [field Item10] : Object | +| Tuples.cs:37:17:37:48 | (..., ...) : ValueTuple> [field Item10] : Object | Tuples.cs:40:14:40:14 | access to local variable x : ValueTuple> [field Item10] : Object | +| Tuples.cs:37:18:37:19 | access to local variable o1 : Object | Tuples.cs:37:17:37:48 | (..., ...) : ValueTuple> [field Item1] : Object | | Tuples.cs:37:18:37:19 | access to local variable o1 : Object | Tuples.cs:37:17:37:48 | (..., ...) : ValueTuple> [field Item1] : Object | | Tuples.cs:37:46:37:47 | access to local variable o2 : Object | Tuples.cs:37:17:37:48 | (..., ...) : ValueTuple> [field Item10] : Object | +| Tuples.cs:37:46:37:47 | access to local variable o2 : Object | Tuples.cs:37:17:37:48 | (..., ...) : ValueTuple> [field Item10] : Object | +| Tuples.cs:38:14:38:14 | access to local variable x : ValueTuple> [field Item1] : Object | Tuples.cs:38:14:38:20 | access to field Item1 | | Tuples.cs:38:14:38:14 | access to local variable x : ValueTuple> [field Item1] : Object | Tuples.cs:38:14:38:20 | access to field Item1 | | Tuples.cs:40:14:40:14 | access to local variable x : ValueTuple> [field Item10] : Object | Tuples.cs:40:14:40:21 | access to field Item10 | +| Tuples.cs:40:14:40:14 | access to local variable x : ValueTuple> [field Item10] : Object | Tuples.cs:40:14:40:21 | access to field Item10 | +| Tuples.cs:45:17:45:33 | call to method Source : String | Tuples.cs:46:48:46:48 | access to local variable o : String | | Tuples.cs:45:17:45:33 | call to method Source : String | Tuples.cs:46:48:46:48 | access to local variable o : String | | Tuples.cs:46:17:46:55 | (...) ... : ValueTuple [field Item1] : String | Tuples.cs:47:14:47:14 | access to local variable x : ValueTuple [field Item1] : String | +| Tuples.cs:46:17:46:55 | (...) ... : ValueTuple [field Item1] : String | Tuples.cs:47:14:47:14 | access to local variable x : ValueTuple [field Item1] : String | +| Tuples.cs:46:47:46:55 | (..., ...) : ValueTuple [field Item1] : String | Tuples.cs:46:17:46:55 | (...) ... : ValueTuple [field Item1] : String | | Tuples.cs:46:47:46:55 | (..., ...) : ValueTuple [field Item1] : String | Tuples.cs:46:17:46:55 | (...) ... : ValueTuple [field Item1] : String | | Tuples.cs:46:48:46:48 | access to local variable o : String | Tuples.cs:46:47:46:55 | (..., ...) : ValueTuple [field Item1] : String | +| Tuples.cs:46:48:46:48 | access to local variable o : String | Tuples.cs:46:47:46:55 | (..., ...) : ValueTuple [field Item1] : String | +| Tuples.cs:47:14:47:14 | access to local variable x : ValueTuple [field Item1] : String | Tuples.cs:47:14:47:20 | access to field Item1 | | Tuples.cs:47:14:47:14 | access to local variable x : ValueTuple [field Item1] : String | Tuples.cs:47:14:47:20 | access to field Item1 | | Tuples.cs:57:18:57:34 | call to method Source : String | Tuples.cs:59:18:59:19 | access to local variable o1 : String | +| Tuples.cs:57:18:57:34 | call to method Source : String | Tuples.cs:59:18:59:19 | access to local variable o1 : String | +| Tuples.cs:58:18:58:34 | call to method Source : String | Tuples.cs:59:26:59:27 | access to local variable o2 : String | | Tuples.cs:58:18:58:34 | call to method Source : String | Tuples.cs:59:26:59:27 | access to local variable o2 : String | | Tuples.cs:59:17:59:32 | (..., ...) : ValueTuple,Int32> [field Item1] : String | Tuples.cs:62:18:62:57 | SSA def(t) : ValueTuple,Int32> [field Item1] : String | +| Tuples.cs:59:17:59:32 | (..., ...) : ValueTuple,Int32> [field Item1] : String | Tuples.cs:62:18:62:57 | SSA def(t) : ValueTuple,Int32> [field Item1] : String | +| Tuples.cs:59:17:59:32 | (..., ...) : ValueTuple,Int32> [field Item1] : String | Tuples.cs:67:18:67:35 | (..., ...) : ValueTuple,Int32> [field Item1] : String | | Tuples.cs:59:17:59:32 | (..., ...) : ValueTuple,Int32> [field Item1] : String | Tuples.cs:67:18:67:35 | (..., ...) : ValueTuple,Int32> [field Item1] : String | | Tuples.cs:59:17:59:32 | (..., ...) : ValueTuple,Int32> [field Item1] : String | Tuples.cs:87:18:87:35 | (..., ...) : ValueTuple,Int32> [field Item1] : String | +| Tuples.cs:59:17:59:32 | (..., ...) : ValueTuple,Int32> [field Item1] : String | Tuples.cs:87:18:87:35 | (..., ...) : ValueTuple,Int32> [field Item1] : String | +| Tuples.cs:59:17:59:32 | (..., ...) : ValueTuple,Int32> [field Item2, field Item2] : String | Tuples.cs:62:18:62:57 | SSA def(t) : ValueTuple,Int32> [field Item2, field Item2] : String | | Tuples.cs:59:17:59:32 | (..., ...) : ValueTuple,Int32> [field Item2, field Item2] : String | Tuples.cs:62:18:62:57 | SSA def(t) : ValueTuple,Int32> [field Item2, field Item2] : String | | Tuples.cs:59:17:59:32 | (..., ...) : ValueTuple,Int32> [field Item2, field Item2] : String | Tuples.cs:67:18:67:35 | (..., ...) : ValueTuple,Int32> [field Item2, field Item2] : String | +| Tuples.cs:59:17:59:32 | (..., ...) : ValueTuple,Int32> [field Item2, field Item2] : String | Tuples.cs:67:18:67:35 | (..., ...) : ValueTuple,Int32> [field Item2, field Item2] : String | +| Tuples.cs:59:17:59:32 | (..., ...) : ValueTuple,Int32> [field Item2, field Item2] : String | Tuples.cs:87:18:87:35 | (..., ...) : ValueTuple,Int32> [field Item2, field Item2] : String | | Tuples.cs:59:17:59:32 | (..., ...) : ValueTuple,Int32> [field Item2, field Item2] : String | Tuples.cs:87:18:87:35 | (..., ...) : ValueTuple,Int32> [field Item2, field Item2] : String | | Tuples.cs:59:18:59:19 | access to local variable o1 : String | Tuples.cs:59:17:59:32 | (..., ...) : ValueTuple,Int32> [field Item1] : String | +| Tuples.cs:59:18:59:19 | access to local variable o1 : String | Tuples.cs:59:17:59:32 | (..., ...) : ValueTuple,Int32> [field Item1] : String | +| Tuples.cs:59:22:59:28 | (..., ...) : ValueTuple [field Item2] : String | Tuples.cs:59:17:59:32 | (..., ...) : ValueTuple,Int32> [field Item2, field Item2] : String | | Tuples.cs:59:22:59:28 | (..., ...) : ValueTuple [field Item2] : String | Tuples.cs:59:17:59:32 | (..., ...) : ValueTuple,Int32> [field Item2, field Item2] : String | | Tuples.cs:59:26:59:27 | access to local variable o2 : String | Tuples.cs:59:22:59:28 | (..., ...) : ValueTuple [field Item2] : String | +| Tuples.cs:59:26:59:27 | access to local variable o2 : String | Tuples.cs:59:22:59:28 | (..., ...) : ValueTuple [field Item2] : String | +| Tuples.cs:62:18:62:57 | SSA def(t) : ValueTuple,Int32> [field Item1] : String | Tuples.cs:63:22:63:22 | access to local variable t : ValueTuple,Int32> [field Item1] : String | | Tuples.cs:62:18:62:57 | SSA def(t) : ValueTuple,Int32> [field Item1] : String | Tuples.cs:63:22:63:22 | access to local variable t : ValueTuple,Int32> [field Item1] : String | | Tuples.cs:62:18:62:57 | SSA def(t) : ValueTuple,Int32> [field Item2, field Item2] : String | Tuples.cs:64:22:64:22 | access to local variable t : ValueTuple,Int32> [field Item2, field Item2] : String | +| Tuples.cs:62:18:62:57 | SSA def(t) : ValueTuple,Int32> [field Item2, field Item2] : String | Tuples.cs:64:22:64:22 | access to local variable t : ValueTuple,Int32> [field Item2, field Item2] : String | +| Tuples.cs:63:22:63:22 | access to local variable t : ValueTuple,Int32> [field Item1] : String | Tuples.cs:63:22:63:28 | access to field Item1 | | Tuples.cs:63:22:63:22 | access to local variable t : ValueTuple,Int32> [field Item1] : String | Tuples.cs:63:22:63:28 | access to field Item1 | | Tuples.cs:64:22:64:22 | access to local variable t : ValueTuple,Int32> [field Item2, field Item2] : String | Tuples.cs:64:22:64:28 | access to field Item2 : ValueTuple [field Item2] : String | +| Tuples.cs:64:22:64:22 | access to local variable t : ValueTuple,Int32> [field Item2, field Item2] : String | Tuples.cs:64:22:64:28 | access to field Item2 : ValueTuple [field Item2] : String | +| Tuples.cs:64:22:64:28 | access to field Item2 : ValueTuple [field Item2] : String | Tuples.cs:64:22:64:34 | access to field Item2 | | Tuples.cs:64:22:64:28 | access to field Item2 : ValueTuple [field Item2] : String | Tuples.cs:64:22:64:34 | access to field Item2 | | Tuples.cs:67:18:67:35 | (..., ...) : ValueTuple [field Item2] : String | Tuples.cs:67:30:67:30 | SSA def(c) : String | +| Tuples.cs:67:18:67:35 | (..., ...) : ValueTuple [field Item2] : String | Tuples.cs:67:30:67:30 | SSA def(c) : String | +| Tuples.cs:67:18:67:35 | (..., ...) : ValueTuple,Int32> [field Item1] : String | Tuples.cs:67:23:67:23 | SSA def(a) : String | | Tuples.cs:67:18:67:35 | (..., ...) : ValueTuple,Int32> [field Item1] : String | Tuples.cs:67:23:67:23 | SSA def(a) : String | | Tuples.cs:67:18:67:35 | (..., ...) : ValueTuple,Int32> [field Item2, field Item2] : String | Tuples.cs:67:18:67:35 | (..., ...) : ValueTuple [field Item2] : String | +| Tuples.cs:67:18:67:35 | (..., ...) : ValueTuple,Int32> [field Item2, field Item2] : String | Tuples.cs:67:18:67:35 | (..., ...) : ValueTuple [field Item2] : String | +| Tuples.cs:67:23:67:23 | SSA def(a) : String | Tuples.cs:68:22:68:22 | access to local variable a | | Tuples.cs:67:23:67:23 | SSA def(a) : String | Tuples.cs:68:22:68:22 | access to local variable a | | Tuples.cs:67:30:67:30 | SSA def(c) : String | Tuples.cs:69:22:69:22 | access to local variable c | +| Tuples.cs:67:30:67:30 | SSA def(c) : String | Tuples.cs:69:22:69:22 | access to local variable c | +| Tuples.cs:87:18:87:35 | (..., ...) : ValueTuple [field Item2] : String | Tuples.cs:87:30:87:30 | SSA def(r) : String | | Tuples.cs:87:18:87:35 | (..., ...) : ValueTuple [field Item2] : String | Tuples.cs:87:30:87:30 | SSA def(r) : String | | Tuples.cs:87:18:87:35 | (..., ...) : ValueTuple,Int32> [field Item1] : String | Tuples.cs:87:23:87:23 | SSA def(p) : String | +| Tuples.cs:87:18:87:35 | (..., ...) : ValueTuple,Int32> [field Item1] : String | Tuples.cs:87:23:87:23 | SSA def(p) : String | +| Tuples.cs:87:18:87:35 | (..., ...) : ValueTuple,Int32> [field Item2, field Item2] : String | Tuples.cs:87:18:87:35 | (..., ...) : ValueTuple [field Item2] : String | | Tuples.cs:87:18:87:35 | (..., ...) : ValueTuple,Int32> [field Item2, field Item2] : String | Tuples.cs:87:18:87:35 | (..., ...) : ValueTuple [field Item2] : String | | Tuples.cs:87:23:87:23 | SSA def(p) : String | Tuples.cs:89:18:89:18 | access to local variable p | +| Tuples.cs:87:23:87:23 | SSA def(p) : String | Tuples.cs:89:18:89:18 | access to local variable p | +| Tuples.cs:87:30:87:30 | SSA def(r) : String | Tuples.cs:90:18:90:18 | access to local variable r | | Tuples.cs:87:30:87:30 | SSA def(r) : String | Tuples.cs:90:18:90:18 | access to local variable r | | Tuples.cs:99:17:99:33 | call to method Source : String | Tuples.cs:100:24:100:24 | access to local variable o : String | +| Tuples.cs:99:17:99:33 | call to method Source : String | Tuples.cs:100:24:100:24 | access to local variable o : String | +| Tuples.cs:100:17:100:28 | object creation of type R1 : R1 [property i] : String | Tuples.cs:101:14:101:14 | access to local variable r : R1 [property i] : String | | Tuples.cs:100:17:100:28 | object creation of type R1 : R1 [property i] : String | Tuples.cs:101:14:101:14 | access to local variable r : R1 [property i] : String | | Tuples.cs:100:24:100:24 | access to local variable o : String | Tuples.cs:100:17:100:28 | object creation of type R1 : R1 [property i] : String | +| Tuples.cs:100:24:100:24 | access to local variable o : String | Tuples.cs:100:17:100:28 | object creation of type R1 : R1 [property i] : String | +| Tuples.cs:101:14:101:14 | access to local variable r : R1 [property i] : String | Tuples.cs:101:14:101:16 | access to property i | | Tuples.cs:101:14:101:14 | access to local variable r : R1 [property i] : String | Tuples.cs:101:14:101:16 | access to property i | | Tuples.cs:118:17:118:33 | call to method Source : Object | Tuples.cs:121:28:121:28 | access to local variable o : Object | +| Tuples.cs:118:17:118:33 | call to method Source : Object | Tuples.cs:121:28:121:28 | access to local variable o : Object | +| Tuples.cs:118:17:118:33 | call to method Source : Object | Tuples.cs:122:14:122:15 | access to local variable x1 | | Tuples.cs:118:17:118:33 | call to method Source : Object | Tuples.cs:122:14:122:15 | access to local variable x1 | | Tuples.cs:118:17:118:33 | call to method Source : Object | Tuples.cs:125:25:125:25 | access to local variable o : Object | +| Tuples.cs:118:17:118:33 | call to method Source : Object | Tuples.cs:125:25:125:25 | access to local variable o : Object | +| Tuples.cs:118:17:118:33 | call to method Source : Object | Tuples.cs:126:14:126:15 | access to local variable x2 | | Tuples.cs:118:17:118:33 | call to method Source : Object | Tuples.cs:126:14:126:15 | access to local variable x2 | | Tuples.cs:118:17:118:33 | call to method Source : Object | Tuples.cs:129:31:129:31 | access to local variable o : Object | +| Tuples.cs:118:17:118:33 | call to method Source : Object | Tuples.cs:129:31:129:31 | access to local variable o : Object | +| Tuples.cs:118:17:118:33 | call to method Source : Object | Tuples.cs:130:14:130:15 | access to local variable y3 | | Tuples.cs:118:17:118:33 | call to method Source : Object | Tuples.cs:130:14:130:15 | access to local variable y3 | | Tuples.cs:118:17:118:33 | call to method Source : Object | Tuples.cs:133:28:133:28 | access to local variable o : Object | +| Tuples.cs:118:17:118:33 | call to method Source : Object | Tuples.cs:133:28:133:28 | access to local variable o : Object | +| Tuples.cs:118:17:118:33 | call to method Source : Object | Tuples.cs:134:14:134:15 | access to local variable y4 | | Tuples.cs:118:17:118:33 | call to method Source : Object | Tuples.cs:134:14:134:15 | access to local variable y4 | | Tuples.cs:121:9:121:23 | (..., ...) : ValueTuple [field Item1] : Object | Tuples.cs:121:9:121:32 | SSA def(x1) : Object | +| Tuples.cs:121:9:121:23 | (..., ...) : ValueTuple [field Item1] : Object | Tuples.cs:121:9:121:32 | SSA def(x1) : Object | +| Tuples.cs:121:9:121:32 | SSA def(x1) : Object | Tuples.cs:122:14:122:15 | access to local variable x1 | | Tuples.cs:121:9:121:32 | SSA def(x1) : Object | Tuples.cs:122:14:122:15 | access to local variable x1 | | Tuples.cs:121:27:121:32 | (..., ...) : ValueTuple [field Item1] : Object | Tuples.cs:121:9:121:23 | (..., ...) : ValueTuple [field Item1] : Object | +| Tuples.cs:121:27:121:32 | (..., ...) : ValueTuple [field Item1] : Object | Tuples.cs:121:9:121:23 | (..., ...) : ValueTuple [field Item1] : Object | +| Tuples.cs:121:28:121:28 | access to local variable o : Object | Tuples.cs:121:27:121:32 | (..., ...) : ValueTuple [field Item1] : Object | | Tuples.cs:121:28:121:28 | access to local variable o : Object | Tuples.cs:121:27:121:32 | (..., ...) : ValueTuple [field Item1] : Object | | Tuples.cs:125:9:125:20 | (..., ...) : ValueTuple [field Item1] : Object | Tuples.cs:125:9:125:29 | SSA def(x2) : Object | +| Tuples.cs:125:9:125:20 | (..., ...) : ValueTuple [field Item1] : Object | Tuples.cs:125:9:125:29 | SSA def(x2) : Object | +| Tuples.cs:125:9:125:29 | SSA def(x2) : Object | Tuples.cs:126:14:126:15 | access to local variable x2 | | Tuples.cs:125:9:125:29 | SSA def(x2) : Object | Tuples.cs:126:14:126:15 | access to local variable x2 | | Tuples.cs:125:24:125:29 | (..., ...) : ValueTuple [field Item1] : Object | Tuples.cs:125:9:125:20 | (..., ...) : ValueTuple [field Item1] : Object | +| Tuples.cs:125:24:125:29 | (..., ...) : ValueTuple [field Item1] : Object | Tuples.cs:125:9:125:20 | (..., ...) : ValueTuple [field Item1] : Object | +| Tuples.cs:125:25:125:25 | access to local variable o : Object | Tuples.cs:125:24:125:29 | (..., ...) : ValueTuple [field Item1] : Object | | Tuples.cs:125:25:125:25 | access to local variable o : Object | Tuples.cs:125:24:125:29 | (..., ...) : ValueTuple [field Item1] : Object | | Tuples.cs:129:9:129:23 | (..., ...) : ValueTuple [field Item2] : Object | Tuples.cs:129:9:129:32 | SSA def(y3) : Object | +| Tuples.cs:129:9:129:23 | (..., ...) : ValueTuple [field Item2] : Object | Tuples.cs:129:9:129:32 | SSA def(y3) : Object | +| Tuples.cs:129:9:129:32 | SSA def(y3) : Object | Tuples.cs:130:14:130:15 | access to local variable y3 | | Tuples.cs:129:9:129:32 | SSA def(y3) : Object | Tuples.cs:130:14:130:15 | access to local variable y3 | | Tuples.cs:129:27:129:32 | (..., ...) : ValueTuple [field Item2] : Object | Tuples.cs:129:9:129:23 | (..., ...) : ValueTuple [field Item2] : Object | +| Tuples.cs:129:27:129:32 | (..., ...) : ValueTuple [field Item2] : Object | Tuples.cs:129:9:129:23 | (..., ...) : ValueTuple [field Item2] : Object | +| Tuples.cs:129:31:129:31 | access to local variable o : Object | Tuples.cs:129:27:129:32 | (..., ...) : ValueTuple [field Item2] : Object | | Tuples.cs:129:31:129:31 | access to local variable o : Object | Tuples.cs:129:27:129:32 | (..., ...) : ValueTuple [field Item2] : Object | | Tuples.cs:133:9:133:20 | (..., ...) : ValueTuple [field Item2] : Object | Tuples.cs:133:9:133:29 | SSA def(y4) : Object | +| Tuples.cs:133:9:133:20 | (..., ...) : ValueTuple [field Item2] : Object | Tuples.cs:133:9:133:29 | SSA def(y4) : Object | +| Tuples.cs:133:9:133:29 | SSA def(y4) : Object | Tuples.cs:134:14:134:15 | access to local variable y4 | | Tuples.cs:133:9:133:29 | SSA def(y4) : Object | Tuples.cs:134:14:134:15 | access to local variable y4 | | Tuples.cs:133:24:133:29 | (..., ...) : ValueTuple [field Item2] : Object | Tuples.cs:133:9:133:20 | (..., ...) : ValueTuple [field Item2] : Object | +| Tuples.cs:133:24:133:29 | (..., ...) : ValueTuple [field Item2] : Object | Tuples.cs:133:9:133:20 | (..., ...) : ValueTuple [field Item2] : Object | +| Tuples.cs:133:28:133:28 | access to local variable o : Object | Tuples.cs:133:24:133:29 | (..., ...) : ValueTuple [field Item2] : Object | | Tuples.cs:133:28:133:28 | access to local variable o : Object | Tuples.cs:133:24:133:29 | (..., ...) : ValueTuple [field Item2] : Object | nodes | Tuples.cs:7:18:7:34 | call to method Source : Object | semmle.label | call to method Source : Object | +| Tuples.cs:7:18:7:34 | call to method Source : Object | semmle.label | call to method Source : Object | +| Tuples.cs:8:18:8:34 | call to method Source : Object | semmle.label | call to method Source : Object | | Tuples.cs:8:18:8:34 | call to method Source : Object | semmle.label | call to method Source : Object | | Tuples.cs:10:17:10:32 | (..., ...) : ValueTuple> [field Item1] : Object | semmle.label | (..., ...) : ValueTuple> [field Item1] : Object | +| Tuples.cs:10:17:10:32 | (..., ...) : ValueTuple> [field Item1] : Object | semmle.label | (..., ...) : ValueTuple> [field Item1] : Object | +| Tuples.cs:10:17:10:32 | (..., ...) : ValueTuple> [field Item2, field Item2] : Object | semmle.label | (..., ...) : ValueTuple> [field Item2, field Item2] : Object | | Tuples.cs:10:17:10:32 | (..., ...) : ValueTuple> [field Item2, field Item2] : Object | semmle.label | (..., ...) : ValueTuple> [field Item2, field Item2] : Object | | Tuples.cs:10:21:10:22 | access to local variable o1 : Object | semmle.label | access to local variable o1 : Object | +| Tuples.cs:10:21:10:22 | access to local variable o1 : Object | semmle.label | access to local variable o1 : Object | +| Tuples.cs:10:25:10:31 | (..., ...) : ValueTuple [field Item2] : Object | semmle.label | (..., ...) : ValueTuple [field Item2] : Object | | Tuples.cs:10:25:10:31 | (..., ...) : ValueTuple [field Item2] : Object | semmle.label | (..., ...) : ValueTuple [field Item2] : Object | | Tuples.cs:10:29:10:30 | access to local variable o2 : Object | semmle.label | access to local variable o2 : Object | +| Tuples.cs:10:29:10:30 | access to local variable o2 : Object | semmle.label | access to local variable o2 : Object | +| Tuples.cs:11:9:11:23 | (..., ...) : ValueTuple [field Item2] : Object | semmle.label | (..., ...) : ValueTuple [field Item2] : Object | | Tuples.cs:11:9:11:23 | (..., ...) : ValueTuple [field Item2] : Object | semmle.label | (..., ...) : ValueTuple [field Item2] : Object | | Tuples.cs:11:9:11:23 | (..., ...) : ValueTuple> [field Item1] : Object | semmle.label | (..., ...) : ValueTuple> [field Item1] : Object | +| Tuples.cs:11:9:11:23 | (..., ...) : ValueTuple> [field Item1] : Object | semmle.label | (..., ...) : ValueTuple> [field Item1] : Object | +| Tuples.cs:11:9:11:23 | (..., ...) : ValueTuple> [field Item2, field Item2] : Object | semmle.label | (..., ...) : ValueTuple> [field Item2, field Item2] : Object | | Tuples.cs:11:9:11:23 | (..., ...) : ValueTuple> [field Item2, field Item2] : Object | semmle.label | (..., ...) : ValueTuple> [field Item2, field Item2] : Object | | Tuples.cs:11:9:11:27 | SSA def(a) : Object | semmle.label | SSA def(a) : Object | +| Tuples.cs:11:9:11:27 | SSA def(a) : Object | semmle.label | SSA def(a) : Object | +| Tuples.cs:11:9:11:27 | SSA def(c) : Object | semmle.label | SSA def(c) : Object | | Tuples.cs:11:9:11:27 | SSA def(c) : Object | semmle.label | SSA def(c) : Object | | Tuples.cs:12:14:12:14 | access to local variable a | semmle.label | access to local variable a | +| Tuples.cs:12:14:12:14 | access to local variable a | semmle.label | access to local variable a | +| Tuples.cs:14:14:14:14 | access to local variable c | semmle.label | access to local variable c | | Tuples.cs:14:14:14:14 | access to local variable c | semmle.label | access to local variable c | | Tuples.cs:16:9:16:19 | (..., ...) : ValueTuple> [field Item1] : Object | semmle.label | (..., ...) : ValueTuple> [field Item1] : Object | +| Tuples.cs:16:9:16:19 | (..., ...) : ValueTuple> [field Item1] : Object | semmle.label | (..., ...) : ValueTuple> [field Item1] : Object | +| Tuples.cs:16:9:16:19 | (..., ...) : ValueTuple> [field Item2, field Item2] : Object | semmle.label | (..., ...) : ValueTuple> [field Item2, field Item2] : Object | | Tuples.cs:16:9:16:19 | (..., ...) : ValueTuple> [field Item2, field Item2] : Object | semmle.label | (..., ...) : ValueTuple> [field Item2, field Item2] : Object | | Tuples.cs:16:9:16:23 | SSA def(a) : Object | semmle.label | SSA def(a) : Object | +| Tuples.cs:16:9:16:23 | SSA def(a) : Object | semmle.label | SSA def(a) : Object | +| Tuples.cs:16:9:16:23 | SSA def(c) : Object | semmle.label | SSA def(c) : Object | | Tuples.cs:16:9:16:23 | SSA def(c) : Object | semmle.label | SSA def(c) : Object | | Tuples.cs:16:13:16:18 | (..., ...) : ValueTuple [field Item2] : Object | semmle.label | (..., ...) : ValueTuple [field Item2] : Object | +| Tuples.cs:16:13:16:18 | (..., ...) : ValueTuple [field Item2] : Object | semmle.label | (..., ...) : ValueTuple [field Item2] : Object | +| Tuples.cs:17:14:17:14 | access to local variable a | semmle.label | access to local variable a | | Tuples.cs:17:14:17:14 | access to local variable a | semmle.label | access to local variable a | | Tuples.cs:19:14:19:14 | access to local variable c | semmle.label | access to local variable c | +| Tuples.cs:19:14:19:14 | access to local variable c | semmle.label | access to local variable c | +| Tuples.cs:21:9:21:22 | (..., ...) : ValueTuple> [field Item1] : Object | semmle.label | (..., ...) : ValueTuple> [field Item1] : Object | | Tuples.cs:21:9:21:22 | (..., ...) : ValueTuple> [field Item1] : Object | semmle.label | (..., ...) : ValueTuple> [field Item1] : Object | | Tuples.cs:21:9:21:22 | (..., ...) : ValueTuple> [field Item2, field Item2] : Object | semmle.label | (..., ...) : ValueTuple> [field Item2, field Item2] : Object | +| Tuples.cs:21:9:21:22 | (..., ...) : ValueTuple> [field Item2, field Item2] : Object | semmle.label | (..., ...) : ValueTuple> [field Item2, field Item2] : Object | +| Tuples.cs:21:9:21:26 | SSA def(p) : Object | semmle.label | SSA def(p) : Object | | Tuples.cs:21:9:21:26 | SSA def(p) : Object | semmle.label | SSA def(p) : Object | | Tuples.cs:21:9:21:26 | SSA def(q) : ValueTuple [field Item2] : Object | semmle.label | SSA def(q) : ValueTuple [field Item2] : Object | +| Tuples.cs:21:9:21:26 | SSA def(q) : ValueTuple [field Item2] : Object | semmle.label | SSA def(q) : ValueTuple [field Item2] : Object | +| Tuples.cs:22:14:22:14 | access to local variable p | semmle.label | access to local variable p | | Tuples.cs:22:14:22:14 | access to local variable p | semmle.label | access to local variable p | | Tuples.cs:24:14:24:14 | access to local variable q : ValueTuple [field Item2] : Object | semmle.label | access to local variable q : ValueTuple [field Item2] : Object | +| Tuples.cs:24:14:24:14 | access to local variable q : ValueTuple [field Item2] : Object | semmle.label | access to local variable q : ValueTuple [field Item2] : Object | +| Tuples.cs:24:14:24:20 | access to field Item2 | semmle.label | access to field Item2 | | Tuples.cs:24:14:24:20 | access to field Item2 | semmle.label | access to field Item2 | | Tuples.cs:26:14:26:14 | access to local variable x : ValueTuple> [field Item1] : Object | semmle.label | access to local variable x : ValueTuple> [field Item1] : Object | +| Tuples.cs:26:14:26:14 | access to local variable x : ValueTuple> [field Item1] : Object | semmle.label | access to local variable x : ValueTuple> [field Item1] : Object | +| Tuples.cs:26:14:26:20 | access to field Item1 | semmle.label | access to field Item1 | | Tuples.cs:26:14:26:20 | access to field Item1 | semmle.label | access to field Item1 | | Tuples.cs:27:14:27:14 | access to local variable x : ValueTuple> [field Item1] : Object | semmle.label | access to local variable x : ValueTuple> [field Item1] : Object | +| Tuples.cs:27:14:27:14 | access to local variable x : ValueTuple> [field Item1] : Object | semmle.label | access to local variable x : ValueTuple> [field Item1] : Object | +| Tuples.cs:27:14:27:16 | access to field Item1 | semmle.label | access to field Item1 | | Tuples.cs:27:14:27:16 | access to field Item1 | semmle.label | access to field Item1 | | Tuples.cs:29:14:29:14 | access to local variable x : ValueTuple> [field Item2, field Item2] : Object | semmle.label | access to local variable x : ValueTuple> [field Item2, field Item2] : Object | +| Tuples.cs:29:14:29:14 | access to local variable x : ValueTuple> [field Item2, field Item2] : Object | semmle.label | access to local variable x : ValueTuple> [field Item2, field Item2] : Object | +| Tuples.cs:29:14:29:20 | access to field Item2 : ValueTuple [field Item2] : Object | semmle.label | access to field Item2 : ValueTuple [field Item2] : Object | | Tuples.cs:29:14:29:20 | access to field Item2 : ValueTuple [field Item2] : Object | semmle.label | access to field Item2 : ValueTuple [field Item2] : Object | | Tuples.cs:29:14:29:26 | access to field Item2 | semmle.label | access to field Item2 | +| Tuples.cs:29:14:29:26 | access to field Item2 | semmle.label | access to field Item2 | +| Tuples.cs:34:18:34:34 | call to method Source : Object | semmle.label | call to method Source : Object | | Tuples.cs:34:18:34:34 | call to method Source : Object | semmle.label | call to method Source : Object | | Tuples.cs:35:18:35:34 | call to method Source : Object | semmle.label | call to method Source : Object | +| Tuples.cs:35:18:35:34 | call to method Source : Object | semmle.label | call to method Source : Object | +| Tuples.cs:37:17:37:48 | (..., ...) : ValueTuple> [field Item1] : Object | semmle.label | (..., ...) : ValueTuple> [field Item1] : Object | | Tuples.cs:37:17:37:48 | (..., ...) : ValueTuple> [field Item1] : Object | semmle.label | (..., ...) : ValueTuple> [field Item1] : Object | | Tuples.cs:37:17:37:48 | (..., ...) : ValueTuple> [field Item10] : Object | semmle.label | (..., ...) : ValueTuple> [field Item10] : Object | +| Tuples.cs:37:17:37:48 | (..., ...) : ValueTuple> [field Item10] : Object | semmle.label | (..., ...) : ValueTuple> [field Item10] : Object | +| Tuples.cs:37:18:37:19 | access to local variable o1 : Object | semmle.label | access to local variable o1 : Object | | Tuples.cs:37:18:37:19 | access to local variable o1 : Object | semmle.label | access to local variable o1 : Object | | Tuples.cs:37:46:37:47 | access to local variable o2 : Object | semmle.label | access to local variable o2 : Object | +| Tuples.cs:37:46:37:47 | access to local variable o2 : Object | semmle.label | access to local variable o2 : Object | +| Tuples.cs:38:14:38:14 | access to local variable x : ValueTuple> [field Item1] : Object | semmle.label | access to local variable x : ValueTuple> [field Item1] : Object | | Tuples.cs:38:14:38:14 | access to local variable x : ValueTuple> [field Item1] : Object | semmle.label | access to local variable x : ValueTuple> [field Item1] : Object | | Tuples.cs:38:14:38:20 | access to field Item1 | semmle.label | access to field Item1 | +| Tuples.cs:38:14:38:20 | access to field Item1 | semmle.label | access to field Item1 | +| Tuples.cs:40:14:40:14 | access to local variable x : ValueTuple> [field Item10] : Object | semmle.label | access to local variable x : ValueTuple> [field Item10] : Object | | Tuples.cs:40:14:40:14 | access to local variable x : ValueTuple> [field Item10] : Object | semmle.label | access to local variable x : ValueTuple> [field Item10] : Object | | Tuples.cs:40:14:40:21 | access to field Item10 | semmle.label | access to field Item10 | +| Tuples.cs:40:14:40:21 | access to field Item10 | semmle.label | access to field Item10 | +| Tuples.cs:45:17:45:33 | call to method Source : String | semmle.label | call to method Source : String | | Tuples.cs:45:17:45:33 | call to method Source : String | semmle.label | call to method Source : String | | Tuples.cs:46:17:46:55 | (...) ... : ValueTuple [field Item1] : String | semmle.label | (...) ... : ValueTuple [field Item1] : String | +| Tuples.cs:46:17:46:55 | (...) ... : ValueTuple [field Item1] : String | semmle.label | (...) ... : ValueTuple [field Item1] : String | +| Tuples.cs:46:47:46:55 | (..., ...) : ValueTuple [field Item1] : String | semmle.label | (..., ...) : ValueTuple [field Item1] : String | | Tuples.cs:46:47:46:55 | (..., ...) : ValueTuple [field Item1] : String | semmle.label | (..., ...) : ValueTuple [field Item1] : String | | Tuples.cs:46:48:46:48 | access to local variable o : String | semmle.label | access to local variable o : String | +| Tuples.cs:46:48:46:48 | access to local variable o : String | semmle.label | access to local variable o : String | +| Tuples.cs:47:14:47:14 | access to local variable x : ValueTuple [field Item1] : String | semmle.label | access to local variable x : ValueTuple [field Item1] : String | | Tuples.cs:47:14:47:14 | access to local variable x : ValueTuple [field Item1] : String | semmle.label | access to local variable x : ValueTuple [field Item1] : String | | Tuples.cs:47:14:47:20 | access to field Item1 | semmle.label | access to field Item1 | +| Tuples.cs:47:14:47:20 | access to field Item1 | semmle.label | access to field Item1 | +| Tuples.cs:57:18:57:34 | call to method Source : String | semmle.label | call to method Source : String | | Tuples.cs:57:18:57:34 | call to method Source : String | semmle.label | call to method Source : String | | Tuples.cs:58:18:58:34 | call to method Source : String | semmle.label | call to method Source : String | +| Tuples.cs:58:18:58:34 | call to method Source : String | semmle.label | call to method Source : String | +| Tuples.cs:59:17:59:32 | (..., ...) : ValueTuple,Int32> [field Item1] : String | semmle.label | (..., ...) : ValueTuple,Int32> [field Item1] : String | | Tuples.cs:59:17:59:32 | (..., ...) : ValueTuple,Int32> [field Item1] : String | semmle.label | (..., ...) : ValueTuple,Int32> [field Item1] : String | | Tuples.cs:59:17:59:32 | (..., ...) : ValueTuple,Int32> [field Item2, field Item2] : String | semmle.label | (..., ...) : ValueTuple,Int32> [field Item2, field Item2] : String | +| Tuples.cs:59:17:59:32 | (..., ...) : ValueTuple,Int32> [field Item2, field Item2] : String | semmle.label | (..., ...) : ValueTuple,Int32> [field Item2, field Item2] : String | +| Tuples.cs:59:18:59:19 | access to local variable o1 : String | semmle.label | access to local variable o1 : String | | Tuples.cs:59:18:59:19 | access to local variable o1 : String | semmle.label | access to local variable o1 : String | | Tuples.cs:59:22:59:28 | (..., ...) : ValueTuple [field Item2] : String | semmle.label | (..., ...) : ValueTuple [field Item2] : String | +| Tuples.cs:59:22:59:28 | (..., ...) : ValueTuple [field Item2] : String | semmle.label | (..., ...) : ValueTuple [field Item2] : String | +| Tuples.cs:59:26:59:27 | access to local variable o2 : String | semmle.label | access to local variable o2 : String | | Tuples.cs:59:26:59:27 | access to local variable o2 : String | semmle.label | access to local variable o2 : String | | Tuples.cs:62:18:62:57 | SSA def(t) : ValueTuple,Int32> [field Item1] : String | semmle.label | SSA def(t) : ValueTuple,Int32> [field Item1] : String | +| Tuples.cs:62:18:62:57 | SSA def(t) : ValueTuple,Int32> [field Item1] : String | semmle.label | SSA def(t) : ValueTuple,Int32> [field Item1] : String | +| Tuples.cs:62:18:62:57 | SSA def(t) : ValueTuple,Int32> [field Item2, field Item2] : String | semmle.label | SSA def(t) : ValueTuple,Int32> [field Item2, field Item2] : String | | Tuples.cs:62:18:62:57 | SSA def(t) : ValueTuple,Int32> [field Item2, field Item2] : String | semmle.label | SSA def(t) : ValueTuple,Int32> [field Item2, field Item2] : String | | Tuples.cs:63:22:63:22 | access to local variable t : ValueTuple,Int32> [field Item1] : String | semmle.label | access to local variable t : ValueTuple,Int32> [field Item1] : String | +| Tuples.cs:63:22:63:22 | access to local variable t : ValueTuple,Int32> [field Item1] : String | semmle.label | access to local variable t : ValueTuple,Int32> [field Item1] : String | +| Tuples.cs:63:22:63:28 | access to field Item1 | semmle.label | access to field Item1 | | Tuples.cs:63:22:63:28 | access to field Item1 | semmle.label | access to field Item1 | | Tuples.cs:64:22:64:22 | access to local variable t : ValueTuple,Int32> [field Item2, field Item2] : String | semmle.label | access to local variable t : ValueTuple,Int32> [field Item2, field Item2] : String | +| Tuples.cs:64:22:64:22 | access to local variable t : ValueTuple,Int32> [field Item2, field Item2] : String | semmle.label | access to local variable t : ValueTuple,Int32> [field Item2, field Item2] : String | +| Tuples.cs:64:22:64:28 | access to field Item2 : ValueTuple [field Item2] : String | semmle.label | access to field Item2 : ValueTuple [field Item2] : String | | Tuples.cs:64:22:64:28 | access to field Item2 : ValueTuple [field Item2] : String | semmle.label | access to field Item2 : ValueTuple [field Item2] : String | | Tuples.cs:64:22:64:34 | access to field Item2 | semmle.label | access to field Item2 | +| Tuples.cs:64:22:64:34 | access to field Item2 | semmle.label | access to field Item2 | +| Tuples.cs:67:18:67:35 | (..., ...) : ValueTuple [field Item2] : String | semmle.label | (..., ...) : ValueTuple [field Item2] : String | | Tuples.cs:67:18:67:35 | (..., ...) : ValueTuple [field Item2] : String | semmle.label | (..., ...) : ValueTuple [field Item2] : String | | Tuples.cs:67:18:67:35 | (..., ...) : ValueTuple,Int32> [field Item1] : String | semmle.label | (..., ...) : ValueTuple,Int32> [field Item1] : String | +| Tuples.cs:67:18:67:35 | (..., ...) : ValueTuple,Int32> [field Item1] : String | semmle.label | (..., ...) : ValueTuple,Int32> [field Item1] : String | +| Tuples.cs:67:18:67:35 | (..., ...) : ValueTuple,Int32> [field Item2, field Item2] : String | semmle.label | (..., ...) : ValueTuple,Int32> [field Item2, field Item2] : String | | Tuples.cs:67:18:67:35 | (..., ...) : ValueTuple,Int32> [field Item2, field Item2] : String | semmle.label | (..., ...) : ValueTuple,Int32> [field Item2, field Item2] : String | | Tuples.cs:67:23:67:23 | SSA def(a) : String | semmle.label | SSA def(a) : String | +| Tuples.cs:67:23:67:23 | SSA def(a) : String | semmle.label | SSA def(a) : String | +| Tuples.cs:67:30:67:30 | SSA def(c) : String | semmle.label | SSA def(c) : String | | Tuples.cs:67:30:67:30 | SSA def(c) : String | semmle.label | SSA def(c) : String | | Tuples.cs:68:22:68:22 | access to local variable a | semmle.label | access to local variable a | +| Tuples.cs:68:22:68:22 | access to local variable a | semmle.label | access to local variable a | +| Tuples.cs:69:22:69:22 | access to local variable c | semmle.label | access to local variable c | | Tuples.cs:69:22:69:22 | access to local variable c | semmle.label | access to local variable c | | Tuples.cs:87:18:87:35 | (..., ...) : ValueTuple [field Item2] : String | semmle.label | (..., ...) : ValueTuple [field Item2] : String | +| Tuples.cs:87:18:87:35 | (..., ...) : ValueTuple [field Item2] : String | semmle.label | (..., ...) : ValueTuple [field Item2] : String | +| Tuples.cs:87:18:87:35 | (..., ...) : ValueTuple,Int32> [field Item1] : String | semmle.label | (..., ...) : ValueTuple,Int32> [field Item1] : String | | Tuples.cs:87:18:87:35 | (..., ...) : ValueTuple,Int32> [field Item1] : String | semmle.label | (..., ...) : ValueTuple,Int32> [field Item1] : String | | Tuples.cs:87:18:87:35 | (..., ...) : ValueTuple,Int32> [field Item2, field Item2] : String | semmle.label | (..., ...) : ValueTuple,Int32> [field Item2, field Item2] : String | +| Tuples.cs:87:18:87:35 | (..., ...) : ValueTuple,Int32> [field Item2, field Item2] : String | semmle.label | (..., ...) : ValueTuple,Int32> [field Item2, field Item2] : String | +| Tuples.cs:87:23:87:23 | SSA def(p) : String | semmle.label | SSA def(p) : String | | Tuples.cs:87:23:87:23 | SSA def(p) : String | semmle.label | SSA def(p) : String | | Tuples.cs:87:30:87:30 | SSA def(r) : String | semmle.label | SSA def(r) : String | +| Tuples.cs:87:30:87:30 | SSA def(r) : String | semmle.label | SSA def(r) : String | +| Tuples.cs:89:18:89:18 | access to local variable p | semmle.label | access to local variable p | | Tuples.cs:89:18:89:18 | access to local variable p | semmle.label | access to local variable p | | Tuples.cs:90:18:90:18 | access to local variable r | semmle.label | access to local variable r | +| Tuples.cs:90:18:90:18 | access to local variable r | semmle.label | access to local variable r | +| Tuples.cs:99:17:99:33 | call to method Source : String | semmle.label | call to method Source : String | | Tuples.cs:99:17:99:33 | call to method Source : String | semmle.label | call to method Source : String | | Tuples.cs:100:17:100:28 | object creation of type R1 : R1 [property i] : String | semmle.label | object creation of type R1 : R1 [property i] : String | +| Tuples.cs:100:17:100:28 | object creation of type R1 : R1 [property i] : String | semmle.label | object creation of type R1 : R1 [property i] : String | +| Tuples.cs:100:24:100:24 | access to local variable o : String | semmle.label | access to local variable o : String | | Tuples.cs:100:24:100:24 | access to local variable o : String | semmle.label | access to local variable o : String | | Tuples.cs:101:14:101:14 | access to local variable r : R1 [property i] : String | semmle.label | access to local variable r : R1 [property i] : String | +| Tuples.cs:101:14:101:14 | access to local variable r : R1 [property i] : String | semmle.label | access to local variable r : R1 [property i] : String | +| Tuples.cs:101:14:101:16 | access to property i | semmle.label | access to property i | | Tuples.cs:101:14:101:16 | access to property i | semmle.label | access to property i | | Tuples.cs:118:17:118:33 | call to method Source : Object | semmle.label | call to method Source : Object | +| Tuples.cs:118:17:118:33 | call to method Source : Object | semmle.label | call to method Source : Object | +| Tuples.cs:121:9:121:23 | (..., ...) : ValueTuple [field Item1] : Object | semmle.label | (..., ...) : ValueTuple [field Item1] : Object | | Tuples.cs:121:9:121:23 | (..., ...) : ValueTuple [field Item1] : Object | semmle.label | (..., ...) : ValueTuple [field Item1] : Object | | Tuples.cs:121:9:121:32 | SSA def(x1) : Object | semmle.label | SSA def(x1) : Object | +| Tuples.cs:121:9:121:32 | SSA def(x1) : Object | semmle.label | SSA def(x1) : Object | +| Tuples.cs:121:27:121:32 | (..., ...) : ValueTuple [field Item1] : Object | semmle.label | (..., ...) : ValueTuple [field Item1] : Object | | Tuples.cs:121:27:121:32 | (..., ...) : ValueTuple [field Item1] : Object | semmle.label | (..., ...) : ValueTuple [field Item1] : Object | | Tuples.cs:121:28:121:28 | access to local variable o : Object | semmle.label | access to local variable o : Object | +| Tuples.cs:121:28:121:28 | access to local variable o : Object | semmle.label | access to local variable o : Object | +| Tuples.cs:122:14:122:15 | access to local variable x1 | semmle.label | access to local variable x1 | | Tuples.cs:122:14:122:15 | access to local variable x1 | semmle.label | access to local variable x1 | | Tuples.cs:125:9:125:20 | (..., ...) : ValueTuple [field Item1] : Object | semmle.label | (..., ...) : ValueTuple [field Item1] : Object | +| Tuples.cs:125:9:125:20 | (..., ...) : ValueTuple [field Item1] : Object | semmle.label | (..., ...) : ValueTuple [field Item1] : Object | +| Tuples.cs:125:9:125:29 | SSA def(x2) : Object | semmle.label | SSA def(x2) : Object | | Tuples.cs:125:9:125:29 | SSA def(x2) : Object | semmle.label | SSA def(x2) : Object | | Tuples.cs:125:24:125:29 | (..., ...) : ValueTuple [field Item1] : Object | semmle.label | (..., ...) : ValueTuple [field Item1] : Object | +| Tuples.cs:125:24:125:29 | (..., ...) : ValueTuple [field Item1] : Object | semmle.label | (..., ...) : ValueTuple [field Item1] : Object | +| Tuples.cs:125:25:125:25 | access to local variable o : Object | semmle.label | access to local variable o : Object | | Tuples.cs:125:25:125:25 | access to local variable o : Object | semmle.label | access to local variable o : Object | | Tuples.cs:126:14:126:15 | access to local variable x2 | semmle.label | access to local variable x2 | +| Tuples.cs:126:14:126:15 | access to local variable x2 | semmle.label | access to local variable x2 | +| Tuples.cs:129:9:129:23 | (..., ...) : ValueTuple [field Item2] : Object | semmle.label | (..., ...) : ValueTuple [field Item2] : Object | | Tuples.cs:129:9:129:23 | (..., ...) : ValueTuple [field Item2] : Object | semmle.label | (..., ...) : ValueTuple [field Item2] : Object | | Tuples.cs:129:9:129:32 | SSA def(y3) : Object | semmle.label | SSA def(y3) : Object | +| Tuples.cs:129:9:129:32 | SSA def(y3) : Object | semmle.label | SSA def(y3) : Object | +| Tuples.cs:129:27:129:32 | (..., ...) : ValueTuple [field Item2] : Object | semmle.label | (..., ...) : ValueTuple [field Item2] : Object | | Tuples.cs:129:27:129:32 | (..., ...) : ValueTuple [field Item2] : Object | semmle.label | (..., ...) : ValueTuple [field Item2] : Object | | Tuples.cs:129:31:129:31 | access to local variable o : Object | semmle.label | access to local variable o : Object | +| Tuples.cs:129:31:129:31 | access to local variable o : Object | semmle.label | access to local variable o : Object | +| Tuples.cs:130:14:130:15 | access to local variable y3 | semmle.label | access to local variable y3 | | Tuples.cs:130:14:130:15 | access to local variable y3 | semmle.label | access to local variable y3 | | Tuples.cs:133:9:133:20 | (..., ...) : ValueTuple [field Item2] : Object | semmle.label | (..., ...) : ValueTuple [field Item2] : Object | +| Tuples.cs:133:9:133:20 | (..., ...) : ValueTuple [field Item2] : Object | semmle.label | (..., ...) : ValueTuple [field Item2] : Object | +| Tuples.cs:133:9:133:29 | SSA def(y4) : Object | semmle.label | SSA def(y4) : Object | | Tuples.cs:133:9:133:29 | SSA def(y4) : Object | semmle.label | SSA def(y4) : Object | | Tuples.cs:133:24:133:29 | (..., ...) : ValueTuple [field Item2] : Object | semmle.label | (..., ...) : ValueTuple [field Item2] : Object | +| Tuples.cs:133:24:133:29 | (..., ...) : ValueTuple [field Item2] : Object | semmle.label | (..., ...) : ValueTuple [field Item2] : Object | | Tuples.cs:133:28:133:28 | access to local variable o : Object | semmle.label | access to local variable o : Object | +| Tuples.cs:133:28:133:28 | access to local variable o : Object | semmle.label | access to local variable o : Object | +| Tuples.cs:134:14:134:15 | access to local variable y4 | semmle.label | access to local variable y4 | | Tuples.cs:134:14:134:15 | access to local variable y4 | semmle.label | access to local variable y4 | subpaths #select | Tuples.cs:12:14:12:14 | access to local variable a | Tuples.cs:7:18:7:34 | call to method Source : Object | Tuples.cs:12:14:12:14 | access to local variable a | $@ | Tuples.cs:7:18:7:34 | call to method Source : Object | call to method Source : Object | +| Tuples.cs:12:14:12:14 | access to local variable a | Tuples.cs:7:18:7:34 | call to method Source : Object | Tuples.cs:12:14:12:14 | access to local variable a | $@ | Tuples.cs:7:18:7:34 | call to method Source : Object | call to method Source : Object | +| Tuples.cs:14:14:14:14 | access to local variable c | Tuples.cs:8:18:8:34 | call to method Source : Object | Tuples.cs:14:14:14:14 | access to local variable c | $@ | Tuples.cs:8:18:8:34 | call to method Source : Object | call to method Source : Object | | Tuples.cs:14:14:14:14 | access to local variable c | Tuples.cs:8:18:8:34 | call to method Source : Object | Tuples.cs:14:14:14:14 | access to local variable c | $@ | Tuples.cs:8:18:8:34 | call to method Source : Object | call to method Source : Object | | Tuples.cs:17:14:17:14 | access to local variable a | Tuples.cs:7:18:7:34 | call to method Source : Object | Tuples.cs:17:14:17:14 | access to local variable a | $@ | Tuples.cs:7:18:7:34 | call to method Source : Object | call to method Source : Object | +| Tuples.cs:17:14:17:14 | access to local variable a | Tuples.cs:7:18:7:34 | call to method Source : Object | Tuples.cs:17:14:17:14 | access to local variable a | $@ | Tuples.cs:7:18:7:34 | call to method Source : Object | call to method Source : Object | +| Tuples.cs:19:14:19:14 | access to local variable c | Tuples.cs:8:18:8:34 | call to method Source : Object | Tuples.cs:19:14:19:14 | access to local variable c | $@ | Tuples.cs:8:18:8:34 | call to method Source : Object | call to method Source : Object | | Tuples.cs:19:14:19:14 | access to local variable c | Tuples.cs:8:18:8:34 | call to method Source : Object | Tuples.cs:19:14:19:14 | access to local variable c | $@ | Tuples.cs:8:18:8:34 | call to method Source : Object | call to method Source : Object | | Tuples.cs:22:14:22:14 | access to local variable p | Tuples.cs:7:18:7:34 | call to method Source : Object | Tuples.cs:22:14:22:14 | access to local variable p | $@ | Tuples.cs:7:18:7:34 | call to method Source : Object | call to method Source : Object | +| Tuples.cs:22:14:22:14 | access to local variable p | Tuples.cs:7:18:7:34 | call to method Source : Object | Tuples.cs:22:14:22:14 | access to local variable p | $@ | Tuples.cs:7:18:7:34 | call to method Source : Object | call to method Source : Object | +| Tuples.cs:24:14:24:20 | access to field Item2 | Tuples.cs:8:18:8:34 | call to method Source : Object | Tuples.cs:24:14:24:20 | access to field Item2 | $@ | Tuples.cs:8:18:8:34 | call to method Source : Object | call to method Source : Object | | Tuples.cs:24:14:24:20 | access to field Item2 | Tuples.cs:8:18:8:34 | call to method Source : Object | Tuples.cs:24:14:24:20 | access to field Item2 | $@ | Tuples.cs:8:18:8:34 | call to method Source : Object | call to method Source : Object | | Tuples.cs:26:14:26:20 | access to field Item1 | Tuples.cs:7:18:7:34 | call to method Source : Object | Tuples.cs:26:14:26:20 | access to field Item1 | $@ | Tuples.cs:7:18:7:34 | call to method Source : Object | call to method Source : Object | +| Tuples.cs:26:14:26:20 | access to field Item1 | Tuples.cs:7:18:7:34 | call to method Source : Object | Tuples.cs:26:14:26:20 | access to field Item1 | $@ | Tuples.cs:7:18:7:34 | call to method Source : Object | call to method Source : Object | +| Tuples.cs:27:14:27:16 | access to field Item1 | Tuples.cs:7:18:7:34 | call to method Source : Object | Tuples.cs:27:14:27:16 | access to field Item1 | $@ | Tuples.cs:7:18:7:34 | call to method Source : Object | call to method Source : Object | | Tuples.cs:27:14:27:16 | access to field Item1 | Tuples.cs:7:18:7:34 | call to method Source : Object | Tuples.cs:27:14:27:16 | access to field Item1 | $@ | Tuples.cs:7:18:7:34 | call to method Source : Object | call to method Source : Object | | Tuples.cs:29:14:29:26 | access to field Item2 | Tuples.cs:8:18:8:34 | call to method Source : Object | Tuples.cs:29:14:29:26 | access to field Item2 | $@ | Tuples.cs:8:18:8:34 | call to method Source : Object | call to method Source : Object | +| Tuples.cs:29:14:29:26 | access to field Item2 | Tuples.cs:8:18:8:34 | call to method Source : Object | Tuples.cs:29:14:29:26 | access to field Item2 | $@ | Tuples.cs:8:18:8:34 | call to method Source : Object | call to method Source : Object | +| Tuples.cs:38:14:38:20 | access to field Item1 | Tuples.cs:34:18:34:34 | call to method Source : Object | Tuples.cs:38:14:38:20 | access to field Item1 | $@ | Tuples.cs:34:18:34:34 | call to method Source : Object | call to method Source : Object | | Tuples.cs:38:14:38:20 | access to field Item1 | Tuples.cs:34:18:34:34 | call to method Source : Object | Tuples.cs:38:14:38:20 | access to field Item1 | $@ | Tuples.cs:34:18:34:34 | call to method Source : Object | call to method Source : Object | | Tuples.cs:40:14:40:21 | access to field Item10 | Tuples.cs:35:18:35:34 | call to method Source : Object | Tuples.cs:40:14:40:21 | access to field Item10 | $@ | Tuples.cs:35:18:35:34 | call to method Source : Object | call to method Source : Object | +| Tuples.cs:40:14:40:21 | access to field Item10 | Tuples.cs:35:18:35:34 | call to method Source : Object | Tuples.cs:40:14:40:21 | access to field Item10 | $@ | Tuples.cs:35:18:35:34 | call to method Source : Object | call to method Source : Object | +| Tuples.cs:47:14:47:20 | access to field Item1 | Tuples.cs:45:17:45:33 | call to method Source : String | Tuples.cs:47:14:47:20 | access to field Item1 | $@ | Tuples.cs:45:17:45:33 | call to method Source : String | call to method Source : String | | Tuples.cs:47:14:47:20 | access to field Item1 | Tuples.cs:45:17:45:33 | call to method Source : String | Tuples.cs:47:14:47:20 | access to field Item1 | $@ | Tuples.cs:45:17:45:33 | call to method Source : String | call to method Source : String | | Tuples.cs:63:22:63:28 | access to field Item1 | Tuples.cs:57:18:57:34 | call to method Source : String | Tuples.cs:63:22:63:28 | access to field Item1 | $@ | Tuples.cs:57:18:57:34 | call to method Source : String | call to method Source : String | +| Tuples.cs:63:22:63:28 | access to field Item1 | Tuples.cs:57:18:57:34 | call to method Source : String | Tuples.cs:63:22:63:28 | access to field Item1 | $@ | Tuples.cs:57:18:57:34 | call to method Source : String | call to method Source : String | +| Tuples.cs:64:22:64:34 | access to field Item2 | Tuples.cs:58:18:58:34 | call to method Source : String | Tuples.cs:64:22:64:34 | access to field Item2 | $@ | Tuples.cs:58:18:58:34 | call to method Source : String | call to method Source : String | | Tuples.cs:64:22:64:34 | access to field Item2 | Tuples.cs:58:18:58:34 | call to method Source : String | Tuples.cs:64:22:64:34 | access to field Item2 | $@ | Tuples.cs:58:18:58:34 | call to method Source : String | call to method Source : String | | Tuples.cs:68:22:68:22 | access to local variable a | Tuples.cs:57:18:57:34 | call to method Source : String | Tuples.cs:68:22:68:22 | access to local variable a | $@ | Tuples.cs:57:18:57:34 | call to method Source : String | call to method Source : String | +| Tuples.cs:68:22:68:22 | access to local variable a | Tuples.cs:57:18:57:34 | call to method Source : String | Tuples.cs:68:22:68:22 | access to local variable a | $@ | Tuples.cs:57:18:57:34 | call to method Source : String | call to method Source : String | +| Tuples.cs:69:22:69:22 | access to local variable c | Tuples.cs:58:18:58:34 | call to method Source : String | Tuples.cs:69:22:69:22 | access to local variable c | $@ | Tuples.cs:58:18:58:34 | call to method Source : String | call to method Source : String | | Tuples.cs:69:22:69:22 | access to local variable c | Tuples.cs:58:18:58:34 | call to method Source : String | Tuples.cs:69:22:69:22 | access to local variable c | $@ | Tuples.cs:58:18:58:34 | call to method Source : String | call to method Source : String | | Tuples.cs:89:18:89:18 | access to local variable p | Tuples.cs:57:18:57:34 | call to method Source : String | Tuples.cs:89:18:89:18 | access to local variable p | $@ | Tuples.cs:57:18:57:34 | call to method Source : String | call to method Source : String | +| Tuples.cs:89:18:89:18 | access to local variable p | Tuples.cs:57:18:57:34 | call to method Source : String | Tuples.cs:89:18:89:18 | access to local variable p | $@ | Tuples.cs:57:18:57:34 | call to method Source : String | call to method Source : String | +| Tuples.cs:90:18:90:18 | access to local variable r | Tuples.cs:58:18:58:34 | call to method Source : String | Tuples.cs:90:18:90:18 | access to local variable r | $@ | Tuples.cs:58:18:58:34 | call to method Source : String | call to method Source : String | | Tuples.cs:90:18:90:18 | access to local variable r | Tuples.cs:58:18:58:34 | call to method Source : String | Tuples.cs:90:18:90:18 | access to local variable r | $@ | Tuples.cs:58:18:58:34 | call to method Source : String | call to method Source : String | | Tuples.cs:101:14:101:16 | access to property i | Tuples.cs:99:17:99:33 | call to method Source : String | Tuples.cs:101:14:101:16 | access to property i | $@ | Tuples.cs:99:17:99:33 | call to method Source : String | call to method Source : String | +| Tuples.cs:101:14:101:16 | access to property i | Tuples.cs:99:17:99:33 | call to method Source : String | Tuples.cs:101:14:101:16 | access to property i | $@ | Tuples.cs:99:17:99:33 | call to method Source : String | call to method Source : String | +| Tuples.cs:122:14:122:15 | access to local variable x1 | Tuples.cs:118:17:118:33 | call to method Source : Object | Tuples.cs:122:14:122:15 | access to local variable x1 | $@ | Tuples.cs:118:17:118:33 | call to method Source : Object | call to method Source : Object | | Tuples.cs:122:14:122:15 | access to local variable x1 | Tuples.cs:118:17:118:33 | call to method Source : Object | Tuples.cs:122:14:122:15 | access to local variable x1 | $@ | Tuples.cs:118:17:118:33 | call to method Source : Object | call to method Source : Object | | Tuples.cs:126:14:126:15 | access to local variable x2 | Tuples.cs:118:17:118:33 | call to method Source : Object | Tuples.cs:126:14:126:15 | access to local variable x2 | $@ | Tuples.cs:118:17:118:33 | call to method Source : Object | call to method Source : Object | +| Tuples.cs:126:14:126:15 | access to local variable x2 | Tuples.cs:118:17:118:33 | call to method Source : Object | Tuples.cs:126:14:126:15 | access to local variable x2 | $@ | Tuples.cs:118:17:118:33 | call to method Source : Object | call to method Source : Object | +| Tuples.cs:130:14:130:15 | access to local variable y3 | Tuples.cs:118:17:118:33 | call to method Source : Object | Tuples.cs:130:14:130:15 | access to local variable y3 | $@ | Tuples.cs:118:17:118:33 | call to method Source : Object | call to method Source : Object | | Tuples.cs:130:14:130:15 | access to local variable y3 | Tuples.cs:118:17:118:33 | call to method Source : Object | Tuples.cs:130:14:130:15 | access to local variable y3 | $@ | Tuples.cs:118:17:118:33 | call to method Source : Object | call to method Source : Object | | Tuples.cs:134:14:134:15 | access to local variable y4 | Tuples.cs:118:17:118:33 | call to method Source : Object | Tuples.cs:134:14:134:15 | access to local variable y4 | $@ | Tuples.cs:118:17:118:33 | call to method Source : Object | call to method Source : Object | +| Tuples.cs:134:14:134:15 | access to local variable y4 | Tuples.cs:118:17:118:33 | call to method Source : Object | Tuples.cs:134:14:134:15 | access to local variable y4 | $@ | Tuples.cs:118:17:118:33 | call to method Source : Object | call to method Source : Object | diff --git a/csharp/ql/test/library-tests/dataflow/tuples/Tuples.ql b/csharp/ql/test/library-tests/dataflow/tuples/Tuples.ql index b01c0f7fcaf..9336e1b28be 100644 --- a/csharp/ql/test/library-tests/dataflow/tuples/Tuples.ql +++ b/csharp/ql/test/library-tests/dataflow/tuples/Tuples.ql @@ -3,9 +3,10 @@ */ import csharp -import DefaultValueFlow::PathGraph import TestUtilities.InlineFlowTest +import DefaultFlowTest +import PathGraph -from DefaultValueFlow::PathNode source, DefaultValueFlow::PathNode sink -where DefaultValueFlow::flowPath(source, sink) +from PathNode source, PathNode sink +where flowPath(source, sink) select sink, source, sink, "$@", source, source.toString() diff --git a/csharp/ql/test/query-tests/API Abuse/CallToGCCollect/options b/csharp/ql/test/query-tests/API Abuse/CallToGCCollect/options new file mode 100644 index 00000000000..75c39b4541b --- /dev/null +++ b/csharp/ql/test/query-tests/API Abuse/CallToGCCollect/options @@ -0,0 +1,2 @@ +semmle-extractor-options: /nostdlib /noconfig +semmle-extractor-options: --load-sources-from-project:${testdir}/../../../resources/stubs/_frameworks/Microsoft.NETCore.App/Microsoft.NETCore.App.csproj diff --git a/csharp/ql/test/query-tests/API Abuse/CallToObsoleteMethod/options b/csharp/ql/test/query-tests/API Abuse/CallToObsoleteMethod/options new file mode 100644 index 00000000000..75c39b4541b --- /dev/null +++ b/csharp/ql/test/query-tests/API Abuse/CallToObsoleteMethod/options @@ -0,0 +1,2 @@ +semmle-extractor-options: /nostdlib /noconfig +semmle-extractor-options: --load-sources-from-project:${testdir}/../../../resources/stubs/_frameworks/Microsoft.NETCore.App/Microsoft.NETCore.App.csproj diff --git a/csharp/ql/test/query-tests/API Abuse/ClassDoesNotImplementEquals/options b/csharp/ql/test/query-tests/API Abuse/ClassDoesNotImplementEquals/options new file mode 100644 index 00000000000..75c39b4541b --- /dev/null +++ b/csharp/ql/test/query-tests/API Abuse/ClassDoesNotImplementEquals/options @@ -0,0 +1,2 @@ +semmle-extractor-options: /nostdlib /noconfig +semmle-extractor-options: --load-sources-from-project:${testdir}/../../../resources/stubs/_frameworks/Microsoft.NETCore.App/Microsoft.NETCore.App.csproj diff --git a/csharp/ql/test/query-tests/API Abuse/ClassImplementsICloneable/options b/csharp/ql/test/query-tests/API Abuse/ClassImplementsICloneable/options new file mode 100644 index 00000000000..75c39b4541b --- /dev/null +++ b/csharp/ql/test/query-tests/API Abuse/ClassImplementsICloneable/options @@ -0,0 +1,2 @@ +semmle-extractor-options: /nostdlib /noconfig +semmle-extractor-options: --load-sources-from-project:${testdir}/../../../resources/stubs/_frameworks/Microsoft.NETCore.App/Microsoft.NETCore.App.csproj diff --git a/csharp/ql/test/query-tests/API Abuse/DisposeNotCalledOnException/options b/csharp/ql/test/query-tests/API Abuse/DisposeNotCalledOnException/options index a0b23f3ee8b..7faed1b92ed 100644 --- a/csharp/ql/test/query-tests/API Abuse/DisposeNotCalledOnException/options +++ b/csharp/ql/test/query-tests/API Abuse/DisposeNotCalledOnException/options @@ -1,2 +1,2 @@ -semmle-extractor-options: /r:System.ComponentModel.Primitives.dll /r:${testdir}/../../../resources/assemblies/System.Data.dll /r:System.Data.Common.dll -semmle-extractor-options: /langversion:8.0 +semmle-extractor-options: /nostdlib /noconfig +semmle-extractor-options: --load-sources-from-project:${testdir}/../../../resources/stubs/System.Data.SqlClient/4.8.3/System.Data.SqlClient.csproj diff --git a/csharp/ql/test/query-tests/API Abuse/FormatInvalid/options b/csharp/ql/test/query-tests/API Abuse/FormatInvalid/options index ea0639b2d0a..75c39b4541b 100644 --- a/csharp/ql/test/query-tests/API Abuse/FormatInvalid/options +++ b/csharp/ql/test/query-tests/API Abuse/FormatInvalid/options @@ -1 +1,2 @@ -semmle-extractor-options: /r:System.Runtime.Extensions.dll /r:System.Diagnostics.TraceSource.dll +semmle-extractor-options: /nostdlib /noconfig +semmle-extractor-options: --load-sources-from-project:${testdir}/../../../resources/stubs/_frameworks/Microsoft.NETCore.App/Microsoft.NETCore.App.csproj diff --git a/csharp/ql/test/query-tests/API Abuse/InconsistentEqualsGetHashCode/options b/csharp/ql/test/query-tests/API Abuse/InconsistentEqualsGetHashCode/options new file mode 100644 index 00000000000..75c39b4541b --- /dev/null +++ b/csharp/ql/test/query-tests/API Abuse/InconsistentEqualsGetHashCode/options @@ -0,0 +1,2 @@ +semmle-extractor-options: /nostdlib /noconfig +semmle-extractor-options: --load-sources-from-project:${testdir}/../../../resources/stubs/_frameworks/Microsoft.NETCore.App/Microsoft.NETCore.App.csproj diff --git a/csharp/ql/test/query-tests/API Abuse/IncorrectCompareToSignature/IncorrectCompareToSignature.cs b/csharp/ql/test/query-tests/API Abuse/IncorrectCompareToSignature/IncorrectCompareToSignature.cs index 0ecabd07016..5b5780ed977 100644 --- a/csharp/ql/test/query-tests/API Abuse/IncorrectCompareToSignature/IncorrectCompareToSignature.cs +++ b/csharp/ql/test/query-tests/API Abuse/IncorrectCompareToSignature/IncorrectCompareToSignature.cs @@ -1,15 +1,4 @@ -namespace System -{ - public interface IComparable - { - int CompareTo(object obj); // GOOD: the very definition of IComparable.CompareTo() - } - - public interface IComparable - { - int CompareTo(T other); // GOOD: the very definition of IComparable.CompareTo() - } -} +using System; class C1 { diff --git a/csharp/ql/test/query-tests/API Abuse/IncorrectCompareToSignature/IncorrectCompareToSignature.expected b/csharp/ql/test/query-tests/API Abuse/IncorrectCompareToSignature/IncorrectCompareToSignature.expected index 860009bd3e2..419629319a5 100644 --- a/csharp/ql/test/query-tests/API Abuse/IncorrectCompareToSignature/IncorrectCompareToSignature.expected +++ b/csharp/ql/test/query-tests/API Abuse/IncorrectCompareToSignature/IncorrectCompareToSignature.expected @@ -1,2 +1,2 @@ -| IncorrectCompareToSignature.cs:16:16:16:24 | CompareTo | The parameter of this 'CompareTo' method is of type $@, but $@ does not implement 'IComparable<$@>'. | IncorrectCompareToSignature.cs:14:10:14:10 | T | T | IncorrectCompareToSignature.cs:14:7:14:11 | C1<> | C1<> | IncorrectCompareToSignature.cs:14:10:14:10 | T | T | +| IncorrectCompareToSignature.cs:5:16:5:24 | CompareTo | The parameter of this 'CompareTo' method is of type $@, but $@ does not implement 'IComparable<$@>'. | IncorrectCompareToSignature.cs:3:10:3:10 | T | T | IncorrectCompareToSignature.cs:3:7:3:11 | C1<> | C1<> | IncorrectCompareToSignature.cs:3:10:3:10 | T | T | | IncorrectCompareToSignatureBad.cs:5:16:5:24 | CompareTo | The parameter of this 'CompareTo' method is of type $@, but $@ does not implement 'IComparable<$@>'. | IncorrectCompareToSignatureBad.cs:3:7:3:9 | Bad | Bad | IncorrectCompareToSignatureBad.cs:3:7:3:9 | Bad | Bad | IncorrectCompareToSignatureBad.cs:3:7:3:9 | Bad | Bad | diff --git a/csharp/ql/test/query-tests/API Abuse/IncorrectCompareToSignature/options b/csharp/ql/test/query-tests/API Abuse/IncorrectCompareToSignature/options new file mode 100644 index 00000000000..75c39b4541b --- /dev/null +++ b/csharp/ql/test/query-tests/API Abuse/IncorrectCompareToSignature/options @@ -0,0 +1,2 @@ +semmle-extractor-options: /nostdlib /noconfig +semmle-extractor-options: --load-sources-from-project:${testdir}/../../../resources/stubs/_frameworks/Microsoft.NETCore.App/Microsoft.NETCore.App.csproj diff --git a/csharp/ql/test/query-tests/API Abuse/IncorrectEqualsSignature/options b/csharp/ql/test/query-tests/API Abuse/IncorrectEqualsSignature/options new file mode 100644 index 00000000000..75c39b4541b --- /dev/null +++ b/csharp/ql/test/query-tests/API Abuse/IncorrectEqualsSignature/options @@ -0,0 +1,2 @@ +semmle-extractor-options: /nostdlib /noconfig +semmle-extractor-options: --load-sources-from-project:${testdir}/../../../resources/stubs/_frameworks/Microsoft.NETCore.App/Microsoft.NETCore.App.csproj diff --git a/csharp/ql/test/query-tests/API Abuse/MissingDisposeCall/options b/csharp/ql/test/query-tests/API Abuse/MissingDisposeCall/options index 2d3e53846e4..75c39b4541b 100644 --- a/csharp/ql/test/query-tests/API Abuse/MissingDisposeCall/options +++ b/csharp/ql/test/query-tests/API Abuse/MissingDisposeCall/options @@ -1 +1,2 @@ -semmle-extractor-options: /r:System.ComponentModel.Primitives.dll +semmle-extractor-options: /nostdlib /noconfig +semmle-extractor-options: --load-sources-from-project:${testdir}/../../../resources/stubs/_frameworks/Microsoft.NETCore.App/Microsoft.NETCore.App.csproj diff --git a/csharp/ql/test/query-tests/API Abuse/MissingDisposeMethod/options b/csharp/ql/test/query-tests/API Abuse/MissingDisposeMethod/options index 2d3e53846e4..75c39b4541b 100644 --- a/csharp/ql/test/query-tests/API Abuse/MissingDisposeMethod/options +++ b/csharp/ql/test/query-tests/API Abuse/MissingDisposeMethod/options @@ -1 +1,2 @@ -semmle-extractor-options: /r:System.ComponentModel.Primitives.dll +semmle-extractor-options: /nostdlib /noconfig +semmle-extractor-options: --load-sources-from-project:${testdir}/../../../resources/stubs/_frameworks/Microsoft.NETCore.App/Microsoft.NETCore.App.csproj diff --git a/csharp/ql/test/query-tests/API Abuse/NoDisposeCallOnLocalIDisposable/options b/csharp/ql/test/query-tests/API Abuse/NoDisposeCallOnLocalIDisposable/options index 08e01d88cc1..a02c94f4258 100644 --- a/csharp/ql/test/query-tests/API Abuse/NoDisposeCallOnLocalIDisposable/options +++ b/csharp/ql/test/query-tests/API Abuse/NoDisposeCallOnLocalIDisposable/options @@ -1 +1 @@ -semmle-extractor-options: --cil /langversion:8.0 /r:System.Xml.dll /r:System.Xml.ReaderWriter.dll /r:System.Private.Xml.dll /r:System.ComponentModel.Primitives.dll /r:System.IO.Compression.dll /r:System.Runtime.Extensions.dll +semmle-extractor-options: --cil /r:System.Private.Xml.dll /r:System.IO.Compression.dll diff --git a/csharp/ql/test/query-tests/API Abuse/NonOverridingMethod/options b/csharp/ql/test/query-tests/API Abuse/NonOverridingMethod/options new file mode 100644 index 00000000000..75c39b4541b --- /dev/null +++ b/csharp/ql/test/query-tests/API Abuse/NonOverridingMethod/options @@ -0,0 +1,2 @@ +semmle-extractor-options: /nostdlib /noconfig +semmle-extractor-options: --load-sources-from-project:${testdir}/../../../resources/stubs/_frameworks/Microsoft.NETCore.App/Microsoft.NETCore.App.csproj diff --git a/csharp/ql/test/query-tests/API Abuse/NullArgumentToEquals/options b/csharp/ql/test/query-tests/API Abuse/NullArgumentToEquals/options new file mode 100644 index 00000000000..75c39b4541b --- /dev/null +++ b/csharp/ql/test/query-tests/API Abuse/NullArgumentToEquals/options @@ -0,0 +1,2 @@ +semmle-extractor-options: /nostdlib /noconfig +semmle-extractor-options: --load-sources-from-project:${testdir}/../../../resources/stubs/_frameworks/Microsoft.NETCore.App/Microsoft.NETCore.App.csproj diff --git a/csharp/ql/test/query-tests/API Abuse/UncheckedReturnValue/UncheckedReturnValue.cs b/csharp/ql/test/query-tests/API Abuse/UncheckedReturnValue/UncheckedReturnValue.cs index f4a4944f4fb..cf290a84bcb 100644 --- a/csharp/ql/test/query-tests/API Abuse/UncheckedReturnValue/UncheckedReturnValue.cs +++ b/csharp/ql/test/query-tests/API Abuse/UncheckedReturnValue/UncheckedReturnValue.cs @@ -1,11 +1,6 @@ using System; -class HashSet -{ - public bool Add(T t) - { - return true; - } -} +using System.Text; +using System.Collections.Generic; class C1 { @@ -30,11 +25,6 @@ class C1 } } -class StringBuilder -{ - public StringBuilder Append(string s) { return this; } -} - class C2 { static void Main(string[] args) @@ -59,20 +49,6 @@ class C2 } } -namespace System.IO -{ - public abstract class Stream - { - public abstract int Read(byte[] buffer, int offset, int count); - public virtual int ReadByte() { return 0; } - } - - public class MemoryStream : Stream - { - public override int Read(byte[] buffer, int offset, int count) { return 0; } - } -} - class C3 { static void Main(string[] args) diff --git a/csharp/ql/test/query-tests/API Abuse/UncheckedReturnValue/UncheckedReturnValue.expected b/csharp/ql/test/query-tests/API Abuse/UncheckedReturnValue/UncheckedReturnValue.expected index 371205cbc6b..e463ee956eb 100644 --- a/csharp/ql/test/query-tests/API Abuse/UncheckedReturnValue/UncheckedReturnValue.expected +++ b/csharp/ql/test/query-tests/API Abuse/UncheckedReturnValue/UncheckedReturnValue.expected @@ -1,8 +1,8 @@ -| UncheckedReturnValue.cs:29:9:29:31 | call to method Add | Result of call to 'Add' is ignored, but 90% of calls to this method have their result used. | -| UncheckedReturnValue.cs:91:9:91:26 | call to method Read | Result of call to 'Read' is ignored, but should always be checked. | -| UncheckedReturnValue.cs:92:9:92:20 | call to method ReadByte | Result of call to 'ReadByte' is ignored, but should always be checked. | -| UncheckedReturnValue.cs:109:9:109:17 | call to method M1 | Result of call to 'M1' is ignored, but 90% of calls to this method have their result used. | -| UncheckedReturnValue.cs:130:9:130:21 | call to method M2 | Result of call to 'M2' is ignored, but 90% of calls to this method have their result used. | -| UncheckedReturnValue.cs:142:9:142:20 | call to method M3 | Result of call to 'M3' is ignored, but 90% of calls to this method have their result used. | +| UncheckedReturnValue.cs:24:9:24:31 | call to method Add | Result of call to 'Add' is ignored, but 90% of calls to this method have their result used. | +| UncheckedReturnValue.cs:67:9:67:26 | call to method Read | Result of call to 'Read' is ignored, but should always be checked. | +| UncheckedReturnValue.cs:68:9:68:20 | call to method ReadByte | Result of call to 'ReadByte' is ignored, but should always be checked. | +| UncheckedReturnValue.cs:85:9:85:17 | call to method M1 | Result of call to 'M1' is ignored, but 90% of calls to this method have their result used. | +| UncheckedReturnValue.cs:106:9:106:21 | call to method M2 | Result of call to 'M2' is ignored, but 90% of calls to this method have their result used. | +| UncheckedReturnValue.cs:118:9:118:20 | call to method M3 | Result of call to 'M3' is ignored, but 90% of calls to this method have their result used. | | UncheckedReturnValueBad.cs:29:9:29:20 | call to method DoPrint | Result of call to 'DoPrint' is ignored, but 90% of calls to this method have their result used. | | UncheckedReturnValueBad.cs:36:13:36:40 | call to method Read | Result of call to 'Read' is ignored, but should always be checked. | diff --git a/csharp/ql/test/query-tests/API Abuse/UncheckedReturnValue/options b/csharp/ql/test/query-tests/API Abuse/UncheckedReturnValue/options new file mode 100644 index 00000000000..75c39b4541b --- /dev/null +++ b/csharp/ql/test/query-tests/API Abuse/UncheckedReturnValue/options @@ -0,0 +1,2 @@ +semmle-extractor-options: /nostdlib /noconfig +semmle-extractor-options: --load-sources-from-project:${testdir}/../../../resources/stubs/_frameworks/Microsoft.NETCore.App/Microsoft.NETCore.App.csproj diff --git a/csharp/ql/test/query-tests/AlertSuppression/options b/csharp/ql/test/query-tests/AlertSuppression/options new file mode 100644 index 00000000000..77b22963f5c --- /dev/null +++ b/csharp/ql/test/query-tests/AlertSuppression/options @@ -0,0 +1,2 @@ +semmle-extractor-options: /nostdlib /noconfig +semmle-extractor-options: --load-sources-from-project:${testdir}/../../resources/stubs/_frameworks/Microsoft.NETCore.App/Microsoft.NETCore.App.csproj diff --git a/csharp/ql/test/query-tests/Telemetry/LibraryUsage/ExternalLibraryUsage.cs b/csharp/ql/test/query-tests/Telemetry/LibraryUsage/ExternalLibraryUsage.cs index aaef1776cd3..1ec45beac39 100644 --- a/csharp/ql/test/query-tests/Telemetry/LibraryUsage/ExternalLibraryUsage.cs +++ b/csharp/ql/test/query-tests/Telemetry/LibraryUsage/ExternalLibraryUsage.cs @@ -25,4 +25,10 @@ public class LibraryUsage { var guid1 = Guid.Parse("{12345678-1234-1234-1234-123456789012}"); // Has no flow summary } + + public void M4() + { + var d = new Dictionary(); // Uninteresting parameterless constructor + var e = d.Keys.GetEnumerator().MoveNext(); // Methods on nested classes + } } diff --git a/csharp/ql/test/query-tests/Telemetry/LibraryUsage/ExternalLibraryUsage.expected b/csharp/ql/test/query-tests/Telemetry/LibraryUsage/ExternalLibraryUsage.expected index 7b5c4c57661..54f3920572e 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 | 5 | -| System.Collections.Generic | 2 | +| System.Collections.Generic | 5 | diff --git a/csharp/ql/test/query-tests/Telemetry/LibraryUsage/SupportedExternalTaint.expected b/csharp/ql/test/query-tests/Telemetry/LibraryUsage/SupportedExternalTaint.expected index b386e4acb38..c9f63591888 100644 --- a/csharp/ql/test/query-tests/Telemetry/LibraryUsage/SupportedExternalTaint.expected +++ b/csharp/ql/test/query-tests/Telemetry/LibraryUsage/SupportedExternalTaint.expected @@ -1 +1,3 @@ | System.Collections.Generic#List<>.Add(T) | 2 | +| System.Collections.Generic#Dictionary<,>+KeyCollection.GetEnumerator() | 1 | +| System.Collections.Generic#Dictionary<,>.get_Keys() | 1 | diff --git a/csharp/ql/test/resources/assemblies/System.Data.dll b/csharp/ql/test/resources/assemblies/System.Data.dll deleted file mode 100644 index 070caaa81f3..00000000000 Binary files a/csharp/ql/test/resources/assemblies/System.Data.dll and /dev/null differ diff --git a/csharp/ql/test/resources/assemblies/System.Web.ApplicationServices.dll b/csharp/ql/test/resources/assemblies/System.Web.ApplicationServices.dll deleted file mode 100644 index 85db5a3a7e4..00000000000 Binary files a/csharp/ql/test/resources/assemblies/System.Web.ApplicationServices.dll and /dev/null differ diff --git a/docs/codeql/codeql-for-visual-studio-code/running-codeql-queries-at-scale-with-mrva.rst b/docs/codeql/codeql-for-visual-studio-code/running-codeql-queries-at-scale-with-mrva.rst index 83dc32edf76..02c9d448ffa 100644 --- a/docs/codeql/codeql-for-visual-studio-code/running-codeql-queries-at-scale-with-mrva.rst +++ b/docs/codeql/codeql-for-visual-studio-code/running-codeql-queries-at-scale-with-mrva.rst @@ -153,6 +153,36 @@ For example, if you want to continue analyzing a set of repositories that had re You can then insert the ``new-repo-list`` of repositories into your list of custom repository lists for easy access in the Variant Analysis Repositories panel. +Using GitHub code search to add repositories to a custom list +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +You can use code search directly in the CodeQL extension to add a subset of repositories from GitHub.com to a custom list. + +.. pull-quote:: + + Note + + This feature uses the legacy code search via the code search API. For more information on the syntax to use, see "`Searching code (legacy) `__." + +For example, to add all repositories in the ``rails`` organization on GitHub, you can search ``org:rails``. + +You can add a maximum of 1000 repositories to a custom list per search. + +#. In the Variant Analysis Repositories panel, choose the list that you want to add repositories to. You can create a new list or choose an existing list that already contains repositories. + +#. Right-click on the list you have chosen and then click **Add repositories with GitHub Code Search**. + +#. In the pop-up that appears at the top of the application, under the search bar, select a language for your search from the choices in the dropdown. + + .. image:: ../images/codeql-for-visual-studio-code/variant-analysis-code-search-language.png + :alt: Screenshot of the search bar for using code search to add repositories to a custom list. The search bar asks you to choose a language for your search and has a dropdown list of languages to choose from. + +#. In the search bar, type the search query that you want to use and press **Enter**. + +You can view the progress of your search in the bottom right corner of the application in a box with the text "Searching for repositories...". If you click **Cancel**, no repositories will be added to your list. Once complete, you will see the resulting repositories appear in the dropdown under your custom list in the Variant Analysis Repositories panel. + +Some of the resulting repositories will not have CodeQL databases and some may not allow access by the CodeQL extension for Visual Studio Code. When you run an analysis on the list, the Variant Analysis Results view will show you which repositories were analyzed, which denied access, and which had no CodeQL database. + Troubleshooting variant analysis -------------------------------- diff --git a/docs/codeql/images/codeql-for-visual-studio-code/variant-analysis-code-search-language.png b/docs/codeql/images/codeql-for-visual-studio-code/variant-analysis-code-search-language.png new file mode 100644 index 00000000000..5f2eddec6f6 Binary files /dev/null and b/docs/codeql/images/codeql-for-visual-studio-code/variant-analysis-code-search-language.png differ diff --git a/docs/codeql/ql-language-reference/annotations.rst b/docs/codeql/ql-language-reference/annotations.rst index 70e4321667f..c87b479857e 100644 --- a/docs/codeql/ql-language-reference/annotations.rst +++ b/docs/codeql/ql-language-reference/annotations.rst @@ -126,7 +126,7 @@ body must also be annotated with ``cached``, otherwise a compiler error is repor ``deprecated`` ============== -**Available for**: |classes|, |algebraic datatypes|, |member predicates|, |non-member predicates|, |fields|, |modules|, |aliases| +**Available for**: |classes|, |algebraic datatypes|, |member predicates|, |non-member predicates|, |imports|, |fields|, |modules|, |aliases| The ``deprecated`` annotation is applied to names that are outdated and scheduled for removal in a future release of QL. diff --git a/docs/codeql/ql-language-reference/formulas.rst b/docs/codeql/ql-language-reference/formulas.rst index cdc8c297b7d..8745217decc 100644 --- a/docs/codeql/ql-language-reference/formulas.rst +++ b/docs/codeql/ql-language-reference/formulas.rst @@ -164,6 +164,38 @@ If the call resolves to a predicate without result, then the call is a formula. It is also possible to call a predicate with result. This kind of call is an expression in QL, instead of a formula. For more information, see ":ref:`calls-with-result`." +Member predicates only apply to members of a particular class and calls to +member predicates have a receiver of a matching type. Syntactically, if a call +contains a dot, then the expression before the dot specifies the receiver of +the call. For instance, ``x`` is the receiver for the call ``x.isEven()``. + +For calls to member predicates of the enclosing class on the member itself +(i.e., the value of ``this``), the receiver may be omitted syntactically. In +this case we say the call has an implicit this receiver. For instance, in the +following example the ``isEven()`` call in ``isOdd()`` is a member predicate +call with an implicit this receiver and the call is equivalent to +``this.isEven()``: + +.. code-block:: ql + + class OneTwoThree extends int { + OneTwoThree() { this = 1 or this = 2 or this = 3 } + + predicate isEven() { this = 2 } + + predicate isOdd() { not isEven() } + } + +Use of implicit this receivers can make it harder to spot predicates that introduce +cartesian products by failing to relate the implicit ``this`` variable with +other variables, which can negatively affect query performance. For more +information on cartesian products, see ":ref:`Troubleshooting query performance +`". + +It is possible to enable warnings about implicit this receivers for `CodeQL packs +`__ +through the ``warnOnImplicitThis`` property. + .. _parenthesized-formulas: Parenthesized formulas diff --git a/docs/codeql/ql-language-reference/modules.rst b/docs/codeql/ql-language-reference/modules.rst index 42344c72e3d..ec4097ae019 100644 --- a/docs/codeql/ql-language-reference/modules.rst +++ b/docs/codeql/ql-language-reference/modules.rst @@ -264,7 +264,12 @@ Import statements are used for importing modules. They are of the form: Import statements are usually listed at the beginning of the module. Each import statement imports one module. You can import multiple modules by including multiple import statements (one for each module you want to import). -An import statement can also be :ref:`annotated ` with ``private``. + +An import statement can also be :ref:`annotated ` with +``private`` or ``deprecated``. If an import statement is annotated with +``private`` then the imported names are not reexported. If an imported name is +only reachable through deprecated imports in a given context then usage of the +name in that context will generate deprecation warnings. You can import a module under a different name using the ``as`` keyword, for example ``import javascript as js``. diff --git a/docs/codeql/ql-language-reference/ql-language-specification.rst b/docs/codeql/ql-language-reference/ql-language-specification.rst index 71dcbdce571..9328a0f3ec3 100644 --- a/docs/codeql/ql-language-reference/ql-language-specification.rst +++ b/docs/codeql/ql-language-reference/ql-language-specification.rst @@ -199,24 +199,37 @@ Kinds of modules A module may be: +- A *declared module*, if it is defined by the ``module`` or ``ql`` grammar rules. +- A *non-declared module*, if it is not a *declared module*. + +A *declared module* may be: + - A *file module*, if it is defined implicitly by a QL file or a QLL file. - A *query module*, if it is defined implicitly by a QL file. - A *library module*, if it is not a query module. +A *non-declared module* may be: + +- A *built-in module*. +- An *instantiated module* (see :ref:`Parameterized modules`). +- An *instantiation-nested module* (see :ref:`Parameterized modules`). + A query module must contain one or more queries. Import directives ~~~~~~~~~~~~~~~~~ -An import directive refers to a module identifier: +An import directive refers to a module expression: :: import ::= annotations "import" importModuleExpr ("as" modulename)? - qualId ::= simpleId | qualId "." simpleId + importModuleExpr ::= importModuleId arguments? - importModuleExpr ::= qualId | importModuleExpr "::" modulename arguments? + importModuleId ::= qualId | importModuleExpr "::" modulename + + qualId ::= simpleId | qualId "." simpleId arguments ::= "<" argument ("," argument)* ">" @@ -249,6 +262,73 @@ For qualified identifiers (``a.b``): A qualified module identifier is only valid within an import. +Module expressions contain a module identifier and optional arguments. If arguments are present, the module expression instantiates the module that the identifier resolves to (see :ref:`Parameterized modules`). + +Module expressions cannot refer to :ref:`Parameterized modules`. Instead, parameterized modules must always be fully instantiated when they are referenced. + +.. _Parameterized modules: + +Parameterized modules +~~~~~~~~~~~~~~~~~~~~~ + +Modules with parameters are called *parameterized modules*. A *declared module* has parameters if and only if it is a *library module* and its declaration uses the ``parameters`` syntax. + +*Parameterized modules* can be instantiated with arguments that match the signatures of the parameters: + +- Given a type signature, the corresponding argument must be a type that transitively extends the specified ``extends`` types and is a transitive subtype of the specified ``instanceof`` types. +- Given a predicate signature, the corresponding argument must be a predicate with exactly matching relational types. +- Given a module signature, the corresponding argument must be a module that exports all the specified type and predicate members. Furthermore, the module must be declared as matching the module signature via the ``implements`` syntax. + +The result of instantiating a *parameterized module* is an *instantiated module*. The parameterized module is called the *underlying module* of the *instantiated module*. + +Instantiation-relative and instantiation-nested entities +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Given an *instantiated module*, every entity has a corresponding entity called the *instantiation-relative* entity, which is determined as follows: + +- If the entity is the *underlying module*, its *instantiation-relative* entity is the *instantiated module*. +- If the entity is a parameter of the *underlying module*, its *instantiation-relative* entity is the corresponding argument. +- If the entity is declared inside the *underlying module* or its nested modules, its *instantiation-relative* entity is an *instantiation-nested* entity that is generated by the module instantiation. Parameters of any modules that are nested inside the *underlying module* are considered declared inside the module for this purpose. +- Otherwise, the entity's *instantiation-relative* entity is the initial entity itself. + +When the *instantiation-relative* entity of an entity is an *instantiation-nested* entity, then the initial entity is called the *underlying nested* entity of the *instantiation-nested* entity*, the *instantiated module* is called the *instantiation root* of the *instantiation-nested* entity, and the *underlying module* is called the *underlying root* of the *instantiation-nested* entity. + +The components of an *instantiation-nested* entity are the *instantiation-relative* entities of the components of its *underlying nested* entity. Among other things, this applies to: + +- values in the exported environments of *instantiation-nested* modules, +- relational types of *instantiation-nested* predicates and predicate signatures, +- required signatures of *instantiation-nested* parameters, +- parameters of an *instantiation-nested* *parameterized module*, +- fields and member predicates of *instantiation-nested* dataclasses. + +Given an *instantiated module*, any alias in the program has a corresponding alias called the *instantiation-relative* alias, which targets the *instantiation-relative* entity. + +Applicative instantiation +~~~~~~~~~~~~~~~~~~~~~~~~~ + +Every entity has an *underlying completely uninstantiated* entity that is determined as follows: + +- If the entity is an *instantiated module*, its *underlying completely uninstantiated* entity is the *underlying completely uninstantiated* entity of the *underlying module*. +- If the entity is an *instantiation-nested* entity, its *underlying completely uninstantiated* entity is the *underlying completely uninstantiated* entity of the *underlying nested* entity. +- Otherwise, its *underlying completely uninstantiated* entity is the entity itself. + +An entity is called *completely uninstantiated* entity if it is its own *underlying completely uninstantiated* entity. + +Every *completely uninstantiated* entity has a *relevant set of parameters*, which is the set of all parameters of all the modules that the entity is transitively nested inside. For entities that are not nested inside any modules, the *relevant set of parameters* is empty. + +Note that the *relevant set of parameters* by construction contains only *completely uninstantiated* parameters. + +For a *completely uninstantiated* parameter, the *bottom-up instantiation-resolution* relative to an entity is defined as: + +- If the entity is an *instantiated module* or an *instantiation-nested* entity, the *bottom-up instantiation-resolution* is the *instantiation-relative* entity of the *bottom-up instantiation-resolution* relative to the *underlying module*. +- Otherwise, the *bottom-up instantiation-resolution* is the parameter itself. + +An entity is called *fully instantiated* if none of the *bottom-up instantiation-resolutions* of the parameters in the *relevant set of parameters* of the entity's *underlying completely uninstantiated* entity are parameters. + +Two *instantiated modules* or two *instantiation-nested* entities are considered *equivalent* if they have the same *underlying completely uninstantiated* entity and each parameter in its *relevant set of parameters* has the same *bottom-up instantiation-resolution* relative to either *instantiated module*. + +Module instantiation is applicative, meaning that *equivalent* *instantiated modules* and *equivalent* *instantiation-nested* entities are indistinguishable. + Module references and active modules ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ @@ -268,7 +348,7 @@ QL is a typed language. This section specifies the kinds of types available, the Kinds of types ~~~~~~~~~~~~~~ -Types in QL are either *primitive* types, *database* types, *class* types, *character* types or *class domain* types. +Types in QL are either *primitive* types, *database* types, *class* types, *character* types, *class domain* types, type *parameters*, or *instantiation-nested* types. The primitive types are ``boolean``, ``date``, ``float``, ``int``, and ``string``. @@ -289,7 +369,9 @@ With the exception of class domain types and character types (which cannot be re type ::= (moduleExpr "::")? classname | dbasetype | "boolean" | "date" | "float" | "int" | "string" - moduleExpr ::= modulename arguments? | moduleExpr "::" modulename arguments? + moduleExpr ::= moduleId arguments? + + moduleId ::= modulename | moduleExpr "::" modulename A type reference is resolved to a type as follows: @@ -712,7 +794,7 @@ The following table summarizes the syntactic constructs which can be marked with +----------------+---------+------------+-------------------+-----------------------+---------+--------+---------+---------+------------+ | ``private`` | yes | | yes | yes | yes | yes | yes | yes | yes | +----------------+---------+------------+-------------------+-----------------------+---------+--------+---------+---------+------------+ -| ``deprecated`` | yes | | yes | yes | | yes | yes | yes | yes | +| ``deprecated`` | yes | | yes | yes | yes | yes | yes | yes | yes | +----------------+---------+------------+-------------------+-----------------------+---------+--------+---------+---------+------------+ | ``override`` | | | yes | | | yes | | | | +----------------+---------+------------+-------------------+-----------------------+---------+--------+---------+---------+------------+ @@ -1236,15 +1318,15 @@ The type environment for the arguments is the same as for the call. A valid call with results *resolves* to a set of predicates. The ways a call can resolve are as follows: -- If the call has no receiver and the predicate name is a simple identifier, then the predicate is resolved by looking up its name and arity in the visible member-predicate environment of the enclosing class. +- If the call has no receiver and the predicate reference is a simple identifier, then the call is resolved by looking up the predicate reference and arity in the visible predicate environment of the enclosing class. -- If the call has no receiver and the predicate name is a simple identifier, then the predicate is resolved by looking up its name and arity in the visible predicate environment of the enclosing module. +- If the call has no receiver and the predicate reference is a simple identifier, then the call is resolved by looking up the predicate reference and arity in the visible predicate environment of the enclosing module. -- If the call has no receiver and the predicate name is a selection identifier, then the qualifier is resolved as a module (see "`Module resolution <#module-resolution>`__"). The identifier is then resolved in the exported predicate environment of the qualifier module. +- If the call has no receiver and the predicate reference is a selection identifier, then the qualifier is resolved as a module (see "`Module resolution <#module-resolution>`__") and the call is resolved by looking up the identifier in the exported predicate environment of the qualifier module. -- If the type of the receiver is the same as the enclosing class, the predicate is resolved by looking up its name and arity in the visible predicate environment of the class. +- If the the call has a receiver and the type of the receiver is the same as the enclosing class, the call is resolved by looking up the predicate name and arity in the visible predicate environment of the enclosing class. -- If the type of the receiver is not the same as the enclosing class, the predicate is resolved by looking up its name and arity in the exported predicate environment of the class or domain type. +- If the the call has a receiver and the type of the receiver is not the same as the enclosing class, the call is resolved by looking up the predicate name and arity in the exported predicate environment of the type of the receiver. If all the predicates that the call resolves to are declared on a primitive type, we then restrict to the set of predicates where each argument of the call is a subtype of the corresponding predicate argument type. Then we find all predicates ``p`` from this new set such that there is not another predicate ``p'`` where each argument of ``p'`` is a subtype of the corresponding argument in ``p``. We then say the call resolves to this set instead. @@ -1960,10 +2042,12 @@ Stratification A QL program can be *stratified* to a sequence of *layers*. A layer is a set of predicates and types. -A valid stratification must include each predicate and type in the QL program. It must not include any other predicates or types. +A valid stratification must include each predicate and type in the QL program that is *fully instantiated*. It must not include any other predicates or types. A valid stratification must not include the same predicate in multiple layers. +Each non-abstract predicate has an associated body. For predicates inside *declared modules*, this is the predicate declaration. The body of an *instantiation-nested* predicate is the body of the *underlying nested* predicate where all references and calls have been substituted with the *instantiation-relative* entity or alias. + Formulas, variable declarations and expressions within a predicate body have a *negation polarity* that is positive, negative, or zero. Positive and negative are opposites of each other, while zero is the opposite of itself. The negation polarity of a formula or expression is then determined as follows: - The body of a predicate is positive. diff --git a/go/downgrades/qlpack.yml b/go/downgrades/qlpack.yml index d3e056bea64..7e8e0022eb0 100644 --- a/go/downgrades/qlpack.yml +++ b/go/downgrades/qlpack.yml @@ -2,3 +2,4 @@ name: codeql/go-downgrades groups: go downgrades: . library: true +warnOnImplicitThis: true diff --git a/go/external-packs/codeql/suite-helpers/0.0.2/qlpack.yml b/go/external-packs/codeql/suite-helpers/0.0.2/qlpack.yml index ca0a6732f5a..7c42cb6974c 100644 --- a/go/external-packs/codeql/suite-helpers/0.0.2/qlpack.yml +++ b/go/external-packs/codeql/suite-helpers/0.0.2/qlpack.yml @@ -1,3 +1,4 @@ name: codeql/suite-helpers version: 0.0.2 library: true +warnOnImplicitThis: true diff --git a/go/ql/config/legacy-support/qlpack.yml b/go/ql/config/legacy-support/qlpack.yml index bf3ee4d72a6..23ba44acd80 100644 --- a/go/ql/config/legacy-support/qlpack.yml +++ b/go/ql/config/legacy-support/qlpack.yml @@ -2,3 +2,4 @@ name: legacy-libraries-go version: 0.0.0 # Note libraryPathDependencies is obsolete and should not be used in new qlpacks. libraryPathDependencies: codeql-go +warnOnImplicitThis: true diff --git a/go/ql/examples/qlpack.yml b/go/ql/examples/qlpack.yml index 1f2ca2cd40a..006eb3c17b3 100644 --- a/go/ql/examples/qlpack.yml +++ b/go/ql/examples/qlpack.yml @@ -4,3 +4,4 @@ groups: - examples dependencies: codeql/go-all: ${workspace} +warnOnImplicitThis: true diff --git a/go/ql/integration-tests/all-platforms/go/qlpack.yml b/go/ql/integration-tests/all-platforms/go/qlpack.yml index 3a018bff52a..2fc2dd566dd 100644 --- a/go/ql/integration-tests/all-platforms/go/qlpack.yml +++ b/go/ql/integration-tests/all-platforms/go/qlpack.yml @@ -2,3 +2,4 @@ dependencies: codeql/go-all: '*' codeql/go-tests: '*' codeql/go-queries: '*' +warnOnImplicitThis: true diff --git a/go/ql/integration-tests/linux-only/go/qlpack.yml b/go/ql/integration-tests/linux-only/go/qlpack.yml index 3a018bff52a..2fc2dd566dd 100644 --- a/go/ql/integration-tests/linux-only/go/qlpack.yml +++ b/go/ql/integration-tests/linux-only/go/qlpack.yml @@ -2,3 +2,4 @@ dependencies: codeql/go-all: '*' codeql/go-tests: '*' codeql/go-queries: '*' +warnOnImplicitThis: true diff --git a/go/ql/lib/CHANGELOG.md b/go/ql/lib/CHANGELOG.md index 5f09272c19b..0e0d00161e1 100644 --- a/go/ql/lib/CHANGELOG.md +++ b/go/ql/lib/CHANGELOG.md @@ -1,3 +1,7 @@ +## 0.5.3 + +No user-facing changes. + ## 0.5.2 ### Minor Analysis Improvements diff --git a/go/ql/lib/change-notes/2023-06-14-log-injection-deprecation.md b/go/ql/lib/change-notes/2023-06-14-log-injection-deprecation.md new file mode 100644 index 00000000000..88ec05c17ce --- /dev/null +++ b/go/ql/lib/change-notes/2023-06-14-log-injection-deprecation.md @@ -0,0 +1,4 @@ +--- +category: deprecated +--- +* The `LogInjection::Configuration` taint flow configuration class has been deprecated. Use the `LogInjection::Flow` module instead. \ No newline at end of file diff --git a/go/ql/lib/change-notes/2023-06-20-function-model-path-nodes.md b/go/ql/lib/change-notes/2023-06-20-function-model-path-nodes.md new file mode 100644 index 00000000000..5c616481326 --- /dev/null +++ b/go/ql/lib/change-notes/2023-06-20-function-model-path-nodes.md @@ -0,0 +1,4 @@ +--- +category: minorAnalysis +--- +* When a result of path query flows through a function modeled using `DataFlow::FunctionModel` or `TaintTracking::FunctionModel`, the path now includes nodes corresponding to the input and output to the function. This brings it in line with functions modeled using Models-as-Data. diff --git a/go/ql/lib/change-notes/released/0.5.3.md b/go/ql/lib/change-notes/released/0.5.3.md new file mode 100644 index 00000000000..e97503053f0 --- /dev/null +++ b/go/ql/lib/change-notes/released/0.5.3.md @@ -0,0 +1,3 @@ +## 0.5.3 + +No user-facing changes. diff --git a/go/ql/lib/codeql-pack.release.yml b/go/ql/lib/codeql-pack.release.yml index 2d9d3f587f8..2164e038a5d 100644 --- a/go/ql/lib/codeql-pack.release.yml +++ b/go/ql/lib/codeql-pack.release.yml @@ -1,2 +1,2 @@ --- -lastReleaseVersion: 0.5.2 +lastReleaseVersion: 0.5.3 diff --git a/go/ql/lib/qlpack.yml b/go/ql/lib/qlpack.yml index 287c27187e3..bf8cd0f648e 100644 --- a/go/ql/lib/qlpack.yml +++ b/go/ql/lib/qlpack.yml @@ -1,11 +1,12 @@ name: codeql/go-all -version: 0.5.3-dev +version: 0.5.4-dev groups: go dbscheme: go.dbscheme extractor: go library: true upgrades: upgrades dependencies: + codeql/mad: ${workspace} codeql/tutorial: ${workspace} codeql/util: ${workspace} dataExtensions: diff --git a/go/ql/lib/semmle/go/dataflow/ExternalFlow.qll b/go/ql/lib/semmle/go/dataflow/ExternalFlow.qll index 0c6ee1c3134..73873850982 100644 --- a/go/ql/lib/semmle/go/dataflow/ExternalFlow.qll +++ b/go/ql/lib/semmle/go/dataflow/ExternalFlow.qll @@ -54,6 +54,18 @@ * return value. The return values are zero-indexed * - "ReturnValue[n1..n2]": Similar to "ReturnValue[n]" but selects any * return value in the given range. The range is inclusive at both ends. + * + * For summaries, `input` and `output` may be suffixed by any number of the + * following, separated by ".": + * - "Field[pkg.className.fieldname]": Selects the contents of the field `f` + * which satisfies `f.hasQualifiedName(pkg, className, fieldname)`. + * - "SyntheticField[f]": Selects the contents of the synthetic field `f`. + * - "ArrayElement": Selects an element in an array or slice. + * - "Element": Selects an element in a collection. + * - "MapKey": Selects a key in a map. + * - "MapValue": Selects a value in a map. + * - "Dereference": Selects the value referenced by a pointer. + * * 8. The `kind` column is a tag that can be referenced from QL to determine to * which classes the interpreted elements should be added. For example, for * sources "remote" indicates a default remote flow source, and for summaries @@ -68,6 +80,7 @@ private import internal.FlowSummaryImpl::Private::External private import internal.FlowSummaryImplSpecific private import internal.AccessPathSyntax private import FlowSummary +private import codeql.mad.ModelValidation as SharedModelVal /** * A module importing the frameworks that provide external flow data, @@ -188,13 +201,16 @@ module ModelValidation { ) } - private string getInvalidModelKind() { - exists(string kind | summaryModel(_, _, _, _, _, _, _, _, kind, _) | - not kind = ["taint", "value"] and - result = "Invalid kind \"" + kind + "\" in summary model." - ) + private module KindValConfig implements SharedModelVal::KindValidationConfigSig { + predicate summaryKind(string kind) { summaryModel(_, _, _, _, _, _, _, _, kind, _) } + + predicate sinkKind(string kind) { sinkModel(_, _, _, _, _, _, _, kind, _) } + + predicate sourceKind(string kind) { sourceModel(_, _, _, _, _, _, _, kind, _) } } + private module KindVal = SharedModelVal::KindValidation; + private string getInvalidModelSignature() { exists( string pred, string package, string type, string name, string signature, string ext, @@ -231,7 +247,7 @@ module ModelValidation { msg = [ getInvalidModelSignature(), getInvalidModelInput(), getInvalidModelOutput(), - getInvalidModelKind() + KindVal::getInvalidModelKind() ] } } @@ -342,6 +358,8 @@ predicate parseContent(string component, DataFlow::Content content) { component = "MapKey" and content instanceof DataFlow::MapKeyContent or component = "MapValue" and content instanceof DataFlow::MapValueContent + or + component = "Dereference" and content instanceof DataFlow::PointerContent } cached diff --git a/go/ql/lib/semmle/go/dataflow/internal/DataFlowImpl.qll b/go/ql/lib/semmle/go/dataflow/internal/DataFlowImpl.qll index 984c5ae2018..284fff191ae 100644 --- a/go/ql/lib/semmle/go/dataflow/internal/DataFlowImpl.qll +++ b/go/ql/lib/semmle/go/dataflow/internal/DataFlowImpl.qll @@ -2021,7 +2021,8 @@ module Impl { FlowCheckNode() { castNode(this.asNode()) or clearsContentCached(this.asNode(), _) or - expectsContentCached(this.asNode(), _) + expectsContentCached(this.asNode(), _) or + neverSkipInPathGraph(this.asNode()) } } diff --git a/go/ql/lib/semmle/go/dataflow/internal/DataFlowPrivate.qll b/go/ql/lib/semmle/go/dataflow/internal/DataFlowPrivate.qll index ee146fc2aba..99d22d5c4e8 100644 --- a/go/ql/lib/semmle/go/dataflow/internal/DataFlowPrivate.qll +++ b/go/ql/lib/semmle/go/dataflow/internal/DataFlowPrivate.qll @@ -228,6 +228,16 @@ class CastNode extends ExprNode { override ConversionExpr expr; } +/** + * Holds if `n` should never be skipped over in the `PathGraph` and in path + * explanations. + */ +predicate neverSkipInPathGraph(Node n) { + exists(DataFlow::FunctionModel fm | fm.getAnInputNode(_) = n or fm.getAnOutputNode(_) = n) + or + exists(TaintTracking::FunctionModel fm | fm.getAnInputNode(_) = n or fm.getAnOutputNode(_) = n) +} + class DataFlowExpr = Expr; private newtype TDataFlowType = diff --git a/go/ql/lib/semmle/go/dataflow/internal/FlowSummaryImplSpecific.qll b/go/ql/lib/semmle/go/dataflow/internal/FlowSummaryImplSpecific.qll index 41ae6b50ce3..609790659f8 100644 --- a/go/ql/lib/semmle/go/dataflow/internal/FlowSummaryImplSpecific.qll +++ b/go/ql/lib/semmle/go/dataflow/internal/FlowSummaryImplSpecific.qll @@ -106,6 +106,8 @@ private string getContentSpecific(Content c) { c instanceof MapKeyContent and result = "MapKey" or c instanceof MapValueContent and result = "MapValue" + or + c instanceof PointerContent and result = "Dereference" } /** Gets the textual representation of the content in the format used for flow summaries. */ diff --git a/go/ql/lib/semmle/go/security/LogInjection.qll b/go/ql/lib/semmle/go/security/LogInjection.qll index 12f64c87e4a..70e0947c53b 100644 --- a/go/ql/lib/semmle/go/security/LogInjection.qll +++ b/go/ql/lib/semmle/go/security/LogInjection.qll @@ -17,7 +17,7 @@ module LogInjection { /** * A taint-tracking configuration for reasoning about log injection vulnerabilities. */ - class Configuration extends TaintTracking::Configuration { + deprecated class Configuration extends TaintTracking::Configuration { Configuration() { this = "LogInjection" } override predicate isSource(DataFlow::Node source) { source instanceof Source } @@ -30,4 +30,17 @@ module LogInjection { guard instanceof SanitizerGuard } } + + /** + * A taint-tracking configuration for reasoning about log injection vulnerabilities. + */ + module Config implements DataFlow::ConfigSig { + predicate isSource(DataFlow::Node source) { source instanceof Source } + + predicate isSink(DataFlow::Node sink) { sink instanceof Sink } + + predicate isBarrier(DataFlow::Node sanitizer) { sanitizer instanceof Sanitizer } + } + + module Flow = TaintTracking::Global; } diff --git a/go/ql/src/CHANGELOG.md b/go/ql/src/CHANGELOG.md index 8a1b8bcfebc..61712c5e790 100644 --- a/go/ql/src/CHANGELOG.md +++ b/go/ql/src/CHANGELOG.md @@ -1,3 +1,7 @@ +## 0.5.3 + +No user-facing changes. + ## 0.5.2 No user-facing changes. diff --git a/go/ql/src/Security/CWE-022/ZipSlip.qhelp b/go/ql/src/Security/CWE-022/ZipSlip.qhelp index 1322dc18c13..ecea2eaeec9 100644 --- a/go/ql/src/Security/CWE-022/ZipSlip.qhelp +++ b/go/ql/src/Security/CWE-022/ZipSlip.qhelp @@ -5,9 +5,9 @@

-Extracting files from a malicious zip archive without validating that the destination file path -is within the destination directory can cause files outside the destination directory to be -overwritten, due to the possible presence of directory traversal elements (..) in +Extracting files from a malicious zip file, or similar type of archive, +is at risk of directory traversal attacks if filenames from the archive are +not properly validated. archive paths.

@@ -15,8 +15,8 @@ archive paths. Zip archives contain archive entries representing each file in the archive. These entries include a file path for the entry, but these file paths are not restricted and may contain unexpected special elements such as the directory traversal element (..). If these -file paths are used to determine which output file the contents of an archive item should be written to, then -the file may be written to an unexpected location. This can result in sensitive information being +file paths are used to create a filesystem path, then a file operation may happen in an +unexpected location. This can result in sensitive information being revealed or deleted, or an attacker being able to influence behavior by modifying unexpected files.

diff --git a/go/ql/src/Security/CWE-022/ZipSlip.ql b/go/ql/src/Security/CWE-022/ZipSlip.ql index ceec7dc57e3..5cfb3998f4d 100644 --- a/go/ql/src/Security/CWE-022/ZipSlip.ql +++ b/go/ql/src/Security/CWE-022/ZipSlip.ql @@ -1,8 +1,8 @@ /** - * @name Arbitrary file write during zip extraction ("zip slip") - * @description Extracting files from a malicious zip archive without validating that the - * destination file path is within the destination directory can cause files outside - * the destination directory to be overwritten. + * @name Arbitrary file access during archive extraction ("Zip Slip") + * @description Extracting files from a malicious ZIP file, or similar type of archive, without + * validating that the destination file path is within the destination directory + * can allow an attacker to unexpectedly gain access to resources. * @kind path-problem * @id go/zipslip * @problem.severity error diff --git a/go/ql/src/Security/CWE-117/LogInjection.ql b/go/ql/src/Security/CWE-117/LogInjection.ql index 39952dbbfa1..5b6586c8e4e 100644 --- a/go/ql/src/Security/CWE-117/LogInjection.ql +++ b/go/ql/src/Security/CWE-117/LogInjection.ql @@ -13,9 +13,9 @@ import go import semmle.go.security.LogInjection -import DataFlow::PathGraph +import LogInjection::Flow::PathGraph -from LogInjection::Configuration c, DataFlow::PathNode source, DataFlow::PathNode sink -where c.hasFlowPath(source, sink) +from LogInjection::Flow::PathNode source, LogInjection::Flow::PathNode sink +where LogInjection::Flow::flowPath(source, sink) select sink.getNode(), source, sink, "This log entry depends on a $@.", source.getNode(), "user-provided value" diff --git a/go/ql/src/change-notes/2023-06-16-zipslip-rename.md b/go/ql/src/change-notes/2023-06-16-zipslip-rename.md new file mode 100644 index 00000000000..72913f37c06 --- /dev/null +++ b/go/ql/src/change-notes/2023-06-16-zipslip-rename.md @@ -0,0 +1,4 @@ +--- +category: fix +--- +* The query "Arbitrary file write during zip extraction ("zip slip")" (`go/zipslip`) has been renamed to "Arbitrary file access during archive extraction ("Zip Slip")." diff --git a/go/ql/src/change-notes/released/0.5.3.md b/go/ql/src/change-notes/released/0.5.3.md new file mode 100644 index 00000000000..e97503053f0 --- /dev/null +++ b/go/ql/src/change-notes/released/0.5.3.md @@ -0,0 +1,3 @@ +## 0.5.3 + +No user-facing changes. diff --git a/go/ql/src/codeql-pack.release.yml b/go/ql/src/codeql-pack.release.yml index 2d9d3f587f8..2164e038a5d 100644 --- a/go/ql/src/codeql-pack.release.yml +++ b/go/ql/src/codeql-pack.release.yml @@ -1,2 +1,2 @@ --- -lastReleaseVersion: 0.5.2 +lastReleaseVersion: 0.5.3 diff --git a/go/ql/src/qlpack.yml b/go/ql/src/qlpack.yml index 75963a0708e..ad8b0d5db16 100644 --- a/go/ql/src/qlpack.yml +++ b/go/ql/src/qlpack.yml @@ -1,5 +1,5 @@ name: codeql/go-queries -version: 0.5.3-dev +version: 0.5.4-dev groups: - go - queries diff --git a/go/ql/test/TestUtilities/InlineFlowTest.qll b/go/ql/test/TestUtilities/InlineFlowTest.qll index 0726265699f..4f3cfc7599a 100644 --- a/go/ql/test/TestUtilities/InlineFlowTest.qll +++ b/go/ql/test/TestUtilities/InlineFlowTest.qll @@ -3,37 +3,35 @@ * * Example for a test.ql: * ```ql - * import java + * import go * import TestUtilities.InlineFlowTest + * import DefaultFlowTest + * import PathGraph + * + * from PathNode source, PathNode sink + * where flowPath(source, sink) + * select sink, source, sink, "$@", source, source.toString() * ``` * * To declare expectations, you can use the $hasTaintFlow or $hasValueFlow comments within the test source files. - * Example of the corresponding test file, e.g. Test.java + * Example of the corresponding test file, e.g. Test.go * ```go - * public class Test { - * - * Object source() { return null; } - * String taint() { return null; } - * void sink(Object o) { } - * - * public void test() { - * Object s = source(); - * sink(s); //$hasValueFlow - * String t = "foo" + taint(); - * sink(t); //$hasTaintFlow - * } + * func source() string { return ""; } + * func taint() string { return ""; } + * func sink(s string) { } * + * func test() { + * s := source() + * sink(s) // $ hasValueFlow="s" + * t := "foo" + taint() + * sink(t) // $ hasTaintFlow="t" * } * ``` * - * If you're not interested in a specific flow type, you can disable either value or taint flow expectations as follows: - * ```ql - * class HasFlowTest extends InlineFlowTest { - * override DataFlow::Configuration getTaintFlowConfig() { none() } - * - * override DataFlow::Configuration getValueFlowConfig() { none() } - * } - * ``` + * If you are only interested in value flow, then instead of importing `DefaultFlowTest`, you can import + * `ValueFlowTest`. Similarly, if you are only interested in taint flow, then instead of + * importing `DefaultFlowTest`, you can import `TaintFlowTest`. In both cases + * `DefaultFlowConfig` can be replaced by another implementation of `DataFlow::ConfigSig`. * * If you need more fine-grained tuning, consider implementing a test using `InlineExpectationsTest`. */ @@ -47,57 +45,68 @@ private predicate defaultSource(DataFlow::Node source) { ) } -class DefaultValueFlowConf extends DataFlow::Configuration { - DefaultValueFlowConf() { this = "qltest:defaultValueFlowConf" } - - override predicate isSource(DataFlow::Node source) { defaultSource(source) } - - override predicate isSink(DataFlow::Node sink) { - exists(Function fn | fn.hasQualifiedName(_, "sink") | sink = fn.getACall().getAnArgument()) - } - - override int fieldFlowBranchLimit() { result = 1000 } +private predicate defaultSink(DataFlow::Node sink) { + exists(Function fn | fn.hasQualifiedName(_, "sink") | sink = fn.getACall().getAnArgument()) } -class DefaultTaintFlowConf extends TaintTracking::Configuration { - DefaultTaintFlowConf() { this = "qltest:defaultTaintFlowConf" } +module DefaultFlowConfig implements DataFlow::ConfigSig { + predicate isSource(DataFlow::Node source) { defaultSource(source) } - override predicate isSource(DataFlow::Node source) { defaultSource(source) } + predicate isSink(DataFlow::Node sink) { defaultSink(sink) } - override predicate isSink(DataFlow::Node sink) { - exists(Function fn | fn.hasQualifiedName(_, "sink") | sink = fn.getACall().getAnArgument()) - } - - override int fieldFlowBranchLimit() { result = 1000 } + int fieldFlowBranchLimit() { result = 1000 } } -class InlineFlowTest extends InlineExpectationsTest { - InlineFlowTest() { this = "HasFlowTest" } +private module NoFlowConfig implements DataFlow::ConfigSig { + predicate isSource(DataFlow::Node source) { none() } - override string getARelevantTag() { result = ["hasValueFlow", "hasTaintFlow"] } + predicate isSink(DataFlow::Node sink) { none() } +} - override predicate hasActualResult(Location location, string element, string tag, string value) { - tag = "hasValueFlow" and - exists(DataFlow::Node sink | this.getValueFlowConfig().hasFlowTo(sink) | - sink.hasLocationInfo(location.getFile().getAbsolutePath(), location.getStartLine(), - location.getStartColumn(), location.getEndLine(), location.getEndColumn()) and - element = sink.toString() and - value = "\"" + sink.toString() + "\"" - ) - or - tag = "hasTaintFlow" and - exists(DataFlow::Node src, DataFlow::Node sink | - this.getTaintFlowConfig().hasFlow(src, sink) and - not this.getValueFlowConfig().hasFlow(src, sink) - | - sink.hasLocationInfo(location.getFile().getAbsolutePath(), location.getStartLine(), - location.getStartColumn(), location.getEndLine(), location.getEndColumn()) and - element = sink.toString() and - value = "\"" + sink.toString() + "\"" - ) +module FlowTest { + module ValueFlow = DataFlow::Global; + + module TaintFlow = TaintTracking::Global; + + private module InlineTest implements TestSig { + string getARelevantTag() { result = ["hasValueFlow", "hasTaintFlow"] } + + predicate hasActualResult(Location location, string element, string tag, string value) { + tag = "hasValueFlow" and + exists(DataFlow::Node sink | ValueFlow::flowTo(sink) | + sink.hasLocationInfo(location.getFile().getAbsolutePath(), location.getStartLine(), + location.getStartColumn(), location.getEndLine(), location.getEndColumn()) and + element = sink.toString() and + value = "\"" + sink.toString() + "\"" + ) + or + tag = "hasTaintFlow" and + exists(DataFlow::Node src, DataFlow::Node sink | + TaintFlow::flow(src, sink) and not ValueFlow::flow(src, sink) + | + sink.hasLocationInfo(location.getFile().getAbsolutePath(), location.getStartLine(), + location.getStartColumn(), location.getEndLine(), location.getEndColumn()) and + element = sink.toString() and + value = "\"" + sink.toString() + "\"" + ) + } } - DataFlow::Configuration getValueFlowConfig() { result = any(DefaultValueFlowConf config) } + import MakeTest + import DataFlow::MergePathGraph - DataFlow::Configuration getTaintFlowConfig() { result = any(DefaultTaintFlowConf config) } + predicate flowPath(PathNode source, PathNode sink) { + ValueFlow::flowPath(source.asPathNode1(), sink.asPathNode1()) or + TaintFlow::flowPath(source.asPathNode2(), sink.asPathNode2()) + } +} + +module DefaultFlowTest = FlowTest; + +module ValueFlowTest { + import FlowTest +} + +module TaintFlowTest { + import FlowTest } diff --git a/go/ql/test/experimental/CWE-134/DsnInjection.expected b/go/ql/test/experimental/CWE-134/DsnInjection.expected index de054067a01..531bdb0ead2 100644 --- a/go/ql/test/experimental/CWE-134/DsnInjection.expected +++ b/go/ql/test/experimental/CWE-134/DsnInjection.expected @@ -1,7 +1,11 @@ edges -| Dsn.go:47:10:47:30 | call to FormValue | Dsn.go:50:29:50:33 | dbDSN | +| Dsn.go:47:10:47:30 | call to FormValue | Dsn.go:49:102:49:105 | name | +| Dsn.go:49:11:49:106 | call to Sprintf | Dsn.go:50:29:50:33 | dbDSN | +| Dsn.go:49:102:49:105 | name | Dsn.go:49:11:49:106 | call to Sprintf | nodes | Dsn.go:47:10:47:30 | call to FormValue | semmle.label | call to FormValue | +| Dsn.go:49:11:49:106 | call to Sprintf | semmle.label | call to Sprintf | +| Dsn.go:49:102:49:105 | name | semmle.label | name | | Dsn.go:50:29:50:33 | dbDSN | semmle.label | dbDSN | subpaths #select diff --git a/go/ql/test/experimental/CWE-134/DsnInjectionLocal.expected b/go/ql/test/experimental/CWE-134/DsnInjectionLocal.expected index de5e959d43f..762c5bf3e04 100644 --- a/go/ql/test/experimental/CWE-134/DsnInjectionLocal.expected +++ b/go/ql/test/experimental/CWE-134/DsnInjectionLocal.expected @@ -1,25 +1,34 @@ edges -| Dsn.go:26:11:26:17 | selection of Args | Dsn.go:29:29:29:33 | dbDSN | +| Dsn.go:26:11:26:17 | selection of Args | Dsn.go:28:102:28:109 | index expression | +| Dsn.go:28:11:28:110 | call to Sprintf | Dsn.go:29:29:29:33 | dbDSN | +| Dsn.go:28:102:28:109 | index expression | Dsn.go:28:11:28:110 | call to Sprintf | | Dsn.go:62:2:62:4 | definition of cfg [pointer] | Dsn.go:63:9:63:11 | cfg [pointer] | | Dsn.go:62:2:62:4 | definition of cfg [pointer] | Dsn.go:67:102:67:104 | cfg [pointer] | | Dsn.go:63:9:63:11 | cfg [pointer] | Dsn.go:63:9:63:11 | implicit dereference | | Dsn.go:63:9:63:11 | implicit dereference | Dsn.go:62:2:62:4 | definition of cfg [pointer] | | Dsn.go:63:9:63:11 | implicit dereference | Dsn.go:63:9:63:11 | implicit dereference | -| Dsn.go:63:9:63:11 | implicit dereference | Dsn.go:68:29:68:33 | dbDSN | -| Dsn.go:63:19:63:25 | selection of Args | Dsn.go:63:9:63:11 | implicit dereference | -| Dsn.go:63:19:63:25 | selection of Args | Dsn.go:68:29:68:33 | dbDSN | +| Dsn.go:63:9:63:11 | implicit dereference | Dsn.go:67:102:67:108 | selection of dsn | +| Dsn.go:63:19:63:25 | selection of Args | Dsn.go:63:19:63:29 | slice expression | +| Dsn.go:63:19:63:29 | slice expression | Dsn.go:63:9:63:11 | implicit dereference | +| Dsn.go:67:11:67:109 | call to Sprintf | Dsn.go:68:29:68:33 | dbDSN | | Dsn.go:67:102:67:104 | cfg [pointer] | Dsn.go:67:102:67:104 | implicit dereference | | Dsn.go:67:102:67:104 | implicit dereference | Dsn.go:63:9:63:11 | implicit dereference | -| Dsn.go:67:102:67:104 | implicit dereference | Dsn.go:68:29:68:33 | dbDSN | +| Dsn.go:67:102:67:104 | implicit dereference | Dsn.go:67:102:67:108 | selection of dsn | +| Dsn.go:67:102:67:108 | selection of dsn | Dsn.go:67:11:67:109 | call to Sprintf | nodes | Dsn.go:26:11:26:17 | selection of Args | semmle.label | selection of Args | +| Dsn.go:28:11:28:110 | call to Sprintf | semmle.label | call to Sprintf | +| Dsn.go:28:102:28:109 | index expression | semmle.label | index expression | | Dsn.go:29:29:29:33 | dbDSN | semmle.label | dbDSN | | Dsn.go:62:2:62:4 | definition of cfg [pointer] | semmle.label | definition of cfg [pointer] | | Dsn.go:63:9:63:11 | cfg [pointer] | semmle.label | cfg [pointer] | | Dsn.go:63:9:63:11 | implicit dereference | semmle.label | implicit dereference | | Dsn.go:63:19:63:25 | selection of Args | semmle.label | selection of Args | +| Dsn.go:63:19:63:29 | slice expression | semmle.label | slice expression | +| Dsn.go:67:11:67:109 | call to Sprintf | semmle.label | call to Sprintf | | Dsn.go:67:102:67:104 | cfg [pointer] | semmle.label | cfg [pointer] | | Dsn.go:67:102:67:104 | implicit dereference | semmle.label | implicit dereference | +| Dsn.go:67:102:67:108 | selection of dsn | semmle.label | selection of dsn | | Dsn.go:68:29:68:33 | dbDSN | semmle.label | dbDSN | subpaths #select diff --git a/go/ql/test/experimental/CWE-918/SSRF.expected b/go/ql/test/experimental/CWE-918/SSRF.expected index 5ba4e98208e..d5a4910ad0d 100644 --- a/go/ql/test/experimental/CWE-918/SSRF.expected +++ b/go/ql/test/experimental/CWE-918/SSRF.expected @@ -4,17 +4,23 @@ edges | builtin.go:97:21:97:31 | call to Referer | builtin.go:101:36:101:49 | untrustedInput | | builtin.go:111:21:111:31 | call to Referer | builtin.go:114:15:114:28 | untrustedInput | | builtin.go:129:21:129:31 | call to Referer | builtin.go:132:38:132:51 | untrustedInput | -| new-tests.go:26:26:26:30 | &... | new-tests.go:31:11:31:57 | call to Sprintf | -| new-tests.go:26:26:26:30 | &... | new-tests.go:32:11:32:57 | call to Sprintf | -| new-tests.go:26:26:26:30 | &... | new-tests.go:35:12:35:58 | call to Sprintf | +| new-tests.go:26:26:26:30 | &... | new-tests.go:31:48:31:56 | selection of word | +| new-tests.go:26:26:26:30 | &... | new-tests.go:32:48:32:56 | selection of safe | +| new-tests.go:26:26:26:30 | &... | new-tests.go:35:49:35:57 | selection of word | +| new-tests.go:31:48:31:56 | selection of word | new-tests.go:31:11:31:57 | call to Sprintf | +| new-tests.go:32:48:32:56 | selection of safe | new-tests.go:32:11:32:57 | call to Sprintf | +| new-tests.go:35:49:35:57 | selection of word | new-tests.go:35:12:35:58 | call to Sprintf | | new-tests.go:39:18:39:30 | call to Param | new-tests.go:47:11:47:46 | ...+... | | new-tests.go:49:18:49:30 | call to Query | new-tests.go:50:11:50:46 | ...+... | | new-tests.go:62:2:62:39 | ... := ...[0] | new-tests.go:63:17:63:23 | reqBody | | new-tests.go:62:31:62:38 | selection of Body | new-tests.go:62:2:62:39 | ... := ...[0] | | new-tests.go:63:17:63:23 | reqBody | new-tests.go:63:26:63:30 | &... | -| new-tests.go:63:26:63:30 | &... | new-tests.go:68:11:68:57 | call to Sprintf | -| new-tests.go:63:26:63:30 | &... | new-tests.go:69:11:69:57 | call to Sprintf | -| new-tests.go:63:26:63:30 | &... | new-tests.go:74:12:74:58 | call to Sprintf | +| new-tests.go:63:26:63:30 | &... | new-tests.go:68:48:68:56 | selection of word | +| new-tests.go:63:26:63:30 | &... | new-tests.go:69:48:69:56 | selection of safe | +| new-tests.go:63:26:63:30 | &... | new-tests.go:74:49:74:57 | selection of word | +| new-tests.go:68:48:68:56 | selection of word | new-tests.go:68:11:68:57 | call to Sprintf | +| new-tests.go:69:48:69:56 | selection of safe | new-tests.go:69:11:69:57 | call to Sprintf | +| new-tests.go:74:49:74:57 | selection of word | new-tests.go:74:12:74:58 | call to Sprintf | | new-tests.go:78:18:78:24 | selection of URL | new-tests.go:78:18:78:32 | call to Query | | new-tests.go:78:18:78:32 | call to Query | new-tests.go:78:18:78:46 | call to Get | | new-tests.go:78:18:78:46 | call to Get | new-tests.go:79:11:79:46 | ...+... | @@ -36,8 +42,11 @@ nodes | builtin.go:132:38:132:51 | untrustedInput | semmle.label | untrustedInput | | new-tests.go:26:26:26:30 | &... | semmle.label | &... | | new-tests.go:31:11:31:57 | call to Sprintf | semmle.label | call to Sprintf | +| new-tests.go:31:48:31:56 | selection of word | semmle.label | selection of word | | new-tests.go:32:11:32:57 | call to Sprintf | semmle.label | call to Sprintf | +| new-tests.go:32:48:32:56 | selection of safe | semmle.label | selection of safe | | new-tests.go:35:12:35:58 | call to Sprintf | semmle.label | call to Sprintf | +| new-tests.go:35:49:35:57 | selection of word | semmle.label | selection of word | | new-tests.go:39:18:39:30 | call to Param | semmle.label | call to Param | | new-tests.go:47:11:47:46 | ...+... | semmle.label | ...+... | | new-tests.go:49:18:49:30 | call to Query | semmle.label | call to Query | @@ -47,8 +56,11 @@ nodes | new-tests.go:63:17:63:23 | reqBody | semmle.label | reqBody | | new-tests.go:63:26:63:30 | &... | semmle.label | &... | | new-tests.go:68:11:68:57 | call to Sprintf | semmle.label | call to Sprintf | +| new-tests.go:68:48:68:56 | selection of word | semmle.label | selection of word | | new-tests.go:69:11:69:57 | call to Sprintf | semmle.label | call to Sprintf | +| new-tests.go:69:48:69:56 | selection of safe | semmle.label | selection of safe | | new-tests.go:74:12:74:58 | call to Sprintf | semmle.label | call to Sprintf | +| new-tests.go:74:49:74:57 | selection of word | semmle.label | selection of word | | new-tests.go:78:18:78:24 | selection of URL | semmle.label | selection of URL | | new-tests.go:78:18:78:32 | call to Query | semmle.label | call to Query | | new-tests.go:78:18:78:46 | call to Get | semmle.label | call to Get | diff --git a/go/ql/test/library-tests/semmle/go/dataflow/ExternalFlow/completetest.expected b/go/ql/test/library-tests/semmle/go/dataflow/ExternalFlow/completetest.expected index 81332464f79..105b7026d0c 100644 --- a/go/ql/test/library-tests/semmle/go/dataflow/ExternalFlow/completetest.expected +++ b/go/ql/test/library-tests/semmle/go/dataflow/ExternalFlow/completetest.expected @@ -1,2 +1,3 @@ failures invalidModelRow +testFailures diff --git a/go/ql/test/library-tests/semmle/go/dataflow/ExternalFlow/completetest.ext.yml b/go/ql/test/library-tests/semmle/go/dataflow/ExternalFlow/completetest.ext.yml index 79ca023562c..47e51e573f0 100644 --- a/go/ql/test/library-tests/semmle/go/dataflow/ExternalFlow/completetest.ext.yml +++ b/go/ql/test/library-tests/semmle/go/dataflow/ExternalFlow/completetest.ext.yml @@ -22,7 +22,9 @@ extensions: - ["github.com/nonexistent/test", "", False, "GetMapKey", "", "", "Argument[0].MapKey", "ReturnValue", "value", "manual"] - ["github.com/nonexistent/test", "", False, "SetElement", "", "", "Argument[0]", "ReturnValue.Element", "value", "manual"] - ["github.com/nonexistent/test", "C", False, "Get", "", "", "Argument[-1].Field[github.com/nonexistent/test.C.F]", "ReturnValue", "value", "manual"] + - ["github.com/nonexistent/test", "C", False, "GetThroughPointer", "", "", "Argument[-1].Dereference.Field[github.com/nonexistent/test.C.F]", "ReturnValue", "value", "manual"] - ["github.com/nonexistent/test", "C", False, "Set", "", "", "Argument[0]", "Argument[-1].Field[github.com/nonexistent/test.C.F]", "value", "manual"] + - ["github.com/nonexistent/test", "C", False, "SetThroughPointer", "", "", "Argument[0]", "Argument[-1].Dereference.Field[github.com/nonexistent/test.C.F]", "value", "manual"] - addsTo: pack: codeql/go-all diff --git a/go/ql/test/library-tests/semmle/go/dataflow/ExternalFlow/completetest.ql b/go/ql/test/library-tests/semmle/go/dataflow/ExternalFlow/completetest.ql index 9719a9d69a6..2b719551ae0 100644 --- a/go/ql/test/library-tests/semmle/go/dataflow/ExternalFlow/completetest.ql +++ b/go/ql/test/library-tests/semmle/go/dataflow/ExternalFlow/completetest.ql @@ -8,16 +8,10 @@ import ModelValidation import semmle.go.dataflow.internal.FlowSummaryImpl as FlowSummaryImpl import TestUtilities.InlineFlowTest -class Config extends TaintTracking::Configuration { - Config() { this = "external-flow-test" } +module Config implements DataFlow::ConfigSig { + predicate isSource(DataFlow::Node src) { sourceNode(src, "qltest") } - override predicate isSource(DataFlow::Node src) { sourceNode(src, "qltest") } - - override predicate isSink(DataFlow::Node src) { sinkNode(src, "qltest") } + predicate isSink(DataFlow::Node src) { sinkNode(src, "qltest") } } -class ExternalFlowTest extends InlineFlowTest { - override DataFlow::Configuration getValueFlowConfig() { none() } - - override DataFlow::Configuration getTaintFlowConfig() { result = any(Config config) } -} +import TaintFlowTest diff --git a/go/ql/test/library-tests/semmle/go/dataflow/ExternalFlow/sinks.expected b/go/ql/test/library-tests/semmle/go/dataflow/ExternalFlow/sinks.expected index 21199db73df..38dc380b55b 100644 --- a/go/ql/test/library-tests/semmle/go/dataflow/ExternalFlow/sinks.expected +++ b/go/ql/test/library-tests/semmle/go/dataflow/ExternalFlow/sinks.expected @@ -26,6 +26,10 @@ invalidModelRow | test.go:133:10:133:17 | call to Get | qltest | | test.go:137:10:137:17 | call to Get | qltest | | test.go:142:10:142:17 | call to Get | qltest | -| test.go:148:17:148:20 | arg1 | qltest | -| test.go:148:23:148:26 | arg2 | qltest | -| test.go:148:29:148:32 | arg3 | qltest | +| test.go:146:10:146:14 | selection of F | qltest | +| test.go:149:10:149:32 | call to GetThroughPointer | qltest | +| test.go:153:10:153:32 | call to GetThroughPointer | qltest | +| test.go:158:10:158:32 | call to GetThroughPointer | qltest | +| test.go:164:17:164:20 | arg1 | qltest | +| test.go:164:23:164:26 | arg2 | qltest | +| test.go:164:29:164:32 | arg3 | qltest | diff --git a/go/ql/test/library-tests/semmle/go/dataflow/ExternalFlow/srcs.expected b/go/ql/test/library-tests/semmle/go/dataflow/ExternalFlow/srcs.expected index 7b721110c67..2f1e3256778 100644 --- a/go/ql/test/library-tests/semmle/go/dataflow/ExternalFlow/srcs.expected +++ b/go/ql/test/library-tests/semmle/go/dataflow/ExternalFlow/srcs.expected @@ -17,3 +17,7 @@ invalidModelRow | test.go:132:15:132:22 | call to Src1 | qltest | | test.go:136:9:136:16 | call to Src1 | qltest | | test.go:140:9:140:16 | call to Src1 | qltest | +| test.go:145:24:145:31 | call to Src1 | qltest | +| test.go:148:17:148:24 | call to Src1 | qltest | +| test.go:152:24:152:31 | call to Src1 | qltest | +| test.go:156:24:156:31 | call to Src1 | qltest | diff --git a/go/ql/test/library-tests/semmle/go/dataflow/ExternalFlow/test.go b/go/ql/test/library-tests/semmle/go/dataflow/ExternalFlow/test.go index a70b2868f58..35da086a888 100644 --- a/go/ql/test/library-tests/semmle/go/dataflow/ExternalFlow/test.go +++ b/go/ql/test/library-tests/semmle/go/dataflow/ExternalFlow/test.go @@ -141,6 +141,22 @@ func simpleflow() { c4.Set("") b.Sink1(c4.Get()) // $ SPURIOUS: hasTaintFlow="call to Get" // because we currently don't clear content + cp1 := &test.C{""} + cp1.SetThroughPointer(a.Src1().(string)) + b.Sink1(cp1.F) // $ hasTaintFlow="selection of F" + + cp2 := &test.C{a.Src1().(string)} + b.Sink1(cp2.GetThroughPointer()) // $ hasTaintFlow="call to GetThroughPointer" + + cp3 := &test.C{""} + cp3.SetThroughPointer(a.Src1().(string)) + b.Sink1(cp3.GetThroughPointer()) // $ hasTaintFlow="call to GetThroughPointer" + + cp4 := &test.C{""} + cp4.SetThroughPointer(a.Src1().(string)) + cp4.SetThroughPointer("") + b.Sink1(cp4.GetThroughPointer()) // $ SPURIOUS: hasTaintFlow="call to GetThroughPointer" // because we currently don't clear content + arg1 := src arg2 := src arg3 := src diff --git a/go/ql/test/library-tests/semmle/go/dataflow/ExternalFlow/vendor/github.com/nonexistent/test/stub.go b/go/ql/test/library-tests/semmle/go/dataflow/ExternalFlow/vendor/github.com/nonexistent/test/stub.go index 2f13fce84e7..746f6ac9a6a 100644 --- a/go/ql/test/library-tests/semmle/go/dataflow/ExternalFlow/vendor/github.com/nonexistent/test/stub.go +++ b/go/ql/test/library-tests/semmle/go/dataflow/ExternalFlow/vendor/github.com/nonexistent/test/stub.go @@ -67,3 +67,6 @@ type C struct { func (c C) Set(f string) {} func (c C) Get() string { return "" } + +func (c *C) SetThroughPointer(f string) {} +func (c *C) GetThroughPointer() string { return "" } diff --git a/go/ql/test/library-tests/semmle/go/dataflow/GenericFunctionsAndTypes/Flows.expected b/go/ql/test/library-tests/semmle/go/dataflow/GenericFunctionsAndTypes/Flows.expected index e69de29bb2d..48de9172b36 100644 --- a/go/ql/test/library-tests/semmle/go/dataflow/GenericFunctionsAndTypes/Flows.expected +++ b/go/ql/test/library-tests/semmle/go/dataflow/GenericFunctionsAndTypes/Flows.expected @@ -0,0 +1,2 @@ +failures +testFailures diff --git a/go/ql/test/library-tests/semmle/go/dataflow/GenericFunctionsAndTypes/Flows.ql b/go/ql/test/library-tests/semmle/go/dataflow/GenericFunctionsAndTypes/Flows.ql index 881d262f400..1b27b27d6dc 100644 --- a/go/ql/test/library-tests/semmle/go/dataflow/GenericFunctionsAndTypes/Flows.ql +++ b/go/ql/test/library-tests/semmle/go/dataflow/GenericFunctionsAndTypes/Flows.ql @@ -1,2 +1,3 @@ import go import TestUtilities.InlineFlowTest +import DefaultFlowTest diff --git a/go/ql/test/library-tests/semmle/go/frameworks/Beego/ReflectedXss.expected b/go/ql/test/library-tests/semmle/go/frameworks/Beego/ReflectedXss.expected index 2d29d2b2357..29b131c367a 100644 --- a/go/ql/test/library-tests/semmle/go/frameworks/Beego/ReflectedXss.expected +++ b/go/ql/test/library-tests/semmle/go/frameworks/Beego/ReflectedXss.expected @@ -47,7 +47,7 @@ edges | test.go:240:15:240:36 | call to GetString | test.go:243:21:243:29 | untrusted | | test.go:253:23:253:44 | call to GetCookie | test.go:253:16:253:45 | type conversion | | test.go:264:62:264:83 | call to GetCookie | test.go:264:55:264:84 | type conversion | -| test.go:269:2:269:40 | ... := ...[0] | test.go:277:21:277:61 | call to GetDisplayString | +| test.go:269:2:269:40 | ... := ...[0] | test.go:277:44:277:60 | selection of Filename | | test.go:269:2:269:40 | ... := ...[0] | test.go:278:38:278:49 | genericFiles | | test.go:269:2:269:40 | ... := ...[0] | test.go:279:37:279:48 | genericFiles | | test.go:269:2:269:40 | ... := ...[0] | test.go:285:4:285:15 | genericFiles | @@ -61,6 +61,7 @@ edges | test.go:269:2:269:40 | ... := ...[0] | test.go:295:39:295:50 | genericFiles | | test.go:269:2:269:40 | ... := ...[0] | test.go:296:40:296:51 | genericFiles | | test.go:269:2:269:40 | ... := ...[0] | test.go:297:39:297:50 | genericFiles | +| test.go:277:44:277:60 | selection of Filename | test.go:277:21:277:61 | call to GetDisplayString | | test.go:278:21:278:53 | call to SliceChunk | test.go:278:21:278:92 | selection of Filename | | test.go:278:38:278:49 | genericFiles | test.go:278:21:278:53 | call to SliceChunk | | test.go:279:21:279:60 | call to SliceDiff | test.go:279:21:279:96 | selection of Filename | @@ -177,6 +178,7 @@ nodes | test.go:264:62:264:83 | call to GetCookie | semmle.label | call to GetCookie | | test.go:269:2:269:40 | ... := ...[0] | semmle.label | ... := ...[0] | | test.go:277:21:277:61 | call to GetDisplayString | semmle.label | call to GetDisplayString | +| test.go:277:44:277:60 | selection of Filename | semmle.label | selection of Filename | | test.go:278:21:278:53 | call to SliceChunk | semmle.label | call to SliceChunk | | test.go:278:21:278:92 | selection of Filename | semmle.label | selection of Filename | | test.go:278:38:278:49 | genericFiles | semmle.label | genericFiles | diff --git a/go/ql/test/library-tests/semmle/go/frameworks/ElazarlGoproxy/test.expected b/go/ql/test/library-tests/semmle/go/frameworks/ElazarlGoproxy/test.expected index e69de29bb2d..48de9172b36 100644 --- a/go/ql/test/library-tests/semmle/go/frameworks/ElazarlGoproxy/test.expected +++ b/go/ql/test/library-tests/semmle/go/frameworks/ElazarlGoproxy/test.expected @@ -0,0 +1,2 @@ +failures +testFailures diff --git a/go/ql/test/library-tests/semmle/go/frameworks/ElazarlGoproxy/test.ql b/go/ql/test/library-tests/semmle/go/frameworks/ElazarlGoproxy/test.ql index e8c68c1c3e1..2ffca8a692a 100644 --- a/go/ql/test/library-tests/semmle/go/frameworks/ElazarlGoproxy/test.ql +++ b/go/ql/test/library-tests/semmle/go/frameworks/ElazarlGoproxy/test.ql @@ -1,12 +1,10 @@ import go import TestUtilities.InlineExpectationsTest -class UntrustedFlowSourceTest extends InlineExpectationsTest { - UntrustedFlowSourceTest() { this = "untrustedflowsource" } +module UntrustedFlowSourceTest implements TestSig { + string getARelevantTag() { result = "untrustedflowsource" } - override string getARelevantTag() { result = "untrustedflowsource" } - - override predicate hasActualResult(Location location, string element, string tag, string value) { + predicate hasActualResult(Location location, string element, string tag, string value) { tag = "untrustedflowsource" and value = element and exists(UntrustedFlowSource src | value = "\"" + src.toString() + "\"" | @@ -16,12 +14,10 @@ class UntrustedFlowSourceTest extends InlineExpectationsTest { } } -class HeaderWriteTest extends InlineExpectationsTest { - HeaderWriteTest() { this = "headerwrite" } +module HeaderWriteTest implements TestSig { + string getARelevantTag() { result = "headerwrite" } - override string getARelevantTag() { result = "headerwrite" } - - override predicate hasActualResult(Location location, string element, string tag, string value) { + predicate hasActualResult(Location location, string element, string tag, string value) { tag = "headerwrite" and exists(Http::HeaderWrite hw, string name, string val | element = hw.toString() | hw.definesHeader(name, val) and @@ -32,12 +28,10 @@ class HeaderWriteTest extends InlineExpectationsTest { } } -class LoggerTest extends InlineExpectationsTest { - LoggerTest() { this = "LoggerTest" } +module LoggerTest implements TestSig { + string getARelevantTag() { result = "logger" } - override string getARelevantTag() { result = "logger" } - - override predicate hasActualResult(Location location, string element, string tag, string value) { + predicate hasActualResult(Location location, string element, string tag, string value) { exists(LoggerCall log | log.hasLocationInfo(location.getFile().getAbsolutePath(), location.getStartLine(), location.getStartColumn(), location.getEndLine(), location.getEndColumn()) and @@ -48,32 +42,32 @@ class LoggerTest extends InlineExpectationsTest { } } -class Config extends TaintTracking::Configuration { - Config() { this = "goproxy config" } - - override predicate isSource(DataFlow::Node n) { +module Config implements DataFlow::ConfigSig { + predicate isSource(DataFlow::Node n) { n = any(DataFlow::CallNode c | c.getCalleeName().matches("tainted%")).getResult() } - override predicate isSink(DataFlow::Node n) { + predicate isSink(DataFlow::Node n) { n = any(DataFlow::CallNode cn | cn.getTarget().getName() = "sink").getAnArgument() } } -class TaintFlow extends InlineExpectationsTest { - TaintFlow() { this = "goproxy flow" } +module Flow = TaintTracking::Global; - override string getARelevantTag() { result = "taintflow" } +module TaintFlow implements TestSig { + string getARelevantTag() { result = "taintflow" } - override predicate hasActualResult(Location location, string element, string tag, string value) { + predicate hasActualResult(Location location, string element, string tag, string value) { tag = "taintflow" and value = "" and element = "" and - exists(Config c, DataFlow::Node toNode | + exists(DataFlow::Node toNode | toNode .hasLocationInfo(location.getFile().getAbsolutePath(), location.getStartLine(), location.getStartColumn(), location.getEndLine(), location.getEndColumn()) and - c.hasFlowTo(toNode) + Flow::flowTo(toNode) ) } } + +import MakeTest> diff --git a/go/ql/test/library-tests/semmle/go/frameworks/SQL/QueryString.expected b/go/ql/test/library-tests/semmle/go/frameworks/SQL/QueryString.expected index e69de29bb2d..48de9172b36 100644 --- a/go/ql/test/library-tests/semmle/go/frameworks/SQL/QueryString.expected +++ b/go/ql/test/library-tests/semmle/go/frameworks/SQL/QueryString.expected @@ -0,0 +1,2 @@ +failures +testFailures diff --git a/go/ql/test/library-tests/semmle/go/frameworks/SQL/QueryString.ql b/go/ql/test/library-tests/semmle/go/frameworks/SQL/QueryString.ql index 31852f1b862..6b1c1f70e04 100644 --- a/go/ql/test/library-tests/semmle/go/frameworks/SQL/QueryString.ql +++ b/go/ql/test/library-tests/semmle/go/frameworks/SQL/QueryString.ql @@ -1,12 +1,10 @@ import go import TestUtilities.InlineExpectationsTest -class SqlTest extends InlineExpectationsTest { - SqlTest() { this = "SQLTest" } +module SqlTest implements TestSig { + string getARelevantTag() { result = "query" } - override string getARelevantTag() { result = "query" } - - override predicate hasActualResult(Location location, string element, string tag, string value) { + predicate hasActualResult(Location location, string element, string tag, string value) { tag = "query" and exists(SQL::Query q, SQL::QueryString qs | qs = q.getAQueryString() | q.hasLocationInfo(location.getFile().getAbsolutePath(), location.getStartLine(), @@ -17,12 +15,10 @@ class SqlTest extends InlineExpectationsTest { } } -class QueryString extends InlineExpectationsTest { - QueryString() { this = "QueryString no Query" } +module QueryString implements TestSig { + string getARelevantTag() { result = "querystring" } - override string getARelevantTag() { result = "querystring" } - - override predicate hasActualResult(Location location, string element, string tag, string value) { + predicate hasActualResult(Location location, string element, string tag, string value) { tag = "querystring" and element = "" and exists(SQL::QueryString qs | not exists(SQL::Query q | qs = q.getAQueryString()) | @@ -33,30 +29,30 @@ class QueryString extends InlineExpectationsTest { } } -class Config extends TaintTracking::Configuration { - Config() { this = "pg-orm config" } +module Config implements DataFlow::ConfigSig { + predicate isSource(DataFlow::Node n) { n.asExpr() instanceof StringLit } - override predicate isSource(DataFlow::Node n) { n.asExpr() instanceof StringLit } - - override predicate isSink(DataFlow::Node n) { + predicate isSink(DataFlow::Node n) { n = any(DataFlow::CallNode cn | cn.getTarget().getName() = "sink").getAnArgument() } } -class TaintFlow extends InlineExpectationsTest { - TaintFlow() { this = "pg-orm flow" } +module Flow = TaintTracking::Global; - override string getARelevantTag() { result = "flowfrom" } +module TaintFlow implements TestSig { + string getARelevantTag() { result = "flowfrom" } - override predicate hasActualResult(Location location, string element, string tag, string value) { + predicate hasActualResult(Location location, string element, string tag, string value) { tag = "flowfrom" and element = "" and - exists(Config c, DataFlow::Node fromNode, DataFlow::Node toNode | + exists(DataFlow::Node fromNode, DataFlow::Node toNode | toNode .hasLocationInfo(location.getFile().getAbsolutePath(), location.getStartLine(), location.getStartColumn(), location.getEndLine(), location.getEndColumn()) and - c.hasFlow(fromNode, toNode) and + Flow::flow(fromNode, toNode) and value = fromNode.asExpr().(StringLit).getValue() ) } } + +import MakeTest> diff --git a/go/ql/test/library-tests/semmle/go/frameworks/Yaml/tests.expected b/go/ql/test/library-tests/semmle/go/frameworks/Yaml/tests.expected index e69de29bb2d..48de9172b36 100644 --- a/go/ql/test/library-tests/semmle/go/frameworks/Yaml/tests.expected +++ b/go/ql/test/library-tests/semmle/go/frameworks/Yaml/tests.expected @@ -0,0 +1,2 @@ +failures +testFailures diff --git a/go/ql/test/library-tests/semmle/go/frameworks/Yaml/tests.ql b/go/ql/test/library-tests/semmle/go/frameworks/Yaml/tests.ql index 273f031fc32..c4c0cafb50e 100644 --- a/go/ql/test/library-tests/semmle/go/frameworks/Yaml/tests.ql +++ b/go/ql/test/library-tests/semmle/go/frameworks/Yaml/tests.ql @@ -11,32 +11,29 @@ DataFlow::CallNode getAYamlCall() { isYamlFunction(result.getACalleeIncludingExternals().asFunction()) } -class TaintTransitsFunctionConfig extends TaintTracking::Configuration { - TaintTransitsFunctionConfig() { this = "TaintTransitsFunctionConfig" } - - predicate isSourceSinkPair(DataFlow::Node inNode, DataFlow::Node outNode) { - exists(DataFlow::CallNode cn | cn = getAYamlCall() | - inNode = [cn.getAnArgument(), cn.getReceiver()] and - ( - outNode.(DataFlow::PostUpdateNode).getPreUpdateNode() = - [cn.getAnArgument(), cn.getReceiver()] - or - outNode = cn.getAResult() - ) +predicate isSourceSinkPair(DataFlow::Node inNode, DataFlow::Node outNode) { + exists(DataFlow::CallNode cn | cn = getAYamlCall() | + inNode = [cn.getAnArgument(), cn.getReceiver()] and + ( + outNode.(DataFlow::PostUpdateNode).getPreUpdateNode() = [cn.getAnArgument(), cn.getReceiver()] + or + outNode = cn.getAResult() ) - } - - override predicate isSource(DataFlow::Node n) { this.isSourceSinkPair(n, _) } - - override predicate isSink(DataFlow::Node n) { this.isSourceSinkPair(_, n) } + ) } -class TaintFunctionModelTest extends InlineExpectationsTest { - TaintFunctionModelTest() { this = "TaintFunctionModelTest" } +module TaintTransitsFunctionConfig implements DataFlow::ConfigSig { + predicate isSource(DataFlow::Node n) { isSourceSinkPair(n, _) } - override string getARelevantTag() { result = "ttfnmodelstep" } + predicate isSink(DataFlow::Node n) { isSourceSinkPair(_, n) } +} - override predicate hasActualResult(Location location, string element, string tag, string value) { +module TaintTransitsFunctionFlow = TaintTracking::Global; + +module TaintFunctionModelTest implements TestSig { + string getARelevantTag() { result = "ttfnmodelstep" } + + predicate hasActualResult(Location location, string element, string tag, string value) { tag = "ttfnmodelstep" and ( exists(TaintTracking::FunctionModel model, DataFlow::CallNode call | call = model.getACall() | @@ -46,9 +43,9 @@ class TaintFunctionModelTest extends InlineExpectationsTest { value = "\"" + model.getAnInputNode(call) + " -> " + model.getAnOutputNode(call) + "\"" ) or - exists(TaintTransitsFunctionConfig config, DataFlow::Node arg, DataFlow::Node output | - config.hasFlow(arg, output) and - config.isSourceSinkPair(arg, output) and + exists(DataFlow::Node arg, DataFlow::Node output | + TaintTransitsFunctionFlow::flow(arg, output) and + isSourceSinkPair(arg, output) and arg.hasLocationInfo(location.getFile().getAbsolutePath(), location.getStartLine(), location.getStartColumn(), location.getEndLine(), location.getEndColumn()) and element = arg.toString() and @@ -58,12 +55,10 @@ class TaintFunctionModelTest extends InlineExpectationsTest { } } -class MarshalerTest extends InlineExpectationsTest { - MarshalerTest() { this = "MarshalerTest" } +module MarshalerTest implements TestSig { + string getARelevantTag() { result = "marshaler" } - override string getARelevantTag() { result = "marshaler" } - - override predicate hasActualResult(Location location, string element, string tag, string value) { + predicate hasActualResult(Location location, string element, string tag, string value) { tag = "marshaler" and exists(MarshalingFunction m, DataFlow::CallNode call | call = m.getACall() | call.hasLocationInfo(location.getFile().getAbsolutePath(), location.getStartLine(), @@ -76,12 +71,10 @@ class MarshalerTest extends InlineExpectationsTest { } } -class UnmarshalerTest extends InlineExpectationsTest { - UnmarshalerTest() { this = "UnmarshalerTest" } +module UnmarshalerTest implements TestSig { + string getARelevantTag() { result = "unmarshaler" } - override string getARelevantTag() { result = "unmarshaler" } - - override predicate hasActualResult(Location location, string element, string tag, string value) { + predicate hasActualResult(Location location, string element, string tag, string value) { tag = "unmarshaler" and exists(UnmarshalingFunction m, DataFlow::CallNode call | call = m.getACall() | call.hasLocationInfo(location.getFile().getAbsolutePath(), location.getStartLine(), @@ -93,3 +86,5 @@ class UnmarshalerTest extends InlineExpectationsTest { ) } } + +import MakeTest> diff --git a/go/ql/test/query-tests/Security/CWE-022/TaintedPath.expected b/go/ql/test/query-tests/Security/CWE-022/TaintedPath.expected index 2e7a965b514..307906f7e9b 100644 --- a/go/ql/test/query-tests/Security/CWE-022/TaintedPath.expected +++ b/go/ql/test/query-tests/Security/CWE-022/TaintedPath.expected @@ -1,13 +1,15 @@ edges | TaintedPath.go:13:18:13:22 | selection of URL | TaintedPath.go:13:18:13:30 | call to Query | | TaintedPath.go:13:18:13:30 | call to Query | TaintedPath.go:16:29:16:40 | tainted_path | -| TaintedPath.go:13:18:13:30 | call to Query | TaintedPath.go:20:28:20:69 | call to Join | +| TaintedPath.go:13:18:13:30 | call to Query | TaintedPath.go:20:57:20:68 | tainted_path | +| TaintedPath.go:20:57:20:68 | tainted_path | TaintedPath.go:20:28:20:69 | call to Join | | tst.go:14:2:14:39 | ... := ...[1] | tst.go:17:41:17:56 | selection of Filename | nodes | TaintedPath.go:13:18:13:22 | selection of URL | semmle.label | selection of URL | | TaintedPath.go:13:18:13:30 | call to Query | semmle.label | call to Query | | TaintedPath.go:16:29:16:40 | tainted_path | semmle.label | tainted_path | | TaintedPath.go:20:28:20:69 | call to Join | semmle.label | call to Join | +| TaintedPath.go:20:57:20:68 | tainted_path | semmle.label | tainted_path | | tst.go:14:2:14:39 | ... := ...[1] | semmle.label | ... := ...[1] | | tst.go:17:41:17:56 | selection of Filename | semmle.label | selection of Filename | subpaths diff --git a/go/ql/test/query-tests/Security/CWE-022/ZipSlip.expected b/go/ql/test/query-tests/Security/CWE-022/ZipSlip.expected index 1c3a46096c2..f10f103c963 100644 --- a/go/ql/test/query-tests/Security/CWE-022/ZipSlip.expected +++ b/go/ql/test/query-tests/Security/CWE-022/ZipSlip.expected @@ -1,5 +1,6 @@ edges -| UnsafeUnzipSymlinkGood.go:52:24:52:32 | definition of candidate | UnsafeUnzipSymlinkGood.go:61:31:61:62 | call to Join | +| UnsafeUnzipSymlinkGood.go:52:24:52:32 | definition of candidate | UnsafeUnzipSymlinkGood.go:61:53:61:61 | candidate | +| UnsafeUnzipSymlinkGood.go:61:53:61:61 | candidate | UnsafeUnzipSymlinkGood.go:61:31:61:62 | call to Join | | UnsafeUnzipSymlinkGood.go:72:3:72:25 | ... := ...[0] | UnsafeUnzipSymlinkGood.go:76:24:76:38 | selection of Linkname | | UnsafeUnzipSymlinkGood.go:72:3:72:25 | ... := ...[0] | UnsafeUnzipSymlinkGood.go:76:70:76:80 | selection of Name | | UnsafeUnzipSymlinkGood.go:76:24:76:38 | selection of Linkname | UnsafeUnzipSymlinkGood.go:52:24:52:32 | definition of candidate | @@ -13,6 +14,7 @@ edges nodes | UnsafeUnzipSymlinkGood.go:52:24:52:32 | definition of candidate | semmle.label | definition of candidate | | UnsafeUnzipSymlinkGood.go:61:31:61:62 | call to Join | semmle.label | call to Join | +| UnsafeUnzipSymlinkGood.go:61:53:61:61 | candidate | semmle.label | candidate | | UnsafeUnzipSymlinkGood.go:72:3:72:25 | ... := ...[0] | semmle.label | ... := ...[0] | | UnsafeUnzipSymlinkGood.go:76:24:76:38 | selection of Linkname | semmle.label | selection of Linkname | | UnsafeUnzipSymlinkGood.go:76:70:76:80 | selection of Name | semmle.label | selection of Name | diff --git a/go/ql/test/query-tests/Security/CWE-078/CommandInjection.expected b/go/ql/test/query-tests/Security/CWE-078/CommandInjection.expected index cfc49180bba..19c94698899 100644 --- a/go/ql/test/query-tests/Security/CWE-078/CommandInjection.expected +++ b/go/ql/test/query-tests/Security/CWE-078/CommandInjection.expected @@ -11,25 +11,47 @@ edges | GitSubcommands.go:10:13:10:27 | call to Query | GitSubcommands.go:16:36:16:42 | tainted | | SanitizingDoubleDash.go:9:13:9:19 | selection of URL | SanitizingDoubleDash.go:9:13:9:27 | call to Query | | SanitizingDoubleDash.go:9:13:9:27 | call to Query | SanitizingDoubleDash.go:14:23:14:33 | slice expression | -| SanitizingDoubleDash.go:9:13:9:27 | call to Query | SanitizingDoubleDash.go:40:23:40:30 | arrayLit | -| SanitizingDoubleDash.go:9:13:9:27 | call to Query | SanitizingDoubleDash.go:54:23:54:30 | arrayLit | -| SanitizingDoubleDash.go:9:13:9:27 | call to Query | SanitizingDoubleDash.go:70:23:70:30 | arrayLit | +| SanitizingDoubleDash.go:9:13:9:27 | call to Query | SanitizingDoubleDash.go:39:31:39:37 | tainted | +| SanitizingDoubleDash.go:9:13:9:27 | call to Query | SanitizingDoubleDash.go:53:21:53:28 | arrayLit | +| SanitizingDoubleDash.go:9:13:9:27 | call to Query | SanitizingDoubleDash.go:68:31:68:37 | tainted | | SanitizingDoubleDash.go:9:13:9:27 | call to Query | SanitizingDoubleDash.go:80:23:80:29 | tainted | +| SanitizingDoubleDash.go:39:14:39:44 | call to append | SanitizingDoubleDash.go:40:23:40:30 | arrayLit | +| SanitizingDoubleDash.go:39:31:39:37 | tainted | SanitizingDoubleDash.go:39:14:39:44 | call to append | +| SanitizingDoubleDash.go:53:14:53:35 | call to append | SanitizingDoubleDash.go:54:23:54:30 | arrayLit | +| SanitizingDoubleDash.go:53:21:53:28 | arrayLit | SanitizingDoubleDash.go:53:14:53:35 | call to append | +| SanitizingDoubleDash.go:68:14:68:38 | call to append | SanitizingDoubleDash.go:69:21:69:28 | arrayLit | +| SanitizingDoubleDash.go:68:31:68:37 | tainted | SanitizingDoubleDash.go:68:14:68:38 | call to append | +| SanitizingDoubleDash.go:69:14:69:35 | call to append | SanitizingDoubleDash.go:70:23:70:30 | arrayLit | +| SanitizingDoubleDash.go:69:21:69:28 | arrayLit | SanitizingDoubleDash.go:69:14:69:35 | call to append | | SanitizingDoubleDash.go:92:13:92:19 | selection of URL | SanitizingDoubleDash.go:92:13:92:27 | call to Query | | SanitizingDoubleDash.go:92:13:92:27 | call to Query | SanitizingDoubleDash.go:96:24:96:34 | slice expression | | SanitizingDoubleDash.go:92:13:92:27 | call to Query | SanitizingDoubleDash.go:101:24:101:34 | slice expression | | SanitizingDoubleDash.go:92:13:92:27 | call to Query | SanitizingDoubleDash.go:105:30:105:36 | tainted | | SanitizingDoubleDash.go:92:13:92:27 | call to Query | SanitizingDoubleDash.go:106:24:106:31 | arrayLit | -| SanitizingDoubleDash.go:92:13:92:27 | call to Query | SanitizingDoubleDash.go:112:24:112:31 | arrayLit | -| SanitizingDoubleDash.go:92:13:92:27 | call to Query | SanitizingDoubleDash.go:118:24:118:31 | arrayLit | -| SanitizingDoubleDash.go:92:13:92:27 | call to Query | SanitizingDoubleDash.go:124:24:124:31 | arrayLit | -| SanitizingDoubleDash.go:92:13:92:27 | call to Query | SanitizingDoubleDash.go:130:24:130:31 | arrayLit | -| SanitizingDoubleDash.go:92:13:92:27 | call to Query | SanitizingDoubleDash.go:137:24:137:31 | arrayLit | -| SanitizingDoubleDash.go:92:13:92:27 | call to Query | SanitizingDoubleDash.go:144:24:144:31 | arrayLit | +| SanitizingDoubleDash.go:92:13:92:27 | call to Query | SanitizingDoubleDash.go:111:37:111:43 | tainted | +| SanitizingDoubleDash.go:92:13:92:27 | call to Query | SanitizingDoubleDash.go:117:31:117:37 | tainted | +| SanitizingDoubleDash.go:92:13:92:27 | call to Query | SanitizingDoubleDash.go:123:31:123:37 | tainted | +| SanitizingDoubleDash.go:92:13:92:27 | call to Query | SanitizingDoubleDash.go:129:21:129:28 | arrayLit | +| SanitizingDoubleDash.go:92:13:92:27 | call to Query | SanitizingDoubleDash.go:136:31:136:37 | tainted | +| SanitizingDoubleDash.go:92:13:92:27 | call to Query | SanitizingDoubleDash.go:142:31:142:37 | tainted | | SanitizingDoubleDash.go:92:13:92:27 | call to Query | SanitizingDoubleDash.go:148:30:148:36 | tainted | | SanitizingDoubleDash.go:92:13:92:27 | call to Query | SanitizingDoubleDash.go:152:24:152:30 | tainted | | SanitizingDoubleDash.go:105:15:105:37 | slice literal [array] | SanitizingDoubleDash.go:106:24:106:31 | arrayLit | | SanitizingDoubleDash.go:105:30:105:36 | tainted | SanitizingDoubleDash.go:105:15:105:37 | slice literal [array] | +| SanitizingDoubleDash.go:111:14:111:44 | call to append | SanitizingDoubleDash.go:112:24:112:31 | arrayLit | +| SanitizingDoubleDash.go:111:37:111:43 | tainted | SanitizingDoubleDash.go:111:14:111:44 | call to append | +| SanitizingDoubleDash.go:117:14:117:44 | call to append | SanitizingDoubleDash.go:118:24:118:31 | arrayLit | +| SanitizingDoubleDash.go:117:31:117:37 | tainted | SanitizingDoubleDash.go:117:14:117:44 | call to append | +| SanitizingDoubleDash.go:123:14:123:38 | call to append | SanitizingDoubleDash.go:124:24:124:31 | arrayLit | +| SanitizingDoubleDash.go:123:31:123:37 | tainted | SanitizingDoubleDash.go:123:14:123:38 | call to append | +| SanitizingDoubleDash.go:129:14:129:35 | call to append | SanitizingDoubleDash.go:130:24:130:31 | arrayLit | +| SanitizingDoubleDash.go:129:21:129:28 | arrayLit | SanitizingDoubleDash.go:129:14:129:35 | call to append | +| SanitizingDoubleDash.go:136:14:136:38 | call to append | SanitizingDoubleDash.go:137:24:137:31 | arrayLit | +| SanitizingDoubleDash.go:136:31:136:37 | tainted | SanitizingDoubleDash.go:136:14:136:38 | call to append | +| SanitizingDoubleDash.go:142:14:142:38 | call to append | SanitizingDoubleDash.go:143:21:143:28 | arrayLit | +| SanitizingDoubleDash.go:142:31:142:37 | tainted | SanitizingDoubleDash.go:142:14:142:38 | call to append | +| SanitizingDoubleDash.go:143:14:143:35 | call to append | SanitizingDoubleDash.go:144:24:144:31 | arrayLit | +| SanitizingDoubleDash.go:143:21:143:28 | arrayLit | SanitizingDoubleDash.go:143:14:143:35 | call to append | nodes | ArgumentInjection.go:9:10:9:16 | selection of URL | semmle.label | selection of URL | | ArgumentInjection.go:9:10:9:24 | call to Query | semmle.label | call to Query | @@ -47,8 +69,16 @@ nodes | SanitizingDoubleDash.go:9:13:9:19 | selection of URL | semmle.label | selection of URL | | SanitizingDoubleDash.go:9:13:9:27 | call to Query | semmle.label | call to Query | | SanitizingDoubleDash.go:14:23:14:33 | slice expression | semmle.label | slice expression | +| SanitizingDoubleDash.go:39:14:39:44 | call to append | semmle.label | call to append | +| SanitizingDoubleDash.go:39:31:39:37 | tainted | semmle.label | tainted | | SanitizingDoubleDash.go:40:23:40:30 | arrayLit | semmle.label | arrayLit | +| SanitizingDoubleDash.go:53:14:53:35 | call to append | semmle.label | call to append | +| SanitizingDoubleDash.go:53:21:53:28 | arrayLit | semmle.label | arrayLit | | SanitizingDoubleDash.go:54:23:54:30 | arrayLit | semmle.label | arrayLit | +| SanitizingDoubleDash.go:68:14:68:38 | call to append | semmle.label | call to append | +| SanitizingDoubleDash.go:68:31:68:37 | tainted | semmle.label | tainted | +| SanitizingDoubleDash.go:69:14:69:35 | call to append | semmle.label | call to append | +| SanitizingDoubleDash.go:69:21:69:28 | arrayLit | semmle.label | arrayLit | | SanitizingDoubleDash.go:70:23:70:30 | arrayLit | semmle.label | arrayLit | | SanitizingDoubleDash.go:80:23:80:29 | tainted | semmle.label | tainted | | SanitizingDoubleDash.go:92:13:92:19 | selection of URL | semmle.label | selection of URL | @@ -58,11 +88,25 @@ nodes | SanitizingDoubleDash.go:105:15:105:37 | slice literal [array] | semmle.label | slice literal [array] | | SanitizingDoubleDash.go:105:30:105:36 | tainted | semmle.label | tainted | | SanitizingDoubleDash.go:106:24:106:31 | arrayLit | semmle.label | arrayLit | +| SanitizingDoubleDash.go:111:14:111:44 | call to append | semmle.label | call to append | +| SanitizingDoubleDash.go:111:37:111:43 | tainted | semmle.label | tainted | | SanitizingDoubleDash.go:112:24:112:31 | arrayLit | semmle.label | arrayLit | +| SanitizingDoubleDash.go:117:14:117:44 | call to append | semmle.label | call to append | +| SanitizingDoubleDash.go:117:31:117:37 | tainted | semmle.label | tainted | | SanitizingDoubleDash.go:118:24:118:31 | arrayLit | semmle.label | arrayLit | +| SanitizingDoubleDash.go:123:14:123:38 | call to append | semmle.label | call to append | +| SanitizingDoubleDash.go:123:31:123:37 | tainted | semmle.label | tainted | | SanitizingDoubleDash.go:124:24:124:31 | arrayLit | semmle.label | arrayLit | +| SanitizingDoubleDash.go:129:14:129:35 | call to append | semmle.label | call to append | +| SanitizingDoubleDash.go:129:21:129:28 | arrayLit | semmle.label | arrayLit | | SanitizingDoubleDash.go:130:24:130:31 | arrayLit | semmle.label | arrayLit | +| SanitizingDoubleDash.go:136:14:136:38 | call to append | semmle.label | call to append | +| SanitizingDoubleDash.go:136:31:136:37 | tainted | semmle.label | tainted | | SanitizingDoubleDash.go:137:24:137:31 | arrayLit | semmle.label | arrayLit | +| SanitizingDoubleDash.go:142:14:142:38 | call to append | semmle.label | call to append | +| SanitizingDoubleDash.go:142:31:142:37 | tainted | semmle.label | tainted | +| SanitizingDoubleDash.go:143:14:143:35 | call to append | semmle.label | call to append | +| SanitizingDoubleDash.go:143:21:143:28 | arrayLit | semmle.label | arrayLit | | SanitizingDoubleDash.go:144:24:144:31 | arrayLit | semmle.label | arrayLit | | SanitizingDoubleDash.go:148:30:148:36 | tainted | semmle.label | tainted | | SanitizingDoubleDash.go:152:24:152:30 | tainted | semmle.label | tainted | diff --git a/go/ql/test/query-tests/Security/CWE-078/StoredCommand.expected b/go/ql/test/query-tests/Security/CWE-078/StoredCommand.expected index ea667480966..e0e028fff91 100644 --- a/go/ql/test/query-tests/Security/CWE-078/StoredCommand.expected +++ b/go/ql/test/query-tests/Security/CWE-078/StoredCommand.expected @@ -1,7 +1,12 @@ edges -| StoredCommand.go:11:2:11:27 | ... := ...[0] | StoredCommand.go:14:22:14:28 | cmdName | +| StoredCommand.go:11:2:11:27 | ... := ...[0] | StoredCommand.go:13:2:13:5 | rows | +| StoredCommand.go:13:2:13:5 | rows | StoredCommand.go:13:12:13:19 | &... | +| StoredCommand.go:13:12:13:19 | &... | StoredCommand.go:13:12:13:19 | &... | +| StoredCommand.go:13:12:13:19 | &... | StoredCommand.go:14:22:14:28 | cmdName | nodes | StoredCommand.go:11:2:11:27 | ... := ...[0] | semmle.label | ... := ...[0] | +| StoredCommand.go:13:2:13:5 | rows | semmle.label | rows | +| StoredCommand.go:13:12:13:19 | &... | semmle.label | &... | | StoredCommand.go:14:22:14:28 | cmdName | semmle.label | cmdName | subpaths #select diff --git a/go/ql/test/query-tests/Security/CWE-079/ReflectedXss.expected b/go/ql/test/query-tests/Security/CWE-079/ReflectedXss.expected index 4f3ee95ffe8..31a8d097158 100644 --- a/go/ql/test/query-tests/Security/CWE-079/ReflectedXss.expected +++ b/go/ql/test/query-tests/Security/CWE-079/ReflectedXss.expected @@ -9,19 +9,27 @@ edges | contenttype.go:73:10:73:28 | call to FormValue | contenttype.go:79:11:79:14 | data | | contenttype.go:88:10:88:28 | call to FormValue | contenttype.go:91:4:91:7 | data | | contenttype.go:113:10:113:28 | call to FormValue | contenttype.go:114:50:114:53 | data | -| reflectedxsstest.go:27:2:27:38 | ... := ...[0] | reflectedxsstest.go:28:10:28:57 | type conversion | +| reflectedxsstest.go:27:2:27:38 | ... := ...[0] | reflectedxsstest.go:28:50:28:55 | cookie | +| reflectedxsstest.go:28:17:28:56 | call to Sprintf | reflectedxsstest.go:28:10:28:57 | type conversion | +| reflectedxsstest.go:28:50:28:55 | cookie | reflectedxsstest.go:28:17:28:56 | call to Sprintf | | reflectedxsstest.go:31:2:31:44 | ... := ...[0] | reflectedxsstest.go:32:34:32:37 | file | -| reflectedxsstest.go:31:2:31:44 | ... := ...[1] | reflectedxsstest.go:34:10:34:62 | type conversion | -| reflectedxsstest.go:32:2:32:38 | ... := ...[0] | reflectedxsstest.go:33:10:33:57 | type conversion | +| reflectedxsstest.go:31:2:31:44 | ... := ...[1] | reflectedxsstest.go:34:46:34:60 | selection of Filename | +| reflectedxsstest.go:32:2:32:38 | ... := ...[0] | reflectedxsstest.go:33:49:33:55 | content | | reflectedxsstest.go:32:34:32:37 | file | reflectedxsstest.go:32:2:32:38 | ... := ...[0] | +| reflectedxsstest.go:33:17:33:56 | call to Sprintf | reflectedxsstest.go:33:10:33:57 | type conversion | +| reflectedxsstest.go:33:49:33:55 | content | reflectedxsstest.go:33:17:33:56 | call to Sprintf | +| reflectedxsstest.go:34:17:34:61 | call to Sprintf | reflectedxsstest.go:34:10:34:62 | type conversion | +| reflectedxsstest.go:34:46:34:60 | selection of Filename | reflectedxsstest.go:34:17:34:61 | call to Sprintf | | reflectedxsstest.go:38:2:38:35 | ... := ...[0] | reflectedxsstest.go:39:16:39:21 | reader | | reflectedxsstest.go:39:2:39:32 | ... := ...[0] | reflectedxsstest.go:40:14:40:17 | part | | reflectedxsstest.go:39:2:39:32 | ... := ...[0] | reflectedxsstest.go:42:2:42:5 | part | | reflectedxsstest.go:39:16:39:21 | reader | reflectedxsstest.go:39:2:39:32 | ... := ...[0] | | reflectedxsstest.go:40:14:40:17 | part | reflectedxsstest.go:40:14:40:28 | call to FileName | -| reflectedxsstest.go:40:14:40:28 | call to FileName | reflectedxsstest.go:44:10:44:55 | type conversion | +| reflectedxsstest.go:40:14:40:28 | call to FileName | reflectedxsstest.go:44:46:44:53 | partName | | reflectedxsstest.go:41:2:41:10 | definition of byteSlice | reflectedxsstest.go:45:10:45:18 | byteSlice | | reflectedxsstest.go:42:2:42:5 | part | reflectedxsstest.go:41:2:41:10 | definition of byteSlice | +| reflectedxsstest.go:44:17:44:54 | call to Sprintf | reflectedxsstest.go:44:10:44:55 | type conversion | +| reflectedxsstest.go:44:46:44:53 | partName | reflectedxsstest.go:44:17:44:54 | call to Sprintf | | reflectedxsstest.go:51:14:51:18 | selection of URL | reflectedxsstest.go:51:14:51:26 | call to Query | | reflectedxsstest.go:51:14:51:26 | call to Query | reflectedxsstest.go:54:11:54:21 | type conversion | | tst.go:14:15:14:20 | selection of Form | tst.go:14:15:14:36 | call to Get | @@ -56,12 +64,18 @@ nodes | contenttype.go:114:50:114:53 | data | semmle.label | data | | reflectedxsstest.go:27:2:27:38 | ... := ...[0] | semmle.label | ... := ...[0] | | reflectedxsstest.go:28:10:28:57 | type conversion | semmle.label | type conversion | +| reflectedxsstest.go:28:17:28:56 | call to Sprintf | semmle.label | call to Sprintf | +| reflectedxsstest.go:28:50:28:55 | cookie | semmle.label | cookie | | reflectedxsstest.go:31:2:31:44 | ... := ...[0] | semmle.label | ... := ...[0] | | reflectedxsstest.go:31:2:31:44 | ... := ...[1] | semmle.label | ... := ...[1] | | reflectedxsstest.go:32:2:32:38 | ... := ...[0] | semmle.label | ... := ...[0] | | reflectedxsstest.go:32:34:32:37 | file | semmle.label | file | | reflectedxsstest.go:33:10:33:57 | type conversion | semmle.label | type conversion | +| reflectedxsstest.go:33:17:33:56 | call to Sprintf | semmle.label | call to Sprintf | +| reflectedxsstest.go:33:49:33:55 | content | semmle.label | content | | reflectedxsstest.go:34:10:34:62 | type conversion | semmle.label | type conversion | +| reflectedxsstest.go:34:17:34:61 | call to Sprintf | semmle.label | call to Sprintf | +| reflectedxsstest.go:34:46:34:60 | selection of Filename | semmle.label | selection of Filename | | reflectedxsstest.go:38:2:38:35 | ... := ...[0] | semmle.label | ... := ...[0] | | reflectedxsstest.go:39:2:39:32 | ... := ...[0] | semmle.label | ... := ...[0] | | reflectedxsstest.go:39:16:39:21 | reader | semmle.label | reader | @@ -70,6 +84,8 @@ nodes | reflectedxsstest.go:41:2:41:10 | definition of byteSlice | semmle.label | definition of byteSlice | | reflectedxsstest.go:42:2:42:5 | part | semmle.label | part | | reflectedxsstest.go:44:10:44:55 | type conversion | semmle.label | type conversion | +| reflectedxsstest.go:44:17:44:54 | call to Sprintf | semmle.label | call to Sprintf | +| reflectedxsstest.go:44:46:44:53 | partName | semmle.label | partName | | reflectedxsstest.go:45:10:45:18 | byteSlice | semmle.label | byteSlice | | reflectedxsstest.go:51:14:51:18 | selection of URL | semmle.label | selection of URL | | reflectedxsstest.go:51:14:51:26 | call to Query | semmle.label | call to Query | diff --git a/go/ql/test/query-tests/Security/CWE-079/StoredXss.expected b/go/ql/test/query-tests/Security/CWE-079/StoredXss.expected index 1e513a5d4a9..f51eda4c93c 100644 --- a/go/ql/test/query-tests/Security/CWE-079/StoredXss.expected +++ b/go/ql/test/query-tests/Security/CWE-079/StoredXss.expected @@ -1,11 +1,16 @@ edges | StoredXss.go:13:21:13:31 | call to Name | StoredXss.go:13:21:13:36 | ...+... | -| stored.go:18:3:18:28 | ... := ...[0] | stored.go:30:22:30:25 | name | +| stored.go:18:3:18:28 | ... := ...[0] | stored.go:25:14:25:17 | rows | +| stored.go:25:14:25:17 | rows | stored.go:25:29:25:33 | &... | +| stored.go:25:29:25:33 | &... | stored.go:25:29:25:33 | &... | +| stored.go:25:29:25:33 | &... | stored.go:30:22:30:25 | name | | stored.go:59:30:59:33 | definition of path | stored.go:61:22:61:25 | path | nodes | StoredXss.go:13:21:13:31 | call to Name | semmle.label | call to Name | | StoredXss.go:13:21:13:36 | ...+... | semmle.label | ...+... | | stored.go:18:3:18:28 | ... := ...[0] | semmle.label | ... := ...[0] | +| stored.go:25:14:25:17 | rows | semmle.label | rows | +| stored.go:25:29:25:33 | &... | semmle.label | &... | | stored.go:30:22:30:25 | name | semmle.label | name | | stored.go:59:30:59:33 | definition of path | semmle.label | definition of path | | stored.go:61:22:61:25 | path | semmle.label | path | diff --git a/go/ql/test/query-tests/Security/CWE-089/SqlInjection.expected b/go/ql/test/query-tests/Security/CWE-089/SqlInjection.expected index 93d447e40ab..c1ded7d69f8 100644 --- a/go/ql/test/query-tests/Security/CWE-089/SqlInjection.expected +++ b/go/ql/test/query-tests/Security/CWE-089/SqlInjection.expected @@ -1,21 +1,30 @@ edges +| SqlInjection.go:10:7:11:30 | call to Sprintf | SqlInjection.go:12:11:12:11 | q | | SqlInjection.go:11:3:11:9 | selection of URL | SqlInjection.go:11:3:11:17 | call to Query | -| SqlInjection.go:11:3:11:17 | call to Query | SqlInjection.go:12:11:12:11 | q | +| SqlInjection.go:11:3:11:17 | call to Query | SqlInjection.go:11:3:11:29 | index expression | +| SqlInjection.go:11:3:11:29 | index expression | SqlInjection.go:10:7:11:30 | call to Sprintf | | issue48.go:17:2:17:33 | ... := ...[0] | issue48.go:18:17:18:17 | b | | issue48.go:17:25:17:32 | selection of Body | issue48.go:17:2:17:33 | ... := ...[0] | | issue48.go:18:17:18:17 | b | issue48.go:18:20:18:39 | &... | -| issue48.go:18:20:18:39 | &... | issue48.go:22:11:22:12 | q3 | +| issue48.go:18:20:18:39 | &... | issue48.go:21:3:21:33 | index expression | +| issue48.go:20:8:21:34 | call to Sprintf | issue48.go:22:11:22:12 | q3 | +| issue48.go:21:3:21:33 | index expression | issue48.go:20:8:21:34 | call to Sprintf | | issue48.go:27:2:27:34 | ... := ...[0] | issue48.go:28:17:28:18 | b2 | | issue48.go:27:26:27:33 | selection of Body | issue48.go:27:2:27:34 | ... := ...[0] | | issue48.go:28:17:28:18 | b2 | issue48.go:28:21:28:41 | &... | -| issue48.go:28:21:28:41 | &... | issue48.go:32:11:32:12 | q4 | +| issue48.go:28:21:28:41 | &... | issue48.go:31:3:31:31 | selection of Category | +| issue48.go:30:8:31:32 | call to Sprintf | issue48.go:32:11:32:12 | q4 | +| issue48.go:31:3:31:31 | selection of Category | issue48.go:30:8:31:32 | call to Sprintf | | issue48.go:37:17:37:50 | type conversion | issue48.go:37:53:37:73 | &... | | issue48.go:37:24:37:30 | selection of URL | issue48.go:37:24:37:38 | call to Query | | issue48.go:37:24:37:38 | call to Query | issue48.go:37:17:37:50 | type conversion | -| issue48.go:37:53:37:73 | &... | issue48.go:41:11:41:12 | q5 | +| issue48.go:37:53:37:73 | &... | issue48.go:40:3:40:31 | selection of Category | +| issue48.go:39:8:40:32 | call to Sprintf | issue48.go:41:11:41:12 | q5 | +| issue48.go:40:3:40:31 | selection of Category | issue48.go:39:8:40:32 | call to Sprintf | | main.go:10:11:10:16 | selection of Form | main.go:10:11:10:28 | index expression | | main.go:14:63:14:67 | selection of URL | main.go:14:63:14:75 | call to Query | -| main.go:14:63:14:75 | call to Query | main.go:14:11:14:84 | call to Sprintf | +| main.go:14:63:14:75 | call to Query | main.go:14:63:14:83 | index expression | +| main.go:14:63:14:83 | index expression | main.go:14:11:14:84 | call to Sprintf | | main.go:15:63:15:70 | selection of Header | main.go:15:63:15:84 | call to Get | | main.go:15:63:15:84 | call to Get | main.go:15:11:15:85 | call to Sprintf | | main.go:27:17:30:2 | &... [pointer, Category] | main.go:33:3:33:13 | RequestData [pointer, Category] | @@ -23,9 +32,10 @@ edges | main.go:29:13:29:19 | selection of URL | main.go:29:13:29:27 | call to Query | | main.go:29:13:29:27 | call to Query | main.go:29:13:29:39 | index expression | | main.go:29:13:29:39 | index expression | main.go:27:18:30:2 | struct literal [Category] | +| main.go:32:7:33:23 | call to Sprintf | main.go:34:11:34:11 | q | | main.go:33:3:33:13 | RequestData [pointer, Category] | main.go:33:3:33:13 | implicit dereference [Category] | | main.go:33:3:33:13 | implicit dereference [Category] | main.go:33:3:33:22 | selection of Category | -| main.go:33:3:33:22 | selection of Category | main.go:34:11:34:11 | q | +| main.go:33:3:33:22 | selection of Category | main.go:32:7:33:23 | call to Sprintf | | main.go:38:2:38:12 | definition of RequestData [pointer, Category] | main.go:39:2:39:12 | RequestData [pointer, Category] | | main.go:38:2:38:12 | definition of RequestData [pointer, Category] | main.go:42:3:42:13 | RequestData [pointer, Category] | | main.go:39:2:39:12 | RequestData [pointer, Category] | main.go:39:2:39:12 | implicit dereference [Category] | @@ -33,9 +43,10 @@ edges | main.go:39:25:39:31 | selection of URL | main.go:39:25:39:39 | call to Query | | main.go:39:25:39:39 | call to Query | main.go:39:25:39:51 | index expression | | main.go:39:25:39:51 | index expression | main.go:39:2:39:12 | implicit dereference [Category] | +| main.go:41:7:42:23 | call to Sprintf | main.go:43:11:43:11 | q | | main.go:42:3:42:13 | RequestData [pointer, Category] | main.go:42:3:42:13 | implicit dereference [Category] | | main.go:42:3:42:13 | implicit dereference [Category] | main.go:42:3:42:22 | selection of Category | -| main.go:42:3:42:22 | selection of Category | main.go:43:11:43:11 | q | +| main.go:42:3:42:22 | selection of Category | main.go:41:7:42:23 | call to Sprintf | | main.go:47:2:47:12 | definition of RequestData [pointer, Category] | main.go:48:4:48:14 | RequestData [pointer, Category] | | main.go:47:2:47:12 | definition of RequestData [pointer, Category] | main.go:51:3:51:13 | RequestData [pointer, Category] | | main.go:48:3:48:14 | star expression [Category] | main.go:47:2:47:12 | definition of RequestData [pointer, Category] | @@ -43,9 +54,10 @@ edges | main.go:48:28:48:34 | selection of URL | main.go:48:28:48:42 | call to Query | | main.go:48:28:48:42 | call to Query | main.go:48:28:48:54 | index expression | | main.go:48:28:48:54 | index expression | main.go:48:3:48:14 | star expression [Category] | +| main.go:50:7:51:23 | call to Sprintf | main.go:52:11:52:11 | q | | main.go:51:3:51:13 | RequestData [pointer, Category] | main.go:51:3:51:13 | implicit dereference [Category] | | main.go:51:3:51:13 | implicit dereference [Category] | main.go:51:3:51:22 | selection of Category | -| main.go:51:3:51:22 | selection of Category | main.go:52:11:52:11 | q | +| main.go:51:3:51:22 | selection of Category | main.go:50:7:51:23 | call to Sprintf | | main.go:56:2:56:12 | definition of RequestData [pointer, Category] | main.go:57:4:57:14 | RequestData [pointer, Category] | | main.go:56:2:56:12 | definition of RequestData [pointer, Category] | main.go:60:5:60:15 | RequestData [pointer, Category] | | main.go:57:3:57:14 | star expression [Category] | main.go:56:2:56:12 | definition of RequestData [pointer, Category] | @@ -53,7 +65,8 @@ edges | main.go:57:28:57:34 | selection of URL | main.go:57:28:57:42 | call to Query | | main.go:57:28:57:42 | call to Query | main.go:57:28:57:54 | index expression | | main.go:57:28:57:54 | index expression | main.go:57:3:57:14 | star expression [Category] | -| main.go:60:3:60:25 | selection of Category | main.go:61:11:61:11 | q | +| main.go:59:7:60:26 | call to Sprintf | main.go:61:11:61:11 | q | +| main.go:60:3:60:25 | selection of Category | main.go:59:7:60:26 | call to Sprintf | | main.go:60:4:60:15 | star expression [Category] | main.go:60:3:60:25 | selection of Category | | main.go:60:5:60:15 | RequestData [pointer, Category] | main.go:60:4:60:15 | star expression [Category] | | mongoDB.go:40:20:40:30 | call to Referer | mongoDB.go:57:22:57:29 | pipeline | @@ -71,29 +84,38 @@ edges | mongoDB.go:40:20:40:30 | call to Referer | mongoDB.go:80:22:80:27 | filter | | mongoDB.go:40:20:40:30 | call to Referer | mongoDB.go:81:18:81:25 | pipeline | nodes +| SqlInjection.go:10:7:11:30 | call to Sprintf | semmle.label | call to Sprintf | | SqlInjection.go:11:3:11:9 | selection of URL | semmle.label | selection of URL | | SqlInjection.go:11:3:11:17 | call to Query | semmle.label | call to Query | +| SqlInjection.go:11:3:11:29 | index expression | semmle.label | index expression | | SqlInjection.go:12:11:12:11 | q | semmle.label | q | | issue48.go:17:2:17:33 | ... := ...[0] | semmle.label | ... := ...[0] | | issue48.go:17:25:17:32 | selection of Body | semmle.label | selection of Body | | issue48.go:18:17:18:17 | b | semmle.label | b | | issue48.go:18:20:18:39 | &... | semmle.label | &... | +| issue48.go:20:8:21:34 | call to Sprintf | semmle.label | call to Sprintf | +| issue48.go:21:3:21:33 | index expression | semmle.label | index expression | | issue48.go:22:11:22:12 | q3 | semmle.label | q3 | | issue48.go:27:2:27:34 | ... := ...[0] | semmle.label | ... := ...[0] | | issue48.go:27:26:27:33 | selection of Body | semmle.label | selection of Body | | issue48.go:28:17:28:18 | b2 | semmle.label | b2 | | issue48.go:28:21:28:41 | &... | semmle.label | &... | +| issue48.go:30:8:31:32 | call to Sprintf | semmle.label | call to Sprintf | +| issue48.go:31:3:31:31 | selection of Category | semmle.label | selection of Category | | issue48.go:32:11:32:12 | q4 | semmle.label | q4 | | issue48.go:37:17:37:50 | type conversion | semmle.label | type conversion | | issue48.go:37:24:37:30 | selection of URL | semmle.label | selection of URL | | issue48.go:37:24:37:38 | call to Query | semmle.label | call to Query | | issue48.go:37:53:37:73 | &... | semmle.label | &... | +| issue48.go:39:8:40:32 | call to Sprintf | semmle.label | call to Sprintf | +| issue48.go:40:3:40:31 | selection of Category | semmle.label | selection of Category | | issue48.go:41:11:41:12 | q5 | semmle.label | q5 | | main.go:10:11:10:16 | selection of Form | semmle.label | selection of Form | | main.go:10:11:10:28 | index expression | semmle.label | index expression | | main.go:14:11:14:84 | call to Sprintf | semmle.label | call to Sprintf | | main.go:14:63:14:67 | selection of URL | semmle.label | selection of URL | | main.go:14:63:14:75 | call to Query | semmle.label | call to Query | +| main.go:14:63:14:83 | index expression | semmle.label | index expression | | main.go:15:11:15:85 | call to Sprintf | semmle.label | call to Sprintf | | main.go:15:63:15:70 | selection of Header | semmle.label | selection of Header | | main.go:15:63:15:84 | call to Get | semmle.label | call to Get | @@ -102,6 +124,7 @@ nodes | main.go:29:13:29:19 | selection of URL | semmle.label | selection of URL | | main.go:29:13:29:27 | call to Query | semmle.label | call to Query | | main.go:29:13:29:39 | index expression | semmle.label | index expression | +| main.go:32:7:33:23 | call to Sprintf | semmle.label | call to Sprintf | | main.go:33:3:33:13 | RequestData [pointer, Category] | semmle.label | RequestData [pointer, Category] | | main.go:33:3:33:13 | implicit dereference [Category] | semmle.label | implicit dereference [Category] | | main.go:33:3:33:22 | selection of Category | semmle.label | selection of Category | @@ -112,6 +135,7 @@ nodes | main.go:39:25:39:31 | selection of URL | semmle.label | selection of URL | | main.go:39:25:39:39 | call to Query | semmle.label | call to Query | | main.go:39:25:39:51 | index expression | semmle.label | index expression | +| main.go:41:7:42:23 | call to Sprintf | semmle.label | call to Sprintf | | main.go:42:3:42:13 | RequestData [pointer, Category] | semmle.label | RequestData [pointer, Category] | | main.go:42:3:42:13 | implicit dereference [Category] | semmle.label | implicit dereference [Category] | | main.go:42:3:42:22 | selection of Category | semmle.label | selection of Category | @@ -122,6 +146,7 @@ nodes | main.go:48:28:48:34 | selection of URL | semmle.label | selection of URL | | main.go:48:28:48:42 | call to Query | semmle.label | call to Query | | main.go:48:28:48:54 | index expression | semmle.label | index expression | +| main.go:50:7:51:23 | call to Sprintf | semmle.label | call to Sprintf | | main.go:51:3:51:13 | RequestData [pointer, Category] | semmle.label | RequestData [pointer, Category] | | main.go:51:3:51:13 | implicit dereference [Category] | semmle.label | implicit dereference [Category] | | main.go:51:3:51:22 | selection of Category | semmle.label | selection of Category | @@ -132,6 +157,7 @@ nodes | main.go:57:28:57:34 | selection of URL | semmle.label | selection of URL | | main.go:57:28:57:42 | call to Query | semmle.label | call to Query | | main.go:57:28:57:54 | index expression | semmle.label | index expression | +| main.go:59:7:60:26 | call to Sprintf | semmle.label | call to Sprintf | | main.go:60:3:60:25 | selection of Category | semmle.label | selection of Category | | main.go:60:4:60:15 | star expression [Category] | semmle.label | star expression [Category] | | main.go:60:5:60:15 | RequestData [pointer, Category] | semmle.label | RequestData [pointer, Category] | diff --git a/go/ql/test/query-tests/Security/CWE-117/LogInjectionTest.expected b/go/ql/test/query-tests/Security/CWE-117/LogInjectionTest.expected index e69de29bb2d..48de9172b36 100644 --- a/go/ql/test/query-tests/Security/CWE-117/LogInjectionTest.expected +++ b/go/ql/test/query-tests/Security/CWE-117/LogInjectionTest.expected @@ -0,0 +1,2 @@ +failures +testFailures diff --git a/go/ql/test/query-tests/Security/CWE-117/LogInjectionTest.ql b/go/ql/test/query-tests/Security/CWE-117/LogInjectionTest.ql index 576ce9ba342..298287ec4aa 100644 --- a/go/ql/test/query-tests/Security/CWE-117/LogInjectionTest.ql +++ b/go/ql/test/query-tests/Security/CWE-117/LogInjectionTest.ql @@ -1,11 +1,4 @@ import go import TestUtilities.InlineFlowTest import semmle.go.security.LogInjection - -class LogInjectionTest extends InlineFlowTest { - override DataFlow::Configuration getTaintFlowConfig() { - result = any(LogInjection::Configuration config) - } - - override DataFlow::Configuration getValueFlowConfig() { none() } -} +import TaintFlowTest diff --git a/go/ql/test/query-tests/Security/CWE-327/UnsafeTLS.expected b/go/ql/test/query-tests/Security/CWE-327/UnsafeTLS.expected index ba37534511a..e47a4768465 100644 --- a/go/ql/test/query-tests/Security/CWE-327/UnsafeTLS.expected +++ b/go/ql/test/query-tests/Security/CWE-327/UnsafeTLS.expected @@ -14,9 +14,18 @@ edges | UnsafeTLS.go:305:5:305:47 | selection of TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256 | UnsafeTLS.go:304:18:306:4 | slice literal | | UnsafeTLS.go:313:5:313:45 | selection of TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256 | UnsafeTLS.go:312:18:314:4 | slice literal | | UnsafeTLS.go:329:53:329:93 | selection of TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256 | UnsafeTLS.go:329:25:329:94 | call to append | -| UnsafeTLS.go:334:13:334:38 | call to InsecureCipherSuites | UnsafeTLS.go:336:26:336:58 | call to append | -| UnsafeTLS.go:342:13:342:38 | call to InsecureCipherSuites | UnsafeTLS.go:346:25:346:36 | cipherSuites | -| UnsafeTLS.go:351:13:351:38 | call to InsecureCipherSuites | UnsafeTLS.go:355:25:355:36 | cipherSuites | +| UnsafeTLS.go:334:13:334:38 | call to InsecureCipherSuites | UnsafeTLS.go:336:54:336:57 | selection of ID | +| UnsafeTLS.go:336:54:336:57 | selection of ID | UnsafeTLS.go:336:26:336:58 | call to append | +| UnsafeTLS.go:342:13:342:38 | call to InsecureCipherSuites | UnsafeTLS.go:344:40:344:43 | selection of ID | +| UnsafeTLS.go:344:19:344:44 | call to append | UnsafeTLS.go:344:26:344:37 | cipherSuites | +| UnsafeTLS.go:344:19:344:44 | call to append | UnsafeTLS.go:346:25:346:36 | cipherSuites | +| UnsafeTLS.go:344:26:344:37 | cipherSuites | UnsafeTLS.go:344:19:344:44 | call to append | +| UnsafeTLS.go:344:40:344:43 | selection of ID | UnsafeTLS.go:344:19:344:44 | call to append | +| UnsafeTLS.go:351:13:351:38 | call to InsecureCipherSuites | UnsafeTLS.go:353:40:353:51 | selection of ID | +| UnsafeTLS.go:353:19:353:52 | call to append | UnsafeTLS.go:353:26:353:37 | cipherSuites | +| UnsafeTLS.go:353:19:353:52 | call to append | UnsafeTLS.go:355:25:355:36 | cipherSuites | +| UnsafeTLS.go:353:26:353:37 | cipherSuites | UnsafeTLS.go:353:19:353:52 | call to append | +| UnsafeTLS.go:353:40:353:51 | selection of ID | UnsafeTLS.go:353:19:353:52 | call to append | | UnsafeTLS.go:363:5:363:47 | selection of TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256 | UnsafeTLS.go:362:18:364:4 | slice literal | | UnsafeTLS.go:371:5:371:47 | selection of TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256 | UnsafeTLS.go:370:18:372:4 | slice literal | | UnsafeTLS.go:379:5:379:47 | selection of TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256 | UnsafeTLS.go:378:18:380:4 | slice literal | @@ -94,9 +103,16 @@ nodes | UnsafeTLS.go:329:53:329:93 | selection of TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256 | semmle.label | selection of TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256 | | UnsafeTLS.go:334:13:334:38 | call to InsecureCipherSuites | semmle.label | call to InsecureCipherSuites | | UnsafeTLS.go:336:26:336:58 | call to append | semmle.label | call to append | +| UnsafeTLS.go:336:54:336:57 | selection of ID | semmle.label | selection of ID | | UnsafeTLS.go:342:13:342:38 | call to InsecureCipherSuites | semmle.label | call to InsecureCipherSuites | +| UnsafeTLS.go:344:19:344:44 | call to append | semmle.label | call to append | +| UnsafeTLS.go:344:26:344:37 | cipherSuites | semmle.label | cipherSuites | +| UnsafeTLS.go:344:40:344:43 | selection of ID | semmle.label | selection of ID | | UnsafeTLS.go:346:25:346:36 | cipherSuites | semmle.label | cipherSuites | | UnsafeTLS.go:351:13:351:38 | call to InsecureCipherSuites | semmle.label | call to InsecureCipherSuites | +| UnsafeTLS.go:353:19:353:52 | call to append | semmle.label | call to append | +| UnsafeTLS.go:353:26:353:37 | cipherSuites | semmle.label | cipherSuites | +| UnsafeTLS.go:353:40:353:51 | selection of ID | semmle.label | selection of ID | | UnsafeTLS.go:355:25:355:36 | cipherSuites | semmle.label | cipherSuites | | UnsafeTLS.go:362:18:364:4 | slice literal | semmle.label | slice literal | | UnsafeTLS.go:363:5:363:47 | selection of TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256 | semmle.label | selection of TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256 | diff --git a/go/ql/test/query-tests/Security/CWE-338/InsecureRandomness/InsecureRandomness.expected b/go/ql/test/query-tests/Security/CWE-338/InsecureRandomness/InsecureRandomness.expected index 59af6f28787..e1e037af120 100644 --- a/go/ql/test/query-tests/Security/CWE-338/InsecureRandomness/InsecureRandomness.expected +++ b/go/ql/test/query-tests/Security/CWE-338/InsecureRandomness/InsecureRandomness.expected @@ -1,6 +1,8 @@ edges -| sample.go:15:24:15:63 | type conversion | sample.go:16:9:16:15 | slice expression | -| sample.go:15:49:15:61 | call to Uint32 | sample.go:15:24:15:63 | type conversion | +| sample.go:15:10:15:64 | call to Sum256 | sample.go:16:9:16:15 | slice expression | +| sample.go:15:24:15:63 | type conversion | sample.go:15:10:15:64 | call to Sum256 | +| sample.go:15:31:15:62 | call to Sprintf | sample.go:15:24:15:63 | type conversion | +| sample.go:15:49:15:61 | call to Uint32 | sample.go:15:31:15:62 | call to Sprintf | | sample.go:16:9:16:15 | slice expression | sample.go:26:25:26:30 | call to Guid | | sample.go:33:2:33:6 | definition of nonce | sample.go:37:25:37:29 | nonce | | sample.go:33:2:33:6 | definition of nonce | sample.go:37:32:37:36 | nonce | @@ -8,7 +10,9 @@ edges | sample.go:35:14:35:19 | random | sample.go:33:2:33:6 | definition of nonce | nodes | InsecureRandomness.go:12:18:12:40 | call to Intn | semmle.label | call to Intn | +| sample.go:15:10:15:64 | call to Sum256 | semmle.label | call to Sum256 | | sample.go:15:24:15:63 | type conversion | semmle.label | type conversion | +| sample.go:15:31:15:62 | call to Sprintf | semmle.label | call to Sprintf | | sample.go:15:49:15:61 | call to Uint32 | semmle.label | call to Uint32 | | sample.go:16:9:16:15 | slice expression | semmle.label | slice expression | | sample.go:26:25:26:30 | call to Guid | semmle.label | call to Guid | diff --git a/go/ql/test/query-tests/Security/CWE-352/ConstantOauth2State.expected b/go/ql/test/query-tests/Security/CWE-352/ConstantOauth2State.expected index c21e28717c7..35b84bf249a 100644 --- a/go/ql/test/query-tests/Security/CWE-352/ConstantOauth2State.expected +++ b/go/ql/test/query-tests/Security/CWE-352/ConstantOauth2State.expected @@ -17,7 +17,8 @@ edges | ConstantOauth2State.go:210:9:210:42 | call to AuthCodeURL | ConstantOauth2State.go:211:54:211:56 | url | | ConstantOauth2State.go:232:9:232:42 | call to AuthCodeURL | ConstantOauth2State.go:233:28:233:30 | url | | ConstantOauth2State.go:239:17:239:39 | "http://localhost:8080" | ConstantOauth2State.go:249:9:249:12 | conf | -| ConstantOauth2State.go:256:38:256:60 | "http://localhost:8080" | ConstantOauth2State.go:266:9:266:12 | conf | +| ConstantOauth2State.go:256:17:256:67 | call to Sprintf | ConstantOauth2State.go:266:9:266:12 | conf | +| ConstantOauth2State.go:256:38:256:60 | "http://localhost:8080" | ConstantOauth2State.go:256:17:256:67 | call to Sprintf | | ConstantOauth2State.go:272:17:272:21 | "oob" | ConstantOauth2State.go:282:9:282:12 | conf | nodes | ConstantOauth2State.go:20:26:20:32 | "state" | semmle.label | "state" | @@ -46,6 +47,7 @@ nodes | ConstantOauth2State.go:239:17:239:39 | "http://localhost:8080" | semmle.label | "http://localhost:8080" | | ConstantOauth2State.go:249:9:249:12 | conf | semmle.label | conf | | ConstantOauth2State.go:249:26:249:41 | stateStringConst | semmle.label | stateStringConst | +| ConstantOauth2State.go:256:17:256:67 | call to Sprintf | semmle.label | call to Sprintf | | ConstantOauth2State.go:256:38:256:60 | "http://localhost:8080" | semmle.label | "http://localhost:8080" | | ConstantOauth2State.go:266:9:266:12 | conf | semmle.label | conf | | ConstantOauth2State.go:266:26:266:41 | stateStringConst | semmle.label | stateStringConst | diff --git a/java/documentation/library-coverage/coverage.csv b/java/documentation/library-coverage/coverage.csv index abe364cb2b8..02d4c5aa4c8 100644 --- a/java/documentation/library-coverage/coverage.csv +++ b/java/documentation/library-coverage/coverage.csv @@ -56,8 +56,8 @@ jakarta.ws.rs.core,2,,149,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,2,,,,,,94,55 java.awt,,,3,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,3 java.beans,,,1,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,1, java.io,49,,45,,,22,,,,,,,,,,,,,,27,,,,,,,,,,,,,,,,,,,43,2 -java.lang,18,,92,,,,,,,,,,,,,,8,,,5,,,4,,,1,,,,,,,,,,,,,56,36 -java.net,13,3,20,,,,,,,,,,,,,,,,,,,,,,,,,,13,,,,,,,,,3,20, +java.lang,31,,92,,13,,,,,,,,,,,,8,,,5,,,4,,,1,,,,,,,,,,,,,56,36 +java.net,13,3,21,,,,,,,,,,,,,,,,,,,,,,,,,,13,,,,,,,,,3,21, java.nio,47,,35,,,3,,,,,,,,,,,,,,44,,,,,,,,,,,,,,,,,,,35, java.sql,13,,2,,,,,,,,,,,,,,,,,,,,,,,,,,4,,9,,,,,,,,2, java.util,44,,484,,,,,,,,,,,,,,34,,,,,,,5,2,,1,2,,,,,,,,,,,44,440 @@ -90,11 +90,13 @@ org.apache.commons.codec,,,6,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,6, org.apache.commons.collections,,,800,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,17,783 org.apache.commons.collections4,,,800,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,17,783 org.apache.commons.compress.archivers.tar,,,4,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,4, +org.apache.commons.exec,6,,,,6,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, org.apache.commons.httpclient.util,,,1,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,1, org.apache.commons.io,111,,560,,,2,,,,,,,,,,,,,,94,,,,,,,,,15,,,,,,,,,,546,14 org.apache.commons.jelly,6,,,,,,,,,,,,,,,,,,,,,,,,,,,,6,,,,,,,,,,, org.apache.commons.jexl2,15,,,,,,,,,,,,15,,,,,,,,,,,,,,,,,,,,,,,,,,, org.apache.commons.jexl3,15,,,,,,,,,,,,15,,,,,,,,,,,,,,,,,,,,,,,,,,, +org.apache.commons.lang,,,765,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,594,171 org.apache.commons.lang3,6,,424,,,,,,,,,,,,,,,,,,,6,,,,,,,,,,,,,,,,,293,131 org.apache.commons.logging,6,,,,,,,,,,,,,,,,6,,,,,,,,,,,,,,,,,,,,,,, org.apache.commons.net,9,12,,,,,,,,,,,,,,,,,,3,,,,,,,,,6,,,,,,,,,12,, @@ -136,7 +138,7 @@ org.jenkins.ui.icon,,,42,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,41,1 org.jenkins.ui.symbol,,,32,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,24,8 org.jooq,1,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,1,,,,,,,,, org.json,,,236,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,198,38 -org.kohsuke.stapler,3,,343,,,,,,,,,,,,,,,,,1,,,,,,,,,1,,,,1,,,,,,332,11 +org.kohsuke.stapler,20,24,343,,,,,,,2,,,,,,,,,,9,,,,,,,,,4,,,,5,,,,,24,332,11 org.mvel2,16,,,,,,,,,,,,,,,,,16,,,,,,,,,,,,,,,,,,,,,, org.openjdk.jmh.runner.options,1,,,,,,,,,,,,,,,,,,,1,,,,,,,,,,,,,,,,,,,, org.scijava.log,13,,,,,,,,,,,,,,,,13,,,,,,,,,,,,,,,,,,,,,,, diff --git a/java/documentation/library-coverage/coverage.rst b/java/documentation/library-coverage/coverage.rst index ebcd040ceb8..806100ff6d9 100644 --- a/java/documentation/library-coverage/coverage.rst +++ b/java/documentation/library-coverage/coverage.rst @@ -18,10 +18,10 @@ Java framework & library support `Google Guava `_,``com.google.common.*``,,730,41,7,,,,, JBoss Logging,``org.jboss.logging``,,,324,,,,,, `JSON-java `_,``org.json``,,236,,,,,,, - Java Standard Library,``java.*``,3,682,184,76,,9,,,17 + Java Standard Library,``java.*``,3,683,197,76,,9,,,17 Java extensions,"``javax.*``, ``jakarta.*``",63,611,34,2,4,,1,1,2 Kotlin Standard Library,``kotlin*``,,1847,16,14,,,,,2 `Spring `_,``org.springframework.*``,29,483,115,4,,28,14,,35 - Others,"``antlr``, ``cn.hutool.core.codec``, ``com.alibaba.druid.sql``, ``com.esotericsoftware.kryo.io``, ``com.esotericsoftware.kryo5.io``, ``com.fasterxml.jackson.core``, ``com.fasterxml.jackson.databind``, ``com.google.gson``, ``com.hubspot.jinjava``, ``com.jcraft.jsch``, ``com.mitchellbosecke.pebble``, ``com.opensymphony.xwork2.ognl``, ``com.rabbitmq.client``, ``com.thoughtworks.xstream``, ``com.unboundid.ldap.sdk``, ``com.zaxxer.hikari``, ``flexjson``, ``freemarker.cache``, ``freemarker.template``, ``groovy.lang``, ``groovy.text``, ``groovy.util``, ``hudson``, ``io.jsonwebtoken``, ``io.netty.bootstrap``, ``io.netty.buffer``, ``io.netty.channel``, ``io.netty.handler.codec``, ``io.netty.handler.ssl``, ``io.netty.handler.stream``, ``io.netty.resolver``, ``io.netty.util``, ``javafx.scene.web``, ``jenkins``, ``jodd.json``, ``net.sf.json``, ``net.sf.saxon.s9api``, ``ognl``, ``okhttp3``, ``org.acegisecurity``, ``org.antlr.runtime``, ``org.apache.commons.codec``, ``org.apache.commons.compress.archivers.tar``, ``org.apache.commons.httpclient.util``, ``org.apache.commons.jelly``, ``org.apache.commons.jexl2``, ``org.apache.commons.jexl3``, ``org.apache.commons.logging``, ``org.apache.commons.net``, ``org.apache.commons.ognl``, ``org.apache.directory.ldap.client.api``, ``org.apache.hadoop.fs``, ``org.apache.hadoop.hive.metastore``, ``org.apache.hc.client5.http.async.methods``, ``org.apache.hc.client5.http.classic.methods``, ``org.apache.hc.client5.http.fluent``, ``org.apache.hive.hcatalog.templeton``, ``org.apache.ibatis.jdbc``, ``org.apache.log4j``, ``org.apache.shiro.codec``, ``org.apache.shiro.jndi``, ``org.apache.tools.ant``, ``org.apache.tools.zip``, ``org.apache.velocity.app``, ``org.apache.velocity.runtime``, ``org.codehaus.cargo.container.installer``, ``org.codehaus.groovy.control``, ``org.dom4j``, ``org.eclipse.jetty.client``, ``org.fusesource.leveldbjni``, ``org.geogebra.web.full.main``, ``org.hibernate``, ``org.influxdb``, ``org.jdbi.v3.core``, ``org.jenkins.ui.icon``, ``org.jenkins.ui.symbol``, ``org.jooq``, ``org.kohsuke.stapler``, ``org.mvel2``, ``org.openjdk.jmh.runner.options``, ``org.scijava.log``, ``org.slf4j``, ``org.thymeleaf``, ``org.xml.sax``, ``org.xmlpull.v1``, ``org.yaml.snakeyaml``, ``play.libs.ws``, ``play.mvc``, ``ratpack.core.form``, ``ratpack.core.handling``, ``ratpack.core.http``, ``ratpack.exec``, ``ratpack.form``, ``ratpack.func``, ``ratpack.handling``, ``ratpack.http``, ``ratpack.util``, ``retrofit2``",102,4467,554,81,4,18,18,,197 - Totals,,259,12766,2023,278,14,122,33,1,387 + Others,"``antlr``, ``cn.hutool.core.codec``, ``com.alibaba.druid.sql``, ``com.esotericsoftware.kryo.io``, ``com.esotericsoftware.kryo5.io``, ``com.fasterxml.jackson.core``, ``com.fasterxml.jackson.databind``, ``com.google.gson``, ``com.hubspot.jinjava``, ``com.jcraft.jsch``, ``com.mitchellbosecke.pebble``, ``com.opensymphony.xwork2.ognl``, ``com.rabbitmq.client``, ``com.thoughtworks.xstream``, ``com.unboundid.ldap.sdk``, ``com.zaxxer.hikari``, ``flexjson``, ``freemarker.cache``, ``freemarker.template``, ``groovy.lang``, ``groovy.text``, ``groovy.util``, ``hudson``, ``io.jsonwebtoken``, ``io.netty.bootstrap``, ``io.netty.buffer``, ``io.netty.channel``, ``io.netty.handler.codec``, ``io.netty.handler.ssl``, ``io.netty.handler.stream``, ``io.netty.resolver``, ``io.netty.util``, ``javafx.scene.web``, ``jenkins``, ``jodd.json``, ``net.sf.json``, ``net.sf.saxon.s9api``, ``ognl``, ``okhttp3``, ``org.acegisecurity``, ``org.antlr.runtime``, ``org.apache.commons.codec``, ``org.apache.commons.compress.archivers.tar``, ``org.apache.commons.exec``, ``org.apache.commons.httpclient.util``, ``org.apache.commons.jelly``, ``org.apache.commons.jexl2``, ``org.apache.commons.jexl3``, ``org.apache.commons.lang``, ``org.apache.commons.logging``, ``org.apache.commons.net``, ``org.apache.commons.ognl``, ``org.apache.directory.ldap.client.api``, ``org.apache.hadoop.fs``, ``org.apache.hadoop.hive.metastore``, ``org.apache.hc.client5.http.async.methods``, ``org.apache.hc.client5.http.classic.methods``, ``org.apache.hc.client5.http.fluent``, ``org.apache.hive.hcatalog.templeton``, ``org.apache.ibatis.jdbc``, ``org.apache.log4j``, ``org.apache.shiro.codec``, ``org.apache.shiro.jndi``, ``org.apache.tools.ant``, ``org.apache.tools.zip``, ``org.apache.velocity.app``, ``org.apache.velocity.runtime``, ``org.codehaus.cargo.container.installer``, ``org.codehaus.groovy.control``, ``org.dom4j``, ``org.eclipse.jetty.client``, ``org.fusesource.leveldbjni``, ``org.geogebra.web.full.main``, ``org.hibernate``, ``org.influxdb``, ``org.jdbi.v3.core``, ``org.jenkins.ui.icon``, ``org.jenkins.ui.symbol``, ``org.jooq``, ``org.kohsuke.stapler``, ``org.mvel2``, ``org.openjdk.jmh.runner.options``, ``org.scijava.log``, ``org.slf4j``, ``org.thymeleaf``, ``org.xml.sax``, ``org.xmlpull.v1``, ``org.yaml.snakeyaml``, ``play.libs.ws``, ``play.mvc``, ``ratpack.core.form``, ``ratpack.core.handling``, ``ratpack.core.http``, ``ratpack.exec``, ``ratpack.form``, ``ratpack.func``, ``ratpack.handling``, ``ratpack.http``, ``ratpack.util``, ``retrofit2``",126,5232,577,89,6,18,18,,200 + Totals,,283,13532,2059,286,16,122,33,1,390 diff --git a/java/downgrades/ecfcf050952e54b1155fc89525db84af6ad34aaf/old.dbscheme b/java/downgrades/ecfcf050952e54b1155fc89525db84af6ad34aaf/old.dbscheme new file mode 100644 index 00000000000..ecfcf050952 --- /dev/null +++ b/java/downgrades/ecfcf050952e54b1155fc89525db84af6ad34aaf/old.dbscheme @@ -0,0 +1,1256 @@ +/** + * 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 expanded arguments that were passed to the extractor for a + * compiler invocation. This is similar to `compilation_args`, but + * for a `@@@someFile` argument, it includes the arguments from that + * file, rather than just taking the argument literally. + */ +#keyset[id, num] +compilation_expanded_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_or_interfaces( + unique int id: @classorinterface, + string nodeName: string ref, + int parentid: @package ref, + int sourceid: @classorinterface ref +); + +file_class( + int id: @classorinterface ref +); + +class_object( + unique int id: @classorinterface ref, + unique int instance: @field ref +); + +type_companion_object( + unique int id: @classorinterface ref, + unique int instance: @field ref, + unique int companion_object: @classorinterface 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 + +isInterface( + unique int id: @classorinterface ref +); + +isRecord( + unique int id: @classorinterface 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: @classorinterface ref +); + +isAnnotElem( + int methodid: @method ref +); + +annotValue( + int parentid: @annotation ref, + int id2: @method ref, + unique int value: @expr ref +); + +isEnumType( + int classid: @classorinterface 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: @classorinterface 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: @classorinterface 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 | @classorinterface | @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; + +@classorinterfaceorpackage = @classorinterface | @package; +@classorinterfaceorcallable = @classorinterface | @callable; +@boundedtype = @typevariable | @wildcard; +@reftype = @classorinterface | @array | @boundedtype | @errortype; +@classorarray = @classorinterface | @array; +@type = @primitive | @reftype; +@callable = @method | @constructor; + +/** A program element that has a name. */ +@element = @package | @modifier | @annotation | @errortype | + @locatableElement; + +@locatableElement = @file | @primitive | @classorinterface | @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 = @classorinterface | @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 + // 3: ENUM_ENTRIES +) + +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: @classorinterface ref +) diff --git a/java/downgrades/ecfcf050952e54b1155fc89525db84af6ad34aaf/semmlecode.dbscheme b/java/downgrades/ecfcf050952e54b1155fc89525db84af6ad34aaf/semmlecode.dbscheme new file mode 100644 index 00000000000..7cbc85b1f3e --- /dev/null +++ b/java/downgrades/ecfcf050952e54b1155fc89525db84af6ad34aaf/semmlecode.dbscheme @@ -0,0 +1,1255 @@ +/** + * 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 expanded arguments that were passed to the extractor for a + * compiler invocation. This is similar to `compilation_args`, but + * for a `@@@someFile` argument, it includes the arguments from that + * file, rather than just taking the argument literally. + */ +#keyset[id, num] +compilation_expanded_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_or_interfaces( + unique int id: @classorinterface, + string nodeName: string ref, + int parentid: @package ref, + int sourceid: @classorinterface ref +); + +file_class( + int id: @classorinterface ref +); + +class_object( + unique int id: @classorinterface ref, + unique int instance: @field ref +); + +type_companion_object( + unique int id: @classorinterface ref, + unique int instance: @field ref, + unique int companion_object: @classorinterface 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 + +isInterface( + unique int id: @classorinterface ref +); + +isRecord( + unique int id: @classorinterface 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: @classorinterface ref +); + +isAnnotElem( + int methodid: @method ref +); + +annotValue( + int parentid: @annotation ref, + int id2: @method ref, + unique int value: @expr ref +); + +isEnumType( + int classid: @classorinterface 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: @classorinterface 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: @classorinterface 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 | @classorinterface | @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; + +@classorinterfaceorpackage = @classorinterface | @package; +@classorinterfaceorcallable = @classorinterface | @callable; +@boundedtype = @typevariable | @wildcard; +@reftype = @classorinterface | @array | @boundedtype | @errortype; +@classorarray = @classorinterface | @array; +@type = @primitive | @reftype; +@callable = @method | @constructor; + +/** A program element that has a name. */ +@element = @package | @modifier | @annotation | @errortype | + @locatableElement; + +@locatableElement = @file | @primitive | @classorinterface | @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 = @classorinterface | @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: @classorinterface ref +) diff --git a/java/downgrades/ecfcf050952e54b1155fc89525db84af6ad34aaf/upgrade.properties b/java/downgrades/ecfcf050952e54b1155fc89525db84af6ad34aaf/upgrade.properties new file mode 100644 index 00000000000..5ba64f71d70 --- /dev/null +++ b/java/downgrades/ecfcf050952e54b1155fc89525db84af6ad34aaf/upgrade.properties @@ -0,0 +1,2 @@ +description: Remove ENUM_ENTRIES +compatibility: full diff --git a/java/downgrades/qlpack.yml b/java/downgrades/qlpack.yml index b5a0f1bf411..6489ad2d9f6 100644 --- a/java/downgrades/qlpack.yml +++ b/java/downgrades/qlpack.yml @@ -2,3 +2,4 @@ name: codeql/java-downgrades groups: java downgrades: . library: true +warnOnImplicitThis: true diff --git a/java/kotlin-extractor/build.py b/java/kotlin-extractor/build.py index 2735f6af1c1..009017b0073 100755 --- a/java/kotlin-extractor/build.py +++ b/java/kotlin-extractor/build.py @@ -133,30 +133,6 @@ def find_sources(path): return glob.glob(path + '/**/*.kt', recursive=True) + glob.glob(path + '/**/*.java', recursive=True) -def get_kotlin_lib_folder(): - x = run_process([kotlinc, '-version', '-verbose'], capture_output=True) - output = x.stderr.decode(encoding='UTF-8', errors='strict') - m = re.match( - r'.*\nlogging: using Kotlin home directory ([^\n]+)\n.*', output) - if m is None: - raise Exception('Cannot determine kotlinc home directory') - kotlin_home = m.group(1) - print("Kotlin home directory: " + kotlin_home) - return kotlin_home + '/lib' - - -def get_gradle_lib_folder(): - x = run_process(['gradle', 'getHomeDir'], capture_output=True) - output = x.stdout.decode(encoding='UTF-8', errors='strict') - m = re.search(r'(?m)^> Task :getHomeDir\n([^\n]+)$', output) - if m is None: - print("gradle getHomeDir output:\n" + output, file=sys.stderr) - raise Exception('Cannot determine gradle home directory') - gradle_home = m.group(1) - print("Gradle home directory: " + gradle_home) - return gradle_home + '/lib' - - def find_jar(path, base): fn = path + '/' + base + '.jar' if not os.path.isfile(fn): diff --git a/java/kotlin-extractor/src/main/kotlin/ExternalDeclExtractor.kt b/java/kotlin-extractor/src/main/kotlin/ExternalDeclExtractor.kt index c45e0a454b7..a426c7bd622 100644 --- a/java/kotlin-extractor/src/main/kotlin/ExternalDeclExtractor.kt +++ b/java/kotlin-extractor/src/main/kotlin/ExternalDeclExtractor.kt @@ -14,7 +14,7 @@ import java.util.ArrayList import java.util.HashSet import java.util.zip.GZIPOutputStream -class ExternalDeclExtractor(val logger: FileLogger, val invocationTrapFile: String, val sourceFilePath: String, val primitiveTypeMapping: PrimitiveTypeMapping, val pluginContext: IrPluginContext, val globalExtensionState: KotlinExtractorGlobalState, val diagnosticTrapWriter: TrapWriter) { +class ExternalDeclExtractor(val logger: FileLogger, val invocationTrapFile: String, val sourceFilePath: String, val primitiveTypeMapping: PrimitiveTypeMapping, val pluginContext: IrPluginContext, val globalExtensionState: KotlinExtractorGlobalState, val diagnosticTrapWriter: DiagnosticTrapWriter) { val declBinaryNames = HashMap() val externalDeclsDone = HashSet>() @@ -95,8 +95,8 @@ class ExternalDeclExtractor(val logger: FileLogger, val invocationTrapFile: Stri val binaryPath = getIrClassBinaryPath(containingClass) // We want our comments to be the first thing in the file, - // so start off with a mere TrapWriter - val tw = TrapWriter(logger.loggerBase, TrapLabelManager(), trapFileBW, diagnosticTrapWriter) + // so start off with a PlainTrapWriter + val tw = PlainTrapWriter(logger.loggerBase, TrapLabelManager(), trapFileBW, diagnosticTrapWriter) tw.writeComment("Generated by the CodeQL Kotlin extractor for external dependencies") tw.writeComment("Part of invocation $invocationTrapFile") if (signature != possiblyLongSignature) { diff --git a/java/kotlin-extractor/src/main/kotlin/KotlinExtractorExtension.kt b/java/kotlin-extractor/src/main/kotlin/KotlinExtractorExtension.kt index 9bfcabd20fb..c766d70df33 100644 --- a/java/kotlin-extractor/src/main/kotlin/KotlinExtractorExtension.kt +++ b/java/kotlin-extractor/src/main/kotlin/KotlinExtractorExtension.kt @@ -127,7 +127,7 @@ class KotlinExtractorExtension( val lm = TrapLabelManager() val logCounter = LogCounter() val loggerBase = LoggerBase(logCounter) - val tw = TrapWriter(loggerBase, lm, invocationTrapFileBW, null) + val tw = DiagnosticTrapWriter(loggerBase, lm, invocationTrapFileBW) // The interceptor has already defined #compilation = * val compilation: Label = StringLabel("compilation") tw.writeCompilation_started(compilation) @@ -324,13 +324,13 @@ private fun doFile( trapFileWriter.getTempWriter().use { trapFileBW -> // We want our comments to be the first thing in the file, // so start off with a mere TrapWriter - val tw = TrapWriter(loggerBase, TrapLabelManager(), trapFileBW, fileTrapWriter) + val tw = PlainTrapWriter(loggerBase, TrapLabelManager(), trapFileBW, fileTrapWriter.getDiagnosticTrapWriter()) tw.writeComment("Generated by the CodeQL Kotlin extractor for kotlin source code") tw.writeComment("Part of invocation $invocationTrapFile") // Now elevate to a SourceFileTrapWriter, and populate the // file information val sftw = tw.makeSourceFileTrapWriter(srcFile, true) - val externalDeclExtractor = ExternalDeclExtractor(logger, invocationTrapFile, srcFilePath, primitiveTypeMapping, pluginContext, globalExtensionState, fileTrapWriter) + val externalDeclExtractor = ExternalDeclExtractor(logger, invocationTrapFile, srcFilePath, primitiveTypeMapping, pluginContext, globalExtensionState, fileTrapWriter.getDiagnosticTrapWriter()) val linesOfCode = LinesOfCode(logger, sftw, srcFile) val fileExtractor = KotlinFileExtractor(logger, sftw, linesOfCode, srcFilePath, null, externalDeclExtractor, primitiveTypeMapping, pluginContext, KotlinFileExtractor.DeclarationStack(), globalExtensionState) diff --git a/java/kotlin-extractor/src/main/kotlin/KotlinFileExtractor.kt b/java/kotlin-extractor/src/main/kotlin/KotlinFileExtractor.kt index c471662e631..ef7fafc913a 100644 --- a/java/kotlin-extractor/src/main/kotlin/KotlinFileExtractor.kt +++ b/java/kotlin-extractor/src/main/kotlin/KotlinFileExtractor.kt @@ -157,21 +157,10 @@ open class KotlinFileExtractor( else -> false } - @OptIn(ObsoleteDescriptorBasedAPI::class) private fun isFake(d: IrDeclarationWithVisibility): Boolean { val hasFakeVisibility = d.visibility.let { it is DelegatedDescriptorVisibility && it.delegate == Visibilities.InvisibleFake } || d.isFakeOverride if (hasFakeVisibility && !isJavaBinaryObjectMethodRedeclaration(d)) return true - try { - if ((d as? IrFunction)?.descriptor?.isHiddenToOvercomeSignatureClash == true) { - return true - } - } - catch (e: NotImplementedError) { - // `org.jetbrains.kotlin.ir.descriptors.IrBasedClassConstructorDescriptor.isHiddenToOvercomeSignatureClash` throws the exception - logger.warnElement("Couldn't query if element is fake, deciding it's not.", d, e) - return false - } return false } @@ -1559,7 +1548,7 @@ open class KotlinFileExtractor( val setter = p.setter if (getter == null) { - if (p.modality != Modality.FINAL || !isExternalDeclaration(p)) { + if (!isExternalDeclaration(p)) { logger.warnElement("IrProperty without a getter", p) } } else if (shouldExtractDecl(getter, extractPrivateMembers)) { @@ -1701,12 +1690,13 @@ open class KotlinFileExtractor( private fun extractSyntheticBody(b: IrSyntheticBody, callable: Label) { with("synthetic body", b) { - when (b.kind) { - IrSyntheticBodyKind.ENUM_VALUES -> tw.writeKtSyntheticBody(callable, 1) - IrSyntheticBodyKind.ENUM_VALUEOF -> tw.writeKtSyntheticBody(callable, 2) + val kind = b.kind + when { + kind == IrSyntheticBodyKind.ENUM_VALUES -> tw.writeKtSyntheticBody(callable, 1) + kind == IrSyntheticBodyKind.ENUM_VALUEOF -> tw.writeKtSyntheticBody(callable, 2) + kind == kind_ENUM_ENTRIES -> tw.writeKtSyntheticBody(callable, 3) else -> { - // TODO: Support IrSyntheticBodyKind.ENUM_ENTRIES - logger.errorElement("Unhandled synthetic body kind " + b.kind.javaClass, b) + logger.errorElement("Unhandled synthetic body kind " + kind, b) } } } @@ -5316,7 +5306,10 @@ open class KotlinFileExtractor( private fun extractTypeAccessRecursive(t: IrType, location: Label, parent: Label, idx: Int, typeContext: TypeContext = TypeContext.OTHER): Label { val typeAccessId = extractTypeAccess(useType(t, typeContext), location, parent, idx) if (t is IrSimpleType) { - t.arguments.forEachIndexed { argIdx, arg -> + // From 1.9, the list might change when we call erase, + // so we make a copy that it is safe to iterate over. + val argumentsCopy = t.arguments.toList() + argumentsCopy.forEachIndexed { argIdx, arg -> extractWildcardTypeAccessRecursive(arg, location, typeAccessId, argIdx) } } diff --git a/java/kotlin-extractor/src/main/kotlin/KotlinUsesExtractor.kt b/java/kotlin-extractor/src/main/kotlin/KotlinUsesExtractor.kt index 9c552233158..fdaebe5f1c8 100644 --- a/java/kotlin-extractor/src/main/kotlin/KotlinUsesExtractor.kt +++ b/java/kotlin-extractor/src/main/kotlin/KotlinUsesExtractor.kt @@ -139,13 +139,13 @@ open class KotlinUsesExtractor( if (clsFile == null || isExternalDeclaration(cls)) { val filePath = getIrClassBinaryPath(cls) val newTrapWriter = tw.makeFileTrapWriter(filePath, true) - val newLoggerTrapWriter = logger.tw.makeFileTrapWriter(filePath, false) + val newLoggerTrapWriter = logger.dtw.makeFileTrapWriter(filePath, false) val newLogger = FileLogger(logger.loggerBase, newLoggerTrapWriter) return KotlinFileExtractor(newLogger, newTrapWriter, null, filePath, dependencyCollector, externalClassExtractor, primitiveTypeMapping, pluginContext, newDeclarationStack, globalExtensionState) } val newTrapWriter = tw.makeSourceFileTrapWriter(clsFile, true) - val newLoggerTrapWriter = logger.tw.makeSourceFileTrapWriter(clsFile, false) + val newLoggerTrapWriter = logger.dtw.makeSourceFileTrapWriter(clsFile, false) val newLogger = FileLogger(logger.loggerBase, newLoggerTrapWriter) return KotlinFileExtractor(newLogger, newTrapWriter, null, clsFile.path, dependencyCollector, externalClassExtractor, primitiveTypeMapping, pluginContext, newDeclarationStack, globalExtensionState) } diff --git a/java/kotlin-extractor/src/main/kotlin/TrapWriter.kt b/java/kotlin-extractor/src/main/kotlin/TrapWriter.kt index eb9feeb4559..b27aec9cc52 100644 --- a/java/kotlin-extractor/src/main/kotlin/TrapWriter.kt +++ b/java/kotlin-extractor/src/main/kotlin/TrapWriter.kt @@ -57,7 +57,9 @@ class TrapLabelManager { * share the same `TrapLabelManager` and `BufferedWriter`. */ // TODO lm was `protected` before anonymousTypeMapping and locallyVisibleFunctionLabelMapping moved into it. Should we re-protect it and provide accessors? -open class TrapWriter (protected val loggerBase: LoggerBase, val lm: TrapLabelManager, private val bw: BufferedWriter, val diagnosticTrapWriter: TrapWriter?) { +abstract class TrapWriter (protected val loggerBase: LoggerBase, val lm: TrapLabelManager, private val bw: BufferedWriter) { + abstract fun getDiagnosticTrapWriter(): DiagnosticTrapWriter + /** * Returns the label that is defined to be the given key, if such * a label exists, and `null` otherwise. Most users will want to use @@ -223,7 +225,7 @@ open class TrapWriter (protected val loggerBase: LoggerBase, val lm: TrapLabelMa val len = str.length val newLen = UTF8Util.encodablePrefixLength(str, MAX_STRLEN) if (newLen < len) { - loggerBase.warn(diagnosticTrapWriter ?: this, + loggerBase.warn(this.getDiagnosticTrapWriter(), "Truncated string of length $len", "Truncated string of length $len, starting '${str.take(100)}', ending '${str.takeLast(100)}'") return str.take(newLen) @@ -237,14 +239,43 @@ open class TrapWriter (protected val loggerBase: LoggerBase, val lm: TrapLabelMa * writer etc), but using the given `filePath` for locations. */ fun makeFileTrapWriter(filePath: String, populateFileTables: Boolean) = - FileTrapWriter(loggerBase, lm, bw, diagnosticTrapWriter, filePath, populateFileTables) + FileTrapWriter(loggerBase, lm, bw, this.getDiagnosticTrapWriter(), filePath, populateFileTables) /** * Gets a FileTrapWriter like this one (using the same label manager, * writer etc), but using the given `IrFile` for locations. */ fun makeSourceFileTrapWriter(file: IrFile, populateFileTables: Boolean) = - SourceFileTrapWriter(loggerBase, lm, bw, diagnosticTrapWriter, file, populateFileTables) + SourceFileTrapWriter(loggerBase, lm, bw, this.getDiagnosticTrapWriter(), file, populateFileTables) +} + +/** + * A `PlainTrapWriter` has no additional context of its own. + */ +class PlainTrapWriter ( + loggerBase: LoggerBase, + lm: TrapLabelManager, + bw: BufferedWriter, + val dtw: DiagnosticTrapWriter +): TrapWriter (loggerBase, lm, bw) { + override fun getDiagnosticTrapWriter(): DiagnosticTrapWriter { + return dtw + } +} + +/** + * A `DiagnosticTrapWriter` is a TrapWriter that diagnostics can be + * written to; i.e. it has the #compilation label defined. In practice, + * this means that it is a TrapWriter for the invocation TRAP file. + */ +class DiagnosticTrapWriter ( + loggerBase: LoggerBase, + lm: TrapLabelManager, + bw: BufferedWriter +): TrapWriter (loggerBase, lm, bw) { + override fun getDiagnosticTrapWriter(): DiagnosticTrapWriter { + return this + } } /** @@ -259,16 +290,20 @@ open class FileTrapWriter ( loggerBase: LoggerBase, lm: TrapLabelManager, bw: BufferedWriter, - diagnosticTrapWriter: TrapWriter?, + val dtw: DiagnosticTrapWriter, val filePath: String, populateFileTables: Boolean -): TrapWriter (loggerBase, lm, bw, diagnosticTrapWriter) { +): TrapWriter (loggerBase, lm, bw) { /** * The ID for the file that we are extracting from. */ val fileId = mkFileId(filePath, populateFileTables) + override fun getDiagnosticTrapWriter(): DiagnosticTrapWriter { + return dtw + } + private fun offsetMinOf(default: Int, vararg options: Int?): Int { if (default == UNDEFINED_OFFSET || default == SYNTHETIC_OFFSET) { return default @@ -349,10 +384,10 @@ class SourceFileTrapWriter ( loggerBase: LoggerBase, lm: TrapLabelManager, bw: BufferedWriter, - diagnosticTrapWriter: TrapWriter?, + dtw: DiagnosticTrapWriter, val irFile: IrFile, populateFileTables: Boolean) : - FileTrapWriter(loggerBase, lm, bw, diagnosticTrapWriter, irFile.path, populateFileTables) { + FileTrapWriter(loggerBase, lm, bw, dtw, irFile.path, populateFileTables) { /** * The file entry for the file that we are extracting from. @@ -363,14 +398,14 @@ class SourceFileTrapWriter ( override fun getLocation(startOffset: Int, endOffset: Int): Label { if (startOffset == UNDEFINED_OFFSET || endOffset == UNDEFINED_OFFSET) { if (startOffset != endOffset) { - loggerBase.warn(this, "Location with inconsistent offsets (start $startOffset, end $endOffset)", null) + loggerBase.warn(dtw, "Location with inconsistent offsets (start $startOffset, end $endOffset)", null) } return getWholeFileLocation() } if (startOffset == SYNTHETIC_OFFSET || endOffset == SYNTHETIC_OFFSET) { if (startOffset != endOffset) { - loggerBase.warn(this, "Location with inconsistent offsets (start $startOffset, end $endOffset)", null) + loggerBase.warn(dtw, "Location with inconsistent offsets (start $startOffset, end $endOffset)", null) } return getWholeFileLocation() } @@ -390,14 +425,14 @@ class SourceFileTrapWriter ( override fun getLocationString(e: IrElement): String { if (e.startOffset == UNDEFINED_OFFSET || e.endOffset == UNDEFINED_OFFSET) { if (e.startOffset != e.endOffset) { - loggerBase.warn(this, "Location with inconsistent offsets (start ${e.startOffset}, end ${e.endOffset})", null) + loggerBase.warn(dtw, "Location with inconsistent offsets (start ${e.startOffset}, end ${e.endOffset})", null) } return "" } if (e.startOffset == SYNTHETIC_OFFSET || e.endOffset == SYNTHETIC_OFFSET) { if (e.startOffset != e.endOffset) { - loggerBase.warn(this, "Location with inconsistent offsets (start ${e.startOffset}, end ${e.endOffset})", null) + loggerBase.warn(dtw, "Location with inconsistent offsets (start ${e.startOffset}, end ${e.endOffset})", null) } return "" } diff --git a/java/kotlin-extractor/src/main/kotlin/utils/Logger.kt b/java/kotlin-extractor/src/main/kotlin/utils/Logger.kt index 3b66e527429..2dee392247e 100644 --- a/java/kotlin-extractor/src/main/kotlin/utils/Logger.kt +++ b/java/kotlin-extractor/src/main/kotlin/utils/Logger.kt @@ -107,7 +107,7 @@ open class LoggerBase(val logCounter: LogCounter) { file_number_diagnostic_number = 0 } - fun diagnostic(tw: TrapWriter, severity: Severity, msg: String, extraInfo: String?, locationString: String? = null, mkLocationId: () -> Label = { tw.unknownLocation }) { + fun diagnostic(dtw: DiagnosticTrapWriter, severity: Severity, msg: String, extraInfo: String?, locationString: String? = null, mkLocationId: () -> Label = { dtw.unknownLocation }) { val diagnosticLoc = getDiagnosticLocation() val diagnosticLocStr = if(diagnosticLoc == null) "" else diagnosticLoc val suffix = @@ -121,7 +121,7 @@ open class LoggerBase(val logCounter: LogCounter) { // counting machinery if (verbosity >= 1) { val message = "Severity mismatch ($severity vs ${oldInfo.first}) at $diagnosticLoc" - emitDiagnostic(tw, Severity.Error, "Inconsistency", message, message) + emitDiagnostic(dtw, Severity.Error, "Inconsistency", message, message) } } val newCount = oldInfo.second + 1 @@ -149,18 +149,18 @@ open class LoggerBase(val logCounter: LogCounter) { fullMsgBuilder.append(suffix) val fullMsg = fullMsgBuilder.toString() - emitDiagnostic(tw, severity, diagnosticLocStr, msg, fullMsg, locationString, mkLocationId) + emitDiagnostic(dtw, 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 }) { + private fun emitDiagnostic(dtw: DiagnosticTrapWriter, severity: Severity, diagnosticLocStr: String, msg: String, fullMsg: String, locationString: String? = null, mkLocationId: () -> Label = { dtw.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") // We don't actually make the location until after the `return` above val locationId = mkLocationId() - val diagLabel = tw.getFreshIdLabel() - tw.writeDiagnostics(diagLabel, "CodeQL Kotlin extractor", severity.sev, "", msg, "${logMessage.timestamp} $fullMsg", locationId) - tw.writeDiagnostic_for(diagLabel, StringLabel("compilation"), file_number, file_number_diagnostic_number++) + val diagLabel = dtw.getFreshIdLabel() + dtw.writeDiagnostics(diagLabel, "CodeQL Kotlin extractor", severity.sev, "", msg, "${logMessage.timestamp} $fullMsg", locationId) + dtw.writeDiagnostic_for(diagLabel, StringLabel("compilation"), file_number, file_number_diagnostic_number++) logStream.write(logMessage.toJsonLine()) } @@ -188,28 +188,25 @@ open class LoggerBase(val logCounter: LogCounter) { } } - fun warn(tw: TrapWriter, msg: String, extraInfo: String?) { + fun warn(dtw: DiagnosticTrapWriter, msg: String, extraInfo: String?) { if (verbosity >= 2) { - diagnostic(tw, Severity.Warn, msg, extraInfo) + diagnostic(dtw, Severity.Warn, msg, extraInfo) } } - fun error(tw: TrapWriter, msg: String, extraInfo: String?) { + fun error(dtw: DiagnosticTrapWriter, msg: String, extraInfo: String?) { if (verbosity >= 1) { - diagnostic(tw, Severity.Error, msg, extraInfo) + diagnostic(dtw, Severity.Error, msg, extraInfo) } } - fun printLimitedDiagnosticCounts(tw: TrapWriter) { + fun printLimitedDiagnosticCounts(dtw: DiagnosticTrapWriter) { for((caller, info) in logCounter.diagnosticInfo) { val severity = info.first val count = info.second if(count >= logCounter.diagnosticLimit) { - // 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 message = "Total of $count diagnostics (reached limit of ${logCounter.diagnosticLimit}) from $caller." if (verbosity >= 1) { - emitDiagnostic(tw, severity, "Limit", message, message) + emitDiagnostic(dtw, severity, "Limit", message, message) } } } @@ -224,28 +221,28 @@ open class LoggerBase(val logCounter: LogCounter) { } } -open class Logger(val loggerBase: LoggerBase, open val tw: TrapWriter) { +open class Logger(val loggerBase: LoggerBase, open val dtw: DiagnosticTrapWriter) { fun flush() { - tw.flush() + dtw.flush() loggerBase.flush() } fun trace(msg: String) { - loggerBase.trace(tw, msg) + loggerBase.trace(dtw, msg) } fun trace(msg: String, exn: Throwable) { trace(msg + "\n" + exn.stackTraceToString()) } fun debug(msg: String) { - loggerBase.debug(tw, msg) + loggerBase.debug(dtw, msg) } fun info(msg: String) { - loggerBase.info(tw, msg) + loggerBase.info(dtw, msg) } private fun warn(msg: String, extraInfo: String?) { - loggerBase.warn(tw, msg, extraInfo) + loggerBase.warn(dtw, msg, extraInfo) } fun warn(msg: String, exn: Throwable) { warn(msg, exn.stackTraceToString()) @@ -255,7 +252,7 @@ open class Logger(val loggerBase: LoggerBase, open val tw: TrapWriter) { } private fun error(msg: String, extraInfo: String?) { - loggerBase.error(tw, msg, extraInfo) + loggerBase.error(dtw, msg, extraInfo) } fun error(msg: String) { error(msg, null) @@ -265,16 +262,16 @@ open class Logger(val loggerBase: LoggerBase, open val tw: TrapWriter) { } } -class FileLogger(loggerBase: LoggerBase, override val tw: FileTrapWriter): Logger(loggerBase, tw) { +class FileLogger(loggerBase: LoggerBase, val ftw: FileTrapWriter): Logger(loggerBase, ftw.getDiagnosticTrapWriter()) { fun warnElement(msg: String, element: IrElement, exn: Throwable? = null) { - val locationString = tw.getLocationString(element) - val mkLocationId = { tw.getLocation(element) } - loggerBase.diagnostic(tw, Severity.Warn, msg, exn?.stackTraceToString(), locationString, mkLocationId) + val locationString = ftw.getLocationString(element) + val mkLocationId = { ftw.getLocation(element) } + loggerBase.diagnostic(ftw.getDiagnosticTrapWriter(), Severity.Warn, msg, exn?.stackTraceToString(), locationString, mkLocationId) } fun errorElement(msg: String, element: IrElement, exn: Throwable? = null) { - val locationString = tw.getLocationString(element) - val mkLocationId = { tw.getLocation(element) } - loggerBase.diagnostic(tw, Severity.Error, msg, exn?.stackTraceToString(), locationString, mkLocationId) + val locationString = ftw.getLocationString(element) + val mkLocationId = { ftw.getLocation(element) } + loggerBase.diagnostic(ftw.getDiagnosticTrapWriter(), Severity.Error, msg, exn?.stackTraceToString(), locationString, mkLocationId) } } diff --git a/java/kotlin-extractor/src/main/kotlin/utils/versions/v_1_4_32/SyntheticBodyKind.kt b/java/kotlin-extractor/src/main/kotlin/utils/versions/v_1_4_32/SyntheticBodyKind.kt new file mode 100644 index 00000000000..fd3fc1f8e20 --- /dev/null +++ b/java/kotlin-extractor/src/main/kotlin/utils/versions/v_1_4_32/SyntheticBodyKind.kt @@ -0,0 +1,6 @@ +package com.github.codeql.utils.versions + +import org.jetbrains.kotlin.ir.expressions.IrSyntheticBodyKind + +val kind_ENUM_ENTRIES: IrSyntheticBodyKind? = null + diff --git a/java/kotlin-extractor/src/main/kotlin/utils/versions/v_1_8_0/SyntheticBodyKind.kt b/java/kotlin-extractor/src/main/kotlin/utils/versions/v_1_8_0/SyntheticBodyKind.kt new file mode 100644 index 00000000000..d0dbb5b1247 --- /dev/null +++ b/java/kotlin-extractor/src/main/kotlin/utils/versions/v_1_8_0/SyntheticBodyKind.kt @@ -0,0 +1,6 @@ +package com.github.codeql.utils.versions + +import org.jetbrains.kotlin.ir.expressions.IrSyntheticBodyKind + +val kind_ENUM_ENTRIES: IrSyntheticBodyKind? = IrSyntheticBodyKind.ENUM_ENTRIES + diff --git a/java/ql/consistency-queries/qlpack.yml b/java/ql/consistency-queries/qlpack.yml index e5ab40251bb..1501c7fa736 100644 --- a/java/ql/consistency-queries/qlpack.yml +++ b/java/ql/consistency-queries/qlpack.yml @@ -2,3 +2,4 @@ name: codeql-java-consistency-queries version: 0.0.0 dependencies: codeql/java-all: '*' +warnOnImplicitThis: true diff --git a/java/ql/examples/qlpack.yml b/java/ql/examples/qlpack.yml index 25699c305b0..bbd44274625 100644 --- a/java/ql/examples/qlpack.yml +++ b/java/ql/examples/qlpack.yml @@ -4,3 +4,4 @@ groups: - examples dependencies: codeql/java-all: ${workspace} +warnOnImplicitThis: true diff --git a/java/ql/integration-tests/all-platforms/java/android-sample-old-style-kotlin-build-script-no-wrapper/project/build.gradle.kts b/java/ql/integration-tests/all-platforms/java/android-sample-old-style-kotlin-build-script-no-wrapper/project/build.gradle.kts index 95b77607cb3..75f63e5d4c4 100644 --- a/java/ql/integration-tests/all-platforms/java/android-sample-old-style-kotlin-build-script-no-wrapper/project/build.gradle.kts +++ b/java/ql/integration-tests/all-platforms/java/android-sample-old-style-kotlin-build-script-no-wrapper/project/build.gradle.kts @@ -54,6 +54,9 @@ android { versionName = "1.0" } + lintOptions { + disable("Instantiatable") + } } androidComponents { diff --git a/java/ql/integration-tests/all-platforms/java/android-sample-old-style-kotlin-build-script-no-wrapper/test.expected b/java/ql/integration-tests/all-platforms/java/android-sample-old-style-kotlin-build-script-no-wrapper/test.expected index 5ea6cd8c5ca..94266bb00a1 100644 --- a/java/ql/integration-tests/all-platforms/java/android-sample-old-style-kotlin-build-script-no-wrapper/test.expected +++ b/java/ql/integration-tests/all-platforms/java/android-sample-old-style-kotlin-build-script-no-wrapper/test.expected @@ -13,6 +13,7 @@ xmlFiles | project/build/intermediates/incremental/mergeReleaseJniLibFolders/merger.xml:0:0:0:0 | project/build/intermediates/incremental/mergeReleaseJniLibFolders/merger.xml | | project/build/intermediates/incremental/mergeReleaseResources/merger.xml:0:0:0:0 | project/build/intermediates/incremental/mergeReleaseResources/merger.xml | | project/build/intermediates/incremental/mergeReleaseShaders/merger.xml:0:0:0:0 | project/build/intermediates/incremental/mergeReleaseShaders/merger.xml | +| project/build/intermediates/lint_vital_partial_results/release/out/lint-issues-release.xml:0:0:0:0 | project/build/intermediates/lint_vital_partial_results/release/out/lint-issues-release.xml | | project/build/intermediates/merged_manifest/release/AndroidManifest.xml:0:0:0:0 | project/build/intermediates/merged_manifest/release/AndroidManifest.xml | | project/build/intermediates/merged_manifests/release/AndroidManifest.xml:0:0:0:0 | project/build/intermediates/merged_manifests/release/AndroidManifest.xml | | project/build/intermediates/packaged_manifests/release/AndroidManifest.xml:0:0:0:0 | project/build/intermediates/packaged_manifests/release/AndroidManifest.xml | diff --git a/java/ql/integration-tests/all-platforms/java/android-sample-old-style-kotlin-build-script/project/build.gradle.kts b/java/ql/integration-tests/all-platforms/java/android-sample-old-style-kotlin-build-script/project/build.gradle.kts index 95b77607cb3..75f63e5d4c4 100644 --- a/java/ql/integration-tests/all-platforms/java/android-sample-old-style-kotlin-build-script/project/build.gradle.kts +++ b/java/ql/integration-tests/all-platforms/java/android-sample-old-style-kotlin-build-script/project/build.gradle.kts @@ -54,6 +54,9 @@ android { versionName = "1.0" } + lintOptions { + disable("Instantiatable") + } } androidComponents { diff --git a/java/ql/integration-tests/all-platforms/java/android-sample-old-style-kotlin-build-script/test.expected b/java/ql/integration-tests/all-platforms/java/android-sample-old-style-kotlin-build-script/test.expected index 5ea6cd8c5ca..94266bb00a1 100644 --- a/java/ql/integration-tests/all-platforms/java/android-sample-old-style-kotlin-build-script/test.expected +++ b/java/ql/integration-tests/all-platforms/java/android-sample-old-style-kotlin-build-script/test.expected @@ -13,6 +13,7 @@ xmlFiles | project/build/intermediates/incremental/mergeReleaseJniLibFolders/merger.xml:0:0:0:0 | project/build/intermediates/incremental/mergeReleaseJniLibFolders/merger.xml | | project/build/intermediates/incremental/mergeReleaseResources/merger.xml:0:0:0:0 | project/build/intermediates/incremental/mergeReleaseResources/merger.xml | | project/build/intermediates/incremental/mergeReleaseShaders/merger.xml:0:0:0:0 | project/build/intermediates/incremental/mergeReleaseShaders/merger.xml | +| project/build/intermediates/lint_vital_partial_results/release/out/lint-issues-release.xml:0:0:0:0 | project/build/intermediates/lint_vital_partial_results/release/out/lint-issues-release.xml | | project/build/intermediates/merged_manifest/release/AndroidManifest.xml:0:0:0:0 | project/build/intermediates/merged_manifest/release/AndroidManifest.xml | | project/build/intermediates/merged_manifests/release/AndroidManifest.xml:0:0:0:0 | project/build/intermediates/merged_manifests/release/AndroidManifest.xml | | project/build/intermediates/packaged_manifests/release/AndroidManifest.xml:0:0:0:0 | project/build/intermediates/packaged_manifests/release/AndroidManifest.xml | diff --git a/java/ql/integration-tests/all-platforms/java/android-sample-old-style-no-wrapper/project/build.gradle b/java/ql/integration-tests/all-platforms/java/android-sample-old-style-no-wrapper/project/build.gradle index 7751b92ae54..4d52801f178 100644 --- a/java/ql/integration-tests/all-platforms/java/android-sample-old-style-no-wrapper/project/build.gradle +++ b/java/ql/integration-tests/all-platforms/java/android-sample-old-style-no-wrapper/project/build.gradle @@ -55,4 +55,8 @@ android { } variantFilter { variant -> if (variant.buildType.name == "debug") { setIgnore(true) } } + + lintOptions { + disable "Instantiatable" + } } diff --git a/java/ql/integration-tests/all-platforms/java/android-sample-old-style-no-wrapper/test.expected b/java/ql/integration-tests/all-platforms/java/android-sample-old-style-no-wrapper/test.expected index 5ea6cd8c5ca..94266bb00a1 100644 --- a/java/ql/integration-tests/all-platforms/java/android-sample-old-style-no-wrapper/test.expected +++ b/java/ql/integration-tests/all-platforms/java/android-sample-old-style-no-wrapper/test.expected @@ -13,6 +13,7 @@ xmlFiles | project/build/intermediates/incremental/mergeReleaseJniLibFolders/merger.xml:0:0:0:0 | project/build/intermediates/incremental/mergeReleaseJniLibFolders/merger.xml | | project/build/intermediates/incremental/mergeReleaseResources/merger.xml:0:0:0:0 | project/build/intermediates/incremental/mergeReleaseResources/merger.xml | | project/build/intermediates/incremental/mergeReleaseShaders/merger.xml:0:0:0:0 | project/build/intermediates/incremental/mergeReleaseShaders/merger.xml | +| project/build/intermediates/lint_vital_partial_results/release/out/lint-issues-release.xml:0:0:0:0 | project/build/intermediates/lint_vital_partial_results/release/out/lint-issues-release.xml | | project/build/intermediates/merged_manifest/release/AndroidManifest.xml:0:0:0:0 | project/build/intermediates/merged_manifest/release/AndroidManifest.xml | | project/build/intermediates/merged_manifests/release/AndroidManifest.xml:0:0:0:0 | project/build/intermediates/merged_manifests/release/AndroidManifest.xml | | project/build/intermediates/packaged_manifests/release/AndroidManifest.xml:0:0:0:0 | project/build/intermediates/packaged_manifests/release/AndroidManifest.xml | diff --git a/java/ql/integration-tests/all-platforms/java/android-sample-old-style/project/build.gradle b/java/ql/integration-tests/all-platforms/java/android-sample-old-style/project/build.gradle index 7751b92ae54..4d52801f178 100644 --- a/java/ql/integration-tests/all-platforms/java/android-sample-old-style/project/build.gradle +++ b/java/ql/integration-tests/all-platforms/java/android-sample-old-style/project/build.gradle @@ -55,4 +55,8 @@ android { } variantFilter { variant -> if (variant.buildType.name == "debug") { setIgnore(true) } } + + lintOptions { + disable "Instantiatable" + } } diff --git a/java/ql/integration-tests/all-platforms/java/android-sample-old-style/test.expected b/java/ql/integration-tests/all-platforms/java/android-sample-old-style/test.expected index 5ea6cd8c5ca..94266bb00a1 100644 --- a/java/ql/integration-tests/all-platforms/java/android-sample-old-style/test.expected +++ b/java/ql/integration-tests/all-platforms/java/android-sample-old-style/test.expected @@ -13,6 +13,7 @@ xmlFiles | project/build/intermediates/incremental/mergeReleaseJniLibFolders/merger.xml:0:0:0:0 | project/build/intermediates/incremental/mergeReleaseJniLibFolders/merger.xml | | project/build/intermediates/incremental/mergeReleaseResources/merger.xml:0:0:0:0 | project/build/intermediates/incremental/mergeReleaseResources/merger.xml | | project/build/intermediates/incremental/mergeReleaseShaders/merger.xml:0:0:0:0 | project/build/intermediates/incremental/mergeReleaseShaders/merger.xml | +| project/build/intermediates/lint_vital_partial_results/release/out/lint-issues-release.xml:0:0:0:0 | project/build/intermediates/lint_vital_partial_results/release/out/lint-issues-release.xml | | project/build/intermediates/merged_manifest/release/AndroidManifest.xml:0:0:0:0 | project/build/intermediates/merged_manifest/release/AndroidManifest.xml | | project/build/intermediates/merged_manifests/release/AndroidManifest.xml:0:0:0:0 | project/build/intermediates/merged_manifests/release/AndroidManifest.xml | | project/build/intermediates/packaged_manifests/release/AndroidManifest.xml:0:0:0:0 | project/build/intermediates/packaged_manifests/release/AndroidManifest.xml | diff --git a/java/ql/integration-tests/all-platforms/java/qlpack.yml b/java/ql/integration-tests/all-platforms/java/qlpack.yml index 9ead02fc564..4994af85a75 100644 --- a/java/ql/integration-tests/all-platforms/java/qlpack.yml +++ b/java/ql/integration-tests/all-platforms/java/qlpack.yml @@ -2,3 +2,4 @@ dependencies: codeql/java-all: '*' codeql/java-tests: '*' codeql/java-queries: '*' +warnOnImplicitThis: true diff --git a/java/ql/integration-tests/all-platforms/kotlin/annotation-id-consistency/qlpack.yml b/java/ql/integration-tests/all-platforms/kotlin/annotation-id-consistency/qlpack.yml index 74fbac535d7..eeaa0e9f1b7 100644 --- a/java/ql/integration-tests/all-platforms/kotlin/annotation-id-consistency/qlpack.yml +++ b/java/ql/integration-tests/all-platforms/kotlin/annotation-id-consistency/qlpack.yml @@ -5,3 +5,4 @@ dependencies: codeql/java-queries: '*' dataExtensions: ext/*.model.yml +warnOnImplicitThis: true diff --git a/java/ql/integration-tests/all-platforms/kotlin/default-parameter-mad-flow/qlpack.yml b/java/ql/integration-tests/all-platforms/kotlin/default-parameter-mad-flow/qlpack.yml index f1e981e8791..61fd32f9eb9 100644 --- a/java/ql/integration-tests/all-platforms/kotlin/default-parameter-mad-flow/qlpack.yml +++ b/java/ql/integration-tests/all-platforms/kotlin/default-parameter-mad-flow/qlpack.yml @@ -5,3 +5,4 @@ dependencies: codeql/java-queries: '*' dataExtensions: ext/*.model.yml +warnOnImplicitThis: true diff --git a/java/ql/integration-tests/all-platforms/kotlin/gradle_kotlinx_serialization/qlpack.yml b/java/ql/integration-tests/all-platforms/kotlin/gradle_kotlinx_serialization/qlpack.yml index 8b18f2ea94a..d9848346b6f 100644 --- a/java/ql/integration-tests/all-platforms/kotlin/gradle_kotlinx_serialization/qlpack.yml +++ b/java/ql/integration-tests/all-platforms/kotlin/gradle_kotlinx_serialization/qlpack.yml @@ -3,3 +3,4 @@ dependencies: codeql/java-all: '*' codeql/java-tests: '*' codeql/java-queries: '*' +warnOnImplicitThis: true diff --git a/java/ql/integration-tests/all-platforms/kotlin/kotlin-interface-inherited-default/qlpack.yml b/java/ql/integration-tests/all-platforms/kotlin/kotlin-interface-inherited-default/qlpack.yml index 814d1059ed5..69d3f0b3658 100644 --- a/java/ql/integration-tests/all-platforms/kotlin/kotlin-interface-inherited-default/qlpack.yml +++ b/java/ql/integration-tests/all-platforms/kotlin/kotlin-interface-inherited-default/qlpack.yml @@ -3,3 +3,4 @@ dependencies: codeql/java-all: '*' codeql/java-tests: '*' codeql/java-queries: '*' +warnOnImplicitThis: true diff --git a/java/ql/integration-tests/all-platforms/kotlin/kotlin_java_static_fields/qlpack.yml b/java/ql/integration-tests/all-platforms/kotlin/kotlin_java_static_fields/qlpack.yml index ecc3ee3e4ff..1902aa3a68f 100644 --- a/java/ql/integration-tests/all-platforms/kotlin/kotlin_java_static_fields/qlpack.yml +++ b/java/ql/integration-tests/all-platforms/kotlin/kotlin_java_static_fields/qlpack.yml @@ -3,3 +3,4 @@ dependencies: codeql/java-all: '*' codeql/java-tests: '*' codeql/java-queries: '*' +warnOnImplicitThis: true diff --git a/java/ql/integration-tests/all-platforms/kotlin/qlpack.yml b/java/ql/integration-tests/all-platforms/kotlin/qlpack.yml index 9ead02fc564..4994af85a75 100644 --- a/java/ql/integration-tests/all-platforms/kotlin/qlpack.yml +++ b/java/ql/integration-tests/all-platforms/kotlin/qlpack.yml @@ -2,3 +2,4 @@ dependencies: codeql/java-all: '*' codeql/java-tests: '*' codeql/java-queries: '*' +warnOnImplicitThis: true diff --git a/java/ql/integration-tests/linux-only/kotlin/custom_plugin/qlpack.yml b/java/ql/integration-tests/linux-only/kotlin/custom_plugin/qlpack.yml index e2f6b6de7ba..18ab2b37444 100644 --- a/java/ql/integration-tests/linux-only/kotlin/custom_plugin/qlpack.yml +++ b/java/ql/integration-tests/linux-only/kotlin/custom_plugin/qlpack.yml @@ -1,3 +1,4 @@ name: integrationtest-custom-plugin dependencies: codeql/java-all: '*' +warnOnImplicitThis: true diff --git a/java/ql/integration-tests/linux-only/kotlin/qlpack.yml b/java/ql/integration-tests/linux-only/kotlin/qlpack.yml index a1e82f7365a..b2ae6491ab8 100644 --- a/java/ql/integration-tests/linux-only/kotlin/qlpack.yml +++ b/java/ql/integration-tests/linux-only/kotlin/qlpack.yml @@ -1,2 +1,3 @@ dependencies: codeql/java-all: '*' +warnOnImplicitThis: true diff --git a/java/ql/integration-tests/posix-only/kotlin/qlpack.yml b/java/ql/integration-tests/posix-only/kotlin/qlpack.yml index 0c0975df53f..4994af85a75 100644 --- a/java/ql/integration-tests/posix-only/kotlin/qlpack.yml +++ b/java/ql/integration-tests/posix-only/kotlin/qlpack.yml @@ -2,4 +2,4 @@ dependencies: codeql/java-all: '*' codeql/java-tests: '*' codeql/java-queries: '*' - +warnOnImplicitThis: true diff --git a/java/ql/lib/CHANGELOG.md b/java/ql/lib/CHANGELOG.md index 53fb1470bb9..1056cefb86a 100644 --- a/java/ql/lib/CHANGELOG.md +++ b/java/ql/lib/CHANGELOG.md @@ -1,3 +1,61 @@ +## 0.6.3 + +### New Features + +* Kotlin versions up to 1.9.0 are now supported. + +### Minor Analysis Improvements + +* Added flow through the block arguments of `kotlin.io.use` and `kotlin.with`. +* Added models for the following packages: + + * com.alibaba.druid.sql + * com.fasterxml.jackson.databind + * com.jcraft.jsch + * io.netty.handler.ssl + * okhttp3 + * org.antlr.runtime + * org.fusesource.leveldbjni + * org.influxdb + * org.springframework.core.io + * org.yaml.snakeyaml +* Deleted the deprecated `getRHS` predicate from the `LValue` class, use `getRhs` instead. +* Deleted the deprecated `getCFGNode` predicate from the `SsaVariable` class, use `getCfgNode` instead. +* Deleted many deprecated predicates and classes with uppercase `XML`, `JSON`, `URL`, `API`, etc. in their names. Use the PascalCased versions instead. +* Added models for the following packages: + + * java.lang + * java.nio.file +* Added dataflow models for the Gson deserialization library. +* Added models for the following packages: + + * okhttp3 +* Added more dataflow models for the Play Framework. +* Modified the models related to `java.nio.file.Files.copy` so that generic `[Input|Output]Stream` arguments are not considered file-related sinks. +* Dataflow analysis has a new flow step through constructors of transitive subtypes of `java.io.InputStream` that wrap an underlying data source. Previously, the step only existed for direct subtypes of `java.io.InputStream`. +* Path creation sinks modeled in `PathCreation.qll` have been added to the models-as-data sink kind `path-injection`. +* Updated the regular expression in the `HostnameSanitizer` sanitizer in the `semmle.code.java.security.RequestForgery` library to better detect strings prefixed with a hostname. +* Changed the `android-widget` Java source kind to `remote`. Any custom data extensions that use the `android-widget` source kind will need to be updated accordingly in order to continue working. +* Updated the following Java sink kind names. Any custom data extensions will need to be updated accordingly in order to continue working. + * `sql` to `sql-injection` + * `url-redirect` to `url-redirection` + * `xpath` to `xpath-injection` + * `ssti` to `template-injection` + * `logging` to `log-injection` + * `groovy` to `groovy-injection` + * `jexl` to `jexl-injection` + * `mvel` to `mvel-injection` + * `xslt` to `xslt-injection` + * `ldap` to `ldap-injection` + * `pending-intent-sent` to `pending-intents` + * `intent-start` to `intent-redirection` + * `set-hostname-verifier` to `hostname-verification` + * `header-splitting` to `response-splitting` + * `xss` to `html-injection` and `js-injection` + * `write-file` to `file-system-store` + * `create-file` and `read-file` to `path-injection` + * `open-url` and `jdbc-url` to `request-forgery` + ## 0.6.2 ### Minor Analysis Improvements diff --git a/java/ql/lib/change-notes/2023-04-19-deprecated-execcallable.md b/java/ql/lib/change-notes/2023-04-19-deprecated-execcallable.md new file mode 100644 index 00000000000..fc21d1825bf --- /dev/null +++ b/java/ql/lib/change-notes/2023-04-19-deprecated-execcallable.md @@ -0,0 +1,4 @@ +--- +category: deprecated +--- +* The `ExecCallable` class in `ExternalProcess.qll` has been deprecated. diff --git a/java/ql/lib/change-notes/2023-05-05-java-sink-kind-revamp.md b/java/ql/lib/change-notes/2023-05-05-java-sink-kind-revamp.md deleted file mode 100644 index ef54f491051..00000000000 --- a/java/ql/lib/change-notes/2023-05-05-java-sink-kind-revamp.md +++ /dev/null @@ -1,22 +0,0 @@ ---- -category: minorAnalysis ---- -* Updated the following Java sink kind names. Any custom data extensions will need to be updated accordingly in order to continue working. - * `sql` to `sql-injection` - * `url-redirect` to `url-redirection` - * `xpath` to `xpath-injection` - * `ssti` to `template-injection` - * `logging` to `log-injection` - * `groovy` to `groovy-injection` - * `jexl` to `jexl-injection` - * `mvel` to `mvel-injection` - * `xslt` to `xslt-injection` - * `ldap` to `ldap-injection` - * `pending-intent-sent` to `pending-intents` - * `intent-start` to `intent-redirection` - * `set-hostname-verifier` to `hostname-verification` - * `header-splitting` to `response-splitting` - * `xss` to `html-injection` and `js-injection` - * `write-file` to `file-system-store` - * `create-file` and `read-file` to `path-injection` - * `open-url` and `jdbc-url` to `request-forgery` diff --git a/java/ql/lib/change-notes/2023-05-12-androidwidget-source-kind-to-remote.md b/java/ql/lib/change-notes/2023-05-12-androidwidget-source-kind-to-remote.md deleted file mode 100644 index 7a2714a6527..00000000000 --- a/java/ql/lib/change-notes/2023-05-12-androidwidget-source-kind-to-remote.md +++ /dev/null @@ -1,4 +0,0 @@ ---- -category: minorAnalysis ---- -* Changed the `android-widget` Java source kind to `remote`. Any custom data extensions that use the `android-widget` source kind will need to be updated accordingly in order to continue working. diff --git a/java/ql/lib/change-notes/2023-05-17-change-hostnamesanitizingprefix-regex.md b/java/ql/lib/change-notes/2023-05-17-change-hostnamesanitizingprefix-regex.md deleted file mode 100644 index 8d81c97d9e3..00000000000 --- a/java/ql/lib/change-notes/2023-05-17-change-hostnamesanitizingprefix-regex.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -category: minorAnalysis ---- -* Updated the regular expression in the `HostnameSanitizer` sanitizer in the `semmle.code.java.security.RequestForgery` library to better detect strings prefixed with a hostname. - diff --git a/java/ql/lib/change-notes/2023-05-19-path-injection-sinks-mad.md b/java/ql/lib/change-notes/2023-05-19-path-injection-sinks-mad.md deleted file mode 100644 index ae5cd306c2b..00000000000 --- a/java/ql/lib/change-notes/2023-05-19-path-injection-sinks-mad.md +++ /dev/null @@ -1,4 +0,0 @@ ---- -category: minorAnalysis ---- -* Path creation sinks modeled in `PathCreation.qll` have been added to the models-as-data sink kind `path-injection`. diff --git a/java/ql/lib/change-notes/2023-05-22-inputstreamwrapper-transitive.md b/java/ql/lib/change-notes/2023-05-22-inputstreamwrapper-transitive.md deleted file mode 100644 index bba77d98d89..00000000000 --- a/java/ql/lib/change-notes/2023-05-22-inputstreamwrapper-transitive.md +++ /dev/null @@ -1,4 +0,0 @@ ---- -category: minorAnalysis ---- -* Dataflow analysis has a new flow step through constructors of transitive subtypes of `java.io.InputStream` that wrap an underlying data source. Previously, the step only existed for direct subtypes of `java.io.InputStream`. diff --git a/java/ql/lib/change-notes/2023-05-22-stapler-models.md b/java/ql/lib/change-notes/2023-05-22-stapler-models.md new file mode 100644 index 00000000000..37c7250b953 --- /dev/null +++ b/java/ql/lib/change-notes/2023-05-22-stapler-models.md @@ -0,0 +1,4 @@ +--- +category: minorAnalysis +--- +* Added more models for the Stapler framework. diff --git a/java/ql/lib/change-notes/2023-05-23-java-nio-file-files-copy-models-tweak.md b/java/ql/lib/change-notes/2023-05-23-java-nio-file-files-copy-models-tweak.md deleted file mode 100644 index 85fc9b89197..00000000000 --- a/java/ql/lib/change-notes/2023-05-23-java-nio-file-files-copy-models-tweak.md +++ /dev/null @@ -1,4 +0,0 @@ ---- -category: minorAnalysis ---- -Modified the models related to `java.nio.file.Files.copy` so that generic `[Input|Output]Stream` arguments are not considered file-related sinks. diff --git a/java/ql/lib/change-notes/2023-05-24-kotlin-1.9.0.md b/java/ql/lib/change-notes/2023-05-24-kotlin-1.9.0.md deleted file mode 100644 index f3647cc5488..00000000000 --- a/java/ql/lib/change-notes/2023-05-24-kotlin-1.9.0.md +++ /dev/null @@ -1,4 +0,0 @@ ---- -category: feature ---- -* Kotlin versions up to 1.9.0 are now supported. diff --git a/java/ql/lib/change-notes/2023-05-26-play-framework-models.md b/java/ql/lib/change-notes/2023-05-26-play-framework-models.md deleted file mode 100644 index 69db10413eb..00000000000 --- a/java/ql/lib/change-notes/2023-05-26-play-framework-models.md +++ /dev/null @@ -1,4 +0,0 @@ ---- -category: minorAnalysis ---- -* Added more dataflow models for the Play Framework. diff --git a/java/ql/lib/change-notes/2023-05-30-gson-models.md b/java/ql/lib/change-notes/2023-05-30-gson-models.md deleted file mode 100644 index 306d797ff1a..00000000000 --- a/java/ql/lib/change-notes/2023-05-30-gson-models.md +++ /dev/null @@ -1,4 +0,0 @@ ---- -category: minorAnalysis ---- -* Added dataflow models for the Gson deserialization library. diff --git a/java/ql/lib/change-notes/2023-05-30-new-models.md b/java/ql/lib/change-notes/2023-05-30-new-models.md deleted file mode 100644 index 24e7563d727..00000000000 --- a/java/ql/lib/change-notes/2023-05-30-new-models.md +++ /dev/null @@ -1,6 +0,0 @@ ---- -category: minorAnalysis ---- -* Added models for the following packages: - - * okhttp3 diff --git a/java/ql/lib/change-notes/2023-06-01-new-models.md b/java/ql/lib/change-notes/2023-06-01-new-models.md deleted file mode 100644 index d05b3d4d59d..00000000000 --- a/java/ql/lib/change-notes/2023-06-01-new-models.md +++ /dev/null @@ -1,7 +0,0 @@ ---- -category: minorAnalysis ---- -* Added models for the following packages: - - * java.lang - * java.nio.file diff --git a/java/ql/lib/change-notes/2023-06-02-delete-deps.md b/java/ql/lib/change-notes/2023-06-02-delete-deps.md deleted file mode 100644 index 01b2fd5a457..00000000000 --- a/java/ql/lib/change-notes/2023-06-02-delete-deps.md +++ /dev/null @@ -1,6 +0,0 @@ ---- -category: minorAnalysis ---- -* Deleted the deprecated `getRHS` predicate from the `LValue` class, use `getRhs` instead. -* Deleted the deprecated `getCFGNode` predicate from the `SsaVariable` class, use `getCfgNode` instead. -* Deleted many deprecated predicates and classes with uppercase `XML`, `JSON`, `URL`, `API`, etc. in their names. Use the PascalCased versions instead. \ No newline at end of file diff --git a/java/ql/lib/change-notes/2023-06-06-kotlin-use-with-flow.md b/java/ql/lib/change-notes/2023-06-06-kotlin-use-with-flow.md deleted file mode 100644 index b21f31aae5f..00000000000 --- a/java/ql/lib/change-notes/2023-06-06-kotlin-use-with-flow.md +++ /dev/null @@ -1,4 +0,0 @@ ---- -category: minorAnalysis ---- -* Added flow through the block arguments of `kotlin.io.use` and `kotlin.with`. diff --git a/java/ql/lib/change-notes/2023-06-06-new-models.md b/java/ql/lib/change-notes/2023-06-06-new-models.md deleted file mode 100644 index cbb80968749..00000000000 --- a/java/ql/lib/change-notes/2023-06-06-new-models.md +++ /dev/null @@ -1,15 +0,0 @@ ---- -category: minorAnalysis ---- -* Added models for the following packages: - - * com.alibaba.druid.sql - * com.fasterxml.jackson.databind - * com.jcraft.jsch - * io.netty.handler.ssl - * okhttp3 - * org.antlr.runtime - * org.fusesource.leveldbjni - * org.influxdb - * org.springframework.core.io - * org.yaml.snakeyaml diff --git a/java/ql/lib/change-notes/2023-06-22-url-tostring-model.md b/java/ql/lib/change-notes/2023-06-22-url-tostring-model.md new file mode 100644 index 00000000000..fc5a58ce4e6 --- /dev/null +++ b/java/ql/lib/change-notes/2023-06-22-url-tostring-model.md @@ -0,0 +1,4 @@ +--- +category: minorAnalysis +--- +* Added a missing summary model for the method `java.net.URL.toString`. diff --git a/java/ql/lib/change-notes/released/0.6.3.md b/java/ql/lib/change-notes/released/0.6.3.md new file mode 100644 index 00000000000..05c95272941 --- /dev/null +++ b/java/ql/lib/change-notes/released/0.6.3.md @@ -0,0 +1,57 @@ +## 0.6.3 + +### New Features + +* Kotlin versions up to 1.9.0 are now supported. + +### Minor Analysis Improvements + +* Added flow through the block arguments of `kotlin.io.use` and `kotlin.with`. +* Added models for the following packages: + + * com.alibaba.druid.sql + * com.fasterxml.jackson.databind + * com.jcraft.jsch + * io.netty.handler.ssl + * okhttp3 + * org.antlr.runtime + * org.fusesource.leveldbjni + * org.influxdb + * org.springframework.core.io + * org.yaml.snakeyaml +* Deleted the deprecated `getRHS` predicate from the `LValue` class, use `getRhs` instead. +* Deleted the deprecated `getCFGNode` predicate from the `SsaVariable` class, use `getCfgNode` instead. +* Deleted many deprecated predicates and classes with uppercase `XML`, `JSON`, `URL`, `API`, etc. in their names. Use the PascalCased versions instead. +* Added models for the following packages: + + * java.lang + * java.nio.file +* Added dataflow models for the Gson deserialization library. +* Added models for the following packages: + + * okhttp3 +* Added more dataflow models for the Play Framework. +* Modified the models related to `java.nio.file.Files.copy` so that generic `[Input|Output]Stream` arguments are not considered file-related sinks. +* Dataflow analysis has a new flow step through constructors of transitive subtypes of `java.io.InputStream` that wrap an underlying data source. Previously, the step only existed for direct subtypes of `java.io.InputStream`. +* Path creation sinks modeled in `PathCreation.qll` have been added to the models-as-data sink kind `path-injection`. +* Updated the regular expression in the `HostnameSanitizer` sanitizer in the `semmle.code.java.security.RequestForgery` library to better detect strings prefixed with a hostname. +* Changed the `android-widget` Java source kind to `remote`. Any custom data extensions that use the `android-widget` source kind will need to be updated accordingly in order to continue working. +* Updated the following Java sink kind names. Any custom data extensions will need to be updated accordingly in order to continue working. + * `sql` to `sql-injection` + * `url-redirect` to `url-redirection` + * `xpath` to `xpath-injection` + * `ssti` to `template-injection` + * `logging` to `log-injection` + * `groovy` to `groovy-injection` + * `jexl` to `jexl-injection` + * `mvel` to `mvel-injection` + * `xslt` to `xslt-injection` + * `ldap` to `ldap-injection` + * `pending-intent-sent` to `pending-intents` + * `intent-start` to `intent-redirection` + * `set-hostname-verifier` to `hostname-verification` + * `header-splitting` to `response-splitting` + * `xss` to `html-injection` and `js-injection` + * `write-file` to `file-system-store` + * `create-file` and `read-file` to `path-injection` + * `open-url` and `jdbc-url` to `request-forgery` diff --git a/java/ql/lib/codeql-pack.release.yml b/java/ql/lib/codeql-pack.release.yml index 5501a2a1cc5..b7dafe32c5d 100644 --- a/java/ql/lib/codeql-pack.release.yml +++ b/java/ql/lib/codeql-pack.release.yml @@ -1,2 +1,2 @@ --- -lastReleaseVersion: 0.6.2 +lastReleaseVersion: 0.6.3 diff --git a/java/ql/lib/config/semmlecode.dbscheme b/java/ql/lib/config/semmlecode.dbscheme index 7cbc85b1f3e..ecfcf050952 100644 --- a/java/ql/lib/config/semmlecode.dbscheme +++ b/java/ql/lib/config/semmlecode.dbscheme @@ -1219,6 +1219,7 @@ ktSyntheticBody( int kind: int ref // 1: ENUM_VALUES // 2: ENUM_VALUEOF + // 3: ENUM_ENTRIES ) ktLocalFunction( diff --git a/java/ql/lib/ext/experimental/com.jcraft.jsch.model.yml b/java/ql/lib/ext/experimental/com.jcraft.jsch.model.yml new file mode 100644 index 00000000000..1a8783d91a5 --- /dev/null +++ b/java/ql/lib/ext/experimental/com.jcraft.jsch.model.yml @@ -0,0 +1,6 @@ +extensions: + - addsTo: + pack: codeql/java-all + extensible: experimentalSinkModel + data: + - ["com.jcraft.jsch", "ChannelExec", True, "setCommand", "", "", "Argument[0]", "command-injection", "manual", "jsch-os-injection"] diff --git a/java/ql/lib/ext/generated/org.apache.commons.lang.model.yml b/java/ql/lib/ext/generated/org.apache.commons.lang.model.yml new file mode 100644 index 00000000000..56f9c251388 --- /dev/null +++ b/java/ql/lib/ext/generated/org.apache.commons.lang.model.yml @@ -0,0 +1,1695 @@ +# THIS FILE IS AN AUTO-GENERATED MODELS AS DATA FILE. DO NOT EDIT. +# Definitions of models for the org.apache.commons.lang framework. + +extensions: + - addsTo: + pack: codeql/java-all + extensible: summaryModel + data: + - ["org.apache.commons.lang.builder", "CompareToBuilder", true, "append", "(Object,Object)", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang.builder", "CompareToBuilder", true, "append", "(Object,Object,Comparator)", "", "Argument[this]", "ReturnValue", "value", "df-generated"] + - ["org.apache.commons.lang.builder", "CompareToBuilder", true, "append", "(Object[],Object[])", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang.builder", "CompareToBuilder", true, "append", "(Object[],Object[],Comparator)", "", "Argument[this]", "ReturnValue", "value", "df-generated"] + - ["org.apache.commons.lang.builder", "CompareToBuilder", true, "append", "(boolean,boolean)", "", "Argument[this]", "ReturnValue", "value", "df-generated"] + - ["org.apache.commons.lang.builder", "CompareToBuilder", true, "append", "(boolean[],boolean[])", "", "Argument[this]", "ReturnValue", "value", "df-generated"] + - ["org.apache.commons.lang.builder", "CompareToBuilder", true, "append", "(byte,byte)", "", "Argument[this]", "ReturnValue", "value", "df-generated"] + - ["org.apache.commons.lang.builder", "CompareToBuilder", true, "append", "(byte[],byte[])", "", "Argument[this]", "ReturnValue", "value", "df-generated"] + - ["org.apache.commons.lang.builder", "CompareToBuilder", true, "append", "(char,char)", "", "Argument[this]", "ReturnValue", "value", "df-generated"] + - ["org.apache.commons.lang.builder", "CompareToBuilder", true, "append", "(char[],char[])", "", "Argument[this]", "ReturnValue", "value", "df-generated"] + - ["org.apache.commons.lang.builder", "CompareToBuilder", true, "append", "(double,double)", "", "Argument[this]", "ReturnValue", "value", "df-generated"] + - ["org.apache.commons.lang.builder", "CompareToBuilder", true, "append", "(double[],double[])", "", "Argument[this]", "ReturnValue", "value", "df-generated"] + - ["org.apache.commons.lang.builder", "CompareToBuilder", true, "append", "(float,float)", "", "Argument[this]", "ReturnValue", "value", "df-generated"] + - ["org.apache.commons.lang.builder", "CompareToBuilder", true, "append", "(float[],float[])", "", "Argument[this]", "ReturnValue", "value", "df-generated"] + - ["org.apache.commons.lang.builder", "CompareToBuilder", true, "append", "(int,int)", "", "Argument[this]", "ReturnValue", "value", "df-generated"] + - ["org.apache.commons.lang.builder", "CompareToBuilder", true, "append", "(int[],int[])", "", "Argument[this]", "ReturnValue", "value", "df-generated"] + - ["org.apache.commons.lang.builder", "CompareToBuilder", true, "append", "(long,long)", "", "Argument[this]", "ReturnValue", "value", "df-generated"] + - ["org.apache.commons.lang.builder", "CompareToBuilder", true, "append", "(long[],long[])", "", "Argument[this]", "ReturnValue", "value", "df-generated"] + - ["org.apache.commons.lang.builder", "CompareToBuilder", true, "append", "(short,short)", "", "Argument[this]", "ReturnValue", "value", "df-generated"] + - ["org.apache.commons.lang.builder", "CompareToBuilder", true, "append", "(short[],short[])", "", "Argument[this]", "ReturnValue", "value", "df-generated"] + - ["org.apache.commons.lang.builder", "CompareToBuilder", true, "appendSuper", "(int)", "", "Argument[this]", "ReturnValue", "value", "df-generated"] + - ["org.apache.commons.lang.builder", "EqualsBuilder", true, "append", "(Object,Object)", "", "Argument[this]", "ReturnValue", "value", "df-generated"] + - ["org.apache.commons.lang.builder", "EqualsBuilder", true, "append", "(Object[],Object[])", "", "Argument[this]", "ReturnValue", "value", "df-generated"] + - ["org.apache.commons.lang.builder", "EqualsBuilder", true, "append", "(boolean,boolean)", "", "Argument[this]", "ReturnValue", "value", "df-generated"] + - ["org.apache.commons.lang.builder", "EqualsBuilder", true, "append", "(boolean[],boolean[])", "", "Argument[this]", "ReturnValue", "value", "df-generated"] + - ["org.apache.commons.lang.builder", "EqualsBuilder", true, "append", "(byte,byte)", "", "Argument[this]", "ReturnValue", "value", "df-generated"] + - ["org.apache.commons.lang.builder", "EqualsBuilder", true, "append", "(byte[],byte[])", "", "Argument[this]", "ReturnValue", "value", "df-generated"] + - ["org.apache.commons.lang.builder", "EqualsBuilder", true, "append", "(char,char)", "", "Argument[this]", "ReturnValue", "value", "df-generated"] + - ["org.apache.commons.lang.builder", "EqualsBuilder", true, "append", "(char[],char[])", "", "Argument[this]", "ReturnValue", "value", "df-generated"] + - ["org.apache.commons.lang.builder", "EqualsBuilder", true, "append", "(double,double)", "", "Argument[this]", "ReturnValue", "value", "df-generated"] + - ["org.apache.commons.lang.builder", "EqualsBuilder", true, "append", "(double[],double[])", "", "Argument[this]", "ReturnValue", "value", "df-generated"] + - ["org.apache.commons.lang.builder", "EqualsBuilder", true, "append", "(float,float)", "", "Argument[this]", "ReturnValue", "value", "df-generated"] + - ["org.apache.commons.lang.builder", "EqualsBuilder", true, "append", "(float[],float[])", "", "Argument[this]", "ReturnValue", "value", "df-generated"] + - ["org.apache.commons.lang.builder", "EqualsBuilder", true, "append", "(int,int)", "", "Argument[this]", "ReturnValue", "value", "df-generated"] + - ["org.apache.commons.lang.builder", "EqualsBuilder", true, "append", "(int[],int[])", "", "Argument[this]", "ReturnValue", "value", "df-generated"] + - ["org.apache.commons.lang.builder", "EqualsBuilder", true, "append", "(long,long)", "", "Argument[this]", "ReturnValue", "value", "df-generated"] + - ["org.apache.commons.lang.builder", "EqualsBuilder", true, "append", "(long[],long[])", "", "Argument[this]", "ReturnValue", "value", "df-generated"] + - ["org.apache.commons.lang.builder", "EqualsBuilder", true, "append", "(short,short)", "", "Argument[this]", "ReturnValue", "value", "df-generated"] + - ["org.apache.commons.lang.builder", "EqualsBuilder", true, "append", "(short[],short[])", "", "Argument[this]", "ReturnValue", "value", "df-generated"] + - ["org.apache.commons.lang.builder", "EqualsBuilder", true, "appendSuper", "(boolean)", "", "Argument[this]", "ReturnValue", "value", "df-generated"] + - ["org.apache.commons.lang.builder", "HashCodeBuilder", true, "append", "(Object)", "", "Argument[this]", "ReturnValue", "value", "df-generated"] + - ["org.apache.commons.lang.builder", "HashCodeBuilder", true, "append", "(Object[])", "", "Argument[this]", "ReturnValue", "value", "df-generated"] + - ["org.apache.commons.lang.builder", "HashCodeBuilder", true, "append", "(boolean)", "", "Argument[this]", "ReturnValue", "value", "df-generated"] + - ["org.apache.commons.lang.builder", "HashCodeBuilder", true, "append", "(boolean[])", "", "Argument[this]", "ReturnValue", "value", "df-generated"] + - ["org.apache.commons.lang.builder", "HashCodeBuilder", true, "append", "(byte)", "", "Argument[this]", "ReturnValue", "value", "df-generated"] + - ["org.apache.commons.lang.builder", "HashCodeBuilder", true, "append", "(byte[])", "", "Argument[this]", "ReturnValue", "value", "df-generated"] + - ["org.apache.commons.lang.builder", "HashCodeBuilder", true, "append", "(char)", "", "Argument[this]", "ReturnValue", "value", "df-generated"] + - ["org.apache.commons.lang.builder", "HashCodeBuilder", true, "append", "(char[])", "", "Argument[this]", "ReturnValue", "value", "df-generated"] + - ["org.apache.commons.lang.builder", "HashCodeBuilder", true, "append", "(double)", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang.builder", "HashCodeBuilder", true, "append", "(double[])", "", "Argument[this]", "ReturnValue", "value", "df-generated"] + - ["org.apache.commons.lang.builder", "HashCodeBuilder", true, "append", "(float)", "", "Argument[this]", "ReturnValue", "value", "df-generated"] + - ["org.apache.commons.lang.builder", "HashCodeBuilder", true, "append", "(float[])", "", "Argument[this]", "ReturnValue", "value", "df-generated"] + - ["org.apache.commons.lang.builder", "HashCodeBuilder", true, "append", "(int)", "", "Argument[this]", "ReturnValue", "value", "df-generated"] + - ["org.apache.commons.lang.builder", "HashCodeBuilder", true, "append", "(int[])", "", "Argument[this]", "ReturnValue", "value", "df-generated"] + - ["org.apache.commons.lang.builder", "HashCodeBuilder", true, "append", "(long)", "", "Argument[this]", "ReturnValue", "value", "df-generated"] + - ["org.apache.commons.lang.builder", "HashCodeBuilder", true, "append", "(long[])", "", "Argument[this]", "ReturnValue", "value", "df-generated"] + - ["org.apache.commons.lang.builder", "HashCodeBuilder", true, "append", "(short)", "", "Argument[this]", "ReturnValue", "value", "df-generated"] + - ["org.apache.commons.lang.builder", "HashCodeBuilder", true, "append", "(short[])", "", "Argument[this]", "ReturnValue", "value", "df-generated"] + - ["org.apache.commons.lang.builder", "HashCodeBuilder", true, "appendSuper", "(int)", "", "Argument[this]", "ReturnValue", "value", "df-generated"] + - ["org.apache.commons.lang.builder", "ReflectionToStringBuilder", true, "ReflectionToStringBuilder", "(Object)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.commons.lang.builder", "ReflectionToStringBuilder", true, "ReflectionToStringBuilder", "(Object,ToStringStyle)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.commons.lang.builder", "ReflectionToStringBuilder", true, "ReflectionToStringBuilder", "(Object,ToStringStyle)", "", "Argument[1]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.commons.lang.builder", "ReflectionToStringBuilder", true, "ReflectionToStringBuilder", "(Object,ToStringStyle,StringBuffer)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.commons.lang.builder", "ReflectionToStringBuilder", true, "ReflectionToStringBuilder", "(Object,ToStringStyle,StringBuffer)", "", "Argument[1]", "Argument[2]", "taint", "df-generated"] + - ["org.apache.commons.lang.builder", "ReflectionToStringBuilder", true, "ReflectionToStringBuilder", "(Object,ToStringStyle,StringBuffer)", "", "Argument[1]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.commons.lang.builder", "ReflectionToStringBuilder", true, "ReflectionToStringBuilder", "(Object,ToStringStyle,StringBuffer)", "", "Argument[2]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.commons.lang.builder", "ReflectionToStringBuilder", true, "ReflectionToStringBuilder", "(Object,ToStringStyle,StringBuffer,Class,boolean)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.commons.lang.builder", "ReflectionToStringBuilder", true, "ReflectionToStringBuilder", "(Object,ToStringStyle,StringBuffer,Class,boolean)", "", "Argument[1]", "Argument[2]", "taint", "df-generated"] + - ["org.apache.commons.lang.builder", "ReflectionToStringBuilder", true, "ReflectionToStringBuilder", "(Object,ToStringStyle,StringBuffer,Class,boolean)", "", "Argument[1]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.commons.lang.builder", "ReflectionToStringBuilder", true, "ReflectionToStringBuilder", "(Object,ToStringStyle,StringBuffer,Class,boolean)", "", "Argument[2]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.commons.lang.builder", "ReflectionToStringBuilder", true, "ReflectionToStringBuilder", "(Object,ToStringStyle,StringBuffer,Class,boolean,boolean)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.commons.lang.builder", "ReflectionToStringBuilder", true, "ReflectionToStringBuilder", "(Object,ToStringStyle,StringBuffer,Class,boolean,boolean)", "", "Argument[1]", "Argument[2]", "taint", "df-generated"] + - ["org.apache.commons.lang.builder", "ReflectionToStringBuilder", true, "ReflectionToStringBuilder", "(Object,ToStringStyle,StringBuffer,Class,boolean,boolean)", "", "Argument[1]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.commons.lang.builder", "ReflectionToStringBuilder", true, "ReflectionToStringBuilder", "(Object,ToStringStyle,StringBuffer,Class,boolean,boolean)", "", "Argument[2]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.commons.lang.builder", "ReflectionToStringBuilder", true, "getExcludeFieldNames", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang.builder", "ReflectionToStringBuilder", true, "reflectionAppendArray", "(Object)", "", "Argument[this]", "ReturnValue", "value", "df-generated"] + - ["org.apache.commons.lang.builder", "ReflectionToStringBuilder", true, "setExcludeFieldNames", "(String[])", "", "Argument[this]", "ReturnValue", "value", "df-generated"] + - ["org.apache.commons.lang.builder", "ReflectionToStringBuilder", true, "toString", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang.builder", "ToStringBuilder", true, "ToStringBuilder", "(Object)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.commons.lang.builder", "ToStringBuilder", true, "ToStringBuilder", "(Object,ToStringStyle)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.commons.lang.builder", "ToStringBuilder", true, "ToStringBuilder", "(Object,ToStringStyle)", "", "Argument[1]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.commons.lang.builder", "ToStringBuilder", true, "ToStringBuilder", "(Object,ToStringStyle,StringBuffer)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.commons.lang.builder", "ToStringBuilder", true, "ToStringBuilder", "(Object,ToStringStyle,StringBuffer)", "", "Argument[1]", "Argument[2]", "taint", "df-generated"] + - ["org.apache.commons.lang.builder", "ToStringBuilder", true, "ToStringBuilder", "(Object,ToStringStyle,StringBuffer)", "", "Argument[1]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.commons.lang.builder", "ToStringBuilder", true, "ToStringBuilder", "(Object,ToStringStyle,StringBuffer)", "", "Argument[2]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.commons.lang.builder", "ToStringBuilder", true, "append", "(Object)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.commons.lang.builder", "ToStringBuilder", true, "append", "(Object)", "", "Argument[this]", "ReturnValue", "value", "df-generated"] + - ["org.apache.commons.lang.builder", "ToStringBuilder", true, "append", "(Object[])", "", "Argument[0].ArrayElement", "Argument[this]", "taint", "df-generated"] + - ["org.apache.commons.lang.builder", "ToStringBuilder", true, "append", "(Object[])", "", "Argument[this]", "ReturnValue", "value", "df-generated"] + - ["org.apache.commons.lang.builder", "ToStringBuilder", true, "append", "(String,Object)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.commons.lang.builder", "ToStringBuilder", true, "append", "(String,Object)", "", "Argument[1]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.commons.lang.builder", "ToStringBuilder", true, "append", "(String,Object)", "", "Argument[this]", "ReturnValue", "value", "df-generated"] + - ["org.apache.commons.lang.builder", "ToStringBuilder", true, "append", "(String,Object,boolean)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.commons.lang.builder", "ToStringBuilder", true, "append", "(String,Object,boolean)", "", "Argument[1]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.commons.lang.builder", "ToStringBuilder", true, "append", "(String,Object,boolean)", "", "Argument[this]", "ReturnValue", "value", "df-generated"] + - ["org.apache.commons.lang.builder", "ToStringBuilder", true, "append", "(String,Object[])", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.commons.lang.builder", "ToStringBuilder", true, "append", "(String,Object[])", "", "Argument[1].ArrayElement", "Argument[this]", "taint", "df-generated"] + - ["org.apache.commons.lang.builder", "ToStringBuilder", true, "append", "(String,Object[])", "", "Argument[this]", "ReturnValue", "value", "df-generated"] + - ["org.apache.commons.lang.builder", "ToStringBuilder", true, "append", "(String,Object[],boolean)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.commons.lang.builder", "ToStringBuilder", true, "append", "(String,Object[],boolean)", "", "Argument[1].ArrayElement", "Argument[this]", "taint", "df-generated"] + - ["org.apache.commons.lang.builder", "ToStringBuilder", true, "append", "(String,Object[],boolean)", "", "Argument[this]", "ReturnValue", "value", "df-generated"] + - ["org.apache.commons.lang.builder", "ToStringBuilder", true, "append", "(String,boolean)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.commons.lang.builder", "ToStringBuilder", true, "append", "(String,boolean)", "", "Argument[this]", "ReturnValue", "value", "df-generated"] + - ["org.apache.commons.lang.builder", "ToStringBuilder", true, "append", "(String,boolean[])", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.commons.lang.builder", "ToStringBuilder", true, "append", "(String,boolean[])", "", "Argument[this]", "ReturnValue", "value", "df-generated"] + - ["org.apache.commons.lang.builder", "ToStringBuilder", true, "append", "(String,boolean[],boolean)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.commons.lang.builder", "ToStringBuilder", true, "append", "(String,boolean[],boolean)", "", "Argument[this]", "ReturnValue", "value", "df-generated"] + - ["org.apache.commons.lang.builder", "ToStringBuilder", true, "append", "(String,byte)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.commons.lang.builder", "ToStringBuilder", true, "append", "(String,byte)", "", "Argument[this]", "ReturnValue", "value", "df-generated"] + - ["org.apache.commons.lang.builder", "ToStringBuilder", true, "append", "(String,byte[])", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.commons.lang.builder", "ToStringBuilder", true, "append", "(String,byte[])", "", "Argument[this]", "ReturnValue", "value", "df-generated"] + - ["org.apache.commons.lang.builder", "ToStringBuilder", true, "append", "(String,byte[],boolean)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.commons.lang.builder", "ToStringBuilder", true, "append", "(String,byte[],boolean)", "", "Argument[this]", "ReturnValue", "value", "df-generated"] + - ["org.apache.commons.lang.builder", "ToStringBuilder", true, "append", "(String,char)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.commons.lang.builder", "ToStringBuilder", true, "append", "(String,char)", "", "Argument[this]", "ReturnValue", "value", "df-generated"] + - ["org.apache.commons.lang.builder", "ToStringBuilder", true, "append", "(String,char[])", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.commons.lang.builder", "ToStringBuilder", true, "append", "(String,char[])", "", "Argument[this]", "ReturnValue", "value", "df-generated"] + - ["org.apache.commons.lang.builder", "ToStringBuilder", true, "append", "(String,char[],boolean)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.commons.lang.builder", "ToStringBuilder", true, "append", "(String,char[],boolean)", "", "Argument[this]", "ReturnValue", "value", "df-generated"] + - ["org.apache.commons.lang.builder", "ToStringBuilder", true, "append", "(String,double)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.commons.lang.builder", "ToStringBuilder", true, "append", "(String,double)", "", "Argument[this]", "ReturnValue", "value", "df-generated"] + - ["org.apache.commons.lang.builder", "ToStringBuilder", true, "append", "(String,double[])", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.commons.lang.builder", "ToStringBuilder", true, "append", "(String,double[])", "", "Argument[this]", "ReturnValue", "value", "df-generated"] + - ["org.apache.commons.lang.builder", "ToStringBuilder", true, "append", "(String,double[],boolean)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.commons.lang.builder", "ToStringBuilder", true, "append", "(String,double[],boolean)", "", "Argument[this]", "ReturnValue", "value", "df-generated"] + - ["org.apache.commons.lang.builder", "ToStringBuilder", true, "append", "(String,float)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.commons.lang.builder", "ToStringBuilder", true, "append", "(String,float)", "", "Argument[this]", "ReturnValue", "value", "df-generated"] + - ["org.apache.commons.lang.builder", "ToStringBuilder", true, "append", "(String,float[])", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.commons.lang.builder", "ToStringBuilder", true, "append", "(String,float[])", "", "Argument[this]", "ReturnValue", "value", "df-generated"] + - ["org.apache.commons.lang.builder", "ToStringBuilder", true, "append", "(String,float[],boolean)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.commons.lang.builder", "ToStringBuilder", true, "append", "(String,float[],boolean)", "", "Argument[this]", "ReturnValue", "value", "df-generated"] + - ["org.apache.commons.lang.builder", "ToStringBuilder", true, "append", "(String,int)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.commons.lang.builder", "ToStringBuilder", true, "append", "(String,int)", "", "Argument[this]", "ReturnValue", "value", "df-generated"] + - ["org.apache.commons.lang.builder", "ToStringBuilder", true, "append", "(String,int[])", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.commons.lang.builder", "ToStringBuilder", true, "append", "(String,int[])", "", "Argument[this]", "ReturnValue", "value", "df-generated"] + - ["org.apache.commons.lang.builder", "ToStringBuilder", true, "append", "(String,int[],boolean)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.commons.lang.builder", "ToStringBuilder", true, "append", "(String,int[],boolean)", "", "Argument[this]", "ReturnValue", "value", "df-generated"] + - ["org.apache.commons.lang.builder", "ToStringBuilder", true, "append", "(String,long)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.commons.lang.builder", "ToStringBuilder", true, "append", "(String,long)", "", "Argument[this]", "ReturnValue", "value", "df-generated"] + - ["org.apache.commons.lang.builder", "ToStringBuilder", true, "append", "(String,long[])", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.commons.lang.builder", "ToStringBuilder", true, "append", "(String,long[])", "", "Argument[this]", "ReturnValue", "value", "df-generated"] + - ["org.apache.commons.lang.builder", "ToStringBuilder", true, "append", "(String,long[],boolean)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.commons.lang.builder", "ToStringBuilder", true, "append", "(String,long[],boolean)", "", "Argument[this]", "ReturnValue", "value", "df-generated"] + - ["org.apache.commons.lang.builder", "ToStringBuilder", true, "append", "(String,short)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.commons.lang.builder", "ToStringBuilder", true, "append", "(String,short)", "", "Argument[this]", "ReturnValue", "value", "df-generated"] + - ["org.apache.commons.lang.builder", "ToStringBuilder", true, "append", "(String,short[])", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.commons.lang.builder", "ToStringBuilder", true, "append", "(String,short[])", "", "Argument[this]", "ReturnValue", "value", "df-generated"] + - ["org.apache.commons.lang.builder", "ToStringBuilder", true, "append", "(String,short[],boolean)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.commons.lang.builder", "ToStringBuilder", true, "append", "(String,short[],boolean)", "", "Argument[this]", "ReturnValue", "value", "df-generated"] + - ["org.apache.commons.lang.builder", "ToStringBuilder", true, "append", "(boolean)", "", "Argument[this]", "ReturnValue", "value", "df-generated"] + - ["org.apache.commons.lang.builder", "ToStringBuilder", true, "append", "(boolean[])", "", "Argument[this]", "ReturnValue", "value", "df-generated"] + - ["org.apache.commons.lang.builder", "ToStringBuilder", true, "append", "(byte)", "", "Argument[this]", "ReturnValue", "value", "df-generated"] + - ["org.apache.commons.lang.builder", "ToStringBuilder", true, "append", "(byte[])", "", "Argument[this]", "ReturnValue", "value", "df-generated"] + - ["org.apache.commons.lang.builder", "ToStringBuilder", true, "append", "(char)", "", "Argument[this]", "ReturnValue", "value", "df-generated"] + - ["org.apache.commons.lang.builder", "ToStringBuilder", true, "append", "(char[])", "", "Argument[this]", "ReturnValue", "value", "df-generated"] + - ["org.apache.commons.lang.builder", "ToStringBuilder", true, "append", "(double)", "", "Argument[this]", "ReturnValue", "value", "df-generated"] + - ["org.apache.commons.lang.builder", "ToStringBuilder", true, "append", "(double[])", "", "Argument[this]", "ReturnValue", "value", "df-generated"] + - ["org.apache.commons.lang.builder", "ToStringBuilder", true, "append", "(float)", "", "Argument[this]", "ReturnValue", "value", "df-generated"] + - ["org.apache.commons.lang.builder", "ToStringBuilder", true, "append", "(float[])", "", "Argument[this]", "ReturnValue", "value", "df-generated"] + - ["org.apache.commons.lang.builder", "ToStringBuilder", true, "append", "(int)", "", "Argument[this]", "ReturnValue", "value", "df-generated"] + - ["org.apache.commons.lang.builder", "ToStringBuilder", true, "append", "(int[])", "", "Argument[this]", "ReturnValue", "value", "df-generated"] + - ["org.apache.commons.lang.builder", "ToStringBuilder", true, "append", "(long)", "", "Argument[this]", "ReturnValue", "value", "df-generated"] + - ["org.apache.commons.lang.builder", "ToStringBuilder", true, "append", "(long[])", "", "Argument[this]", "ReturnValue", "value", "df-generated"] + - ["org.apache.commons.lang.builder", "ToStringBuilder", true, "append", "(short)", "", "Argument[this]", "ReturnValue", "value", "df-generated"] + - ["org.apache.commons.lang.builder", "ToStringBuilder", true, "append", "(short[])", "", "Argument[this]", "ReturnValue", "value", "df-generated"] + - ["org.apache.commons.lang.builder", "ToStringBuilder", true, "appendAsObjectToString", "(Object)", "", "Argument[this]", "ReturnValue", "value", "df-generated"] + - ["org.apache.commons.lang.builder", "ToStringBuilder", true, "appendSuper", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.commons.lang.builder", "ToStringBuilder", true, "appendSuper", "(String)", "", "Argument[this]", "ReturnValue", "value", "df-generated"] + - ["org.apache.commons.lang.builder", "ToStringBuilder", true, "appendToString", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.commons.lang.builder", "ToStringBuilder", true, "appendToString", "(String)", "", "Argument[this]", "ReturnValue", "value", "df-generated"] + - ["org.apache.commons.lang.builder", "ToStringBuilder", true, "getObject", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang.builder", "ToStringBuilder", true, "getStringBuffer", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang.builder", "ToStringBuilder", true, "getStyle", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang.builder", "ToStringBuilder", true, "toString", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang.builder", "ToStringStyle", true, "append", "(StringBuffer,String,Object,Boolean)", "", "Argument[1]", "Argument[0]", "taint", "df-generated"] + - ["org.apache.commons.lang.builder", "ToStringStyle", true, "append", "(StringBuffer,String,Object,Boolean)", "", "Argument[2]", "Argument[0]", "taint", "df-generated"] + - ["org.apache.commons.lang.builder", "ToStringStyle", true, "append", "(StringBuffer,String,Object,Boolean)", "", "Argument[this]", "Argument[0]", "taint", "df-generated"] + - ["org.apache.commons.lang.builder", "ToStringStyle", true, "append", "(StringBuffer,String,Object[],Boolean)", "", "Argument[1]", "Argument[0]", "taint", "df-generated"] + - ["org.apache.commons.lang.builder", "ToStringStyle", true, "append", "(StringBuffer,String,Object[],Boolean)", "", "Argument[2].ArrayElement", "Argument[0]", "taint", "df-generated"] + - ["org.apache.commons.lang.builder", "ToStringStyle", true, "append", "(StringBuffer,String,Object[],Boolean)", "", "Argument[this]", "Argument[0]", "taint", "df-generated"] + - ["org.apache.commons.lang.builder", "ToStringStyle", true, "append", "(StringBuffer,String,boolean)", "", "Argument[1]", "Argument[0]", "taint", "df-generated"] + - ["org.apache.commons.lang.builder", "ToStringStyle", true, "append", "(StringBuffer,String,boolean)", "", "Argument[this]", "Argument[0]", "taint", "df-generated"] + - ["org.apache.commons.lang.builder", "ToStringStyle", true, "append", "(StringBuffer,String,boolean[],Boolean)", "", "Argument[1]", "Argument[0]", "taint", "df-generated"] + - ["org.apache.commons.lang.builder", "ToStringStyle", true, "append", "(StringBuffer,String,boolean[],Boolean)", "", "Argument[this]", "Argument[0]", "taint", "df-generated"] + - ["org.apache.commons.lang.builder", "ToStringStyle", true, "append", "(StringBuffer,String,byte)", "", "Argument[1]", "Argument[0]", "taint", "df-generated"] + - ["org.apache.commons.lang.builder", "ToStringStyle", true, "append", "(StringBuffer,String,byte)", "", "Argument[this]", "Argument[0]", "taint", "df-generated"] + - ["org.apache.commons.lang.builder", "ToStringStyle", true, "append", "(StringBuffer,String,byte[],Boolean)", "", "Argument[1]", "Argument[0]", "taint", "df-generated"] + - ["org.apache.commons.lang.builder", "ToStringStyle", true, "append", "(StringBuffer,String,byte[],Boolean)", "", "Argument[this]", "Argument[0]", "taint", "df-generated"] + - ["org.apache.commons.lang.builder", "ToStringStyle", true, "append", "(StringBuffer,String,char)", "", "Argument[1]", "Argument[0]", "taint", "df-generated"] + - ["org.apache.commons.lang.builder", "ToStringStyle", true, "append", "(StringBuffer,String,char)", "", "Argument[this]", "Argument[0]", "taint", "df-generated"] + - ["org.apache.commons.lang.builder", "ToStringStyle", true, "append", "(StringBuffer,String,char[],Boolean)", "", "Argument[1]", "Argument[0]", "taint", "df-generated"] + - ["org.apache.commons.lang.builder", "ToStringStyle", true, "append", "(StringBuffer,String,char[],Boolean)", "", "Argument[this]", "Argument[0]", "taint", "df-generated"] + - ["org.apache.commons.lang.builder", "ToStringStyle", true, "append", "(StringBuffer,String,double)", "", "Argument[1]", "Argument[0]", "taint", "df-generated"] + - ["org.apache.commons.lang.builder", "ToStringStyle", true, "append", "(StringBuffer,String,double)", "", "Argument[this]", "Argument[0]", "taint", "df-generated"] + - ["org.apache.commons.lang.builder", "ToStringStyle", true, "append", "(StringBuffer,String,double[],Boolean)", "", "Argument[1]", "Argument[0]", "taint", "df-generated"] + - ["org.apache.commons.lang.builder", "ToStringStyle", true, "append", "(StringBuffer,String,double[],Boolean)", "", "Argument[this]", "Argument[0]", "taint", "df-generated"] + - ["org.apache.commons.lang.builder", "ToStringStyle", true, "append", "(StringBuffer,String,float)", "", "Argument[1]", "Argument[0]", "taint", "df-generated"] + - ["org.apache.commons.lang.builder", "ToStringStyle", true, "append", "(StringBuffer,String,float)", "", "Argument[this]", "Argument[0]", "taint", "df-generated"] + - ["org.apache.commons.lang.builder", "ToStringStyle", true, "append", "(StringBuffer,String,float[],Boolean)", "", "Argument[1]", "Argument[0]", "taint", "df-generated"] + - ["org.apache.commons.lang.builder", "ToStringStyle", true, "append", "(StringBuffer,String,float[],Boolean)", "", "Argument[this]", "Argument[0]", "taint", "df-generated"] + - ["org.apache.commons.lang.builder", "ToStringStyle", true, "append", "(StringBuffer,String,int)", "", "Argument[1]", "Argument[0]", "taint", "df-generated"] + - ["org.apache.commons.lang.builder", "ToStringStyle", true, "append", "(StringBuffer,String,int)", "", "Argument[this]", "Argument[0]", "taint", "df-generated"] + - ["org.apache.commons.lang.builder", "ToStringStyle", true, "append", "(StringBuffer,String,int[],Boolean)", "", "Argument[1]", "Argument[0]", "taint", "df-generated"] + - ["org.apache.commons.lang.builder", "ToStringStyle", true, "append", "(StringBuffer,String,int[],Boolean)", "", "Argument[this]", "Argument[0]", "taint", "df-generated"] + - ["org.apache.commons.lang.builder", "ToStringStyle", true, "append", "(StringBuffer,String,long)", "", "Argument[1]", "Argument[0]", "taint", "df-generated"] + - ["org.apache.commons.lang.builder", "ToStringStyle", true, "append", "(StringBuffer,String,long)", "", "Argument[this]", "Argument[0]", "taint", "df-generated"] + - ["org.apache.commons.lang.builder", "ToStringStyle", true, "append", "(StringBuffer,String,long[],Boolean)", "", "Argument[1]", "Argument[0]", "taint", "df-generated"] + - ["org.apache.commons.lang.builder", "ToStringStyle", true, "append", "(StringBuffer,String,long[],Boolean)", "", "Argument[this]", "Argument[0]", "taint", "df-generated"] + - ["org.apache.commons.lang.builder", "ToStringStyle", true, "append", "(StringBuffer,String,short)", "", "Argument[1]", "Argument[0]", "taint", "df-generated"] + - ["org.apache.commons.lang.builder", "ToStringStyle", true, "append", "(StringBuffer,String,short)", "", "Argument[this]", "Argument[0]", "taint", "df-generated"] + - ["org.apache.commons.lang.builder", "ToStringStyle", true, "append", "(StringBuffer,String,short[],Boolean)", "", "Argument[1]", "Argument[0]", "taint", "df-generated"] + - ["org.apache.commons.lang.builder", "ToStringStyle", true, "append", "(StringBuffer,String,short[],Boolean)", "", "Argument[this]", "Argument[0]", "taint", "df-generated"] + - ["org.apache.commons.lang.builder", "ToStringStyle", true, "appendEnd", "(StringBuffer,Object)", "", "Argument[this]", "Argument[0]", "taint", "df-generated"] + - ["org.apache.commons.lang.builder", "ToStringStyle", true, "appendStart", "(StringBuffer,Object)", "", "Argument[this]", "Argument[0]", "taint", "df-generated"] + - ["org.apache.commons.lang.builder", "ToStringStyle", true, "appendSuper", "(StringBuffer,String)", "", "Argument[1]", "Argument[0]", "taint", "df-generated"] + - ["org.apache.commons.lang.builder", "ToStringStyle", true, "appendSuper", "(StringBuffer,String)", "", "Argument[this]", "Argument[0]", "taint", "df-generated"] + - ["org.apache.commons.lang.builder", "ToStringStyle", true, "appendToString", "(StringBuffer,String)", "", "Argument[1]", "Argument[0]", "taint", "df-generated"] + - ["org.apache.commons.lang.builder", "ToStringStyle", true, "appendToString", "(StringBuffer,String)", "", "Argument[this]", "Argument[0]", "taint", "df-generated"] + - ["org.apache.commons.lang.builder", "ToStringStyle", true, "getArrayEnd", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang.builder", "ToStringStyle", true, "getArraySeparator", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang.builder", "ToStringStyle", true, "getArrayStart", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang.builder", "ToStringStyle", true, "getContentEnd", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang.builder", "ToStringStyle", true, "getContentStart", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang.builder", "ToStringStyle", true, "getFieldNameValueSeparator", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang.builder", "ToStringStyle", true, "getFieldSeparator", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang.builder", "ToStringStyle", true, "getNullText", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang.builder", "ToStringStyle", true, "getSizeEndText", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang.builder", "ToStringStyle", true, "getSizeStartText", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang.builder", "ToStringStyle", true, "getSummaryObjectEndText", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang.builder", "ToStringStyle", true, "getSummaryObjectStartText", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang.builder", "ToStringStyle", true, "setArrayEnd", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.commons.lang.builder", "ToStringStyle", true, "setArraySeparator", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.commons.lang.builder", "ToStringStyle", true, "setArrayStart", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.commons.lang.builder", "ToStringStyle", true, "setContentEnd", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.commons.lang.builder", "ToStringStyle", true, "setContentStart", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.commons.lang.builder", "ToStringStyle", true, "setFieldNameValueSeparator", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.commons.lang.builder", "ToStringStyle", true, "setFieldSeparator", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.commons.lang.builder", "ToStringStyle", true, "setNullText", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.commons.lang.builder", "ToStringStyle", true, "setSizeEndText", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.commons.lang.builder", "ToStringStyle", true, "setSizeStartText", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.commons.lang.builder", "ToStringStyle", true, "setSummaryObjectEndText", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.commons.lang.builder", "ToStringStyle", true, "setSummaryObjectStartText", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.commons.lang.enums", "Enum", true, "getName", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang.enums", "Enum", true, "toString", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang.enums", "ValuedEnum", true, "toString", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang.exception", "ExceptionUtils", true, "getCause", "(Throwable)", "", "Argument[0]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang.exception", "ExceptionUtils", true, "getCause", "(Throwable,String[])", "", "Argument[0]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang.exception", "ExceptionUtils", true, "getMessage", "(Throwable)", "", "Argument[0]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang.exception", "ExceptionUtils", true, "getRootCause", "(Throwable)", "", "Argument[0]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang.exception", "ExceptionUtils", true, "getRootCauseMessage", "(Throwable)", "", "Argument[0]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang.exception", "ExceptionUtils", true, "getRootCauseStackTrace", "(Throwable)", "", "Argument[0]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang.exception", "ExceptionUtils", true, "getThrowableList", "(Throwable)", "", "Argument[0]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang.exception", "ExceptionUtils", true, "getThrowables", "(Throwable)", "", "Argument[0]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang.exception", "Nestable", true, "getCause", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang.exception", "Nestable", true, "getMessage", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang.exception", "Nestable", true, "getMessage", "(int)", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang.exception", "Nestable", true, "getThrowable", "(int)", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang.exception", "Nestable", true, "getThrowables", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang.exception", "NestableDelegate", true, "NestableDelegate", "(Nestable)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.commons.lang.exception", "NestableDelegate", true, "getMessage", "(String)", "", "Argument[0]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang.exception", "NestableDelegate", true, "getMessage", "(int)", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang.exception", "NestableDelegate", true, "getMessages", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang.exception", "NestableDelegate", true, "getThrowable", "(int)", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang.exception", "NestableDelegate", true, "getThrowables", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang.exception", "NestableError", true, "NestableError", "(String,Throwable)", "", "Argument[1]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.commons.lang.exception", "NestableError", true, "NestableError", "(Throwable)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.commons.lang.exception", "NestableException", true, "NestableException", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.commons.lang.exception", "NestableException", true, "NestableException", "(String,Throwable)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.commons.lang.exception", "NestableException", true, "NestableException", "(String,Throwable)", "", "Argument[1]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.commons.lang.exception", "NestableException", true, "NestableException", "(Throwable)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.commons.lang.exception", "NestableRuntimeException", true, "NestableRuntimeException", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.commons.lang.exception", "NestableRuntimeException", true, "NestableRuntimeException", "(String,Throwable)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.commons.lang.exception", "NestableRuntimeException", true, "NestableRuntimeException", "(String,Throwable)", "", "Argument[1]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.commons.lang.exception", "NestableRuntimeException", true, "NestableRuntimeException", "(Throwable)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.commons.lang.math", "DoubleRange", false, "toString", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang.math", "FloatRange", false, "toString", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang.math", "Fraction", false, "abs", "()", "", "Argument[this]", "ReturnValue", "value", "df-generated"] + - ["org.apache.commons.lang.math", "Fraction", false, "pow", "(int)", "", "Argument[this]", "ReturnValue", "value", "df-generated"] + - ["org.apache.commons.lang.math", "Fraction", false, "reduce", "()", "", "Argument[this]", "ReturnValue", "value", "df-generated"] + - ["org.apache.commons.lang.math", "IntRange", false, "toString", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang.math", "LongRange", false, "toString", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang.math", "NumberRange", false, "toString", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang.mutable", "Mutable", true, "getValue", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang.mutable", "Mutable", true, "setValue", "(Object)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.commons.lang.mutable", "MutableObject", true, "MutableObject", "(Object)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.commons.lang.reflect", "ConstructorUtils", true, "getAccessibleConstructor", "(Constructor)", "", "Argument[0]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang.reflect", "MethodUtils", true, "getAccessibleMethod", "(Method)", "", "Argument[0]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang.text", "CompositeFormat", true, "CompositeFormat", "(Format,Format)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.commons.lang.text", "CompositeFormat", true, "CompositeFormat", "(Format,Format)", "", "Argument[1]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.commons.lang.text", "CompositeFormat", true, "getFormatter", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang.text", "CompositeFormat", true, "getParser", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang.text", "ExtendedMessageFormat", true, "ExtendedMessageFormat", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.commons.lang.text", "ExtendedMessageFormat", true, "ExtendedMessageFormat", "(String,Locale)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.commons.lang.text", "ExtendedMessageFormat", true, "ExtendedMessageFormat", "(String,Locale,Map)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.commons.lang.text", "ExtendedMessageFormat", true, "ExtendedMessageFormat", "(String,Locale,Map)", "", "Argument[2].Element", "Argument[this]", "taint", "df-generated"] + - ["org.apache.commons.lang.text", "ExtendedMessageFormat", true, "ExtendedMessageFormat", "(String,Map)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.commons.lang.text", "ExtendedMessageFormat", true, "ExtendedMessageFormat", "(String,Map)", "", "Argument[1].Element", "Argument[this]", "taint", "df-generated"] + - ["org.apache.commons.lang.text", "StrBuilder", true, "StrBuilder", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.commons.lang.text", "StrBuilder", true, "append", "(Object)", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang.text", "StrBuilder", true, "append", "(StrBuilder)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.commons.lang.text", "StrBuilder", true, "append", "(StrBuilder)", "", "Argument[this]", "ReturnValue", "value", "df-generated"] + - ["org.apache.commons.lang.text", "StrBuilder", true, "append", "(StrBuilder,int,int)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.commons.lang.text", "StrBuilder", true, "append", "(StrBuilder,int,int)", "", "Argument[this]", "ReturnValue", "value", "df-generated"] + - ["org.apache.commons.lang.text", "StrBuilder", true, "append", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.commons.lang.text", "StrBuilder", true, "append", "(String)", "", "Argument[this]", "ReturnValue", "value", "df-generated"] + - ["org.apache.commons.lang.text", "StrBuilder", true, "append", "(String,int,int)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.commons.lang.text", "StrBuilder", true, "append", "(String,int,int)", "", "Argument[this]", "ReturnValue", "value", "df-generated"] + - ["org.apache.commons.lang.text", "StrBuilder", true, "append", "(StringBuffer)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.commons.lang.text", "StrBuilder", true, "append", "(StringBuffer)", "", "Argument[this]", "ReturnValue", "value", "df-generated"] + - ["org.apache.commons.lang.text", "StrBuilder", true, "append", "(StringBuffer,int,int)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.commons.lang.text", "StrBuilder", true, "append", "(StringBuffer,int,int)", "", "Argument[this]", "ReturnValue", "value", "df-generated"] + - ["org.apache.commons.lang.text", "StrBuilder", true, "append", "(boolean)", "", "Argument[this]", "ReturnValue", "value", "df-generated"] + - ["org.apache.commons.lang.text", "StrBuilder", true, "append", "(char)", "", "Argument[this]", "ReturnValue", "value", "df-generated"] + - ["org.apache.commons.lang.text", "StrBuilder", true, "append", "(char[])", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.commons.lang.text", "StrBuilder", true, "append", "(char[])", "", "Argument[this]", "ReturnValue", "value", "df-generated"] + - ["org.apache.commons.lang.text", "StrBuilder", true, "append", "(char[],int,int)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.commons.lang.text", "StrBuilder", true, "append", "(char[],int,int)", "", "Argument[this]", "ReturnValue", "value", "df-generated"] + - ["org.apache.commons.lang.text", "StrBuilder", true, "append", "(double)", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang.text", "StrBuilder", true, "append", "(float)", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang.text", "StrBuilder", true, "append", "(int)", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang.text", "StrBuilder", true, "append", "(long)", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang.text", "StrBuilder", true, "appendAll", "(Collection)", "", "Argument[this]", "ReturnValue", "value", "df-generated"] + - ["org.apache.commons.lang.text", "StrBuilder", true, "appendAll", "(Iterator)", "", "Argument[this]", "ReturnValue", "value", "df-generated"] + - ["org.apache.commons.lang.text", "StrBuilder", true, "appendAll", "(Object[])", "", "Argument[this]", "ReturnValue", "value", "df-generated"] + - ["org.apache.commons.lang.text", "StrBuilder", true, "appendFixedWidthPadLeft", "(Object,int,char)", "", "Argument[this]", "ReturnValue", "value", "df-generated"] + - ["org.apache.commons.lang.text", "StrBuilder", true, "appendFixedWidthPadLeft", "(int,int,char)", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang.text", "StrBuilder", true, "appendFixedWidthPadRight", "(Object,int,char)", "", "Argument[this]", "ReturnValue", "value", "df-generated"] + - ["org.apache.commons.lang.text", "StrBuilder", true, "appendFixedWidthPadRight", "(int,int,char)", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang.text", "StrBuilder", true, "appendNewLine", "()", "", "Argument[this]", "ReturnValue", "value", "df-generated"] + - ["org.apache.commons.lang.text", "StrBuilder", true, "appendNull", "()", "", "Argument[this]", "ReturnValue", "value", "df-generated"] + - ["org.apache.commons.lang.text", "StrBuilder", true, "appendPadding", "(int,char)", "", "Argument[this]", "ReturnValue", "value", "df-generated"] + - ["org.apache.commons.lang.text", "StrBuilder", true, "appendSeparator", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.commons.lang.text", "StrBuilder", true, "appendSeparator", "(String)", "", "Argument[this]", "ReturnValue", "value", "df-generated"] + - ["org.apache.commons.lang.text", "StrBuilder", true, "appendSeparator", "(String,int)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.commons.lang.text", "StrBuilder", true, "appendSeparator", "(String,int)", "", "Argument[this]", "ReturnValue", "value", "df-generated"] + - ["org.apache.commons.lang.text", "StrBuilder", true, "appendSeparator", "(char)", "", "Argument[this]", "ReturnValue", "value", "df-generated"] + - ["org.apache.commons.lang.text", "StrBuilder", true, "appendSeparator", "(char,int)", "", "Argument[this]", "ReturnValue", "value", "df-generated"] + - ["org.apache.commons.lang.text", "StrBuilder", true, "appendWithSeparators", "(Collection,String)", "", "Argument[1]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.commons.lang.text", "StrBuilder", true, "appendWithSeparators", "(Collection,String)", "", "Argument[this]", "ReturnValue", "value", "df-generated"] + - ["org.apache.commons.lang.text", "StrBuilder", true, "appendWithSeparators", "(Iterator,String)", "", "Argument[1]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.commons.lang.text", "StrBuilder", true, "appendWithSeparators", "(Iterator,String)", "", "Argument[this]", "ReturnValue", "value", "df-generated"] + - ["org.apache.commons.lang.text", "StrBuilder", true, "appendWithSeparators", "(Object[],String)", "", "Argument[1]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.commons.lang.text", "StrBuilder", true, "appendWithSeparators", "(Object[],String)", "", "Argument[this]", "ReturnValue", "value", "df-generated"] + - ["org.apache.commons.lang.text", "StrBuilder", true, "appendln", "(Object)", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang.text", "StrBuilder", true, "appendln", "(boolean)", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang.text", "StrBuilder", true, "appendln", "(char)", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang.text", "StrBuilder", true, "appendln", "(double)", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang.text", "StrBuilder", true, "appendln", "(float)", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang.text", "StrBuilder", true, "appendln", "(int)", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang.text", "StrBuilder", true, "appendln", "(long)", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang.text", "StrBuilder", true, "clear", "()", "", "Argument[this]", "ReturnValue", "value", "df-generated"] + - ["org.apache.commons.lang.text", "StrBuilder", true, "delete", "(int,int)", "", "Argument[this]", "ReturnValue", "value", "df-generated"] + - ["org.apache.commons.lang.text", "StrBuilder", true, "deleteAll", "(StrMatcher)", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang.text", "StrBuilder", true, "deleteAll", "(String)", "", "Argument[this]", "ReturnValue", "value", "df-generated"] + - ["org.apache.commons.lang.text", "StrBuilder", true, "deleteAll", "(char)", "", "Argument[this]", "ReturnValue", "value", "df-generated"] + - ["org.apache.commons.lang.text", "StrBuilder", true, "deleteCharAt", "(int)", "", "Argument[this]", "ReturnValue", "value", "df-generated"] + - ["org.apache.commons.lang.text", "StrBuilder", true, "deleteFirst", "(StrMatcher)", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang.text", "StrBuilder", true, "deleteFirst", "(String)", "", "Argument[this]", "ReturnValue", "value", "df-generated"] + - ["org.apache.commons.lang.text", "StrBuilder", true, "deleteFirst", "(char)", "", "Argument[this]", "ReturnValue", "value", "df-generated"] + - ["org.apache.commons.lang.text", "StrBuilder", true, "ensureCapacity", "(int)", "", "Argument[this]", "ReturnValue", "value", "df-generated"] + - ["org.apache.commons.lang.text", "StrBuilder", true, "getChars", "(char[])", "", "Argument[0]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang.text", "StrBuilder", true, "getChars", "(char[])", "", "Argument[this]", "Argument[0]", "taint", "df-generated"] + - ["org.apache.commons.lang.text", "StrBuilder", true, "getChars", "(char[])", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang.text", "StrBuilder", true, "getChars", "(int,int,char[],int)", "", "Argument[this]", "Argument[2]", "taint", "df-generated"] + - ["org.apache.commons.lang.text", "StrBuilder", true, "getNewLineText", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang.text", "StrBuilder", true, "getNullText", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang.text", "StrBuilder", true, "insert", "(int,Object)", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang.text", "StrBuilder", true, "insert", "(int,String)", "", "Argument[1]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.commons.lang.text", "StrBuilder", true, "insert", "(int,String)", "", "Argument[this]", "ReturnValue", "value", "df-generated"] + - ["org.apache.commons.lang.text", "StrBuilder", true, "insert", "(int,boolean)", "", "Argument[this]", "ReturnValue", "value", "df-generated"] + - ["org.apache.commons.lang.text", "StrBuilder", true, "insert", "(int,char)", "", "Argument[this]", "ReturnValue", "value", "df-generated"] + - ["org.apache.commons.lang.text", "StrBuilder", true, "insert", "(int,char[])", "", "Argument[1]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.commons.lang.text", "StrBuilder", true, "insert", "(int,char[])", "", "Argument[this]", "ReturnValue", "value", "df-generated"] + - ["org.apache.commons.lang.text", "StrBuilder", true, "insert", "(int,char[],int,int)", "", "Argument[1]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.commons.lang.text", "StrBuilder", true, "insert", "(int,char[],int,int)", "", "Argument[this]", "ReturnValue", "value", "df-generated"] + - ["org.apache.commons.lang.text", "StrBuilder", true, "insert", "(int,double)", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang.text", "StrBuilder", true, "insert", "(int,float)", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang.text", "StrBuilder", true, "insert", "(int,int)", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang.text", "StrBuilder", true, "insert", "(int,long)", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang.text", "StrBuilder", true, "leftString", "(int)", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang.text", "StrBuilder", true, "midString", "(int,int)", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang.text", "StrBuilder", true, "minimizeCapacity", "()", "", "Argument[this]", "ReturnValue", "value", "df-generated"] + - ["org.apache.commons.lang.text", "StrBuilder", true, "replace", "(StrMatcher,String,int,int,int)", "", "Argument[1]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.commons.lang.text", "StrBuilder", true, "replace", "(StrMatcher,String,int,int,int)", "", "Argument[1]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang.text", "StrBuilder", true, "replace", "(StrMatcher,String,int,int,int)", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang.text", "StrBuilder", true, "replace", "(int,int,String)", "", "Argument[2]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.commons.lang.text", "StrBuilder", true, "replace", "(int,int,String)", "", "Argument[this]", "ReturnValue", "value", "df-generated"] + - ["org.apache.commons.lang.text", "StrBuilder", true, "replaceAll", "(StrMatcher,String)", "", "Argument[1]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.commons.lang.text", "StrBuilder", true, "replaceAll", "(StrMatcher,String)", "", "Argument[1]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang.text", "StrBuilder", true, "replaceAll", "(StrMatcher,String)", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang.text", "StrBuilder", true, "replaceAll", "(String,String)", "", "Argument[1]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.commons.lang.text", "StrBuilder", true, "replaceAll", "(String,String)", "", "Argument[this]", "ReturnValue", "value", "df-generated"] + - ["org.apache.commons.lang.text", "StrBuilder", true, "replaceAll", "(char,char)", "", "Argument[this]", "ReturnValue", "value", "df-generated"] + - ["org.apache.commons.lang.text", "StrBuilder", true, "replaceFirst", "(StrMatcher,String)", "", "Argument[1]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.commons.lang.text", "StrBuilder", true, "replaceFirst", "(StrMatcher,String)", "", "Argument[1]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang.text", "StrBuilder", true, "replaceFirst", "(StrMatcher,String)", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang.text", "StrBuilder", true, "replaceFirst", "(String,String)", "", "Argument[1]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.commons.lang.text", "StrBuilder", true, "replaceFirst", "(String,String)", "", "Argument[this]", "ReturnValue", "value", "df-generated"] + - ["org.apache.commons.lang.text", "StrBuilder", true, "replaceFirst", "(char,char)", "", "Argument[this]", "ReturnValue", "value", "df-generated"] + - ["org.apache.commons.lang.text", "StrBuilder", true, "reverse", "()", "", "Argument[this]", "ReturnValue", "value", "df-generated"] + - ["org.apache.commons.lang.text", "StrBuilder", true, "rightString", "(int)", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang.text", "StrBuilder", true, "setCharAt", "(int,char)", "", "Argument[this]", "ReturnValue", "value", "df-generated"] + - ["org.apache.commons.lang.text", "StrBuilder", true, "setLength", "(int)", "", "Argument[this]", "ReturnValue", "value", "df-generated"] + - ["org.apache.commons.lang.text", "StrBuilder", true, "setNewLineText", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.commons.lang.text", "StrBuilder", true, "setNewLineText", "(String)", "", "Argument[this]", "ReturnValue", "value", "df-generated"] + - ["org.apache.commons.lang.text", "StrBuilder", true, "setNullText", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.commons.lang.text", "StrBuilder", true, "setNullText", "(String)", "", "Argument[this]", "ReturnValue", "value", "df-generated"] + - ["org.apache.commons.lang.text", "StrBuilder", true, "substring", "(int)", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang.text", "StrBuilder", true, "substring", "(int,int)", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang.text", "StrBuilder", true, "toCharArray", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang.text", "StrBuilder", true, "toCharArray", "(int,int)", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang.text", "StrBuilder", true, "toString", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang.text", "StrBuilder", true, "toStringBuffer", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang.text", "StrBuilder", true, "trim", "()", "", "Argument[this]", "ReturnValue", "value", "df-generated"] + - ["org.apache.commons.lang.text", "StrLookup", true, "mapLookup", "(Map)", "", "Argument[0].Element", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang.text", "StrMatcher", true, "charSetMatcher", "(String)", "", "Argument[0]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang.text", "StrMatcher", true, "charSetMatcher", "(char[])", "", "Argument[0]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang.text", "StrMatcher", true, "stringMatcher", "(String)", "", "Argument[0]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang.text", "StrSubstitutor", true, "StrSubstitutor", "(Map)", "", "Argument[0].Element", "Argument[this]", "taint", "df-generated"] + - ["org.apache.commons.lang.text", "StrSubstitutor", true, "StrSubstitutor", "(Map,String,String)", "", "Argument[0].Element", "Argument[this]", "taint", "df-generated"] + - ["org.apache.commons.lang.text", "StrSubstitutor", true, "StrSubstitutor", "(Map,String,String)", "", "Argument[1]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.commons.lang.text", "StrSubstitutor", true, "StrSubstitutor", "(Map,String,String)", "", "Argument[2]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.commons.lang.text", "StrSubstitutor", true, "StrSubstitutor", "(Map,String,String,char)", "", "Argument[0].Element", "Argument[this]", "taint", "df-generated"] + - ["org.apache.commons.lang.text", "StrSubstitutor", true, "StrSubstitutor", "(Map,String,String,char)", "", "Argument[1]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.commons.lang.text", "StrSubstitutor", true, "StrSubstitutor", "(Map,String,String,char)", "", "Argument[2]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.commons.lang.text", "StrSubstitutor", true, "StrSubstitutor", "(StrLookup)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.commons.lang.text", "StrSubstitutor", true, "StrSubstitutor", "(StrLookup,StrMatcher,StrMatcher,char)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.commons.lang.text", "StrSubstitutor", true, "StrSubstitutor", "(StrLookup,StrMatcher,StrMatcher,char)", "", "Argument[1]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.commons.lang.text", "StrSubstitutor", true, "StrSubstitutor", "(StrLookup,StrMatcher,StrMatcher,char)", "", "Argument[2]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.commons.lang.text", "StrSubstitutor", true, "StrSubstitutor", "(StrLookup,String,String,char)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.commons.lang.text", "StrSubstitutor", true, "StrSubstitutor", "(StrLookup,String,String,char)", "", "Argument[1]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.commons.lang.text", "StrSubstitutor", true, "StrSubstitutor", "(StrLookup,String,String,char)", "", "Argument[2]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.commons.lang.text", "StrSubstitutor", true, "getVariablePrefixMatcher", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang.text", "StrSubstitutor", true, "getVariableResolver", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang.text", "StrSubstitutor", true, "getVariableSuffixMatcher", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang.text", "StrSubstitutor", true, "replace", "(StrBuilder)", "", "Argument[0]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang.text", "StrSubstitutor", true, "replace", "(StrBuilder,int,int)", "", "Argument[0]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang.text", "StrSubstitutor", true, "replace", "(String)", "", "Argument[0]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang.text", "StrSubstitutor", true, "replace", "(String,int,int)", "", "Argument[0]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang.text", "StrSubstitutor", true, "replace", "(StringBuffer)", "", "Argument[0]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang.text", "StrSubstitutor", true, "replace", "(StringBuffer,int,int)", "", "Argument[0]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang.text", "StrSubstitutor", true, "replace", "(char[])", "", "Argument[0]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang.text", "StrSubstitutor", true, "replace", "(char[],int,int)", "", "Argument[0]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang.text", "StrSubstitutor", true, "setVariablePrefix", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.commons.lang.text", "StrSubstitutor", true, "setVariablePrefix", "(String)", "", "Argument[0]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang.text", "StrSubstitutor", true, "setVariablePrefix", "(String)", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang.text", "StrSubstitutor", true, "setVariablePrefix", "(char)", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang.text", "StrSubstitutor", true, "setVariablePrefixMatcher", "(StrMatcher)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.commons.lang.text", "StrSubstitutor", true, "setVariablePrefixMatcher", "(StrMatcher)", "", "Argument[this]", "ReturnValue", "value", "df-generated"] + - ["org.apache.commons.lang.text", "StrSubstitutor", true, "setVariableResolver", "(StrLookup)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.commons.lang.text", "StrSubstitutor", true, "setVariableSuffix", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.commons.lang.text", "StrSubstitutor", true, "setVariableSuffix", "(String)", "", "Argument[0]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang.text", "StrSubstitutor", true, "setVariableSuffix", "(String)", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang.text", "StrSubstitutor", true, "setVariableSuffix", "(char)", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang.text", "StrSubstitutor", true, "setVariableSuffixMatcher", "(StrMatcher)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.commons.lang.text", "StrSubstitutor", true, "setVariableSuffixMatcher", "(StrMatcher)", "", "Argument[this]", "ReturnValue", "value", "df-generated"] + - ["org.apache.commons.lang.text", "StrTokenizer", true, "StrTokenizer", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.commons.lang.text", "StrTokenizer", true, "StrTokenizer", "(String,StrMatcher)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.commons.lang.text", "StrTokenizer", true, "StrTokenizer", "(String,StrMatcher)", "", "Argument[1]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.commons.lang.text", "StrTokenizer", true, "StrTokenizer", "(String,StrMatcher,StrMatcher)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.commons.lang.text", "StrTokenizer", true, "StrTokenizer", "(String,StrMatcher,StrMatcher)", "", "Argument[1]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.commons.lang.text", "StrTokenizer", true, "StrTokenizer", "(String,StrMatcher,StrMatcher)", "", "Argument[2]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.commons.lang.text", "StrTokenizer", true, "StrTokenizer", "(String,String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.commons.lang.text", "StrTokenizer", true, "StrTokenizer", "(String,String)", "", "Argument[1]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.commons.lang.text", "StrTokenizer", true, "StrTokenizer", "(String,char)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.commons.lang.text", "StrTokenizer", true, "StrTokenizer", "(String,char,char)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.commons.lang.text", "StrTokenizer", true, "StrTokenizer", "(char[])", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.commons.lang.text", "StrTokenizer", true, "StrTokenizer", "(char[],StrMatcher)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.commons.lang.text", "StrTokenizer", true, "StrTokenizer", "(char[],StrMatcher)", "", "Argument[1]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.commons.lang.text", "StrTokenizer", true, "StrTokenizer", "(char[],StrMatcher,StrMatcher)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.commons.lang.text", "StrTokenizer", true, "StrTokenizer", "(char[],StrMatcher,StrMatcher)", "", "Argument[1]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.commons.lang.text", "StrTokenizer", true, "StrTokenizer", "(char[],StrMatcher,StrMatcher)", "", "Argument[2]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.commons.lang.text", "StrTokenizer", true, "StrTokenizer", "(char[],String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.commons.lang.text", "StrTokenizer", true, "StrTokenizer", "(char[],String)", "", "Argument[1]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.commons.lang.text", "StrTokenizer", true, "StrTokenizer", "(char[],char)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.commons.lang.text", "StrTokenizer", true, "StrTokenizer", "(char[],char,char)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.commons.lang.text", "StrTokenizer", true, "getCSVInstance", "(String)", "", "Argument[0]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang.text", "StrTokenizer", true, "getCSVInstance", "(char[])", "", "Argument[0]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang.text", "StrTokenizer", true, "getContent", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang.text", "StrTokenizer", true, "getDelimiterMatcher", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang.text", "StrTokenizer", true, "getIgnoredMatcher", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang.text", "StrTokenizer", true, "getQuoteMatcher", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang.text", "StrTokenizer", true, "getTSVInstance", "(String)", "", "Argument[0]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang.text", "StrTokenizer", true, "getTSVInstance", "(char[])", "", "Argument[0]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang.text", "StrTokenizer", true, "getTokenArray", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang.text", "StrTokenizer", true, "getTokenList", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang.text", "StrTokenizer", true, "getTrimmerMatcher", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang.text", "StrTokenizer", true, "nextToken", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang.text", "StrTokenizer", true, "previousToken", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang.text", "StrTokenizer", true, "reset", "()", "", "Argument[this]", "ReturnValue", "value", "df-generated"] + - ["org.apache.commons.lang.text", "StrTokenizer", true, "reset", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.commons.lang.text", "StrTokenizer", true, "reset", "(String)", "", "Argument[this]", "ReturnValue", "value", "df-generated"] + - ["org.apache.commons.lang.text", "StrTokenizer", true, "reset", "(char[])", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.commons.lang.text", "StrTokenizer", true, "reset", "(char[])", "", "Argument[this]", "ReturnValue", "value", "df-generated"] + - ["org.apache.commons.lang.text", "StrTokenizer", true, "setDelimiterChar", "(char)", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang.text", "StrTokenizer", true, "setDelimiterMatcher", "(StrMatcher)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.commons.lang.text", "StrTokenizer", true, "setDelimiterMatcher", "(StrMatcher)", "", "Argument[this]", "ReturnValue", "value", "df-generated"] + - ["org.apache.commons.lang.text", "StrTokenizer", true, "setDelimiterString", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.commons.lang.text", "StrTokenizer", true, "setDelimiterString", "(String)", "", "Argument[0]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang.text", "StrTokenizer", true, "setDelimiterString", "(String)", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang.text", "StrTokenizer", true, "setEmptyTokenAsNull", "(boolean)", "", "Argument[this]", "ReturnValue", "value", "df-generated"] + - ["org.apache.commons.lang.text", "StrTokenizer", true, "setIgnoreEmptyTokens", "(boolean)", "", "Argument[this]", "ReturnValue", "value", "df-generated"] + - ["org.apache.commons.lang.text", "StrTokenizer", true, "setIgnoredChar", "(char)", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang.text", "StrTokenizer", true, "setIgnoredMatcher", "(StrMatcher)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.commons.lang.text", "StrTokenizer", true, "setIgnoredMatcher", "(StrMatcher)", "", "Argument[this]", "ReturnValue", "value", "df-generated"] + - ["org.apache.commons.lang.text", "StrTokenizer", true, "setQuoteChar", "(char)", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang.text", "StrTokenizer", true, "setQuoteMatcher", "(StrMatcher)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.commons.lang.text", "StrTokenizer", true, "setQuoteMatcher", "(StrMatcher)", "", "Argument[this]", "ReturnValue", "value", "df-generated"] + - ["org.apache.commons.lang.text", "StrTokenizer", true, "setTrimmerMatcher", "(StrMatcher)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.commons.lang.text", "StrTokenizer", true, "setTrimmerMatcher", "(StrMatcher)", "", "Argument[this]", "ReturnValue", "value", "df-generated"] + - ["org.apache.commons.lang.text", "StrTokenizer", true, "toString", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang.time", "DateUtils", true, "iterator", "(Calendar,int)", "", "Argument[0]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang.time", "DateUtils", true, "iterator", "(Object,int)", "", "Argument[0]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang.time", "DateUtils", true, "round", "(Calendar,int)", "", "Argument[0]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang.time", "DateUtils", true, "truncate", "(Calendar,int)", "", "Argument[0]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang.time", "FastDateFormat", true, "format", "(Calendar,StringBuffer)", "", "Argument[1]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang.time", "FastDateFormat", true, "format", "(Date,StringBuffer)", "", "Argument[1]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang.time", "FastDateFormat", true, "format", "(long,StringBuffer)", "", "Argument[1]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang.time", "FastDateFormat", true, "getDateInstance", "(int,Locale)", "", "Argument[1]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang.time", "FastDateFormat", true, "getDateInstance", "(int,TimeZone)", "", "Argument[1]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang.time", "FastDateFormat", true, "getDateInstance", "(int,TimeZone,Locale)", "", "Argument[1]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang.time", "FastDateFormat", true, "getDateInstance", "(int,TimeZone,Locale)", "", "Argument[2]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang.time", "FastDateFormat", true, "getDateTimeInstance", "(int,int,Locale)", "", "Argument[2]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang.time", "FastDateFormat", true, "getDateTimeInstance", "(int,int,TimeZone)", "", "Argument[2]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang.time", "FastDateFormat", true, "getDateTimeInstance", "(int,int,TimeZone,Locale)", "", "Argument[2]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang.time", "FastDateFormat", true, "getDateTimeInstance", "(int,int,TimeZone,Locale)", "", "Argument[3]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang.time", "FastDateFormat", true, "getInstance", "(String)", "", "Argument[0]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang.time", "FastDateFormat", true, "getInstance", "(String,Locale)", "", "Argument[0]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang.time", "FastDateFormat", true, "getInstance", "(String,Locale)", "", "Argument[1]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang.time", "FastDateFormat", true, "getInstance", "(String,TimeZone)", "", "Argument[0]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang.time", "FastDateFormat", true, "getInstance", "(String,TimeZone)", "", "Argument[1]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang.time", "FastDateFormat", true, "getInstance", "(String,TimeZone,Locale)", "", "Argument[0]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang.time", "FastDateFormat", true, "getInstance", "(String,TimeZone,Locale)", "", "Argument[1]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang.time", "FastDateFormat", true, "getInstance", "(String,TimeZone,Locale)", "", "Argument[2]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang.time", "FastDateFormat", true, "getLocale", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang.time", "FastDateFormat", true, "getPattern", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang.time", "FastDateFormat", true, "getTimeInstance", "(int,Locale)", "", "Argument[1]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang.time", "FastDateFormat", true, "getTimeInstance", "(int,TimeZone)", "", "Argument[1]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang.time", "FastDateFormat", true, "getTimeInstance", "(int,TimeZone,Locale)", "", "Argument[1]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang.time", "FastDateFormat", true, "getTimeInstance", "(int,TimeZone,Locale)", "", "Argument[2]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang.time", "FastDateFormat", true, "getTimeZone", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang.time", "FastDateFormat", true, "toString", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang", "ArrayUtils", true, "add", "(Object[],Object)", "", "Argument[0].ArrayElement", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang", "ArrayUtils", true, "add", "(Object[],Object)", "", "Argument[1]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang", "ArrayUtils", true, "add", "(Object[],int,Object)", "", "Argument[0].ArrayElement", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang", "ArrayUtils", true, "add", "(byte[],byte)", "", "Argument[0]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang", "ArrayUtils", true, "add", "(byte[],int,byte)", "", "Argument[0]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang", "ArrayUtils", true, "add", "(char[],char)", "", "Argument[0]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang", "ArrayUtils", true, "add", "(char[],int,char)", "", "Argument[0]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang", "ArrayUtils", true, "addAll", "(Object[],Object[])", "", "Argument[0].ArrayElement", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang", "ArrayUtils", true, "addAll", "(Object[],Object[])", "", "Argument[1].ArrayElement", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang", "ArrayUtils", true, "addAll", "(byte[],byte[])", "", "Argument[0]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang", "ArrayUtils", true, "addAll", "(byte[],byte[])", "", "Argument[1]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang", "ArrayUtils", true, "addAll", "(char[],char[])", "", "Argument[0]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang", "ArrayUtils", true, "addAll", "(char[],char[])", "", "Argument[1]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang", "ArrayUtils", true, "clone", "(Object[])", "", "Argument[0].ArrayElement", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang", "ArrayUtils", true, "clone", "(byte[])", "", "Argument[0]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang", "ArrayUtils", true, "clone", "(char[])", "", "Argument[0]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang", "ArrayUtils", true, "remove", "(Object[],int)", "", "Argument[0].ArrayElement", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang", "ArrayUtils", true, "remove", "(byte[],int)", "", "Argument[0]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang", "ArrayUtils", true, "remove", "(char[],int)", "", "Argument[0]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang", "ArrayUtils", true, "removeElement", "(Object[],Object)", "", "Argument[0].ArrayElement", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang", "ArrayUtils", true, "removeElement", "(byte[],byte)", "", "Argument[0]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang", "ArrayUtils", true, "removeElement", "(char[],char)", "", "Argument[0]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang", "ArrayUtils", true, "subarray", "(Object[],int,int)", "", "Argument[0].ArrayElement", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang", "ArrayUtils", true, "subarray", "(byte[],int,int)", "", "Argument[0]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang", "ArrayUtils", true, "subarray", "(char[],int,int)", "", "Argument[0]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang", "ArrayUtils", true, "toMap", "(Object[])", "", "Argument[0].ArrayElement", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang", "ArrayUtils", true, "toString", "(Object)", "", "Argument[0]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang", "ArrayUtils", true, "toString", "(Object,String)", "", "Argument[0]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang", "ArrayUtils", true, "toString", "(Object,String)", "", "Argument[1]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang", "BooleanUtils", true, "toString", "(Boolean,String,String,String)", "", "Argument[1]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang", "BooleanUtils", true, "toString", "(Boolean,String,String,String)", "", "Argument[2]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang", "BooleanUtils", true, "toString", "(Boolean,String,String,String)", "", "Argument[3]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang", "BooleanUtils", true, "toString", "(boolean,String,String)", "", "Argument[1]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang", "BooleanUtils", true, "toString", "(boolean,String,String)", "", "Argument[2]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang", "CharRange", false, "toString", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang", "CharSet", true, "getCharRanges", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang", "CharSetUtils", true, "delete", "(String,String)", "", "Argument[0]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang", "CharSetUtils", true, "delete", "(String,String[])", "", "Argument[0]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang", "CharSetUtils", true, "squeeze", "(String,String)", "", "Argument[0]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang", "CharSetUtils", true, "squeeze", "(String,String[])", "", "Argument[0]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang", "CharSetUtils", true, "translate", "(String,String,String)", "", "Argument[0]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang", "ClassUtils", true, "getPackageCanonicalName", "(Object,String)", "", "Argument[1]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang", "ClassUtils", true, "getPackageCanonicalName", "(String)", "", "Argument[0]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang", "ClassUtils", true, "getPackageName", "(Object,String)", "", "Argument[1]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang", "ClassUtils", true, "getPackageName", "(String)", "", "Argument[0]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang", "ClassUtils", true, "getShortCanonicalName", "(Object,String)", "", "Argument[1]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang", "ClassUtils", true, "getShortCanonicalName", "(String)", "", "Argument[0]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang", "ClassUtils", true, "getShortClassName", "(Object,String)", "", "Argument[1]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang", "ClassUtils", true, "getShortClassName", "(String)", "", "Argument[0]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang", "ClassUtils", true, "primitivesToWrappers", "(Class[])", "", "Argument[0].ArrayElement", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang", "ClassUtils", true, "wrappersToPrimitives", "(Class[])", "", "Argument[0].ArrayElement", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang", "IllegalClassException", true, "IllegalClassException", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.commons.lang", "IncompleteArgumentException", true, "IncompleteArgumentException", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.commons.lang", "IncompleteArgumentException", true, "IncompleteArgumentException", "(String,String[])", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.commons.lang", "LocaleUtils", true, "localeLookupList", "(Locale)", "", "Argument[0]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang", "LocaleUtils", true, "localeLookupList", "(Locale,Locale)", "", "Argument[0]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang", "LocaleUtils", true, "localeLookupList", "(Locale,Locale)", "", "Argument[1]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang", "NotImplementedException", true, "NotImplementedException", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.commons.lang", "NotImplementedException", true, "NotImplementedException", "(String,Throwable)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.commons.lang", "NotImplementedException", true, "NotImplementedException", "(String,Throwable)", "", "Argument[1]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.commons.lang", "NotImplementedException", true, "NotImplementedException", "(Throwable)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.commons.lang", "NullArgumentException", true, "NullArgumentException", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.commons.lang", "ObjectUtils", true, "appendIdentityToString", "(StringBuffer,Object)", "", "Argument[0]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang", "ObjectUtils", true, "defaultIfNull", "(Object,Object)", "", "Argument[0]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang", "ObjectUtils", true, "defaultIfNull", "(Object,Object)", "", "Argument[1]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang", "ObjectUtils", true, "max", "(Comparable,Comparable)", "", "Argument[0]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang", "ObjectUtils", true, "max", "(Comparable,Comparable)", "", "Argument[1]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang", "ObjectUtils", true, "min", "(Comparable,Comparable)", "", "Argument[0]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang", "ObjectUtils", true, "min", "(Comparable,Comparable)", "", "Argument[1]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang", "ObjectUtils", true, "toString", "(Object,String)", "", "Argument[1]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang", "SerializationException", true, "SerializationException", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.commons.lang", "SerializationException", true, "SerializationException", "(String,Throwable)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.commons.lang", "SerializationException", true, "SerializationException", "(String,Throwable)", "", "Argument[1]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.commons.lang", "SerializationException", true, "SerializationException", "(Throwable)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.commons.lang", "SerializationUtils", true, "deserialize", "(InputStream)", "", "Argument[0]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang", "SerializationUtils", true, "deserialize", "(byte[])", "", "Argument[0]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang", "StringEscapeUtils", true, "escapeSql", "(String)", "", "Argument[0]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang", "StringEscapeUtils", true, "unescapeCsv", "(String)", "", "Argument[0]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang", "StringEscapeUtils", true, "unescapeCsv", "(Writer,String)", "", "Argument[1]", "Argument[0]", "taint", "df-generated"] + - ["org.apache.commons.lang", "StringEscapeUtils", true, "unescapeHtml", "(String)", "", "Argument[0]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang", "StringEscapeUtils", true, "unescapeHtml", "(Writer,String)", "", "Argument[1]", "Argument[0]", "taint", "df-generated"] + - ["org.apache.commons.lang", "StringEscapeUtils", true, "unescapeXml", "(String)", "", "Argument[0]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang", "StringEscapeUtils", true, "unescapeXml", "(Writer,String)", "", "Argument[1]", "Argument[0]", "taint", "df-generated"] + - ["org.apache.commons.lang", "StringUtils", true, "abbreviate", "(String,int)", "", "Argument[0]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang", "StringUtils", true, "abbreviate", "(String,int,int)", "", "Argument[0]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang", "StringUtils", true, "capitalise", "(String)", "", "Argument[0]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang", "StringUtils", true, "capitaliseAllWords", "(String)", "", "Argument[0]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang", "StringUtils", true, "capitalize", "(String)", "", "Argument[0]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang", "StringUtils", true, "center", "(String,int)", "", "Argument[0]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang", "StringUtils", true, "center", "(String,int,String)", "", "Argument[0]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang", "StringUtils", true, "center", "(String,int,String)", "", "Argument[2]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang", "StringUtils", true, "center", "(String,int,char)", "", "Argument[0]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang", "StringUtils", true, "chomp", "(String)", "", "Argument[0]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang", "StringUtils", true, "chomp", "(String,String)", "", "Argument[0]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang", "StringUtils", true, "chompLast", "(String)", "", "Argument[0]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang", "StringUtils", true, "chompLast", "(String,String)", "", "Argument[0]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang", "StringUtils", true, "chop", "(String)", "", "Argument[0]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang", "StringUtils", true, "chopNewline", "(String)", "", "Argument[0]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang", "StringUtils", true, "clean", "(String)", "", "Argument[0]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang", "StringUtils", true, "concatenate", "(Object[])", "", "Argument[0].ArrayElement", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang", "StringUtils", true, "defaultIfEmpty", "(String,String)", "", "Argument[0]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang", "StringUtils", true, "defaultIfEmpty", "(String,String)", "", "Argument[1]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang", "StringUtils", true, "defaultString", "(String)", "", "Argument[0]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang", "StringUtils", true, "defaultString", "(String,String)", "", "Argument[0]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang", "StringUtils", true, "defaultString", "(String,String)", "", "Argument[1]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang", "StringUtils", true, "deleteSpaces", "(String)", "", "Argument[0]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang", "StringUtils", true, "deleteWhitespace", "(String)", "", "Argument[0]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang", "StringUtils", true, "difference", "(String,String)", "", "Argument[0]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang", "StringUtils", true, "difference", "(String,String)", "", "Argument[1]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang", "StringUtils", true, "getChomp", "(String,String)", "", "Argument[0]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang", "StringUtils", true, "getChomp", "(String,String)", "", "Argument[1]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang", "StringUtils", true, "getCommonPrefix", "(String[])", "", "Argument[0].ArrayElement", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang", "StringUtils", true, "getNestedString", "(String,String)", "", "Argument[0]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang", "StringUtils", true, "getNestedString", "(String,String,String)", "", "Argument[0]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang", "StringUtils", true, "getPrechomp", "(String,String)", "", "Argument[0]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang", "StringUtils", true, "join", "(Collection,String)", "", "Argument[0].Element", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang", "StringUtils", true, "join", "(Collection,String)", "", "Argument[1]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang", "StringUtils", true, "join", "(Collection,char)", "", "Argument[0].Element", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang", "StringUtils", true, "join", "(Iterator,String)", "", "Argument[0].Element", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang", "StringUtils", true, "join", "(Iterator,String)", "", "Argument[1]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang", "StringUtils", true, "join", "(Iterator,char)", "", "Argument[0].Element", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang", "StringUtils", true, "join", "(Object[])", "", "Argument[0].ArrayElement", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang", "StringUtils", true, "join", "(Object[],String)", "", "Argument[0].ArrayElement", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang", "StringUtils", true, "join", "(Object[],String)", "", "Argument[1]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang", "StringUtils", true, "join", "(Object[],String,int,int)", "", "Argument[0].ArrayElement", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang", "StringUtils", true, "join", "(Object[],String,int,int)", "", "Argument[1]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang", "StringUtils", true, "join", "(Object[],char)", "", "Argument[0].ArrayElement", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang", "StringUtils", true, "join", "(Object[],char,int,int)", "", "Argument[0].ArrayElement", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang", "StringUtils", true, "left", "(String,int)", "", "Argument[0]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang", "StringUtils", true, "leftPad", "(String,int)", "", "Argument[0]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang", "StringUtils", true, "leftPad", "(String,int,String)", "", "Argument[0]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang", "StringUtils", true, "leftPad", "(String,int,String)", "", "Argument[2]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang", "StringUtils", true, "leftPad", "(String,int,char)", "", "Argument[0]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang", "StringUtils", true, "lowerCase", "(String)", "", "Argument[0]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang", "StringUtils", true, "mid", "(String,int,int)", "", "Argument[0]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang", "StringUtils", true, "overlay", "(String,String,int,int)", "", "Argument[0]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang", "StringUtils", true, "overlay", "(String,String,int,int)", "", "Argument[1]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang", "StringUtils", true, "overlayString", "(String,String,int,int)", "", "Argument[0]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang", "StringUtils", true, "overlayString", "(String,String,int,int)", "", "Argument[1]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang", "StringUtils", true, "prechomp", "(String,String)", "", "Argument[0]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang", "StringUtils", true, "remove", "(String,String)", "", "Argument[0]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang", "StringUtils", true, "remove", "(String,char)", "", "Argument[0]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang", "StringUtils", true, "removeEnd", "(String,String)", "", "Argument[0]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang", "StringUtils", true, "removeEndIgnoreCase", "(String,String)", "", "Argument[0]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang", "StringUtils", true, "removeStart", "(String,String)", "", "Argument[0]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang", "StringUtils", true, "removeStartIgnoreCase", "(String,String)", "", "Argument[0]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang", "StringUtils", true, "repeat", "(String,int)", "", "Argument[0]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang", "StringUtils", true, "replace", "(String,String,String)", "", "Argument[0]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang", "StringUtils", true, "replace", "(String,String,String)", "", "Argument[2]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang", "StringUtils", true, "replace", "(String,String,String,int)", "", "Argument[0]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang", "StringUtils", true, "replace", "(String,String,String,int)", "", "Argument[2]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang", "StringUtils", true, "replaceChars", "(String,String,String)", "", "Argument[0]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang", "StringUtils", true, "replaceChars", "(String,char,char)", "", "Argument[0]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang", "StringUtils", true, "replaceEach", "(String,String[],String[])", "", "Argument[0]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang", "StringUtils", true, "replaceEach", "(String,String[],String[])", "", "Argument[2].ArrayElement", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang", "StringUtils", true, "replaceEachRepeatedly", "(String,String[],String[])", "", "Argument[0]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang", "StringUtils", true, "replaceEachRepeatedly", "(String,String[],String[])", "", "Argument[2].ArrayElement", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang", "StringUtils", true, "replaceOnce", "(String,String,String)", "", "Argument[0]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang", "StringUtils", true, "replaceOnce", "(String,String,String)", "", "Argument[2]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang", "StringUtils", true, "reverse", "(String)", "", "Argument[0]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang", "StringUtils", true, "reverseDelimited", "(String,char)", "", "Argument[0]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang", "StringUtils", true, "reverseDelimitedString", "(String,String)", "", "Argument[0]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang", "StringUtils", true, "reverseDelimitedString", "(String,String)", "", "Argument[1]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang", "StringUtils", true, "right", "(String,int)", "", "Argument[0]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang", "StringUtils", true, "rightPad", "(String,int)", "", "Argument[0]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang", "StringUtils", true, "rightPad", "(String,int,String)", "", "Argument[0]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang", "StringUtils", true, "rightPad", "(String,int,String)", "", "Argument[2]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang", "StringUtils", true, "rightPad", "(String,int,char)", "", "Argument[0]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang", "StringUtils", true, "split", "(String)", "", "Argument[0]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang", "StringUtils", true, "split", "(String,String)", "", "Argument[0]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang", "StringUtils", true, "split", "(String,String,int)", "", "Argument[0]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang", "StringUtils", true, "split", "(String,char)", "", "Argument[0]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang", "StringUtils", true, "splitByCharacterType", "(String)", "", "Argument[0]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang", "StringUtils", true, "splitByCharacterTypeCamelCase", "(String)", "", "Argument[0]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang", "StringUtils", true, "splitByWholeSeparator", "(String,String)", "", "Argument[0]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang", "StringUtils", true, "splitByWholeSeparator", "(String,String,int)", "", "Argument[0]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang", "StringUtils", true, "splitByWholeSeparatorPreserveAllTokens", "(String,String)", "", "Argument[0]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang", "StringUtils", true, "splitByWholeSeparatorPreserveAllTokens", "(String,String,int)", "", "Argument[0]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang", "StringUtils", true, "splitPreserveAllTokens", "(String)", "", "Argument[0]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang", "StringUtils", true, "splitPreserveAllTokens", "(String,String)", "", "Argument[0]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang", "StringUtils", true, "splitPreserveAllTokens", "(String,String,int)", "", "Argument[0]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang", "StringUtils", true, "splitPreserveAllTokens", "(String,char)", "", "Argument[0]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang", "StringUtils", true, "strip", "(String)", "", "Argument[0]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang", "StringUtils", true, "strip", "(String,String)", "", "Argument[0]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang", "StringUtils", true, "stripAll", "(String[])", "", "Argument[0].ArrayElement", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang", "StringUtils", true, "stripAll", "(String[],String)", "", "Argument[0].ArrayElement", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang", "StringUtils", true, "stripEnd", "(String,String)", "", "Argument[0]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang", "StringUtils", true, "stripStart", "(String,String)", "", "Argument[0]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang", "StringUtils", true, "stripToEmpty", "(String)", "", "Argument[0]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang", "StringUtils", true, "stripToNull", "(String)", "", "Argument[0]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang", "StringUtils", true, "substring", "(String,int)", "", "Argument[0]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang", "StringUtils", true, "substring", "(String,int,int)", "", "Argument[0]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang", "StringUtils", true, "substringAfter", "(String,String)", "", "Argument[0]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang", "StringUtils", true, "substringAfterLast", "(String,String)", "", "Argument[0]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang", "StringUtils", true, "substringBefore", "(String,String)", "", "Argument[0]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang", "StringUtils", true, "substringBeforeLast", "(String,String)", "", "Argument[0]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang", "StringUtils", true, "substringBetween", "(String,String)", "", "Argument[0]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang", "StringUtils", true, "substringBetween", "(String,String,String)", "", "Argument[0]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang", "StringUtils", true, "substringsBetween", "(String,String,String)", "", "Argument[0]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang", "StringUtils", true, "swapCase", "(String)", "", "Argument[0]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang", "StringUtils", true, "trim", "(String)", "", "Argument[0]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang", "StringUtils", true, "trimToEmpty", "(String)", "", "Argument[0]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang", "StringUtils", true, "trimToNull", "(String)", "", "Argument[0]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang", "StringUtils", true, "uncapitalise", "(String)", "", "Argument[0]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang", "StringUtils", true, "uncapitalize", "(String)", "", "Argument[0]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang", "StringUtils", true, "upperCase", "(String)", "", "Argument[0]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang", "UnhandledException", true, "UnhandledException", "(String,Throwable)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.commons.lang", "UnhandledException", true, "UnhandledException", "(String,Throwable)", "", "Argument[1]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.commons.lang", "UnhandledException", true, "UnhandledException", "(Throwable)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.commons.lang", "WordUtils", true, "abbreviate", "(String,int,int,String)", "", "Argument[0]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang", "WordUtils", true, "abbreviate", "(String,int,int,String)", "", "Argument[3]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang", "WordUtils", true, "capitalize", "(String)", "", "Argument[0]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang", "WordUtils", true, "capitalize", "(String,char[])", "", "Argument[0]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang", "WordUtils", true, "capitalizeFully", "(String)", "", "Argument[0]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang", "WordUtils", true, "capitalizeFully", "(String,char[])", "", "Argument[0]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang", "WordUtils", true, "initials", "(String)", "", "Argument[0]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang", "WordUtils", true, "initials", "(String,char[])", "", "Argument[0]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang", "WordUtils", true, "swapCase", "(String)", "", "Argument[0]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang", "WordUtils", true, "uncapitalize", "(String)", "", "Argument[0]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang", "WordUtils", true, "uncapitalize", "(String,char[])", "", "Argument[0]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang", "WordUtils", true, "wrap", "(String,int)", "", "Argument[0]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang", "WordUtils", true, "wrap", "(String,int,String,boolean)", "", "Argument[0]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang", "WordUtils", true, "wrap", "(String,int,String,boolean)", "", "Argument[2]", "ReturnValue", "taint", "df-generated"] + + + - addsTo: + pack: codeql/java-all + extensible: neutralModel + data: + - ["org.apache.commons.lang.builder", "CompareToBuilder", "reflectionCompare", "(Object,Object)", "summary", "df-generated"] + - ["org.apache.commons.lang.builder", "CompareToBuilder", "reflectionCompare", "(Object,Object,Collection)", "summary", "df-generated"] + - ["org.apache.commons.lang.builder", "CompareToBuilder", "reflectionCompare", "(Object,Object,String[])", "summary", "df-generated"] + - ["org.apache.commons.lang.builder", "CompareToBuilder", "reflectionCompare", "(Object,Object,boolean)", "summary", "df-generated"] + - ["org.apache.commons.lang.builder", "CompareToBuilder", "reflectionCompare", "(Object,Object,boolean,Class)", "summary", "df-generated"] + - ["org.apache.commons.lang.builder", "CompareToBuilder", "reflectionCompare", "(Object,Object,boolean,Class,String[])", "summary", "df-generated"] + - ["org.apache.commons.lang.builder", "CompareToBuilder", "toComparison", "()", "summary", "df-generated"] + - ["org.apache.commons.lang.builder", "EqualsBuilder", "isEquals", "()", "summary", "df-generated"] + - ["org.apache.commons.lang.builder", "EqualsBuilder", "reflectionEquals", "(Object,Object)", "summary", "df-generated"] + - ["org.apache.commons.lang.builder", "EqualsBuilder", "reflectionEquals", "(Object,Object,Collection)", "summary", "df-generated"] + - ["org.apache.commons.lang.builder", "EqualsBuilder", "reflectionEquals", "(Object,Object,String[])", "summary", "df-generated"] + - ["org.apache.commons.lang.builder", "EqualsBuilder", "reflectionEquals", "(Object,Object,boolean)", "summary", "df-generated"] + - ["org.apache.commons.lang.builder", "EqualsBuilder", "reflectionEquals", "(Object,Object,boolean,Class)", "summary", "df-generated"] + - ["org.apache.commons.lang.builder", "EqualsBuilder", "reflectionEquals", "(Object,Object,boolean,Class,String[])", "summary", "df-generated"] + - ["org.apache.commons.lang.builder", "HashCodeBuilder", "HashCodeBuilder", "(int,int)", "summary", "df-generated"] + - ["org.apache.commons.lang.builder", "HashCodeBuilder", "reflectionHashCode", "(Object)", "summary", "df-generated"] + - ["org.apache.commons.lang.builder", "HashCodeBuilder", "reflectionHashCode", "(Object,Collection)", "summary", "df-generated"] + - ["org.apache.commons.lang.builder", "HashCodeBuilder", "reflectionHashCode", "(Object,String[])", "summary", "df-generated"] + - ["org.apache.commons.lang.builder", "HashCodeBuilder", "reflectionHashCode", "(Object,boolean)", "summary", "df-generated"] + - ["org.apache.commons.lang.builder", "HashCodeBuilder", "reflectionHashCode", "(int,int,Object)", "summary", "df-generated"] + - ["org.apache.commons.lang.builder", "HashCodeBuilder", "reflectionHashCode", "(int,int,Object,boolean)", "summary", "df-generated"] + - ["org.apache.commons.lang.builder", "HashCodeBuilder", "reflectionHashCode", "(int,int,Object,boolean,Class)", "summary", "df-generated"] + - ["org.apache.commons.lang.builder", "HashCodeBuilder", "reflectionHashCode", "(int,int,Object,boolean,Class,String[])", "summary", "df-generated"] + - ["org.apache.commons.lang.builder", "HashCodeBuilder", "toHashCode", "()", "summary", "df-generated"] + - ["org.apache.commons.lang.builder", "ReflectionToStringBuilder", "getUpToClass", "()", "summary", "df-generated"] + - ["org.apache.commons.lang.builder", "ReflectionToStringBuilder", "isAppendStatics", "()", "summary", "df-generated"] + - ["org.apache.commons.lang.builder", "ReflectionToStringBuilder", "isAppendTransients", "()", "summary", "df-generated"] + - ["org.apache.commons.lang.builder", "ReflectionToStringBuilder", "setAppendStatics", "(boolean)", "summary", "df-generated"] + - ["org.apache.commons.lang.builder", "ReflectionToStringBuilder", "setAppendTransients", "(boolean)", "summary", "df-generated"] + - ["org.apache.commons.lang.builder", "ReflectionToStringBuilder", "setUpToClass", "(Class)", "summary", "df-generated"] + - ["org.apache.commons.lang.builder", "ReflectionToStringBuilder", "toString", "(Object)", "summary", "df-generated"] + - ["org.apache.commons.lang.builder", "ReflectionToStringBuilder", "toString", "(Object,ToStringStyle)", "summary", "df-generated"] + - ["org.apache.commons.lang.builder", "ReflectionToStringBuilder", "toString", "(Object,ToStringStyle,boolean)", "summary", "df-generated"] + - ["org.apache.commons.lang.builder", "ReflectionToStringBuilder", "toString", "(Object,ToStringStyle,boolean,Class)", "summary", "df-generated"] + - ["org.apache.commons.lang.builder", "ReflectionToStringBuilder", "toString", "(Object,ToStringStyle,boolean,boolean)", "summary", "df-generated"] + - ["org.apache.commons.lang.builder", "ReflectionToStringBuilder", "toString", "(Object,ToStringStyle,boolean,boolean,Class)", "summary", "df-generated"] + - ["org.apache.commons.lang.builder", "ReflectionToStringBuilder", "toStringExclude", "(Object,Collection)", "summary", "df-generated"] + - ["org.apache.commons.lang.builder", "ReflectionToStringBuilder", "toStringExclude", "(Object,String)", "summary", "df-generated"] + - ["org.apache.commons.lang.builder", "ReflectionToStringBuilder", "toStringExclude", "(Object,String[])", "summary", "df-generated"] + - ["org.apache.commons.lang.builder", "ToStringBuilder", "getDefaultStyle", "()", "summary", "df-generated"] + - ["org.apache.commons.lang.builder", "ToStringBuilder", "reflectionToString", "(Object)", "summary", "df-generated"] + - ["org.apache.commons.lang.builder", "ToStringBuilder", "reflectionToString", "(Object,ToStringStyle)", "summary", "df-generated"] + - ["org.apache.commons.lang.builder", "ToStringBuilder", "reflectionToString", "(Object,ToStringStyle,boolean)", "summary", "df-generated"] + - ["org.apache.commons.lang.builder", "ToStringBuilder", "reflectionToString", "(Object,ToStringStyle,boolean,Class)", "summary", "df-generated"] + - ["org.apache.commons.lang.builder", "ToStringBuilder", "setDefaultStyle", "(ToStringStyle)", "summary", "df-generated"] + - ["org.apache.commons.lang.builder", "ToStringStyle", "isArrayContentDetail", "()", "summary", "df-generated"] + - ["org.apache.commons.lang.builder", "ToStringStyle", "isDefaultFullDetail", "()", "summary", "df-generated"] + - ["org.apache.commons.lang.builder", "ToStringStyle", "isFieldSeparatorAtEnd", "()", "summary", "df-generated"] + - ["org.apache.commons.lang.builder", "ToStringStyle", "isFieldSeparatorAtStart", "()", "summary", "df-generated"] + - ["org.apache.commons.lang.builder", "ToStringStyle", "isShortClassName", "()", "summary", "df-generated"] + - ["org.apache.commons.lang.builder", "ToStringStyle", "isUseClassName", "()", "summary", "df-generated"] + - ["org.apache.commons.lang.builder", "ToStringStyle", "isUseFieldNames", "()", "summary", "df-generated"] + - ["org.apache.commons.lang.builder", "ToStringStyle", "isUseIdentityHashCode", "()", "summary", "df-generated"] + - ["org.apache.commons.lang.builder", "ToStringStyle", "isUseShortClassName", "()", "summary", "df-generated"] + - ["org.apache.commons.lang.builder", "ToStringStyle", "setArrayContentDetail", "(boolean)", "summary", "df-generated"] + - ["org.apache.commons.lang.builder", "ToStringStyle", "setDefaultFullDetail", "(boolean)", "summary", "df-generated"] + - ["org.apache.commons.lang.builder", "ToStringStyle", "setFieldSeparatorAtEnd", "(boolean)", "summary", "df-generated"] + - ["org.apache.commons.lang.builder", "ToStringStyle", "setFieldSeparatorAtStart", "(boolean)", "summary", "df-generated"] + - ["org.apache.commons.lang.builder", "ToStringStyle", "setShortClassName", "(boolean)", "summary", "df-generated"] + - ["org.apache.commons.lang.builder", "ToStringStyle", "setUseClassName", "(boolean)", "summary", "df-generated"] + - ["org.apache.commons.lang.builder", "ToStringStyle", "setUseFieldNames", "(boolean)", "summary", "df-generated"] + - ["org.apache.commons.lang.builder", "ToStringStyle", "setUseIdentityHashCode", "(boolean)", "summary", "df-generated"] + - ["org.apache.commons.lang.builder", "ToStringStyle", "setUseShortClassName", "(boolean)", "summary", "df-generated"] + - ["org.apache.commons.lang.enums", "Enum", "getEnumClass", "()", "summary", "df-generated"] + - ["org.apache.commons.lang.enums", "EnumUtils", "getEnum", "(Class,String)", "summary", "df-generated"] + - ["org.apache.commons.lang.enums", "EnumUtils", "getEnum", "(Class,int)", "summary", "df-generated"] + - ["org.apache.commons.lang.enums", "EnumUtils", "getEnumList", "(Class)", "summary", "df-generated"] + - ["org.apache.commons.lang.enums", "EnumUtils", "getEnumMap", "(Class)", "summary", "df-generated"] + - ["org.apache.commons.lang.enums", "EnumUtils", "iterator", "(Class)", "summary", "df-generated"] + - ["org.apache.commons.lang.enums", "ValuedEnum", "getValue", "()", "summary", "df-generated"] + - ["org.apache.commons.lang.exception", "ExceptionUtils", "addCauseMethodName", "(String)", "summary", "df-generated"] + - ["org.apache.commons.lang.exception", "ExceptionUtils", "getFullStackTrace", "(Throwable)", "summary", "df-generated"] + - ["org.apache.commons.lang.exception", "ExceptionUtils", "getStackFrames", "(Throwable)", "summary", "df-generated"] + - ["org.apache.commons.lang.exception", "ExceptionUtils", "getStackTrace", "(Throwable)", "summary", "df-generated"] + - ["org.apache.commons.lang.exception", "ExceptionUtils", "getThrowableCount", "(Throwable)", "summary", "df-generated"] + - ["org.apache.commons.lang.exception", "ExceptionUtils", "indexOfThrowable", "(Throwable,Class)", "summary", "df-generated"] + - ["org.apache.commons.lang.exception", "ExceptionUtils", "indexOfThrowable", "(Throwable,Class,int)", "summary", "df-generated"] + - ["org.apache.commons.lang.exception", "ExceptionUtils", "indexOfType", "(Throwable,Class)", "summary", "df-generated"] + - ["org.apache.commons.lang.exception", "ExceptionUtils", "indexOfType", "(Throwable,Class,int)", "summary", "df-generated"] + - ["org.apache.commons.lang.exception", "ExceptionUtils", "isCauseMethodName", "(String)", "summary", "df-generated"] + - ["org.apache.commons.lang.exception", "ExceptionUtils", "isNestedThrowable", "(Throwable)", "summary", "df-generated"] + - ["org.apache.commons.lang.exception", "ExceptionUtils", "isThrowableNested", "()", "summary", "df-generated"] + - ["org.apache.commons.lang.exception", "ExceptionUtils", "printRootCauseStackTrace", "(Throwable)", "summary", "df-generated"] + - ["org.apache.commons.lang.exception", "ExceptionUtils", "printRootCauseStackTrace", "(Throwable,PrintStream)", "summary", "df-generated"] + - ["org.apache.commons.lang.exception", "ExceptionUtils", "printRootCauseStackTrace", "(Throwable,PrintWriter)", "summary", "df-generated"] + - ["org.apache.commons.lang.exception", "ExceptionUtils", "removeCauseMethodName", "(String)", "summary", "df-generated"] + - ["org.apache.commons.lang.exception", "ExceptionUtils", "removeCommonFrames", "(List,List)", "summary", "df-generated"] + - ["org.apache.commons.lang.exception", "ExceptionUtils", "setCause", "(Throwable,Throwable)", "summary", "df-generated"] + - ["org.apache.commons.lang.exception", "Nestable", "getMessages", "()", "summary", "df-generated"] + - ["org.apache.commons.lang.exception", "Nestable", "getThrowableCount", "()", "summary", "df-generated"] + - ["org.apache.commons.lang.exception", "Nestable", "indexOfThrowable", "(Class)", "summary", "df-generated"] + - ["org.apache.commons.lang.exception", "Nestable", "indexOfThrowable", "(Class,int)", "summary", "df-generated"] + - ["org.apache.commons.lang.exception", "Nestable", "printPartialStackTrace", "(PrintWriter)", "summary", "df-generated"] + - ["org.apache.commons.lang.exception", "Nestable", "printStackTrace", "(PrintStream)", "summary", "df-generated"] + - ["org.apache.commons.lang.exception", "Nestable", "printStackTrace", "(PrintWriter)", "summary", "df-generated"] + - ["org.apache.commons.lang.exception", "NestableDelegate", "getThrowableCount", "()", "summary", "df-generated"] + - ["org.apache.commons.lang.exception", "NestableDelegate", "indexOfThrowable", "(Class,int)", "summary", "df-generated"] + - ["org.apache.commons.lang.exception", "NestableDelegate", "printStackTrace", "()", "summary", "df-generated"] + - ["org.apache.commons.lang.exception", "NestableDelegate", "printStackTrace", "(PrintStream)", "summary", "df-generated"] + - ["org.apache.commons.lang.exception", "NestableDelegate", "printStackTrace", "(PrintWriter)", "summary", "df-generated"] + - ["org.apache.commons.lang.exception", "NestableError", "NestableError", "(String)", "summary", "df-generated"] + - ["org.apache.commons.lang.math", "DoubleRange", "DoubleRange", "(Number)", "summary", "df-generated"] + - ["org.apache.commons.lang.math", "DoubleRange", "DoubleRange", "(Number,Number)", "summary", "df-generated"] + - ["org.apache.commons.lang.math", "DoubleRange", "DoubleRange", "(double)", "summary", "df-generated"] + - ["org.apache.commons.lang.math", "DoubleRange", "DoubleRange", "(double,double)", "summary", "df-generated"] + - ["org.apache.commons.lang.math", "FloatRange", "FloatRange", "(Number)", "summary", "df-generated"] + - ["org.apache.commons.lang.math", "FloatRange", "FloatRange", "(Number,Number)", "summary", "df-generated"] + - ["org.apache.commons.lang.math", "FloatRange", "FloatRange", "(float)", "summary", "df-generated"] + - ["org.apache.commons.lang.math", "FloatRange", "FloatRange", "(float,float)", "summary", "df-generated"] + - ["org.apache.commons.lang.math", "Fraction", "add", "(Fraction)", "summary", "df-generated"] + - ["org.apache.commons.lang.math", "Fraction", "divideBy", "(Fraction)", "summary", "df-generated"] + - ["org.apache.commons.lang.math", "Fraction", "getDenominator", "()", "summary", "df-generated"] + - ["org.apache.commons.lang.math", "Fraction", "getFraction", "(String)", "summary", "df-generated"] + - ["org.apache.commons.lang.math", "Fraction", "getFraction", "(double)", "summary", "df-generated"] + - ["org.apache.commons.lang.math", "Fraction", "getFraction", "(int,int)", "summary", "df-generated"] + - ["org.apache.commons.lang.math", "Fraction", "getFraction", "(int,int,int)", "summary", "df-generated"] + - ["org.apache.commons.lang.math", "Fraction", "getNumerator", "()", "summary", "df-generated"] + - ["org.apache.commons.lang.math", "Fraction", "getProperNumerator", "()", "summary", "df-generated"] + - ["org.apache.commons.lang.math", "Fraction", "getProperWhole", "()", "summary", "df-generated"] + - ["org.apache.commons.lang.math", "Fraction", "getReducedFraction", "(int,int)", "summary", "df-generated"] + - ["org.apache.commons.lang.math", "Fraction", "invert", "()", "summary", "df-generated"] + - ["org.apache.commons.lang.math", "Fraction", "multiplyBy", "(Fraction)", "summary", "df-generated"] + - ["org.apache.commons.lang.math", "Fraction", "negate", "()", "summary", "df-generated"] + - ["org.apache.commons.lang.math", "Fraction", "subtract", "(Fraction)", "summary", "df-generated"] + - ["org.apache.commons.lang.math", "Fraction", "toProperString", "()", "summary", "df-generated"] + - ["org.apache.commons.lang.math", "Fraction", "toString", "()", "summary", "df-generated"] + - ["org.apache.commons.lang.math", "IEEE754rUtils", "max", "(double,double)", "summary", "df-generated"] + - ["org.apache.commons.lang.math", "IEEE754rUtils", "max", "(double,double,double)", "summary", "df-generated"] + - ["org.apache.commons.lang.math", "IEEE754rUtils", "max", "(double[])", "summary", "df-generated"] + - ["org.apache.commons.lang.math", "IEEE754rUtils", "max", "(float,float)", "summary", "df-generated"] + - ["org.apache.commons.lang.math", "IEEE754rUtils", "max", "(float,float,float)", "summary", "df-generated"] + - ["org.apache.commons.lang.math", "IEEE754rUtils", "max", "(float[])", "summary", "df-generated"] + - ["org.apache.commons.lang.math", "IEEE754rUtils", "min", "(double,double)", "summary", "df-generated"] + - ["org.apache.commons.lang.math", "IEEE754rUtils", "min", "(double,double,double)", "summary", "df-generated"] + - ["org.apache.commons.lang.math", "IEEE754rUtils", "min", "(double[])", "summary", "df-generated"] + - ["org.apache.commons.lang.math", "IEEE754rUtils", "min", "(float,float)", "summary", "df-generated"] + - ["org.apache.commons.lang.math", "IEEE754rUtils", "min", "(float,float,float)", "summary", "df-generated"] + - ["org.apache.commons.lang.math", "IEEE754rUtils", "min", "(float[])", "summary", "df-generated"] + - ["org.apache.commons.lang.math", "IntRange", "IntRange", "(Number)", "summary", "df-generated"] + - ["org.apache.commons.lang.math", "IntRange", "IntRange", "(Number,Number)", "summary", "df-generated"] + - ["org.apache.commons.lang.math", "IntRange", "IntRange", "(int)", "summary", "df-generated"] + - ["org.apache.commons.lang.math", "IntRange", "IntRange", "(int,int)", "summary", "df-generated"] + - ["org.apache.commons.lang.math", "IntRange", "toArray", "()", "summary", "df-generated"] + - ["org.apache.commons.lang.math", "JVMRandom", "nextLong", "(long)", "summary", "df-generated"] + - ["org.apache.commons.lang.math", "LongRange", "LongRange", "(Number)", "summary", "df-generated"] + - ["org.apache.commons.lang.math", "LongRange", "LongRange", "(Number,Number)", "summary", "df-generated"] + - ["org.apache.commons.lang.math", "LongRange", "LongRange", "(long)", "summary", "df-generated"] + - ["org.apache.commons.lang.math", "LongRange", "LongRange", "(long,long)", "summary", "df-generated"] + - ["org.apache.commons.lang.math", "LongRange", "toArray", "()", "summary", "df-generated"] + - ["org.apache.commons.lang.math", "NumberRange", "NumberRange", "(Number)", "summary", "df-generated"] + - ["org.apache.commons.lang.math", "NumberRange", "NumberRange", "(Number,Number)", "summary", "df-generated"] + - ["org.apache.commons.lang.math", "NumberUtils", "compare", "(double,double)", "summary", "df-generated"] + - ["org.apache.commons.lang.math", "NumberUtils", "compare", "(float,float)", "summary", "df-generated"] + - ["org.apache.commons.lang.math", "NumberUtils", "createBigDecimal", "(String)", "summary", "df-generated"] + - ["org.apache.commons.lang.math", "NumberUtils", "createBigInteger", "(String)", "summary", "df-generated"] + - ["org.apache.commons.lang.math", "NumberUtils", "createDouble", "(String)", "summary", "df-generated"] + - ["org.apache.commons.lang.math", "NumberUtils", "createFloat", "(String)", "summary", "df-generated"] + - ["org.apache.commons.lang.math", "NumberUtils", "createInteger", "(String)", "summary", "df-generated"] + - ["org.apache.commons.lang.math", "NumberUtils", "createLong", "(String)", "summary", "df-generated"] + - ["org.apache.commons.lang.math", "NumberUtils", "createNumber", "(String)", "summary", "df-generated"] + - ["org.apache.commons.lang.math", "NumberUtils", "isDigits", "(String)", "summary", "df-generated"] + - ["org.apache.commons.lang.math", "NumberUtils", "isNumber", "(String)", "summary", "df-generated"] + - ["org.apache.commons.lang.math", "NumberUtils", "max", "(byte,byte,byte)", "summary", "df-generated"] + - ["org.apache.commons.lang.math", "NumberUtils", "max", "(byte[])", "summary", "df-generated"] + - ["org.apache.commons.lang.math", "NumberUtils", "max", "(double,double,double)", "summary", "df-generated"] + - ["org.apache.commons.lang.math", "NumberUtils", "max", "(double[])", "summary", "df-generated"] + - ["org.apache.commons.lang.math", "NumberUtils", "max", "(float,float,float)", "summary", "df-generated"] + - ["org.apache.commons.lang.math", "NumberUtils", "max", "(float[])", "summary", "df-generated"] + - ["org.apache.commons.lang.math", "NumberUtils", "max", "(int,int,int)", "summary", "df-generated"] + - ["org.apache.commons.lang.math", "NumberUtils", "max", "(int[])", "summary", "df-generated"] + - ["org.apache.commons.lang.math", "NumberUtils", "max", "(long,long,long)", "summary", "df-generated"] + - ["org.apache.commons.lang.math", "NumberUtils", "max", "(long[])", "summary", "df-generated"] + - ["org.apache.commons.lang.math", "NumberUtils", "max", "(short,short,short)", "summary", "df-generated"] + - ["org.apache.commons.lang.math", "NumberUtils", "max", "(short[])", "summary", "df-generated"] + - ["org.apache.commons.lang.math", "NumberUtils", "min", "(byte,byte,byte)", "summary", "df-generated"] + - ["org.apache.commons.lang.math", "NumberUtils", "min", "(byte[])", "summary", "df-generated"] + - ["org.apache.commons.lang.math", "NumberUtils", "min", "(double,double,double)", "summary", "df-generated"] + - ["org.apache.commons.lang.math", "NumberUtils", "min", "(double[])", "summary", "df-generated"] + - ["org.apache.commons.lang.math", "NumberUtils", "min", "(float,float,float)", "summary", "df-generated"] + - ["org.apache.commons.lang.math", "NumberUtils", "min", "(float[])", "summary", "df-generated"] + - ["org.apache.commons.lang.math", "NumberUtils", "min", "(int,int,int)", "summary", "df-generated"] + - ["org.apache.commons.lang.math", "NumberUtils", "min", "(int[])", "summary", "df-generated"] + - ["org.apache.commons.lang.math", "NumberUtils", "min", "(long,long,long)", "summary", "df-generated"] + - ["org.apache.commons.lang.math", "NumberUtils", "min", "(long[])", "summary", "df-generated"] + - ["org.apache.commons.lang.math", "NumberUtils", "min", "(short,short,short)", "summary", "df-generated"] + - ["org.apache.commons.lang.math", "NumberUtils", "min", "(short[])", "summary", "df-generated"] + - ["org.apache.commons.lang.math", "NumberUtils", "stringToInt", "(String)", "summary", "df-generated"] + - ["org.apache.commons.lang.math", "NumberUtils", "stringToInt", "(String,int)", "summary", "df-generated"] + - ["org.apache.commons.lang.math", "NumberUtils", "toDouble", "(String)", "summary", "df-generated"] + - ["org.apache.commons.lang.math", "NumberUtils", "toDouble", "(String,double)", "summary", "df-generated"] + - ["org.apache.commons.lang.math", "NumberUtils", "toFloat", "(String)", "summary", "df-generated"] + - ["org.apache.commons.lang.math", "NumberUtils", "toFloat", "(String,float)", "summary", "df-generated"] + - ["org.apache.commons.lang.math", "NumberUtils", "toInt", "(String)", "summary", "df-generated"] + - ["org.apache.commons.lang.math", "NumberUtils", "toInt", "(String,int)", "summary", "df-generated"] + - ["org.apache.commons.lang.math", "NumberUtils", "toLong", "(String)", "summary", "df-generated"] + - ["org.apache.commons.lang.math", "NumberUtils", "toLong", "(String,long)", "summary", "df-generated"] + - ["org.apache.commons.lang.math", "RandomUtils", "nextBoolean", "()", "summary", "df-generated"] + - ["org.apache.commons.lang.math", "RandomUtils", "nextBoolean", "(Random)", "summary", "df-generated"] + - ["org.apache.commons.lang.math", "RandomUtils", "nextDouble", "()", "summary", "df-generated"] + - ["org.apache.commons.lang.math", "RandomUtils", "nextDouble", "(Random)", "summary", "df-generated"] + - ["org.apache.commons.lang.math", "RandomUtils", "nextFloat", "()", "summary", "df-generated"] + - ["org.apache.commons.lang.math", "RandomUtils", "nextFloat", "(Random)", "summary", "df-generated"] + - ["org.apache.commons.lang.math", "RandomUtils", "nextInt", "()", "summary", "df-generated"] + - ["org.apache.commons.lang.math", "RandomUtils", "nextInt", "(Random)", "summary", "df-generated"] + - ["org.apache.commons.lang.math", "RandomUtils", "nextInt", "(Random,int)", "summary", "df-generated"] + - ["org.apache.commons.lang.math", "RandomUtils", "nextInt", "(int)", "summary", "df-generated"] + - ["org.apache.commons.lang.math", "RandomUtils", "nextLong", "()", "summary", "df-generated"] + - ["org.apache.commons.lang.math", "RandomUtils", "nextLong", "(Random)", "summary", "df-generated"] + - ["org.apache.commons.lang.math", "Range", "containsDouble", "(Number)", "summary", "df-generated"] + - ["org.apache.commons.lang.math", "Range", "containsDouble", "(double)", "summary", "df-generated"] + - ["org.apache.commons.lang.math", "Range", "containsFloat", "(Number)", "summary", "df-generated"] + - ["org.apache.commons.lang.math", "Range", "containsFloat", "(float)", "summary", "df-generated"] + - ["org.apache.commons.lang.math", "Range", "containsInteger", "(Number)", "summary", "df-generated"] + - ["org.apache.commons.lang.math", "Range", "containsInteger", "(int)", "summary", "df-generated"] + - ["org.apache.commons.lang.math", "Range", "containsLong", "(Number)", "summary", "df-generated"] + - ["org.apache.commons.lang.math", "Range", "containsLong", "(long)", "summary", "df-generated"] + - ["org.apache.commons.lang.math", "Range", "containsNumber", "(Number)", "summary", "df-generated"] + - ["org.apache.commons.lang.math", "Range", "containsRange", "(Range)", "summary", "df-generated"] + - ["org.apache.commons.lang.math", "Range", "getMaximumDouble", "()", "summary", "df-generated"] + - ["org.apache.commons.lang.math", "Range", "getMaximumFloat", "()", "summary", "df-generated"] + - ["org.apache.commons.lang.math", "Range", "getMaximumInteger", "()", "summary", "df-generated"] + - ["org.apache.commons.lang.math", "Range", "getMaximumLong", "()", "summary", "df-generated"] + - ["org.apache.commons.lang.math", "Range", "getMaximumNumber", "()", "summary", "df-generated"] + - ["org.apache.commons.lang.math", "Range", "getMinimumDouble", "()", "summary", "df-generated"] + - ["org.apache.commons.lang.math", "Range", "getMinimumFloat", "()", "summary", "df-generated"] + - ["org.apache.commons.lang.math", "Range", "getMinimumInteger", "()", "summary", "df-generated"] + - ["org.apache.commons.lang.math", "Range", "getMinimumLong", "()", "summary", "df-generated"] + - ["org.apache.commons.lang.math", "Range", "getMinimumNumber", "()", "summary", "df-generated"] + - ["org.apache.commons.lang.math", "Range", "overlapsRange", "(Range)", "summary", "df-generated"] + - ["org.apache.commons.lang.math", "Range", "toString", "()", "summary", "df-generated"] + - ["org.apache.commons.lang.mutable", "Mutable", "getValue", "()", "summary", "df-generated"] + - ["org.apache.commons.lang.mutable", "Mutable", "setValue", "(Object)", "summary", "df-generated"] + - ["org.apache.commons.lang.mutable", "MutableBoolean", "MutableBoolean", "(Boolean)", "summary", "df-generated"] + - ["org.apache.commons.lang.mutable", "MutableBoolean", "MutableBoolean", "(boolean)", "summary", "df-generated"] + - ["org.apache.commons.lang.mutable", "MutableBoolean", "booleanValue", "()", "summary", "df-generated"] + - ["org.apache.commons.lang.mutable", "MutableBoolean", "setValue", "(boolean)", "summary", "df-generated"] + - ["org.apache.commons.lang.mutable", "MutableBoolean", "toString", "()", "summary", "df-generated"] + - ["org.apache.commons.lang.mutable", "MutableByte", "MutableByte", "(Number)", "summary", "df-generated"] + - ["org.apache.commons.lang.mutable", "MutableByte", "MutableByte", "(byte)", "summary", "df-generated"] + - ["org.apache.commons.lang.mutable", "MutableByte", "add", "(Number)", "summary", "df-generated"] + - ["org.apache.commons.lang.mutable", "MutableByte", "add", "(byte)", "summary", "df-generated"] + - ["org.apache.commons.lang.mutable", "MutableByte", "decrement", "()", "summary", "df-generated"] + - ["org.apache.commons.lang.mutable", "MutableByte", "increment", "()", "summary", "df-generated"] + - ["org.apache.commons.lang.mutable", "MutableByte", "setValue", "(byte)", "summary", "df-generated"] + - ["org.apache.commons.lang.mutable", "MutableByte", "subtract", "(Number)", "summary", "df-generated"] + - ["org.apache.commons.lang.mutable", "MutableByte", "subtract", "(byte)", "summary", "df-generated"] + - ["org.apache.commons.lang.mutable", "MutableByte", "toByte", "()", "summary", "df-generated"] + - ["org.apache.commons.lang.mutable", "MutableByte", "toString", "()", "summary", "df-generated"] + - ["org.apache.commons.lang.mutable", "MutableDouble", "MutableDouble", "(Number)", "summary", "df-generated"] + - ["org.apache.commons.lang.mutable", "MutableDouble", "MutableDouble", "(double)", "summary", "df-generated"] + - ["org.apache.commons.lang.mutable", "MutableDouble", "add", "(Number)", "summary", "df-generated"] + - ["org.apache.commons.lang.mutable", "MutableDouble", "add", "(double)", "summary", "df-generated"] + - ["org.apache.commons.lang.mutable", "MutableDouble", "decrement", "()", "summary", "df-generated"] + - ["org.apache.commons.lang.mutable", "MutableDouble", "increment", "()", "summary", "df-generated"] + - ["org.apache.commons.lang.mutable", "MutableDouble", "isInfinite", "()", "summary", "df-generated"] + - ["org.apache.commons.lang.mutable", "MutableDouble", "isNaN", "()", "summary", "df-generated"] + - ["org.apache.commons.lang.mutable", "MutableDouble", "setValue", "(double)", "summary", "df-generated"] + - ["org.apache.commons.lang.mutable", "MutableDouble", "subtract", "(Number)", "summary", "df-generated"] + - ["org.apache.commons.lang.mutable", "MutableDouble", "subtract", "(double)", "summary", "df-generated"] + - ["org.apache.commons.lang.mutable", "MutableDouble", "toDouble", "()", "summary", "df-generated"] + - ["org.apache.commons.lang.mutable", "MutableDouble", "toString", "()", "summary", "df-generated"] + - ["org.apache.commons.lang.mutable", "MutableFloat", "MutableFloat", "(Number)", "summary", "df-generated"] + - ["org.apache.commons.lang.mutable", "MutableFloat", "MutableFloat", "(float)", "summary", "df-generated"] + - ["org.apache.commons.lang.mutable", "MutableFloat", "add", "(Number)", "summary", "df-generated"] + - ["org.apache.commons.lang.mutable", "MutableFloat", "add", "(float)", "summary", "df-generated"] + - ["org.apache.commons.lang.mutable", "MutableFloat", "decrement", "()", "summary", "df-generated"] + - ["org.apache.commons.lang.mutable", "MutableFloat", "increment", "()", "summary", "df-generated"] + - ["org.apache.commons.lang.mutable", "MutableFloat", "isInfinite", "()", "summary", "df-generated"] + - ["org.apache.commons.lang.mutable", "MutableFloat", "isNaN", "()", "summary", "df-generated"] + - ["org.apache.commons.lang.mutable", "MutableFloat", "setValue", "(float)", "summary", "df-generated"] + - ["org.apache.commons.lang.mutable", "MutableFloat", "subtract", "(Number)", "summary", "df-generated"] + - ["org.apache.commons.lang.mutable", "MutableFloat", "subtract", "(float)", "summary", "df-generated"] + - ["org.apache.commons.lang.mutable", "MutableFloat", "toFloat", "()", "summary", "df-generated"] + - ["org.apache.commons.lang.mutable", "MutableFloat", "toString", "()", "summary", "df-generated"] + - ["org.apache.commons.lang.mutable", "MutableInt", "MutableInt", "(Number)", "summary", "df-generated"] + - ["org.apache.commons.lang.mutable", "MutableInt", "MutableInt", "(int)", "summary", "df-generated"] + - ["org.apache.commons.lang.mutable", "MutableInt", "add", "(Number)", "summary", "df-generated"] + - ["org.apache.commons.lang.mutable", "MutableInt", "add", "(int)", "summary", "df-generated"] + - ["org.apache.commons.lang.mutable", "MutableInt", "decrement", "()", "summary", "df-generated"] + - ["org.apache.commons.lang.mutable", "MutableInt", "increment", "()", "summary", "df-generated"] + - ["org.apache.commons.lang.mutable", "MutableInt", "setValue", "(int)", "summary", "df-generated"] + - ["org.apache.commons.lang.mutable", "MutableInt", "subtract", "(Number)", "summary", "df-generated"] + - ["org.apache.commons.lang.mutable", "MutableInt", "subtract", "(int)", "summary", "df-generated"] + - ["org.apache.commons.lang.mutable", "MutableInt", "toInteger", "()", "summary", "df-generated"] + - ["org.apache.commons.lang.mutable", "MutableInt", "toString", "()", "summary", "df-generated"] + - ["org.apache.commons.lang.mutable", "MutableLong", "MutableLong", "(Number)", "summary", "df-generated"] + - ["org.apache.commons.lang.mutable", "MutableLong", "MutableLong", "(long)", "summary", "df-generated"] + - ["org.apache.commons.lang.mutable", "MutableLong", "add", "(Number)", "summary", "df-generated"] + - ["org.apache.commons.lang.mutable", "MutableLong", "add", "(long)", "summary", "df-generated"] + - ["org.apache.commons.lang.mutable", "MutableLong", "decrement", "()", "summary", "df-generated"] + - ["org.apache.commons.lang.mutable", "MutableLong", "increment", "()", "summary", "df-generated"] + - ["org.apache.commons.lang.mutable", "MutableLong", "setValue", "(long)", "summary", "df-generated"] + - ["org.apache.commons.lang.mutable", "MutableLong", "subtract", "(Number)", "summary", "df-generated"] + - ["org.apache.commons.lang.mutable", "MutableLong", "subtract", "(long)", "summary", "df-generated"] + - ["org.apache.commons.lang.mutable", "MutableLong", "toLong", "()", "summary", "df-generated"] + - ["org.apache.commons.lang.mutable", "MutableLong", "toString", "()", "summary", "df-generated"] + - ["org.apache.commons.lang.mutable", "MutableObject", "toString", "()", "summary", "df-generated"] + - ["org.apache.commons.lang.mutable", "MutableShort", "MutableShort", "(Number)", "summary", "df-generated"] + - ["org.apache.commons.lang.mutable", "MutableShort", "MutableShort", "(short)", "summary", "df-generated"] + - ["org.apache.commons.lang.mutable", "MutableShort", "add", "(Number)", "summary", "df-generated"] + - ["org.apache.commons.lang.mutable", "MutableShort", "add", "(short)", "summary", "df-generated"] + - ["org.apache.commons.lang.mutable", "MutableShort", "decrement", "()", "summary", "df-generated"] + - ["org.apache.commons.lang.mutable", "MutableShort", "increment", "()", "summary", "df-generated"] + - ["org.apache.commons.lang.mutable", "MutableShort", "setValue", "(short)", "summary", "df-generated"] + - ["org.apache.commons.lang.mutable", "MutableShort", "subtract", "(Number)", "summary", "df-generated"] + - ["org.apache.commons.lang.mutable", "MutableShort", "subtract", "(short)", "summary", "df-generated"] + - ["org.apache.commons.lang.mutable", "MutableShort", "toShort", "()", "summary", "df-generated"] + - ["org.apache.commons.lang.mutable", "MutableShort", "toString", "()", "summary", "df-generated"] + - ["org.apache.commons.lang.reflect", "ConstructorUtils", "getAccessibleConstructor", "(Class,Class)", "summary", "df-generated"] + - ["org.apache.commons.lang.reflect", "ConstructorUtils", "getAccessibleConstructor", "(Class,Class[])", "summary", "df-generated"] + - ["org.apache.commons.lang.reflect", "ConstructorUtils", "getMatchingAccessibleConstructor", "(Class,Class[])", "summary", "df-generated"] + - ["org.apache.commons.lang.reflect", "ConstructorUtils", "invokeConstructor", "(Class,Object)", "summary", "df-generated"] + - ["org.apache.commons.lang.reflect", "ConstructorUtils", "invokeConstructor", "(Class,Object[])", "summary", "df-generated"] + - ["org.apache.commons.lang.reflect", "ConstructorUtils", "invokeConstructor", "(Class,Object[],Class[])", "summary", "df-generated"] + - ["org.apache.commons.lang.reflect", "ConstructorUtils", "invokeExactConstructor", "(Class,Object)", "summary", "df-generated"] + - ["org.apache.commons.lang.reflect", "ConstructorUtils", "invokeExactConstructor", "(Class,Object[])", "summary", "df-generated"] + - ["org.apache.commons.lang.reflect", "ConstructorUtils", "invokeExactConstructor", "(Class,Object[],Class[])", "summary", "df-generated"] + - ["org.apache.commons.lang.reflect", "FieldUtils", "getDeclaredField", "(Class,String)", "summary", "df-generated"] + - ["org.apache.commons.lang.reflect", "FieldUtils", "getDeclaredField", "(Class,String,boolean)", "summary", "df-generated"] + - ["org.apache.commons.lang.reflect", "FieldUtils", "getField", "(Class,String)", "summary", "df-generated"] + - ["org.apache.commons.lang.reflect", "FieldUtils", "getField", "(Class,String,boolean)", "summary", "df-generated"] + - ["org.apache.commons.lang.reflect", "FieldUtils", "readDeclaredField", "(Object,String)", "summary", "df-generated"] + - ["org.apache.commons.lang.reflect", "FieldUtils", "readDeclaredField", "(Object,String,boolean)", "summary", "df-generated"] + - ["org.apache.commons.lang.reflect", "FieldUtils", "readDeclaredStaticField", "(Class,String)", "summary", "df-generated"] + - ["org.apache.commons.lang.reflect", "FieldUtils", "readDeclaredStaticField", "(Class,String,boolean)", "summary", "df-generated"] + - ["org.apache.commons.lang.reflect", "FieldUtils", "readField", "(Field,Object)", "summary", "df-generated"] + - ["org.apache.commons.lang.reflect", "FieldUtils", "readField", "(Field,Object,boolean)", "summary", "df-generated"] + - ["org.apache.commons.lang.reflect", "FieldUtils", "readField", "(Object,String)", "summary", "df-generated"] + - ["org.apache.commons.lang.reflect", "FieldUtils", "readField", "(Object,String,boolean)", "summary", "df-generated"] + - ["org.apache.commons.lang.reflect", "FieldUtils", "readStaticField", "(Class,String)", "summary", "df-generated"] + - ["org.apache.commons.lang.reflect", "FieldUtils", "readStaticField", "(Class,String,boolean)", "summary", "df-generated"] + - ["org.apache.commons.lang.reflect", "FieldUtils", "readStaticField", "(Field)", "summary", "df-generated"] + - ["org.apache.commons.lang.reflect", "FieldUtils", "readStaticField", "(Field,boolean)", "summary", "df-generated"] + - ["org.apache.commons.lang.reflect", "FieldUtils", "writeDeclaredField", "(Object,String,Object)", "summary", "df-generated"] + - ["org.apache.commons.lang.reflect", "FieldUtils", "writeDeclaredField", "(Object,String,Object,boolean)", "summary", "df-generated"] + - ["org.apache.commons.lang.reflect", "FieldUtils", "writeDeclaredStaticField", "(Class,String,Object)", "summary", "df-generated"] + - ["org.apache.commons.lang.reflect", "FieldUtils", "writeDeclaredStaticField", "(Class,String,Object,boolean)", "summary", "df-generated"] + - ["org.apache.commons.lang.reflect", "FieldUtils", "writeField", "(Field,Object,Object)", "summary", "df-generated"] + - ["org.apache.commons.lang.reflect", "FieldUtils", "writeField", "(Field,Object,Object,boolean)", "summary", "df-generated"] + - ["org.apache.commons.lang.reflect", "FieldUtils", "writeField", "(Object,String,Object)", "summary", "df-generated"] + - ["org.apache.commons.lang.reflect", "FieldUtils", "writeField", "(Object,String,Object,boolean)", "summary", "df-generated"] + - ["org.apache.commons.lang.reflect", "FieldUtils", "writeStaticField", "(Class,String,Object)", "summary", "df-generated"] + - ["org.apache.commons.lang.reflect", "FieldUtils", "writeStaticField", "(Class,String,Object,boolean)", "summary", "df-generated"] + - ["org.apache.commons.lang.reflect", "FieldUtils", "writeStaticField", "(Field,Object)", "summary", "df-generated"] + - ["org.apache.commons.lang.reflect", "FieldUtils", "writeStaticField", "(Field,Object,boolean)", "summary", "df-generated"] + - ["org.apache.commons.lang.reflect", "MethodUtils", "clearCache", "()", "summary", "df-generated"] + - ["org.apache.commons.lang.reflect", "MethodUtils", "getAccessibleMethod", "(Class,String,Class)", "summary", "df-generated"] + - ["org.apache.commons.lang.reflect", "MethodUtils", "getAccessibleMethod", "(Class,String,Class[])", "summary", "df-generated"] + - ["org.apache.commons.lang.reflect", "MethodUtils", "getMatchingAccessibleMethod", "(Class,String,Class[])", "summary", "df-generated"] + - ["org.apache.commons.lang.reflect", "MethodUtils", "invokeExactMethod", "(Object,String,Object)", "summary", "df-generated"] + - ["org.apache.commons.lang.reflect", "MethodUtils", "invokeExactMethod", "(Object,String,Object[])", "summary", "df-generated"] + - ["org.apache.commons.lang.reflect", "MethodUtils", "invokeExactMethod", "(Object,String,Object[],Class[])", "summary", "df-generated"] + - ["org.apache.commons.lang.reflect", "MethodUtils", "invokeExactStaticMethod", "(Class,String,Object)", "summary", "df-generated"] + - ["org.apache.commons.lang.reflect", "MethodUtils", "invokeExactStaticMethod", "(Class,String,Object[])", "summary", "df-generated"] + - ["org.apache.commons.lang.reflect", "MethodUtils", "invokeExactStaticMethod", "(Class,String,Object[],Class[])", "summary", "df-generated"] + - ["org.apache.commons.lang.reflect", "MethodUtils", "invokeMethod", "(Object,String,Object)", "summary", "df-generated"] + - ["org.apache.commons.lang.reflect", "MethodUtils", "invokeMethod", "(Object,String,Object[])", "summary", "df-generated"] + - ["org.apache.commons.lang.reflect", "MethodUtils", "invokeMethod", "(Object,String,Object[],Class[])", "summary", "df-generated"] + - ["org.apache.commons.lang.reflect", "MethodUtils", "invokeStaticMethod", "(Class,String,Object)", "summary", "df-generated"] + - ["org.apache.commons.lang.reflect", "MethodUtils", "invokeStaticMethod", "(Class,String,Object[])", "summary", "df-generated"] + - ["org.apache.commons.lang.reflect", "MethodUtils", "invokeStaticMethod", "(Class,String,Object[],Class[])", "summary", "df-generated"] + - ["org.apache.commons.lang.reflect", "MethodUtils", "setCacheMethods", "(boolean)", "summary", "df-generated"] + - ["org.apache.commons.lang.text", "CompositeFormat", "reformat", "(String)", "summary", "df-generated"] + - ["org.apache.commons.lang.text", "StrBuilder", "StrBuilder", "(int)", "summary", "df-generated"] + - ["org.apache.commons.lang.text", "StrBuilder", "appendln", "(StrBuilder)", "summary", "df-generated"] + - ["org.apache.commons.lang.text", "StrBuilder", "appendln", "(StrBuilder,int,int)", "summary", "df-generated"] + - ["org.apache.commons.lang.text", "StrBuilder", "appendln", "(String)", "summary", "df-generated"] + - ["org.apache.commons.lang.text", "StrBuilder", "appendln", "(String,int,int)", "summary", "df-generated"] + - ["org.apache.commons.lang.text", "StrBuilder", "appendln", "(StringBuffer)", "summary", "df-generated"] + - ["org.apache.commons.lang.text", "StrBuilder", "appendln", "(StringBuffer,int,int)", "summary", "df-generated"] + - ["org.apache.commons.lang.text", "StrBuilder", "appendln", "(char[])", "summary", "df-generated"] + - ["org.apache.commons.lang.text", "StrBuilder", "appendln", "(char[],int,int)", "summary", "df-generated"] + - ["org.apache.commons.lang.text", "StrBuilder", "asReader", "()", "summary", "df-generated"] + - ["org.apache.commons.lang.text", "StrBuilder", "asTokenizer", "()", "summary", "df-generated"] + - ["org.apache.commons.lang.text", "StrBuilder", "asWriter", "()", "summary", "df-generated"] + - ["org.apache.commons.lang.text", "StrBuilder", "capacity", "()", "summary", "df-generated"] + - ["org.apache.commons.lang.text", "StrBuilder", "charAt", "(int)", "summary", "df-generated"] + - ["org.apache.commons.lang.text", "StrBuilder", "contains", "(StrMatcher)", "summary", "df-generated"] + - ["org.apache.commons.lang.text", "StrBuilder", "contains", "(String)", "summary", "df-generated"] + - ["org.apache.commons.lang.text", "StrBuilder", "contains", "(char)", "summary", "df-generated"] + - ["org.apache.commons.lang.text", "StrBuilder", "endsWith", "(String)", "summary", "df-generated"] + - ["org.apache.commons.lang.text", "StrBuilder", "equals", "(StrBuilder)", "summary", "df-generated"] + - ["org.apache.commons.lang.text", "StrBuilder", "equalsIgnoreCase", "(StrBuilder)", "summary", "df-generated"] + - ["org.apache.commons.lang.text", "StrBuilder", "indexOf", "(StrMatcher)", "summary", "df-generated"] + - ["org.apache.commons.lang.text", "StrBuilder", "indexOf", "(StrMatcher,int)", "summary", "df-generated"] + - ["org.apache.commons.lang.text", "StrBuilder", "indexOf", "(String)", "summary", "df-generated"] + - ["org.apache.commons.lang.text", "StrBuilder", "indexOf", "(String,int)", "summary", "df-generated"] + - ["org.apache.commons.lang.text", "StrBuilder", "indexOf", "(char)", "summary", "df-generated"] + - ["org.apache.commons.lang.text", "StrBuilder", "indexOf", "(char,int)", "summary", "df-generated"] + - ["org.apache.commons.lang.text", "StrBuilder", "isEmpty", "()", "summary", "df-generated"] + - ["org.apache.commons.lang.text", "StrBuilder", "lastIndexOf", "(StrMatcher)", "summary", "df-generated"] + - ["org.apache.commons.lang.text", "StrBuilder", "lastIndexOf", "(StrMatcher,int)", "summary", "df-generated"] + - ["org.apache.commons.lang.text", "StrBuilder", "lastIndexOf", "(String)", "summary", "df-generated"] + - ["org.apache.commons.lang.text", "StrBuilder", "lastIndexOf", "(String,int)", "summary", "df-generated"] + - ["org.apache.commons.lang.text", "StrBuilder", "lastIndexOf", "(char)", "summary", "df-generated"] + - ["org.apache.commons.lang.text", "StrBuilder", "lastIndexOf", "(char,int)", "summary", "df-generated"] + - ["org.apache.commons.lang.text", "StrBuilder", "length", "()", "summary", "df-generated"] + - ["org.apache.commons.lang.text", "StrBuilder", "size", "()", "summary", "df-generated"] + - ["org.apache.commons.lang.text", "StrBuilder", "startsWith", "(String)", "summary", "df-generated"] + - ["org.apache.commons.lang.text", "StrLookup", "lookup", "(String)", "summary", "df-generated"] + - ["org.apache.commons.lang.text", "StrLookup", "noneLookup", "()", "summary", "df-generated"] + - ["org.apache.commons.lang.text", "StrLookup", "systemPropertiesLookup", "()", "summary", "df-generated"] + - ["org.apache.commons.lang.text", "StrMatcher", "charMatcher", "(char)", "summary", "df-generated"] + - ["org.apache.commons.lang.text", "StrMatcher", "commaMatcher", "()", "summary", "df-generated"] + - ["org.apache.commons.lang.text", "StrMatcher", "doubleQuoteMatcher", "()", "summary", "df-generated"] + - ["org.apache.commons.lang.text", "StrMatcher", "isMatch", "(char[],int)", "summary", "df-generated"] + - ["org.apache.commons.lang.text", "StrMatcher", "isMatch", "(char[],int,int,int)", "summary", "df-generated"] + - ["org.apache.commons.lang.text", "StrMatcher", "noneMatcher", "()", "summary", "df-generated"] + - ["org.apache.commons.lang.text", "StrMatcher", "quoteMatcher", "()", "summary", "df-generated"] + - ["org.apache.commons.lang.text", "StrMatcher", "singleQuoteMatcher", "()", "summary", "df-generated"] + - ["org.apache.commons.lang.text", "StrMatcher", "spaceMatcher", "()", "summary", "df-generated"] + - ["org.apache.commons.lang.text", "StrMatcher", "splitMatcher", "()", "summary", "df-generated"] + - ["org.apache.commons.lang.text", "StrMatcher", "tabMatcher", "()", "summary", "df-generated"] + - ["org.apache.commons.lang.text", "StrMatcher", "trimMatcher", "()", "summary", "df-generated"] + - ["org.apache.commons.lang.text", "StrSubstitutor", "getEscapeChar", "()", "summary", "df-generated"] + - ["org.apache.commons.lang.text", "StrSubstitutor", "replace", "(Object)", "summary", "df-generated"] + - ["org.apache.commons.lang.text", "StrSubstitutor", "replace", "(Object,Map)", "summary", "df-generated"] + - ["org.apache.commons.lang.text", "StrSubstitutor", "replace", "(Object,Map,String,String)", "summary", "df-generated"] + - ["org.apache.commons.lang.text", "StrSubstitutor", "replaceIn", "(StrBuilder)", "summary", "df-generated"] + - ["org.apache.commons.lang.text", "StrSubstitutor", "replaceIn", "(StrBuilder,int,int)", "summary", "df-generated"] + - ["org.apache.commons.lang.text", "StrSubstitutor", "replaceIn", "(StringBuffer)", "summary", "df-generated"] + - ["org.apache.commons.lang.text", "StrSubstitutor", "replaceIn", "(StringBuffer,int,int)", "summary", "df-generated"] + - ["org.apache.commons.lang.text", "StrSubstitutor", "replaceSystemProperties", "(Object)", "summary", "df-generated"] + - ["org.apache.commons.lang.text", "StrSubstitutor", "setEscapeChar", "(char)", "summary", "df-generated"] + - ["org.apache.commons.lang.text", "StrTokenizer", "getCSVInstance", "()", "summary", "df-generated"] + - ["org.apache.commons.lang.text", "StrTokenizer", "getTSVInstance", "()", "summary", "df-generated"] + - ["org.apache.commons.lang.text", "StrTokenizer", "isEmptyTokenAsNull", "()", "summary", "df-generated"] + - ["org.apache.commons.lang.text", "StrTokenizer", "isIgnoreEmptyTokens", "()", "summary", "df-generated"] + - ["org.apache.commons.lang.text", "StrTokenizer", "size", "()", "summary", "df-generated"] + - ["org.apache.commons.lang.time", "DateFormatUtils", "format", "(Calendar,String)", "summary", "df-generated"] + - ["org.apache.commons.lang.time", "DateFormatUtils", "format", "(Calendar,String,Locale)", "summary", "df-generated"] + - ["org.apache.commons.lang.time", "DateFormatUtils", "format", "(Calendar,String,TimeZone)", "summary", "df-generated"] + - ["org.apache.commons.lang.time", "DateFormatUtils", "format", "(Calendar,String,TimeZone,Locale)", "summary", "df-generated"] + - ["org.apache.commons.lang.time", "DateFormatUtils", "format", "(Date,String)", "summary", "df-generated"] + - ["org.apache.commons.lang.time", "DateFormatUtils", "format", "(Date,String,Locale)", "summary", "df-generated"] + - ["org.apache.commons.lang.time", "DateFormatUtils", "format", "(Date,String,TimeZone)", "summary", "df-generated"] + - ["org.apache.commons.lang.time", "DateFormatUtils", "format", "(Date,String,TimeZone,Locale)", "summary", "df-generated"] + - ["org.apache.commons.lang.time", "DateFormatUtils", "format", "(long,String)", "summary", "df-generated"] + - ["org.apache.commons.lang.time", "DateFormatUtils", "format", "(long,String,Locale)", "summary", "df-generated"] + - ["org.apache.commons.lang.time", "DateFormatUtils", "format", "(long,String,TimeZone)", "summary", "df-generated"] + - ["org.apache.commons.lang.time", "DateFormatUtils", "format", "(long,String,TimeZone,Locale)", "summary", "df-generated"] + - ["org.apache.commons.lang.time", "DateFormatUtils", "formatUTC", "(Date,String)", "summary", "df-generated"] + - ["org.apache.commons.lang.time", "DateFormatUtils", "formatUTC", "(Date,String,Locale)", "summary", "df-generated"] + - ["org.apache.commons.lang.time", "DateFormatUtils", "formatUTC", "(long,String)", "summary", "df-generated"] + - ["org.apache.commons.lang.time", "DateFormatUtils", "formatUTC", "(long,String,Locale)", "summary", "df-generated"] + - ["org.apache.commons.lang.time", "DateUtils", "add", "(Date,int,int)", "summary", "df-generated"] + - ["org.apache.commons.lang.time", "DateUtils", "addDays", "(Date,int)", "summary", "df-generated"] + - ["org.apache.commons.lang.time", "DateUtils", "addHours", "(Date,int)", "summary", "df-generated"] + - ["org.apache.commons.lang.time", "DateUtils", "addMilliseconds", "(Date,int)", "summary", "df-generated"] + - ["org.apache.commons.lang.time", "DateUtils", "addMinutes", "(Date,int)", "summary", "df-generated"] + - ["org.apache.commons.lang.time", "DateUtils", "addMonths", "(Date,int)", "summary", "df-generated"] + - ["org.apache.commons.lang.time", "DateUtils", "addSeconds", "(Date,int)", "summary", "df-generated"] + - ["org.apache.commons.lang.time", "DateUtils", "addWeeks", "(Date,int)", "summary", "df-generated"] + - ["org.apache.commons.lang.time", "DateUtils", "addYears", "(Date,int)", "summary", "df-generated"] + - ["org.apache.commons.lang.time", "DateUtils", "getFragmentInDays", "(Calendar,int)", "summary", "df-generated"] + - ["org.apache.commons.lang.time", "DateUtils", "getFragmentInDays", "(Date,int)", "summary", "df-generated"] + - ["org.apache.commons.lang.time", "DateUtils", "getFragmentInHours", "(Calendar,int)", "summary", "df-generated"] + - ["org.apache.commons.lang.time", "DateUtils", "getFragmentInHours", "(Date,int)", "summary", "df-generated"] + - ["org.apache.commons.lang.time", "DateUtils", "getFragmentInMilliseconds", "(Calendar,int)", "summary", "df-generated"] + - ["org.apache.commons.lang.time", "DateUtils", "getFragmentInMilliseconds", "(Date,int)", "summary", "df-generated"] + - ["org.apache.commons.lang.time", "DateUtils", "getFragmentInMinutes", "(Calendar,int)", "summary", "df-generated"] + - ["org.apache.commons.lang.time", "DateUtils", "getFragmentInMinutes", "(Date,int)", "summary", "df-generated"] + - ["org.apache.commons.lang.time", "DateUtils", "getFragmentInSeconds", "(Calendar,int)", "summary", "df-generated"] + - ["org.apache.commons.lang.time", "DateUtils", "getFragmentInSeconds", "(Date,int)", "summary", "df-generated"] + - ["org.apache.commons.lang.time", "DateUtils", "isSameDay", "(Calendar,Calendar)", "summary", "df-generated"] + - ["org.apache.commons.lang.time", "DateUtils", "isSameDay", "(Date,Date)", "summary", "df-generated"] + - ["org.apache.commons.lang.time", "DateUtils", "isSameInstant", "(Calendar,Calendar)", "summary", "df-generated"] + - ["org.apache.commons.lang.time", "DateUtils", "isSameInstant", "(Date,Date)", "summary", "df-generated"] + - ["org.apache.commons.lang.time", "DateUtils", "isSameLocalTime", "(Calendar,Calendar)", "summary", "df-generated"] + - ["org.apache.commons.lang.time", "DateUtils", "iterator", "(Date,int)", "summary", "df-generated"] + - ["org.apache.commons.lang.time", "DateUtils", "parseDate", "(String,String[])", "summary", "df-generated"] + - ["org.apache.commons.lang.time", "DateUtils", "round", "(Date,int)", "summary", "df-generated"] + - ["org.apache.commons.lang.time", "DateUtils", "round", "(Object,int)", "summary", "df-generated"] + - ["org.apache.commons.lang.time", "DateUtils", "setDays", "(Date,int)", "summary", "df-generated"] + - ["org.apache.commons.lang.time", "DateUtils", "setHours", "(Date,int)", "summary", "df-generated"] + - ["org.apache.commons.lang.time", "DateUtils", "setMilliseconds", "(Date,int)", "summary", "df-generated"] + - ["org.apache.commons.lang.time", "DateUtils", "setMinutes", "(Date,int)", "summary", "df-generated"] + - ["org.apache.commons.lang.time", "DateUtils", "setMonths", "(Date,int)", "summary", "df-generated"] + - ["org.apache.commons.lang.time", "DateUtils", "setSeconds", "(Date,int)", "summary", "df-generated"] + - ["org.apache.commons.lang.time", "DateUtils", "setYears", "(Date,int)", "summary", "df-generated"] + - ["org.apache.commons.lang.time", "DateUtils", "truncate", "(Date,int)", "summary", "df-generated"] + - ["org.apache.commons.lang.time", "DateUtils", "truncate", "(Object,int)", "summary", "df-generated"] + - ["org.apache.commons.lang.time", "DurationFormatUtils", "formatDuration", "(long,String)", "summary", "df-generated"] + - ["org.apache.commons.lang.time", "DurationFormatUtils", "formatDuration", "(long,String,boolean)", "summary", "df-generated"] + - ["org.apache.commons.lang.time", "DurationFormatUtils", "formatDurationHMS", "(long)", "summary", "df-generated"] + - ["org.apache.commons.lang.time", "DurationFormatUtils", "formatDurationISO", "(long)", "summary", "df-generated"] + - ["org.apache.commons.lang.time", "DurationFormatUtils", "formatDurationWords", "(long,boolean,boolean)", "summary", "df-generated"] + - ["org.apache.commons.lang.time", "DurationFormatUtils", "formatPeriod", "(long,long,String)", "summary", "df-generated"] + - ["org.apache.commons.lang.time", "DurationFormatUtils", "formatPeriod", "(long,long,String,boolean,TimeZone)", "summary", "df-generated"] + - ["org.apache.commons.lang.time", "DurationFormatUtils", "formatPeriodISO", "(long,long)", "summary", "df-generated"] + - ["org.apache.commons.lang.time", "FastDateFormat", "format", "(Calendar)", "summary", "df-generated"] + - ["org.apache.commons.lang.time", "FastDateFormat", "format", "(Date)", "summary", "df-generated"] + - ["org.apache.commons.lang.time", "FastDateFormat", "format", "(long)", "summary", "df-generated"] + - ["org.apache.commons.lang.time", "FastDateFormat", "getDateInstance", "(int)", "summary", "df-generated"] + - ["org.apache.commons.lang.time", "FastDateFormat", "getDateTimeInstance", "(int,int)", "summary", "df-generated"] + - ["org.apache.commons.lang.time", "FastDateFormat", "getInstance", "()", "summary", "df-generated"] + - ["org.apache.commons.lang.time", "FastDateFormat", "getMaxLengthEstimate", "()", "summary", "df-generated"] + - ["org.apache.commons.lang.time", "FastDateFormat", "getTimeInstance", "(int)", "summary", "df-generated"] + - ["org.apache.commons.lang.time", "FastDateFormat", "getTimeZoneOverridesCalendar", "()", "summary", "df-generated"] + - ["org.apache.commons.lang.time", "StopWatch", "getSplitTime", "()", "summary", "df-generated"] + - ["org.apache.commons.lang.time", "StopWatch", "getStartTime", "()", "summary", "df-generated"] + - ["org.apache.commons.lang.time", "StopWatch", "getTime", "()", "summary", "df-generated"] + - ["org.apache.commons.lang.time", "StopWatch", "reset", "()", "summary", "df-generated"] + - ["org.apache.commons.lang.time", "StopWatch", "resume", "()", "summary", "df-generated"] + - ["org.apache.commons.lang.time", "StopWatch", "split", "()", "summary", "df-generated"] + - ["org.apache.commons.lang.time", "StopWatch", "start", "()", "summary", "df-generated"] + - ["org.apache.commons.lang.time", "StopWatch", "stop", "()", "summary", "df-generated"] + - ["org.apache.commons.lang.time", "StopWatch", "suspend", "()", "summary", "df-generated"] + - ["org.apache.commons.lang.time", "StopWatch", "toSplitString", "()", "summary", "df-generated"] + - ["org.apache.commons.lang.time", "StopWatch", "toString", "()", "summary", "df-generated"] + - ["org.apache.commons.lang.time", "StopWatch", "unsplit", "()", "summary", "df-generated"] + - ["org.apache.commons.lang", "ArrayUtils", "add", "(boolean[],boolean)", "summary", "df-generated"] + - ["org.apache.commons.lang", "ArrayUtils", "add", "(boolean[],int,boolean)", "summary", "df-generated"] + - ["org.apache.commons.lang", "ArrayUtils", "add", "(double[],double)", "summary", "df-generated"] + - ["org.apache.commons.lang", "ArrayUtils", "add", "(double[],int,double)", "summary", "df-generated"] + - ["org.apache.commons.lang", "ArrayUtils", "add", "(float[],float)", "summary", "df-generated"] + - ["org.apache.commons.lang", "ArrayUtils", "add", "(float[],int,float)", "summary", "df-generated"] + - ["org.apache.commons.lang", "ArrayUtils", "add", "(int[],int)", "summary", "df-generated"] + - ["org.apache.commons.lang", "ArrayUtils", "add", "(int[],int,int)", "summary", "df-generated"] + - ["org.apache.commons.lang", "ArrayUtils", "add", "(long[],int,long)", "summary", "df-generated"] + - ["org.apache.commons.lang", "ArrayUtils", "add", "(long[],long)", "summary", "df-generated"] + - ["org.apache.commons.lang", "ArrayUtils", "add", "(short[],int,short)", "summary", "df-generated"] + - ["org.apache.commons.lang", "ArrayUtils", "add", "(short[],short)", "summary", "df-generated"] + - ["org.apache.commons.lang", "ArrayUtils", "addAll", "(boolean[],boolean[])", "summary", "df-generated"] + - ["org.apache.commons.lang", "ArrayUtils", "addAll", "(double[],double[])", "summary", "df-generated"] + - ["org.apache.commons.lang", "ArrayUtils", "addAll", "(float[],float[])", "summary", "df-generated"] + - ["org.apache.commons.lang", "ArrayUtils", "addAll", "(int[],int[])", "summary", "df-generated"] + - ["org.apache.commons.lang", "ArrayUtils", "addAll", "(long[],long[])", "summary", "df-generated"] + - ["org.apache.commons.lang", "ArrayUtils", "addAll", "(short[],short[])", "summary", "df-generated"] + - ["org.apache.commons.lang", "ArrayUtils", "clone", "(boolean[])", "summary", "df-generated"] + - ["org.apache.commons.lang", "ArrayUtils", "clone", "(double[])", "summary", "df-generated"] + - ["org.apache.commons.lang", "ArrayUtils", "clone", "(float[])", "summary", "df-generated"] + - ["org.apache.commons.lang", "ArrayUtils", "clone", "(int[])", "summary", "df-generated"] + - ["org.apache.commons.lang", "ArrayUtils", "clone", "(long[])", "summary", "df-generated"] + - ["org.apache.commons.lang", "ArrayUtils", "clone", "(short[])", "summary", "df-generated"] + - ["org.apache.commons.lang", "ArrayUtils", "contains", "(Object[],Object)", "summary", "df-generated"] + - ["org.apache.commons.lang", "ArrayUtils", "contains", "(boolean[],boolean)", "summary", "df-generated"] + - ["org.apache.commons.lang", "ArrayUtils", "contains", "(byte[],byte)", "summary", "df-generated"] + - ["org.apache.commons.lang", "ArrayUtils", "contains", "(char[],char)", "summary", "df-generated"] + - ["org.apache.commons.lang", "ArrayUtils", "contains", "(double[],double)", "summary", "df-generated"] + - ["org.apache.commons.lang", "ArrayUtils", "contains", "(double[],double,double)", "summary", "df-generated"] + - ["org.apache.commons.lang", "ArrayUtils", "contains", "(float[],float)", "summary", "df-generated"] + - ["org.apache.commons.lang", "ArrayUtils", "contains", "(int[],int)", "summary", "df-generated"] + - ["org.apache.commons.lang", "ArrayUtils", "contains", "(long[],long)", "summary", "df-generated"] + - ["org.apache.commons.lang", "ArrayUtils", "contains", "(short[],short)", "summary", "df-generated"] + - ["org.apache.commons.lang", "ArrayUtils", "getLength", "(Object)", "summary", "df-generated"] + - ["org.apache.commons.lang", "ArrayUtils", "hashCode", "(Object)", "summary", "df-generated"] + - ["org.apache.commons.lang", "ArrayUtils", "indexOf", "(Object[],Object)", "summary", "df-generated"] + - ["org.apache.commons.lang", "ArrayUtils", "indexOf", "(Object[],Object,int)", "summary", "df-generated"] + - ["org.apache.commons.lang", "ArrayUtils", "indexOf", "(boolean[],boolean)", "summary", "df-generated"] + - ["org.apache.commons.lang", "ArrayUtils", "indexOf", "(boolean[],boolean,int)", "summary", "df-generated"] + - ["org.apache.commons.lang", "ArrayUtils", "indexOf", "(byte[],byte)", "summary", "df-generated"] + - ["org.apache.commons.lang", "ArrayUtils", "indexOf", "(byte[],byte,int)", "summary", "df-generated"] + - ["org.apache.commons.lang", "ArrayUtils", "indexOf", "(char[],char)", "summary", "df-generated"] + - ["org.apache.commons.lang", "ArrayUtils", "indexOf", "(char[],char,int)", "summary", "df-generated"] + - ["org.apache.commons.lang", "ArrayUtils", "indexOf", "(double[],double)", "summary", "df-generated"] + - ["org.apache.commons.lang", "ArrayUtils", "indexOf", "(double[],double,double)", "summary", "df-generated"] + - ["org.apache.commons.lang", "ArrayUtils", "indexOf", "(double[],double,int)", "summary", "df-generated"] + - ["org.apache.commons.lang", "ArrayUtils", "indexOf", "(double[],double,int,double)", "summary", "df-generated"] + - ["org.apache.commons.lang", "ArrayUtils", "indexOf", "(float[],float)", "summary", "df-generated"] + - ["org.apache.commons.lang", "ArrayUtils", "indexOf", "(float[],float,int)", "summary", "df-generated"] + - ["org.apache.commons.lang", "ArrayUtils", "indexOf", "(int[],int)", "summary", "df-generated"] + - ["org.apache.commons.lang", "ArrayUtils", "indexOf", "(int[],int,int)", "summary", "df-generated"] + - ["org.apache.commons.lang", "ArrayUtils", "indexOf", "(long[],long)", "summary", "df-generated"] + - ["org.apache.commons.lang", "ArrayUtils", "indexOf", "(long[],long,int)", "summary", "df-generated"] + - ["org.apache.commons.lang", "ArrayUtils", "indexOf", "(short[],short)", "summary", "df-generated"] + - ["org.apache.commons.lang", "ArrayUtils", "indexOf", "(short[],short,int)", "summary", "df-generated"] + - ["org.apache.commons.lang", "ArrayUtils", "isEmpty", "(Object[])", "summary", "df-generated"] + - ["org.apache.commons.lang", "ArrayUtils", "isEmpty", "(boolean[])", "summary", "df-generated"] + - ["org.apache.commons.lang", "ArrayUtils", "isEmpty", "(byte[])", "summary", "df-generated"] + - ["org.apache.commons.lang", "ArrayUtils", "isEmpty", "(char[])", "summary", "df-generated"] + - ["org.apache.commons.lang", "ArrayUtils", "isEmpty", "(double[])", "summary", "df-generated"] + - ["org.apache.commons.lang", "ArrayUtils", "isEmpty", "(float[])", "summary", "df-generated"] + - ["org.apache.commons.lang", "ArrayUtils", "isEmpty", "(int[])", "summary", "df-generated"] + - ["org.apache.commons.lang", "ArrayUtils", "isEmpty", "(long[])", "summary", "df-generated"] + - ["org.apache.commons.lang", "ArrayUtils", "isEmpty", "(short[])", "summary", "df-generated"] + - ["org.apache.commons.lang", "ArrayUtils", "isEquals", "(Object,Object)", "summary", "df-generated"] + - ["org.apache.commons.lang", "ArrayUtils", "isSameLength", "(Object[],Object[])", "summary", "df-generated"] + - ["org.apache.commons.lang", "ArrayUtils", "isSameLength", "(boolean[],boolean[])", "summary", "df-generated"] + - ["org.apache.commons.lang", "ArrayUtils", "isSameLength", "(byte[],byte[])", "summary", "df-generated"] + - ["org.apache.commons.lang", "ArrayUtils", "isSameLength", "(char[],char[])", "summary", "df-generated"] + - ["org.apache.commons.lang", "ArrayUtils", "isSameLength", "(double[],double[])", "summary", "df-generated"] + - ["org.apache.commons.lang", "ArrayUtils", "isSameLength", "(float[],float[])", "summary", "df-generated"] + - ["org.apache.commons.lang", "ArrayUtils", "isSameLength", "(int[],int[])", "summary", "df-generated"] + - ["org.apache.commons.lang", "ArrayUtils", "isSameLength", "(long[],long[])", "summary", "df-generated"] + - ["org.apache.commons.lang", "ArrayUtils", "isSameLength", "(short[],short[])", "summary", "df-generated"] + - ["org.apache.commons.lang", "ArrayUtils", "isSameType", "(Object,Object)", "summary", "df-generated"] + - ["org.apache.commons.lang", "ArrayUtils", "lastIndexOf", "(Object[],Object)", "summary", "df-generated"] + - ["org.apache.commons.lang", "ArrayUtils", "lastIndexOf", "(Object[],Object,int)", "summary", "df-generated"] + - ["org.apache.commons.lang", "ArrayUtils", "lastIndexOf", "(boolean[],boolean)", "summary", "df-generated"] + - ["org.apache.commons.lang", "ArrayUtils", "lastIndexOf", "(boolean[],boolean,int)", "summary", "df-generated"] + - ["org.apache.commons.lang", "ArrayUtils", "lastIndexOf", "(byte[],byte)", "summary", "df-generated"] + - ["org.apache.commons.lang", "ArrayUtils", "lastIndexOf", "(byte[],byte,int)", "summary", "df-generated"] + - ["org.apache.commons.lang", "ArrayUtils", "lastIndexOf", "(char[],char)", "summary", "df-generated"] + - ["org.apache.commons.lang", "ArrayUtils", "lastIndexOf", "(char[],char,int)", "summary", "df-generated"] + - ["org.apache.commons.lang", "ArrayUtils", "lastIndexOf", "(double[],double)", "summary", "df-generated"] + - ["org.apache.commons.lang", "ArrayUtils", "lastIndexOf", "(double[],double,double)", "summary", "df-generated"] + - ["org.apache.commons.lang", "ArrayUtils", "lastIndexOf", "(double[],double,int)", "summary", "df-generated"] + - ["org.apache.commons.lang", "ArrayUtils", "lastIndexOf", "(double[],double,int,double)", "summary", "df-generated"] + - ["org.apache.commons.lang", "ArrayUtils", "lastIndexOf", "(float[],float)", "summary", "df-generated"] + - ["org.apache.commons.lang", "ArrayUtils", "lastIndexOf", "(float[],float,int)", "summary", "df-generated"] + - ["org.apache.commons.lang", "ArrayUtils", "lastIndexOf", "(int[],int)", "summary", "df-generated"] + - ["org.apache.commons.lang", "ArrayUtils", "lastIndexOf", "(int[],int,int)", "summary", "df-generated"] + - ["org.apache.commons.lang", "ArrayUtils", "lastIndexOf", "(long[],long)", "summary", "df-generated"] + - ["org.apache.commons.lang", "ArrayUtils", "lastIndexOf", "(long[],long,int)", "summary", "df-generated"] + - ["org.apache.commons.lang", "ArrayUtils", "lastIndexOf", "(short[],short)", "summary", "df-generated"] + - ["org.apache.commons.lang", "ArrayUtils", "lastIndexOf", "(short[],short,int)", "summary", "df-generated"] + - ["org.apache.commons.lang", "ArrayUtils", "remove", "(boolean[],int)", "summary", "df-generated"] + - ["org.apache.commons.lang", "ArrayUtils", "remove", "(double[],int)", "summary", "df-generated"] + - ["org.apache.commons.lang", "ArrayUtils", "remove", "(float[],int)", "summary", "df-generated"] + - ["org.apache.commons.lang", "ArrayUtils", "remove", "(int[],int)", "summary", "df-generated"] + - ["org.apache.commons.lang", "ArrayUtils", "remove", "(long[],int)", "summary", "df-generated"] + - ["org.apache.commons.lang", "ArrayUtils", "remove", "(short[],int)", "summary", "df-generated"] + - ["org.apache.commons.lang", "ArrayUtils", "removeElement", "(boolean[],boolean)", "summary", "df-generated"] + - ["org.apache.commons.lang", "ArrayUtils", "removeElement", "(double[],double)", "summary", "df-generated"] + - ["org.apache.commons.lang", "ArrayUtils", "removeElement", "(float[],float)", "summary", "df-generated"] + - ["org.apache.commons.lang", "ArrayUtils", "removeElement", "(int[],int)", "summary", "df-generated"] + - ["org.apache.commons.lang", "ArrayUtils", "removeElement", "(long[],long)", "summary", "df-generated"] + - ["org.apache.commons.lang", "ArrayUtils", "removeElement", "(short[],short)", "summary", "df-generated"] + - ["org.apache.commons.lang", "ArrayUtils", "reverse", "(Object[])", "summary", "df-generated"] + - ["org.apache.commons.lang", "ArrayUtils", "reverse", "(boolean[])", "summary", "df-generated"] + - ["org.apache.commons.lang", "ArrayUtils", "reverse", "(byte[])", "summary", "df-generated"] + - ["org.apache.commons.lang", "ArrayUtils", "reverse", "(char[])", "summary", "df-generated"] + - ["org.apache.commons.lang", "ArrayUtils", "reverse", "(double[])", "summary", "df-generated"] + - ["org.apache.commons.lang", "ArrayUtils", "reverse", "(float[])", "summary", "df-generated"] + - ["org.apache.commons.lang", "ArrayUtils", "reverse", "(int[])", "summary", "df-generated"] + - ["org.apache.commons.lang", "ArrayUtils", "reverse", "(long[])", "summary", "df-generated"] + - ["org.apache.commons.lang", "ArrayUtils", "reverse", "(short[])", "summary", "df-generated"] + - ["org.apache.commons.lang", "ArrayUtils", "subarray", "(boolean[],int,int)", "summary", "df-generated"] + - ["org.apache.commons.lang", "ArrayUtils", "subarray", "(double[],int,int)", "summary", "df-generated"] + - ["org.apache.commons.lang", "ArrayUtils", "subarray", "(float[],int,int)", "summary", "df-generated"] + - ["org.apache.commons.lang", "ArrayUtils", "subarray", "(int[],int,int)", "summary", "df-generated"] + - ["org.apache.commons.lang", "ArrayUtils", "subarray", "(long[],int,int)", "summary", "df-generated"] + - ["org.apache.commons.lang", "ArrayUtils", "subarray", "(short[],int,int)", "summary", "df-generated"] + - ["org.apache.commons.lang", "ArrayUtils", "toObject", "(boolean[])", "summary", "df-generated"] + - ["org.apache.commons.lang", "ArrayUtils", "toObject", "(byte[])", "summary", "df-generated"] + - ["org.apache.commons.lang", "ArrayUtils", "toObject", "(char[])", "summary", "df-generated"] + - ["org.apache.commons.lang", "ArrayUtils", "toObject", "(double[])", "summary", "df-generated"] + - ["org.apache.commons.lang", "ArrayUtils", "toObject", "(float[])", "summary", "df-generated"] + - ["org.apache.commons.lang", "ArrayUtils", "toObject", "(int[])", "summary", "df-generated"] + - ["org.apache.commons.lang", "ArrayUtils", "toObject", "(long[])", "summary", "df-generated"] + - ["org.apache.commons.lang", "ArrayUtils", "toObject", "(short[])", "summary", "df-generated"] + - ["org.apache.commons.lang", "ArrayUtils", "toPrimitive", "(Boolean[])", "summary", "df-generated"] + - ["org.apache.commons.lang", "ArrayUtils", "toPrimitive", "(Boolean[],boolean)", "summary", "df-generated"] + - ["org.apache.commons.lang", "ArrayUtils", "toPrimitive", "(Byte[])", "summary", "df-generated"] + - ["org.apache.commons.lang", "ArrayUtils", "toPrimitive", "(Byte[],byte)", "summary", "df-generated"] + - ["org.apache.commons.lang", "ArrayUtils", "toPrimitive", "(Character[])", "summary", "df-generated"] + - ["org.apache.commons.lang", "ArrayUtils", "toPrimitive", "(Character[],char)", "summary", "df-generated"] + - ["org.apache.commons.lang", "ArrayUtils", "toPrimitive", "(Double[])", "summary", "df-generated"] + - ["org.apache.commons.lang", "ArrayUtils", "toPrimitive", "(Double[],double)", "summary", "df-generated"] + - ["org.apache.commons.lang", "ArrayUtils", "toPrimitive", "(Float[])", "summary", "df-generated"] + - ["org.apache.commons.lang", "ArrayUtils", "toPrimitive", "(Float[],float)", "summary", "df-generated"] + - ["org.apache.commons.lang", "ArrayUtils", "toPrimitive", "(Integer[])", "summary", "df-generated"] + - ["org.apache.commons.lang", "ArrayUtils", "toPrimitive", "(Integer[],int)", "summary", "df-generated"] + - ["org.apache.commons.lang", "ArrayUtils", "toPrimitive", "(Long[])", "summary", "df-generated"] + - ["org.apache.commons.lang", "ArrayUtils", "toPrimitive", "(Long[],long)", "summary", "df-generated"] + - ["org.apache.commons.lang", "ArrayUtils", "toPrimitive", "(Short[])", "summary", "df-generated"] + - ["org.apache.commons.lang", "ArrayUtils", "toPrimitive", "(Short[],short)", "summary", "df-generated"] + - ["org.apache.commons.lang", "BitField", "BitField", "(int)", "summary", "df-generated"] + - ["org.apache.commons.lang", "BitField", "clear", "(int)", "summary", "df-generated"] + - ["org.apache.commons.lang", "BitField", "clearByte", "(byte)", "summary", "df-generated"] + - ["org.apache.commons.lang", "BitField", "clearShort", "(short)", "summary", "df-generated"] + - ["org.apache.commons.lang", "BitField", "getRawValue", "(int)", "summary", "df-generated"] + - ["org.apache.commons.lang", "BitField", "getShortRawValue", "(short)", "summary", "df-generated"] + - ["org.apache.commons.lang", "BitField", "getShortValue", "(short)", "summary", "df-generated"] + - ["org.apache.commons.lang", "BitField", "getValue", "(int)", "summary", "df-generated"] + - ["org.apache.commons.lang", "BitField", "isAllSet", "(int)", "summary", "df-generated"] + - ["org.apache.commons.lang", "BitField", "isSet", "(int)", "summary", "df-generated"] + - ["org.apache.commons.lang", "BitField", "set", "(int)", "summary", "df-generated"] + - ["org.apache.commons.lang", "BitField", "setBoolean", "(int,boolean)", "summary", "df-generated"] + - ["org.apache.commons.lang", "BitField", "setByte", "(byte)", "summary", "df-generated"] + - ["org.apache.commons.lang", "BitField", "setByteBoolean", "(byte,boolean)", "summary", "df-generated"] + - ["org.apache.commons.lang", "BitField", "setShort", "(short)", "summary", "df-generated"] + - ["org.apache.commons.lang", "BitField", "setShortBoolean", "(short,boolean)", "summary", "df-generated"] + - ["org.apache.commons.lang", "BitField", "setShortValue", "(short,short)", "summary", "df-generated"] + - ["org.apache.commons.lang", "BitField", "setValue", "(int,int)", "summary", "df-generated"] + - ["org.apache.commons.lang", "BooleanUtils", "isFalse", "(Boolean)", "summary", "df-generated"] + - ["org.apache.commons.lang", "BooleanUtils", "isNotFalse", "(Boolean)", "summary", "df-generated"] + - ["org.apache.commons.lang", "BooleanUtils", "isNotTrue", "(Boolean)", "summary", "df-generated"] + - ["org.apache.commons.lang", "BooleanUtils", "isTrue", "(Boolean)", "summary", "df-generated"] + - ["org.apache.commons.lang", "BooleanUtils", "negate", "(Boolean)", "summary", "df-generated"] + - ["org.apache.commons.lang", "BooleanUtils", "toBoolean", "(Boolean)", "summary", "df-generated"] + - ["org.apache.commons.lang", "BooleanUtils", "toBoolean", "(Integer,Integer,Integer)", "summary", "df-generated"] + - ["org.apache.commons.lang", "BooleanUtils", "toBoolean", "(String)", "summary", "df-generated"] + - ["org.apache.commons.lang", "BooleanUtils", "toBoolean", "(String,String,String)", "summary", "df-generated"] + - ["org.apache.commons.lang", "BooleanUtils", "toBoolean", "(int)", "summary", "df-generated"] + - ["org.apache.commons.lang", "BooleanUtils", "toBoolean", "(int,int,int)", "summary", "df-generated"] + - ["org.apache.commons.lang", "BooleanUtils", "toBooleanDefaultIfNull", "(Boolean,boolean)", "summary", "df-generated"] + - ["org.apache.commons.lang", "BooleanUtils", "toBooleanObject", "(Integer)", "summary", "df-generated"] + - ["org.apache.commons.lang", "BooleanUtils", "toBooleanObject", "(Integer,Integer,Integer,Integer)", "summary", "df-generated"] + - ["org.apache.commons.lang", "BooleanUtils", "toBooleanObject", "(String)", "summary", "df-generated"] + - ["org.apache.commons.lang", "BooleanUtils", "toBooleanObject", "(String,String,String,String)", "summary", "df-generated"] + - ["org.apache.commons.lang", "BooleanUtils", "toBooleanObject", "(boolean)", "summary", "df-generated"] + - ["org.apache.commons.lang", "BooleanUtils", "toBooleanObject", "(int)", "summary", "df-generated"] + - ["org.apache.commons.lang", "BooleanUtils", "toBooleanObject", "(int,int,int,int)", "summary", "df-generated"] + - ["org.apache.commons.lang", "BooleanUtils", "toInteger", "(Boolean,int,int,int)", "summary", "df-generated"] + - ["org.apache.commons.lang", "BooleanUtils", "toInteger", "(boolean)", "summary", "df-generated"] + - ["org.apache.commons.lang", "BooleanUtils", "toInteger", "(boolean,int,int)", "summary", "df-generated"] + - ["org.apache.commons.lang", "BooleanUtils", "toIntegerObject", "(Boolean)", "summary", "df-generated"] + - ["org.apache.commons.lang", "BooleanUtils", "toIntegerObject", "(Boolean,Integer,Integer,Integer)", "summary", "df-generated"] + - ["org.apache.commons.lang", "BooleanUtils", "toIntegerObject", "(boolean)", "summary", "df-generated"] + - ["org.apache.commons.lang", "BooleanUtils", "toIntegerObject", "(boolean,Integer,Integer)", "summary", "df-generated"] + - ["org.apache.commons.lang", "BooleanUtils", "toStringOnOff", "(Boolean)", "summary", "df-generated"] + - ["org.apache.commons.lang", "BooleanUtils", "toStringOnOff", "(boolean)", "summary", "df-generated"] + - ["org.apache.commons.lang", "BooleanUtils", "toStringTrueFalse", "(Boolean)", "summary", "df-generated"] + - ["org.apache.commons.lang", "BooleanUtils", "toStringTrueFalse", "(boolean)", "summary", "df-generated"] + - ["org.apache.commons.lang", "BooleanUtils", "toStringYesNo", "(Boolean)", "summary", "df-generated"] + - ["org.apache.commons.lang", "BooleanUtils", "toStringYesNo", "(boolean)", "summary", "df-generated"] + - ["org.apache.commons.lang", "BooleanUtils", "xor", "(Boolean[])", "summary", "df-generated"] + - ["org.apache.commons.lang", "BooleanUtils", "xor", "(boolean[])", "summary", "df-generated"] + - ["org.apache.commons.lang", "CharEncoding", "isSupported", "(String)", "summary", "df-generated"] + - ["org.apache.commons.lang", "CharRange", "CharRange", "(char)", "summary", "df-generated"] + - ["org.apache.commons.lang", "CharRange", "CharRange", "(char,boolean)", "summary", "df-generated"] + - ["org.apache.commons.lang", "CharRange", "CharRange", "(char,char)", "summary", "df-generated"] + - ["org.apache.commons.lang", "CharRange", "CharRange", "(char,char,boolean)", "summary", "df-generated"] + - ["org.apache.commons.lang", "CharRange", "contains", "(CharRange)", "summary", "df-generated"] + - ["org.apache.commons.lang", "CharRange", "contains", "(char)", "summary", "df-generated"] + - ["org.apache.commons.lang", "CharRange", "getEnd", "()", "summary", "df-generated"] + - ["org.apache.commons.lang", "CharRange", "getStart", "()", "summary", "df-generated"] + - ["org.apache.commons.lang", "CharRange", "isNegated", "()", "summary", "df-generated"] + - ["org.apache.commons.lang", "CharSet", "contains", "(char)", "summary", "df-generated"] + - ["org.apache.commons.lang", "CharSet", "getInstance", "(String)", "summary", "df-generated"] + - ["org.apache.commons.lang", "CharSet", "getInstance", "(String[])", "summary", "df-generated"] + - ["org.apache.commons.lang", "CharSet", "toString", "()", "summary", "df-generated"] + - ["org.apache.commons.lang", "CharSetUtils", "count", "(String,String)", "summary", "df-generated"] + - ["org.apache.commons.lang", "CharSetUtils", "count", "(String,String[])", "summary", "df-generated"] + - ["org.apache.commons.lang", "CharSetUtils", "evaluateSet", "(String[])", "summary", "df-generated"] + - ["org.apache.commons.lang", "CharSetUtils", "keep", "(String,String)", "summary", "df-generated"] + - ["org.apache.commons.lang", "CharSetUtils", "keep", "(String,String[])", "summary", "df-generated"] + - ["org.apache.commons.lang", "CharUtils", "isAscii", "(char)", "summary", "df-generated"] + - ["org.apache.commons.lang", "CharUtils", "isAsciiAlpha", "(char)", "summary", "df-generated"] + - ["org.apache.commons.lang", "CharUtils", "isAsciiAlphaLower", "(char)", "summary", "df-generated"] + - ["org.apache.commons.lang", "CharUtils", "isAsciiAlphaUpper", "(char)", "summary", "df-generated"] + - ["org.apache.commons.lang", "CharUtils", "isAsciiAlphanumeric", "(char)", "summary", "df-generated"] + - ["org.apache.commons.lang", "CharUtils", "isAsciiControl", "(char)", "summary", "df-generated"] + - ["org.apache.commons.lang", "CharUtils", "isAsciiNumeric", "(char)", "summary", "df-generated"] + - ["org.apache.commons.lang", "CharUtils", "isAsciiPrintable", "(char)", "summary", "df-generated"] + - ["org.apache.commons.lang", "CharUtils", "toChar", "(Character)", "summary", "df-generated"] + - ["org.apache.commons.lang", "CharUtils", "toChar", "(Character,char)", "summary", "df-generated"] + - ["org.apache.commons.lang", "CharUtils", "toChar", "(String)", "summary", "df-generated"] + - ["org.apache.commons.lang", "CharUtils", "toChar", "(String,char)", "summary", "df-generated"] + - ["org.apache.commons.lang", "CharUtils", "toCharacterObject", "(String)", "summary", "df-generated"] + - ["org.apache.commons.lang", "CharUtils", "toCharacterObject", "(char)", "summary", "df-generated"] + - ["org.apache.commons.lang", "CharUtils", "toIntValue", "(Character)", "summary", "df-generated"] + - ["org.apache.commons.lang", "CharUtils", "toIntValue", "(Character,int)", "summary", "df-generated"] + - ["org.apache.commons.lang", "CharUtils", "toIntValue", "(char)", "summary", "df-generated"] + - ["org.apache.commons.lang", "CharUtils", "toIntValue", "(char,int)", "summary", "df-generated"] + - ["org.apache.commons.lang", "CharUtils", "toString", "(Character)", "summary", "df-generated"] + - ["org.apache.commons.lang", "CharUtils", "toString", "(char)", "summary", "df-generated"] + - ["org.apache.commons.lang", "CharUtils", "unicodeEscaped", "(Character)", "summary", "df-generated"] + - ["org.apache.commons.lang", "CharUtils", "unicodeEscaped", "(char)", "summary", "df-generated"] + - ["org.apache.commons.lang", "ClassUtils", "convertClassNamesToClasses", "(List)", "summary", "df-generated"] + - ["org.apache.commons.lang", "ClassUtils", "convertClassesToClassNames", "(List)", "summary", "df-generated"] + - ["org.apache.commons.lang", "ClassUtils", "getAllInterfaces", "(Class)", "summary", "df-generated"] + - ["org.apache.commons.lang", "ClassUtils", "getAllSuperclasses", "(Class)", "summary", "df-generated"] + - ["org.apache.commons.lang", "ClassUtils", "getClass", "(ClassLoader,String)", "summary", "df-generated"] + - ["org.apache.commons.lang", "ClassUtils", "getClass", "(ClassLoader,String,boolean)", "summary", "df-generated"] + - ["org.apache.commons.lang", "ClassUtils", "getClass", "(String)", "summary", "df-generated"] + - ["org.apache.commons.lang", "ClassUtils", "getClass", "(String,boolean)", "summary", "df-generated"] + - ["org.apache.commons.lang", "ClassUtils", "getPackageCanonicalName", "(Class)", "summary", "df-generated"] + - ["org.apache.commons.lang", "ClassUtils", "getPackageName", "(Class)", "summary", "df-generated"] + - ["org.apache.commons.lang", "ClassUtils", "getPublicMethod", "(Class,String,Class[])", "summary", "df-generated"] + - ["org.apache.commons.lang", "ClassUtils", "getShortCanonicalName", "(Class)", "summary", "df-generated"] + - ["org.apache.commons.lang", "ClassUtils", "getShortClassName", "(Class)", "summary", "df-generated"] + - ["org.apache.commons.lang", "ClassUtils", "isAssignable", "(Class,Class)", "summary", "df-generated"] + - ["org.apache.commons.lang", "ClassUtils", "isAssignable", "(Class,Class,boolean)", "summary", "df-generated"] + - ["org.apache.commons.lang", "ClassUtils", "isAssignable", "(Class[],Class[])", "summary", "df-generated"] + - ["org.apache.commons.lang", "ClassUtils", "isAssignable", "(Class[],Class[],boolean)", "summary", "df-generated"] + - ["org.apache.commons.lang", "ClassUtils", "isInnerClass", "(Class)", "summary", "df-generated"] + - ["org.apache.commons.lang", "ClassUtils", "primitiveToWrapper", "(Class)", "summary", "df-generated"] + - ["org.apache.commons.lang", "ClassUtils", "toClass", "(Object[])", "summary", "df-generated"] + - ["org.apache.commons.lang", "ClassUtils", "wrapperToPrimitive", "(Class)", "summary", "df-generated"] + - ["org.apache.commons.lang", "IllegalClassException", "IllegalClassException", "(Class,Class)", "summary", "df-generated"] + - ["org.apache.commons.lang", "IllegalClassException", "IllegalClassException", "(Class,Object)", "summary", "df-generated"] + - ["org.apache.commons.lang", "LocaleUtils", "availableLocaleList", "()", "summary", "df-generated"] + - ["org.apache.commons.lang", "LocaleUtils", "availableLocaleSet", "()", "summary", "df-generated"] + - ["org.apache.commons.lang", "LocaleUtils", "countriesByLanguage", "(String)", "summary", "df-generated"] + - ["org.apache.commons.lang", "LocaleUtils", "isAvailableLocale", "(Locale)", "summary", "df-generated"] + - ["org.apache.commons.lang", "LocaleUtils", "languagesByCountry", "(String)", "summary", "df-generated"] + - ["org.apache.commons.lang", "LocaleUtils", "toLocale", "(String)", "summary", "df-generated"] + - ["org.apache.commons.lang", "NotImplementedException", "NotImplementedException", "(Class)", "summary", "df-generated"] + - ["org.apache.commons.lang", "NumberRange", "NumberRange", "(Number)", "summary", "df-generated"] + - ["org.apache.commons.lang", "NumberRange", "NumberRange", "(Number,Number)", "summary", "df-generated"] + - ["org.apache.commons.lang", "NumberRange", "getMaximum", "()", "summary", "df-generated"] + - ["org.apache.commons.lang", "NumberRange", "getMinimum", "()", "summary", "df-generated"] + - ["org.apache.commons.lang", "NumberRange", "includesNumber", "(Number)", "summary", "df-generated"] + - ["org.apache.commons.lang", "NumberRange", "includesRange", "(NumberRange)", "summary", "df-generated"] + - ["org.apache.commons.lang", "NumberRange", "overlaps", "(NumberRange)", "summary", "df-generated"] + - ["org.apache.commons.lang", "NumberRange", "toString", "()", "summary", "df-generated"] + - ["org.apache.commons.lang", "NumberUtils", "compare", "(double,double)", "summary", "df-generated"] + - ["org.apache.commons.lang", "NumberUtils", "compare", "(float,float)", "summary", "df-generated"] + - ["org.apache.commons.lang", "NumberUtils", "createBigDecimal", "(String)", "summary", "df-generated"] + - ["org.apache.commons.lang", "NumberUtils", "createBigInteger", "(String)", "summary", "df-generated"] + - ["org.apache.commons.lang", "NumberUtils", "createDouble", "(String)", "summary", "df-generated"] + - ["org.apache.commons.lang", "NumberUtils", "createFloat", "(String)", "summary", "df-generated"] + - ["org.apache.commons.lang", "NumberUtils", "createInteger", "(String)", "summary", "df-generated"] + - ["org.apache.commons.lang", "NumberUtils", "createLong", "(String)", "summary", "df-generated"] + - ["org.apache.commons.lang", "NumberUtils", "createNumber", "(String)", "summary", "df-generated"] + - ["org.apache.commons.lang", "NumberUtils", "isDigits", "(String)", "summary", "df-generated"] + - ["org.apache.commons.lang", "NumberUtils", "isNumber", "(String)", "summary", "df-generated"] + - ["org.apache.commons.lang", "NumberUtils", "maximum", "(int,int,int)", "summary", "df-generated"] + - ["org.apache.commons.lang", "NumberUtils", "maximum", "(long,long,long)", "summary", "df-generated"] + - ["org.apache.commons.lang", "NumberUtils", "minimum", "(int,int,int)", "summary", "df-generated"] + - ["org.apache.commons.lang", "NumberUtils", "minimum", "(long,long,long)", "summary", "df-generated"] + - ["org.apache.commons.lang", "NumberUtils", "stringToInt", "(String)", "summary", "df-generated"] + - ["org.apache.commons.lang", "NumberUtils", "stringToInt", "(String,int)", "summary", "df-generated"] + - ["org.apache.commons.lang", "ObjectUtils", "equals", "(Object,Object)", "summary", "df-generated"] + - ["org.apache.commons.lang", "ObjectUtils", "hashCode", "(Object)", "summary", "df-generated"] + - ["org.apache.commons.lang", "ObjectUtils", "identityToString", "(Object)", "summary", "df-generated"] + - ["org.apache.commons.lang", "ObjectUtils", "identityToString", "(StringBuffer,Object)", "summary", "df-generated"] + - ["org.apache.commons.lang", "ObjectUtils", "toString", "(Object)", "summary", "df-generated"] + - ["org.apache.commons.lang", "RandomStringUtils", "random", "(int)", "summary", "df-generated"] + - ["org.apache.commons.lang", "RandomStringUtils", "random", "(int,String)", "summary", "df-generated"] + - ["org.apache.commons.lang", "RandomStringUtils", "random", "(int,boolean,boolean)", "summary", "df-generated"] + - ["org.apache.commons.lang", "RandomStringUtils", "random", "(int,char[])", "summary", "df-generated"] + - ["org.apache.commons.lang", "RandomStringUtils", "random", "(int,int,int,boolean,boolean)", "summary", "df-generated"] + - ["org.apache.commons.lang", "RandomStringUtils", "random", "(int,int,int,boolean,boolean,char[])", "summary", "df-generated"] + - ["org.apache.commons.lang", "RandomStringUtils", "random", "(int,int,int,boolean,boolean,char[],Random)", "summary", "df-generated"] + - ["org.apache.commons.lang", "RandomStringUtils", "randomAlphabetic", "(int)", "summary", "df-generated"] + - ["org.apache.commons.lang", "RandomStringUtils", "randomAlphanumeric", "(int)", "summary", "df-generated"] + - ["org.apache.commons.lang", "RandomStringUtils", "randomAscii", "(int)", "summary", "df-generated"] + - ["org.apache.commons.lang", "RandomStringUtils", "randomNumeric", "(int)", "summary", "df-generated"] + - ["org.apache.commons.lang", "SerializationUtils", "clone", "(Serializable)", "summary", "df-generated"] + - ["org.apache.commons.lang", "SerializationUtils", "serialize", "(Serializable)", "summary", "df-generated"] + - ["org.apache.commons.lang", "SerializationUtils", "serialize", "(Serializable,OutputStream)", "summary", "df-generated"] + - ["org.apache.commons.lang", "StringEscapeUtils", "escapeCsv", "(String)", "summary", "df-generated"] + - ["org.apache.commons.lang", "StringEscapeUtils", "escapeCsv", "(Writer,String)", "summary", "df-generated"] + - ["org.apache.commons.lang", "StringEscapeUtils", "escapeHtml", "(String)", "summary", "df-generated"] + - ["org.apache.commons.lang", "StringEscapeUtils", "escapeHtml", "(Writer,String)", "summary", "df-generated"] + - ["org.apache.commons.lang", "StringEscapeUtils", "escapeJava", "(String)", "summary", "df-generated"] + - ["org.apache.commons.lang", "StringEscapeUtils", "escapeJava", "(Writer,String)", "summary", "df-generated"] + - ["org.apache.commons.lang", "StringEscapeUtils", "escapeJavaScript", "(String)", "summary", "df-generated"] + - ["org.apache.commons.lang", "StringEscapeUtils", "escapeJavaScript", "(Writer,String)", "summary", "df-generated"] + - ["org.apache.commons.lang", "StringEscapeUtils", "escapeXml", "(String)", "summary", "df-generated"] + - ["org.apache.commons.lang", "StringEscapeUtils", "escapeXml", "(Writer,String)", "summary", "df-generated"] + - ["org.apache.commons.lang", "StringEscapeUtils", "unescapeJava", "(String)", "summary", "df-generated"] + - ["org.apache.commons.lang", "StringEscapeUtils", "unescapeJava", "(Writer,String)", "summary", "df-generated"] + - ["org.apache.commons.lang", "StringEscapeUtils", "unescapeJavaScript", "(String)", "summary", "df-generated"] + - ["org.apache.commons.lang", "StringEscapeUtils", "unescapeJavaScript", "(Writer,String)", "summary", "df-generated"] + - ["org.apache.commons.lang", "StringUtils", "contains", "(String,String)", "summary", "df-generated"] + - ["org.apache.commons.lang", "StringUtils", "contains", "(String,char)", "summary", "df-generated"] + - ["org.apache.commons.lang", "StringUtils", "containsAny", "(String,String)", "summary", "df-generated"] + - ["org.apache.commons.lang", "StringUtils", "containsAny", "(String,char[])", "summary", "df-generated"] + - ["org.apache.commons.lang", "StringUtils", "containsIgnoreCase", "(String,String)", "summary", "df-generated"] + - ["org.apache.commons.lang", "StringUtils", "containsNone", "(String,String)", "summary", "df-generated"] + - ["org.apache.commons.lang", "StringUtils", "containsNone", "(String,char[])", "summary", "df-generated"] + - ["org.apache.commons.lang", "StringUtils", "containsOnly", "(String,String)", "summary", "df-generated"] + - ["org.apache.commons.lang", "StringUtils", "containsOnly", "(String,char[])", "summary", "df-generated"] + - ["org.apache.commons.lang", "StringUtils", "countMatches", "(String,String)", "summary", "df-generated"] + - ["org.apache.commons.lang", "StringUtils", "endsWith", "(String,String)", "summary", "df-generated"] + - ["org.apache.commons.lang", "StringUtils", "endsWithIgnoreCase", "(String,String)", "summary", "df-generated"] + - ["org.apache.commons.lang", "StringUtils", "equals", "(String,String)", "summary", "df-generated"] + - ["org.apache.commons.lang", "StringUtils", "equalsIgnoreCase", "(String,String)", "summary", "df-generated"] + - ["org.apache.commons.lang", "StringUtils", "escape", "(String)", "summary", "df-generated"] + - ["org.apache.commons.lang", "StringUtils", "getLevenshteinDistance", "(String,String)", "summary", "df-generated"] + - ["org.apache.commons.lang", "StringUtils", "indexOf", "(String,String)", "summary", "df-generated"] + - ["org.apache.commons.lang", "StringUtils", "indexOf", "(String,String,int)", "summary", "df-generated"] + - ["org.apache.commons.lang", "StringUtils", "indexOf", "(String,char)", "summary", "df-generated"] + - ["org.apache.commons.lang", "StringUtils", "indexOf", "(String,char,int)", "summary", "df-generated"] + - ["org.apache.commons.lang", "StringUtils", "indexOfAny", "(String,String)", "summary", "df-generated"] + - ["org.apache.commons.lang", "StringUtils", "indexOfAny", "(String,String[])", "summary", "df-generated"] + - ["org.apache.commons.lang", "StringUtils", "indexOfAny", "(String,char[])", "summary", "df-generated"] + - ["org.apache.commons.lang", "StringUtils", "indexOfAnyBut", "(String,String)", "summary", "df-generated"] + - ["org.apache.commons.lang", "StringUtils", "indexOfAnyBut", "(String,char[])", "summary", "df-generated"] + - ["org.apache.commons.lang", "StringUtils", "indexOfDifference", "(String,String)", "summary", "df-generated"] + - ["org.apache.commons.lang", "StringUtils", "indexOfDifference", "(String[])", "summary", "df-generated"] + - ["org.apache.commons.lang", "StringUtils", "isAlpha", "(String)", "summary", "df-generated"] + - ["org.apache.commons.lang", "StringUtils", "isAlphaSpace", "(String)", "summary", "df-generated"] + - ["org.apache.commons.lang", "StringUtils", "isAlphanumeric", "(String)", "summary", "df-generated"] + - ["org.apache.commons.lang", "StringUtils", "isAlphanumericSpace", "(String)", "summary", "df-generated"] + - ["org.apache.commons.lang", "StringUtils", "isAsciiPrintable", "(String)", "summary", "df-generated"] + - ["org.apache.commons.lang", "StringUtils", "isBlank", "(String)", "summary", "df-generated"] + - ["org.apache.commons.lang", "StringUtils", "isEmpty", "(String)", "summary", "df-generated"] + - ["org.apache.commons.lang", "StringUtils", "isNotBlank", "(String)", "summary", "df-generated"] + - ["org.apache.commons.lang", "StringUtils", "isNotEmpty", "(String)", "summary", "df-generated"] + - ["org.apache.commons.lang", "StringUtils", "isNumeric", "(String)", "summary", "df-generated"] + - ["org.apache.commons.lang", "StringUtils", "isNumericSpace", "(String)", "summary", "df-generated"] + - ["org.apache.commons.lang", "StringUtils", "isWhitespace", "(String)", "summary", "df-generated"] + - ["org.apache.commons.lang", "StringUtils", "lastIndexOf", "(String,String)", "summary", "df-generated"] + - ["org.apache.commons.lang", "StringUtils", "lastIndexOf", "(String,String,int)", "summary", "df-generated"] + - ["org.apache.commons.lang", "StringUtils", "lastIndexOf", "(String,char)", "summary", "df-generated"] + - ["org.apache.commons.lang", "StringUtils", "lastIndexOf", "(String,char,int)", "summary", "df-generated"] + - ["org.apache.commons.lang", "StringUtils", "lastIndexOfAny", "(String,String[])", "summary", "df-generated"] + - ["org.apache.commons.lang", "StringUtils", "length", "(String)", "summary", "df-generated"] + - ["org.apache.commons.lang", "StringUtils", "ordinalIndexOf", "(String,String,int)", "summary", "df-generated"] + - ["org.apache.commons.lang", "StringUtils", "startsWith", "(String,String)", "summary", "df-generated"] + - ["org.apache.commons.lang", "StringUtils", "startsWithIgnoreCase", "(String,String)", "summary", "df-generated"] + - ["org.apache.commons.lang", "SystemUtils", "getJavaHome", "()", "summary", "df-generated"] + - ["org.apache.commons.lang", "SystemUtils", "getJavaIoTmpDir", "()", "summary", "df-generated"] + - ["org.apache.commons.lang", "SystemUtils", "getJavaVersion", "()", "summary", "df-generated"] + - ["org.apache.commons.lang", "SystemUtils", "getUserDir", "()", "summary", "df-generated"] + - ["org.apache.commons.lang", "SystemUtils", "getUserHome", "()", "summary", "df-generated"] + - ["org.apache.commons.lang", "SystemUtils", "isJavaAwtHeadless", "()", "summary", "df-generated"] + - ["org.apache.commons.lang", "SystemUtils", "isJavaVersionAtLeast", "(float)", "summary", "df-generated"] + - ["org.apache.commons.lang", "SystemUtils", "isJavaVersionAtLeast", "(int)", "summary", "df-generated"] + - ["org.apache.commons.lang", "Validate", "allElementsOfType", "(Collection,Class)", "summary", "df-generated"] + - ["org.apache.commons.lang", "Validate", "allElementsOfType", "(Collection,Class,String)", "summary", "df-generated"] + - ["org.apache.commons.lang", "Validate", "isTrue", "(boolean)", "summary", "df-generated"] + - ["org.apache.commons.lang", "Validate", "isTrue", "(boolean,String)", "summary", "df-generated"] + - ["org.apache.commons.lang", "Validate", "isTrue", "(boolean,String,Object)", "summary", "df-generated"] + - ["org.apache.commons.lang", "Validate", "isTrue", "(boolean,String,double)", "summary", "df-generated"] + - ["org.apache.commons.lang", "Validate", "isTrue", "(boolean,String,long)", "summary", "df-generated"] + - ["org.apache.commons.lang", "Validate", "noNullElements", "(Collection)", "summary", "df-generated"] + - ["org.apache.commons.lang", "Validate", "noNullElements", "(Collection,String)", "summary", "df-generated"] + - ["org.apache.commons.lang", "Validate", "noNullElements", "(Object[])", "summary", "df-generated"] + - ["org.apache.commons.lang", "Validate", "noNullElements", "(Object[],String)", "summary", "df-generated"] + - ["org.apache.commons.lang", "Validate", "notEmpty", "(Collection)", "summary", "df-generated"] + - ["org.apache.commons.lang", "Validate", "notEmpty", "(Collection,String)", "summary", "df-generated"] + - ["org.apache.commons.lang", "Validate", "notEmpty", "(Map)", "summary", "df-generated"] + - ["org.apache.commons.lang", "Validate", "notEmpty", "(Map,String)", "summary", "df-generated"] + - ["org.apache.commons.lang", "Validate", "notEmpty", "(Object[])", "summary", "df-generated"] + - ["org.apache.commons.lang", "Validate", "notEmpty", "(Object[],String)", "summary", "df-generated"] + - ["org.apache.commons.lang", "Validate", "notEmpty", "(String)", "summary", "df-generated"] + - ["org.apache.commons.lang", "Validate", "notEmpty", "(String,String)", "summary", "df-generated"] + - ["org.apache.commons.lang", "Validate", "notNull", "(Object)", "summary", "df-generated"] + - ["org.apache.commons.lang", "Validate", "notNull", "(Object,String)", "summary", "df-generated"] diff --git a/java/ql/lib/ext/java.lang.model.yml b/java/ql/lib/ext/java.lang.model.yml index 8625a68caa0..012fb65baab 100644 --- a/java/ql/lib/ext/java.lang.model.yml +++ b/java/ql/lib/ext/java.lang.model.yml @@ -8,30 +8,30 @@ extensions: - ["java.lang", "ClassLoader", True, "getSystemResource", "(String)", "", "Argument[0]", "path-injection", "ai-manual"] - ["java.lang", "ClassLoader", True, "getSystemResourceAsStream", "(String)", "", "Argument[0]", "path-injection", "ai-manual"] - ["java.lang", "Module", True, "getResourceAsStream", "(String)", "", "Argument[0]", "path-injection", "ai-manual"] + - ["java.lang", "ProcessBuilder", False, "command", "(List)", "", "Argument[0]", "command-injection", "manual"] + - ["java.lang", "ProcessBuilder", False, "command", "(String[])", "", "Argument[0]", "command-injection", "ai-manual"] + - ["java.lang", "ProcessBuilder", False, "directory", "(File)", "", "Argument[0]", "command-injection", "ai-manual"] + - ["java.lang", "ProcessBuilder", False, "ProcessBuilder", "(List)", "", "Argument[0]", "command-injection", "ai-manual"] + - ["java.lang", "ProcessBuilder", False, "ProcessBuilder", "(String[])", "", "Argument[0]", "command-injection", "ai-manual"] + - ["java.lang", "Runtime", True, "exec", "(String)", "", "Argument[0]", "command-injection", "ai-manual"] + - ["java.lang", "Runtime", True, "exec", "(String[])", "", "Argument[0]", "command-injection", "ai-manual"] + - ["java.lang", "Runtime", True, "exec", "(String[],String[])", "", "Argument[0]", "command-injection", "ai-manual"] + - ["java.lang", "Runtime", True, "exec", "(String[],String[],File)", "", "Argument[0]", "command-injection", "ai-manual"] + - ["java.lang", "Runtime", True, "exec", "(String[],String[],File)", "", "Argument[2]", "command-injection", "ai-manual"] + - ["java.lang", "Runtime", True, "exec", "(String,String[])", "", "Argument[0]", "command-injection", "ai-manual"] + - ["java.lang", "Runtime", True, "exec", "(String,String[],File)", "", "Argument[0]", "command-injection", "ai-manual"] + - ["java.lang", "Runtime", True, "exec", "(String,String[],File)", "", "Argument[2]", "command-injection", "ai-manual"] # These are potential vulnerabilities, but not for command-injection. No query for this kind of vulnerability currently exists. # - ["java.lang", "Runtime", False, "load", "(String)", "", "Argument[0]", "command-injection", "ai-manual"] # - ["java.lang", "Runtime", False, "loadLibrary", "(String)", "", "Argument[0]", "command-injection", "ai-manual"] - # These are modeled in plain CodeQL. TODO: migrate them. - # - ["java.lang", "ProcessBuilder", False, "command", "(String[])", "", "Argument[0]", "command-injection", "ai-manual"] - # - ["java.lang", "ProcessBuilder", False, "directory", "(File)", "", "Argument[0]", "command-injection", "ai-manual"] - # - ["java.lang", "ProcessBuilder", False, "ProcessBuilder", "(List)", "", "Argument[0]", "command-injection", "ai-manual"] - # - ["java.lang", "ProcessBuilder", False, "ProcessBuilder", "(String[])", "", "Argument[0]", "command-injection", "ai-manual"] - # - ["java.lang", "Runtime", True, "exec", "(String,String[])", "", "Argument[0]", "command-injection", "ai-manual"] - # - ["java.lang", "Runtime", True, "exec", "(String[],String[])", "", "Argument[0]", "command-injection", "ai-manual"] - # - ["java.lang", "Runtime", True, "exec", "(String,String[],File)", "", "Argument[0]", "command-injection", "ai-manual"] - # - ["java.lang", "Runtime", True, "exec", "(String,String[],File)", "", "Argument[2]", "command-injection", "ai-manual"] - # - ["java.lang", "Runtime", True, "exec", "(String)", "", "Argument[0]", "command-injection", "ai-manual"] - # - ["java.lang", "Runtime", True, "exec", "(String[],String[],File)", "", "Argument[0]", "command-injection", "ai-manual"] - # - ["java.lang", "Runtime", True, "exec", "(String[],String[],File)", "", "Argument[2]", "command-injection", "ai-manual"] - # - ["java.lang", "Runtime", True, "exec", "(String[])", "", "Argument[0]", "command-injection", "ai-manual"] - ["java.lang", "String", False, "matches", "(String)", "", "Argument[0]", "regex-use[f-1]", "manual"] - ["java.lang", "String", False, "replaceAll", "(String,String)", "", "Argument[0]", "regex-use[-1]", "manual"] - ["java.lang", "String", False, "replaceFirst", "(String,String)", "", "Argument[0]", "regex-use[-1]", "manual"] - ["java.lang", "String", False, "split", "(String)", "", "Argument[0]", "regex-use[-1]", "manual"] - ["java.lang", "String", False, "split", "(String,int)", "", "Argument[0]", "regex-use[-1]", "manual"] - # These are modeled in plain CodeQL. TODO: migrate them. - # - ["java.lang", "System", False, "load", "(String)", "", "Argument[0]", "command-injection", "ai-manual"] # This is actually injecting a library. - # - ["java.lang", "System", False, "loadLibrary", "(String)", "", "Argument[0]", "command-injection", "ai-manual"] # This is actually injecting a library. + # These are potential vulnerabilities, but not for command-injection. No query for this kind of vulnerability currently exists. + # - ["java.lang", "System", False, "load", "(String)", "", "Argument[0]", "command-injection", "ai-manual"] + # - ["java.lang", "System", False, "loadLibrary", "(String)", "", "Argument[0]", "command-injection", "ai-manual"] - ["java.lang", "System$Logger", True, "log", "(Level,Object)", "", "Argument[1]", "log-injection", "manual"] - ["java.lang", "System$Logger", True, "log", "(Level,ResourceBundle,String,Object[])", "", "Argument[2..3]", "log-injection", "manual"] - ["java.lang", "System$Logger", True, "log", "(Level,ResourceBundle,String,Throwable)", "", "Argument[2]", "log-injection", "manual"] diff --git a/java/ql/lib/ext/java.net.model.yml b/java/ql/lib/ext/java.net.model.yml index 39a4c484112..24591459432 100644 --- a/java/ql/lib/ext/java.net.model.yml +++ b/java/ql/lib/ext/java.net.model.yml @@ -45,7 +45,8 @@ extensions: - ["java.net", "URI", False, "toURL", "", "", "Argument[this]", "ReturnValue", "taint", "manual"] - ["java.net", "URL", False, "URL", "(String)", "", "Argument[0]", "Argument[this]", "taint", "manual"] - ["java.net", "URL", False, "URL", "(URL,String)", "", "Argument[0]", "Argument[this]", "taint", "ai-manual"] - - ["java.net", "URL", False, "URL", "(URL,String)", "", "Argument[1]", "Argument[this]", "taint", "ai-manual"] # @atorralba: review for consistency + - ["java.net", "URL", False, "URL", "(URL,String)", "", "Argument[1]", "Argument[this]", "taint", "ai-manual"] - ["java.net", "URL", False, "toExternalForm", "", "", "Argument[this]", "ReturnValue", "taint", "manual"] - ["java.net", "URL", False, "toURI", "", "", "Argument[this]", "ReturnValue", "taint", "manual"] + - ["java.net", "URL", False, "toString", "", "", "Argument[this]", "ReturnValue", "taint", "manual"] - ["java.net", "URLDecoder", False, "decode", "", "", "Argument[0]", "ReturnValue", "taint", "manual"] diff --git a/java/ql/lib/ext/org.apache.commons.exec.model.yml b/java/ql/lib/ext/org.apache.commons.exec.model.yml new file mode 100644 index 00000000000..314b0996194 --- /dev/null +++ b/java/ql/lib/ext/org.apache.commons.exec.model.yml @@ -0,0 +1,11 @@ +extensions: + - addsTo: + pack: codeql/java-all + extensible: sinkModel + data: + - ["org.apache.commons.exec", "CommandLine", True, "parse", "(String)", "", "Argument[0]", "command-injection", "manual"] + - ["org.apache.commons.exec", "CommandLine", True, "parse", "(String,Map)", "", "Argument[0]", "command-injection", "manual"] + - ["org.apache.commons.exec", "CommandLine", True, "addArguments", "(String)", "", "Argument[0]", "command-injection", "manual"] + - ["org.apache.commons.exec", "CommandLine", True, "addArguments", "(String,boolean)", "", "Argument[0]", "command-injection", "manual"] + - ["org.apache.commons.exec", "CommandLine", True, "addArguments", "(String[])", "", "Argument[0]", "command-injection", "manual"] + - ["org.apache.commons.exec", "CommandLine", True, "addArguments", "(String[],boolean)", "", "Argument[0]", "command-injection", "manual"] diff --git a/java/ql/lib/ext/org.kohsuke.stapler.bind.model.yml b/java/ql/lib/ext/org.kohsuke.stapler.bind.model.yml new file mode 100644 index 00000000000..9152eb7b55b --- /dev/null +++ b/java/ql/lib/ext/org.kohsuke.stapler.bind.model.yml @@ -0,0 +1,6 @@ +extensions: + - addsTo: + pack: codeql/java-all + extensible: sourceModel + data: + - ["org.kohsuke.stapler.bind", "JavaScriptMethod", True, "", "", "Annotated", "Parameter", "remote", "manual"] diff --git a/java/ql/lib/ext/org.kohsuke.stapler.json.model.yml b/java/ql/lib/ext/org.kohsuke.stapler.json.model.yml new file mode 100644 index 00000000000..a06683144e0 --- /dev/null +++ b/java/ql/lib/ext/org.kohsuke.stapler.json.model.yml @@ -0,0 +1,7 @@ +extensions: + - addsTo: + pack: codeql/java-all + extensible: sourceModel + data: + - ["org.kohsuke.stapler.json", "SubmittedForm", True, "", "", "Annotated", "Parameter", "remote", "manual"] + - ["org.kohsuke.stapler.json", "JsonBody", True, "", "", "Annotated", "Parameter", "remote", "manual"] diff --git a/java/ql/lib/ext/org.kohsuke.stapler.model.yml b/java/ql/lib/ext/org.kohsuke.stapler.model.yml index 7a242051485..63bbdbfd52a 100644 --- a/java/ql/lib/ext/org.kohsuke.stapler.model.yml +++ b/java/ql/lib/ext/org.kohsuke.stapler.model.yml @@ -4,4 +4,46 @@ extensions: extensible: sinkModel data: - ["org.kohsuke.stapler", "HttpResponses", True, "redirectTo", "(String)", "", "Argument[0]", "url-redirection", "ai-manual"] + - ["org.kohsuke.stapler", "HttpResponses", True, "redirectTo", "(int,String)", "", "Argument[1]", "url-redirection", "manual"] - ["org.kohsuke.stapler", "HttpResponses", True, "staticResource", "(URL)", "", "Argument[0]", "request-forgery", "ai-manual"] + - ["org.kohsuke.stapler", "HttpResponses", True, "staticResource", "(URL,long)", "", "Argument[0]", "request-forgery", "manual"] + - ["org.kohsuke.stapler", "HttpResponses", True, "html", "(String)", "", "Argument[0]", "html-injection", "manual"] + - ["org.kohsuke.stapler", "HttpResponses", True, "literalHtml", "(String)", "", "Argument[0]", "html-injection", "manual"] + - ["org.kohsuke.stapler", "StaplerResponse", True, "forward", "(Object,String,StaplerRequest)", "", "Argument[1]", "request-forgery", "manual"] + - ["org.kohsuke.stapler", "StaplerResponse", True, "sendRedirect2", "(String)", "", "Argument[0]", "url-redirection", "manual"] + - ["org.kohsuke.stapler", "StaplerResponse", True, "sendRedirect", "(int,String)", "", "Argument[1]", "url-redirection", "manual"] + - ["org.kohsuke.stapler", "StaplerResponse", True, "sendRedirect", "(String)", "", "Argument[0]", "url-redirection", "manual"] + - ["org.kohsuke.stapler", "StaplerResponse", True, "serveFile", "(StaplerRequest,URL)", "", "Argument[1]", "path-injection", "manual"] + - ["org.kohsuke.stapler", "StaplerResponse", True, "serveFile", "(StaplerRequest,URL,long)", "", "Argument[1]", "path-injection", "manual"] + - ["org.kohsuke.stapler", "StaplerResponse", True, "serveLocalizedFile", "(StaplerRequest,URL)", "", "Argument[1]", "path-injection", "manual"] + - ["org.kohsuke.stapler", "StaplerResponse", True, "serveLocalizedFile", "(StaplerRequest,URL,long)", "", "Argument[1]", "path-injection", "manual"] + - ["org.kohsuke.stapler", "StaplerResponse", True, "serveFile", "(StaplerRequest,InputStream,long,long,long,String)", "", "Argument[1]", "path-injection", "manual"] + - ["org.kohsuke.stapler", "StaplerResponse", True, "serveFile", "(StaplerRequest,InputStream,long,long,int,String)", "", "Argument[1]", "path-injection", "manual"] + - ["org.kohsuke.stapler", "StaplerResponse", True, "serveFile", "(StaplerRequest,InputStream,long,long,String)", "", "Argument[1]", "path-injection", "manual"] + - ["org.kohsuke.stapler", "StaplerResponse", True, "serveFile", "(StaplerRequest,InputStream,long,int,String)", "", "Argument[1]", "path-injection", "manual"] + - ["org.kohsuke.stapler", "StaplerResponse", True, "reverseProxyTo", "(URL,StaplerRequest)", "", "Argument[0]", "request-forgery", "manual"] + - addsTo: + pack: codeql/java-all + extensible: sourceModel + data: + - ["org.kohsuke.stapler", "StaplerRequest", True, "getRequestURIWithQueryString", "", "", "ReturnValue", "remote", "manual"] + - ["org.kohsuke.stapler", "StaplerRequest", True, "getRequestURLWithQueryString", "", "", "ReturnValue", "remote", "manual"] + - ["org.kohsuke.stapler", "StaplerRequest", True, "getReferer", "", "", "ReturnValue", "remote", "manual"] + - ["org.kohsuke.stapler", "StaplerRequest", True, "getOriginalRequestURI", "", "", "ReturnValue", "remote", "manual"] + - ["org.kohsuke.stapler", "StaplerRequest", True, "getSubmittedForm", "", "", "ReturnValue", "remote", "manual"] + - ["org.kohsuke.stapler", "StaplerRequest", True, "getFileItem", "", "", "ReturnValue", "remote", "manual"] + - ["org.kohsuke.stapler", "StaplerRequest", True, "bindParametersToList", "", "", "ReturnValue", "remote", "manual"] + - ["org.kohsuke.stapler", "StaplerRequest", True, "bindParameters", "", "", "Argument[0]", "remote", "manual"] + - ["org.kohsuke.stapler", "StaplerRequest", True, "bindParameters", "", "", "ReturnValue", "remote", "manual"] + - ["org.kohsuke.stapler", "StaplerRequest", True, "bindJSON", "", "", "Argument[0]", "remote", "manual"] + - ["org.kohsuke.stapler", "StaplerRequest", True, "bindJSON", "", "", "ReturnValue", "remote", "manual"] + - ["org.kohsuke.stapler", "StaplerRequest", True, "bindJSONToList", "", "", "ReturnValue", "remote", "manual"] + - ["org.kohsuke.stapler", "StaplerRequest", True, "getParameter", "", "", "ReturnValue", "remote", "manual"] + - ["org.kohsuke.stapler", "StaplerRequest", True, "getParameterMap", "", "", "ReturnValue", "remote", "manual"] + - ["org.kohsuke.stapler", "StaplerRequest", True, "getParameterNames", "", "", "ReturnValue", "remote", "manual"] + - ["org.kohsuke.stapler", "StaplerRequest", True, "getParameterValues", "", "", "ReturnValue", "remote", "manual"] + - ["org.kohsuke.stapler", "StaplerRequest", True, "getRestOfPath", "", "", "ReturnValue", "remote", "manual"] + - ["org.kohsuke.stapler", "QueryParameter", True, "", "", "Annotated", "Parameter", "remote", "manual"] + - ["org.kohsuke.stapler", "Header", True, "", "", "Annotated", "Parameter", "remote", "manual"] + - ["org.kohsuke.stapler", "DataBoundConstructor", True, "", "", "Annotated", "Parameter", "remote", "manual"] + - ["org.kohsuke.stapler", "DataBoundSetter", True, "", "", "Annotated", "Parameter", "remote", "manual"] diff --git a/java/ql/lib/qlpack.yml b/java/ql/lib/qlpack.yml index ada2ac9e999..699c78730fd 100644 --- a/java/ql/lib/qlpack.yml +++ b/java/ql/lib/qlpack.yml @@ -1,11 +1,12 @@ name: codeql/java-all -version: 0.6.3-dev +version: 0.6.4-dev groups: java dbscheme: config/semmlecode.dbscheme extractor: java library: true upgrades: upgrades dependencies: + codeql/mad: ${workspace} codeql/regex: ${workspace} codeql/tutorial: ${workspace} codeql/typetracking: ${workspace} diff --git a/java/ql/lib/semmle/code/java/JDK.qll b/java/ql/lib/semmle/code/java/JDK.qll index 78f7defc32f..156cbbc0f93 100644 --- a/java/ql/lib/semmle/code/java/JDK.qll +++ b/java/ql/lib/semmle/code/java/JDK.qll @@ -199,18 +199,18 @@ class TypeFile extends Class { // --- Standard methods --- /** - * Any constructor of class `java.lang.ProcessBuilder`. + * DEPRECATED: Any constructor of class `java.lang.ProcessBuilder`. */ -class ProcessBuilderConstructor extends Constructor, ExecCallable { +deprecated class ProcessBuilderConstructor extends Constructor, ExecCallable { ProcessBuilderConstructor() { this.getDeclaringType() instanceof TypeProcessBuilder } override int getAnExecutedArgument() { result = 0 } } /** - * Any of the methods named `command` on class `java.lang.ProcessBuilder`. + * DEPRECATED: Any of the methods named `command` on class `java.lang.ProcessBuilder`. */ -class MethodProcessBuilderCommand extends Method, ExecCallable { +deprecated class MethodProcessBuilderCommand extends Method, ExecCallable { MethodProcessBuilderCommand() { this.hasName("command") and this.getDeclaringType() instanceof TypeProcessBuilder @@ -220,9 +220,9 @@ class MethodProcessBuilderCommand extends Method, ExecCallable { } /** - * Any method named `exec` on class `java.lang.Runtime`. + * DEPRECATED: Any method named `exec` on class `java.lang.Runtime`. */ -class MethodRuntimeExec extends Method, ExecCallable { +deprecated class MethodRuntimeExec extends Method, ExecCallable { MethodRuntimeExec() { this.hasName("exec") and this.getDeclaringType() instanceof TypeRuntime diff --git a/java/ql/lib/semmle/code/java/dataflow/ExternalFlow.qll b/java/ql/lib/semmle/code/java/dataflow/ExternalFlow.qll index bb8485cd601..c04126d5f7f 100644 --- a/java/ql/lib/semmle/code/java/dataflow/ExternalFlow.qll +++ b/java/ql/lib/semmle/code/java/dataflow/ExternalFlow.qll @@ -87,6 +87,7 @@ private import internal.FlowSummaryImplSpecific as FlowSummaryImplSpecific private import internal.AccessPathSyntax private import ExternalFlowExtensions as Extensions private import FlowSummary +private import codeql.mad.ModelValidation as SharedModelVal /** * A class for activating additional model rows. @@ -265,86 +266,17 @@ module ModelValidation { ) } - private class OutdatedSinkKind extends string { - OutdatedSinkKind() { - this = - [ - "sql", "url-redirect", "xpath", "ssti", "logging", "groovy", "jexl", "mvel", "xslt", - "ldap", "pending-intent-sent", "intent-start", "set-hostname-verifier", - "header-splitting", "xss", "write-file", "create-file", "read-file", "open-url", - "jdbc-url" - ] - } + private module KindValConfig implements SharedModelVal::KindValidationConfigSig { + predicate summaryKind(string kind) { summaryModel(_, _, _, _, _, _, _, _, kind, _) } - private string replacementKind() { - this = ["sql", "xpath", "groovy", "jexl", "mvel", "xslt", "ldap"] and - result = this + "-injection" - or - this = "url-redirect" and result = "url-redirection" - or - this = "ssti" and result = "template-injection" - or - this = "logging" and result = "log-injection" - or - this = "pending-intent-sent" and result = "pending-intents" - or - this = "intent-start" and result = "intent-redirection" - or - this = "set-hostname-verifier" and result = "hostname-verification" - or - this = "header-splitting" and result = "response-splitting" - or - this = "xss" and result = "html-injection\" or \"js-injection" - or - this = "write-file" and result = "file-content-store" - or - this = ["create-file", "read-file"] and result = "path-injection" - or - this = ["open-url", "jdbc-url"] and result = "request-forgery" - } + predicate sinkKind(string kind) { sinkModel(_, _, _, _, _, _, _, kind, _) } - string outdatedMessage() { - result = - "The kind \"" + this + "\" is outdated. Use \"" + this.replacementKind() + "\" instead." - } + predicate sourceKind(string kind) { sourceModel(_, _, _, _, _, _, _, kind, _) } + + predicate neutralKind(string kind) { neutralModel(_, _, _, _, kind, _) } } - private string getInvalidModelKind() { - exists(string kind | summaryModel(_, _, _, _, _, _, _, _, kind, _) | - not kind = ["taint", "value"] and - result = "Invalid kind \"" + kind + "\" in summary model." - ) - or - exists(string kind, string msg | sinkModel(_, _, _, _, _, _, _, kind, _) | - not kind = - [ - "request-forgery", "jndi-injection", "ldap-injection", "sql-injection", "log-injection", - "mvel-injection", "xpath-injection", "groovy-injection", "html-injection", "js-injection", - "ognl-injection", "intent-redirection", "pending-intents", "url-redirection", - "path-injection", "file-content-store", "hostname-verification", "response-splitting", - "information-leak", "xslt-injection", "jexl-injection", "bean-validation", - "template-injection", "fragment-injection", "command-injection" - ] and - not kind.matches("regex-use%") and - not kind.matches("qltest%") and - msg = "Invalid kind \"" + kind + "\" in sink model." and - // The part of this message that refers to outdated sink kinds can be deleted after June 1st, 2024. - if kind instanceof OutdatedSinkKind - then result = msg + " " + kind.(OutdatedSinkKind).outdatedMessage() - else result = msg - ) - or - exists(string kind | sourceModel(_, _, _, _, _, _, _, kind, _) | - not kind = ["remote", "contentprovider", "android-external-storage-dir"] and - not kind.matches("qltest%") and - result = "Invalid kind \"" + kind + "\" in source model." - ) - or - exists(string kind | neutralModel(_, _, _, _, kind, _) | - not kind = ["summary", "source", "sink"] and - result = "Invalid kind \"" + kind + "\" in neutral model." - ) - } + private module KindVal = SharedModelVal::KindValidation; private string getInvalidModelSignature() { exists( @@ -387,7 +319,7 @@ module ModelValidation { msg = [ getInvalidModelSignature(), getInvalidModelInput(), getInvalidModelOutput(), - getInvalidModelKind() + KindVal::getInvalidModelKind() ] } } diff --git a/java/ql/lib/semmle/code/java/dataflow/FlowSources.qll b/java/ql/lib/semmle/code/java/dataflow/FlowSources.qll index 1a009c1f2e6..f049a0cb37b 100644 --- a/java/ql/lib/semmle/code/java/dataflow/FlowSources.qll +++ b/java/ql/lib/semmle/code/java/dataflow/FlowSources.qll @@ -41,6 +41,7 @@ abstract class RemoteFlowSource extends DataFlow::Node { */ private module FlowSources { private import semmle.code.java.frameworks.hudson.Hudson + private import semmle.code.java.frameworks.stapler.Stapler } private class ExternalRemoteFlowSource extends RemoteFlowSource { diff --git a/java/ql/lib/semmle/code/java/dataflow/FlowSteps.qll b/java/ql/lib/semmle/code/java/dataflow/FlowSteps.qll index 9a187c027ff..1619965f0f0 100644 --- a/java/ql/lib/semmle/code/java/dataflow/FlowSteps.qll +++ b/java/ql/lib/semmle/code/java/dataflow/FlowSteps.qll @@ -23,6 +23,7 @@ private module Frameworks { private import semmle.code.java.frameworks.Properties private import semmle.code.java.frameworks.Protobuf private import semmle.code.java.frameworks.ratpack.RatpackExec + private import semmle.code.java.frameworks.stapler.Stapler private import semmle.code.java.JDK } diff --git a/java/ql/lib/semmle/code/java/dataflow/internal/DataFlowImpl.qll b/java/ql/lib/semmle/code/java/dataflow/internal/DataFlowImpl.qll index 984c5ae2018..284fff191ae 100644 --- a/java/ql/lib/semmle/code/java/dataflow/internal/DataFlowImpl.qll +++ b/java/ql/lib/semmle/code/java/dataflow/internal/DataFlowImpl.qll @@ -2021,7 +2021,8 @@ module Impl { FlowCheckNode() { castNode(this.asNode()) or clearsContentCached(this.asNode(), _) or - expectsContentCached(this.asNode(), _) + expectsContentCached(this.asNode(), _) or + neverSkipInPathGraph(this.asNode()) } } diff --git a/java/ql/lib/semmle/code/java/dataflow/internal/DataFlowPrivate.qll b/java/ql/lib/semmle/code/java/dataflow/internal/DataFlowPrivate.qll index ea393dad0bf..216523023d9 100644 --- a/java/ql/lib/semmle/code/java/dataflow/internal/DataFlowPrivate.qll +++ b/java/ql/lib/semmle/code/java/dataflow/internal/DataFlowPrivate.qll @@ -242,6 +242,12 @@ class CastNode extends ExprNode { CastNode() { this.getExpr() instanceof CastingExpr } } +/** + * Holds if `n` should never be skipped over in the `PathGraph` and in path + * explanations. + */ +predicate neverSkipInPathGraph(Node n) { none() } + private newtype TDataFlowCallable = TSrcCallable(Callable c) or TSummarizedCallable(SummarizedCallable c) or diff --git a/java/ql/lib/semmle/code/java/frameworks/apache/Exec.qll b/java/ql/lib/semmle/code/java/frameworks/apache/Exec.qll deleted file mode 100644 index d6876bfae70..00000000000 --- a/java/ql/lib/semmle/code/java/frameworks/apache/Exec.qll +++ /dev/null @@ -1,29 +0,0 @@ -/** Definitions related to the Apache Commons Exec library. */ - -import semmle.code.java.Type -import semmle.code.java.security.ExternalProcess - -/** The class `org.apache.commons.exec.CommandLine`. */ -private class TypeCommandLine extends Class { - TypeCommandLine() { this.hasQualifiedName("org.apache.commons.exec", "CommandLine") } -} - -/** The `parse()` method of the class `org.apache.commons.exec.CommandLine`. */ -private class MethodCommandLineParse extends Method, ExecCallable { - MethodCommandLineParse() { - this.getDeclaringType() instanceof TypeCommandLine and - this.hasName("parse") - } - - override int getAnExecutedArgument() { result = 0 } -} - -/** The `addArguments()` method of the class `org.apache.commons.exec.CommandLine`. */ -private class MethodCommandLineAddArguments extends Method, ExecCallable { - MethodCommandLineAddArguments() { - this.getDeclaringType() instanceof TypeCommandLine and - this.hasName("addArguments") - } - - override int getAnExecutedArgument() { result = 0 } -} diff --git a/java/ql/lib/semmle/code/java/frameworks/hudson/Hudson.qll b/java/ql/lib/semmle/code/java/frameworks/hudson/Hudson.qll index aab962e65aa..c283c23a046 100644 --- a/java/ql/lib/semmle/code/java/frameworks/hudson/Hudson.qll +++ b/java/ql/lib/semmle/code/java/frameworks/hudson/Hudson.qll @@ -2,8 +2,17 @@ import java private import semmle.code.java.dataflow.FlowSources +private import semmle.code.java.frameworks.stapler.Stapler private import semmle.code.java.security.XSS +/** A method declared in a subtype of `hudson.model.Descriptor` that returns an `HttpResponse`. */ +class HudsonWebMethod extends Method { + HudsonWebMethod() { + this.getReturnType().(RefType).getASourceSupertype*() instanceof HttpResponse and + this.getDeclaringType().getASourceSupertype*().hasQualifiedName("hudson.model", "Descriptor") + } +} + private class FilePathRead extends LocalUserInput { FilePathRead() { this.asExpr() diff --git a/java/ql/lib/semmle/code/java/frameworks/stapler/Stapler.qll b/java/ql/lib/semmle/code/java/frameworks/stapler/Stapler.qll new file mode 100644 index 00000000000..f17090ed307 --- /dev/null +++ b/java/ql/lib/semmle/code/java/frameworks/stapler/Stapler.qll @@ -0,0 +1,124 @@ +/** Provides classes and predicates related to the Stapler framework. */ + +import java +private import semmle.code.java.dataflow.DataFlow +private import semmle.code.java.dataflow.FlowSources +private import semmle.code.java.dataflow.FlowSteps +private import semmle.code.java.dataflow.TypeFlow +private import semmle.code.java.frameworks.hudson.Hudson +private import semmle.code.java.frameworks.JavaxAnnotations + +/** + * A callable annotated with a Stapler `DataBound` annotation, + * or that has the `@stapler-constructor` Javadoc annotation. + */ +class DataBoundAnnotated extends Callable { + DataBoundAnnotated() { + exists(Annotation an | + an.getType() + .hasQualifiedName("org.kohsuke.stapler", ["DataBoundConstructor", "DataBoundSetter"]) + | + this = an.getAnnotatedElement() + ) + or + exists(Javadoc doc | doc.getAChild().getText().matches("%@stapler-constructor%") | + doc.getCommentedElement() = this + ) + } +} + +/** The interface `org.kohsuke.stapler.HttpResponse`. */ +class HttpResponse extends Interface { + HttpResponse() { this.hasQualifiedName("org.kohsuke.stapler", "HttpResponse") } +} + +/** + * A remote flow source for parameters annotated with an annotation + * that is itself annotated with `InjectedParameter`. + * + * Such parameters are populated with user-provided data by Stapler. + */ +private class InjectedParameterSource extends RemoteFlowSource { + InjectedParameterSource() { + this.asParameter().getAnAnnotation().getType() instanceof InjectedParameterAnnotatedType + } + + override string getSourceType() { result = "Stapler injected parameter" } +} + +/** + * A dataflow step from the `HttpResponse` return value of a `HudsonWebMethod` + * to the instance parameter of the `generateResponse` method of the appropriate subtype of `HttpResponse`. + * + * This models the rendering process of an `HttpResponse` by Stapler. + */ +private class HttpResponseGetDescriptionStep extends AdditionalValueStep { + override predicate step(DataFlow::Node n1, DataFlow::Node n2) { + exists(ReturnStmt s, GenerateResponseMethod m | + s.getEnclosingCallable() instanceof HudsonWebMethod and + boundOrStaticType(s.getResult(), m.getDeclaringType().getADescendant()) + | + n1.asExpr() = s.getResult() and + n2.(DataFlow::InstanceParameterNode).getCallable() = m + ) + } +} + +/** + * A dataflow step from the post-update node of an instance access in a `DataBoundAnnotated` method + * to the instance parameter of a `PostConstruct` method of the same type. + * + * This models the construction process of a `DataBound` object in Stapler. + */ +private class PostConstructDataBoundAdditionalStep extends AdditionalValueStep { + override predicate step(DataFlow::Node n1, DataFlow::Node n2) { + exists(PostConstructDataBoundMethod postConstruct, DataBoundAnnotated input | + postConstruct.getDeclaringType() = input.getDeclaringType() + | + n1.(DataFlow::PostUpdateNode) + .getPreUpdateNode() + .(DataFlow::InstanceAccessNode) + .getEnclosingCallable() = input and + n2.(DataFlow::InstanceParameterNode).getCallable() = postConstruct + ) + } +} + +/** An annotation type annotated with the `InjectedParameter` annotation. */ +private class InjectedParameterAnnotatedType extends AnnotationType { + InjectedParameterAnnotatedType() { + this.getAnAnnotation().getType().hasQualifiedName("org.kohsuke.stapler", "InjectedParameter") + } +} + +/** The `generateResponse` method of `org.kohsuke.stapler.HttpResponse` or its subtypes. */ +private class GenerateResponseMethod extends Method { + GenerateResponseMethod() { + this.getDeclaringType().getASourceSupertype*() instanceof HttpResponse and + this.hasName("generateResponse") + } +} + +/** Holds if `t` is the static type of `e`, or an upper bound of the runtime type of `e`. */ +private predicate boundOrStaticType(Expr e, RefType t) { + exprTypeFlow(e, t, false) + or + t = e.getType() +} + +/** + * A method called after the construction of a `DataBound` object. + * + * That is, either the `bindResolve` method of a subtype of `org.kohsuke.stapler.DataBoundResolvable`, + * or a method annotated with `javax.annotation.PostConstruct`. + */ +private class PostConstructDataBoundMethod extends Method { + PostConstructDataBoundMethod() { + this.getDeclaringType() + .getASourceSupertype*() + .hasQualifiedName("org.kohsuke.stapler", "DataBoundResolvable") and + this.hasName("bindResolve") + or + this.getAnAnnotation() instanceof PostConstructAnnotation + } +} diff --git a/java/ql/lib/semmle/code/java/security/CommandLineQuery.qll b/java/ql/lib/semmle/code/java/security/CommandLineQuery.qll index b6e3f5b188a..c0d09a9eeab 100644 --- a/java/ql/lib/semmle/code/java/security/CommandLineQuery.qll +++ b/java/ql/lib/semmle/code/java/security/CommandLineQuery.qll @@ -10,8 +10,8 @@ import java private import semmle.code.java.dataflow.FlowSources private import semmle.code.java.dataflow.ExternalFlow -private import semmle.code.java.security.ExternalProcess private import semmle.code.java.security.CommandArguments +private import semmle.code.java.security.ExternalProcess /** A sink for command injection vulnerabilities. */ abstract class CommandInjectionSink extends DataFlow::Node { } @@ -33,9 +33,7 @@ class CommandInjectionAdditionalTaintStep extends Unit { } private class DefaultCommandInjectionSink extends CommandInjectionSink { - DefaultCommandInjectionSink() { - this.asExpr() instanceof ArgumentToExec or sinkNode(this, "command-injection") - } + DefaultCommandInjectionSink() { sinkNode(this, "command-injection") } } private class DefaultCommandInjectionSanitizer extends CommandInjectionSanitizer { @@ -100,7 +98,7 @@ predicate execIsTainted( RemoteUserInputToArgumentToExecFlow::PathNode sink, Expr execArg ) { RemoteUserInputToArgumentToExecFlow::flowPath(source, sink) and - sink.getNode().asExpr() = execArg + argumentToExec(execArg, sink.getNode()) } /** @@ -112,7 +110,7 @@ predicate execIsTainted( */ deprecated predicate execTainted(DataFlow::PathNode source, DataFlow::PathNode sink, Expr execArg) { exists(RemoteUserInputToArgumentToExecFlowConfig conf | - conf.hasFlowPath(source, sink) and sink.getNode().asExpr() = execArg + conf.hasFlowPath(source, sink) and argumentToExec(execArg, sink.getNode()) ) } diff --git a/java/ql/lib/semmle/code/java/security/ExternalProcess.qll b/java/ql/lib/semmle/code/java/security/ExternalProcess.qll index 9a061c7a419..385d2f6c548 100644 --- a/java/ql/lib/semmle/code/java/security/ExternalProcess.qll +++ b/java/ql/lib/semmle/code/java/security/ExternalProcess.qll @@ -1,16 +1,13 @@ /** Definitions related to external processes. */ import semmle.code.java.Member - -private module Instances { - private import semmle.code.java.JDK - private import semmle.code.java.frameworks.apache.Exec -} +private import semmle.code.java.dataflow.DataFlow +private import semmle.code.java.security.CommandLineQuery /** - * A callable that executes a command. + * DEPRECATED: A callable that executes a command. */ -abstract class ExecCallable extends Callable { +abstract deprecated class ExecCallable extends Callable { /** * Gets the index of an argument that will be part of the command that is executed. */ @@ -23,13 +20,19 @@ abstract class ExecCallable extends Callable { * to be executed. */ class ArgumentToExec extends Expr { - ArgumentToExec() { - exists(Call execCall, ExecCallable execCallable, int i | - execCall.getArgument(pragma[only_bind_into](i)) = this and - execCallable = execCall.getCallee() and - i = execCallable.getAnExecutedArgument() - ) - } + ArgumentToExec() { argumentToExec(this, _) } +} + +/** + * Holds if `e` is an expression used as an argument to a call that executes an external command. + * For calls to varargs method calls, this only includes the first argument, which will be the command + * to be executed. + */ +predicate argumentToExec(Expr e, CommandInjectionSink s) { + s.asExpr() = e + or + e.(Argument).isNthVararg(0) and + s.(DataFlow::ImplicitVarargsArray).getCall() = e.(Argument).getCall() } /** diff --git a/java/ql/lib/semmle/code/java/security/SensitiveLoggingQuery.qll b/java/ql/lib/semmle/code/java/security/SensitiveLoggingQuery.qll index 984c9f6fcaa..eb40a045c1f 100644 --- a/java/ql/lib/semmle/code/java/security/SensitiveLoggingQuery.qll +++ b/java/ql/lib/semmle/code/java/security/SensitiveLoggingQuery.qll @@ -5,7 +5,6 @@ private import semmle.code.java.dataflow.ExternalFlow import semmle.code.java.dataflow.TaintTracking import semmle.code.java.security.SensitiveActions import semmle.code.java.frameworks.android.Compose -import DataFlow /** A variable that may hold sensitive information, judging by its name. */ class CredentialExpr extends Expr { @@ -45,7 +44,7 @@ deprecated class SensitiveLoggerConfiguration extends TaintTracking::Configurati sanitizer.getType() instanceof TypeType } - override predicate isSanitizerIn(Node node) { this.isSource(node) } + override predicate isSanitizerIn(DataFlow::Node node) { this.isSource(node) } } /** A data-flow configuration for identifying potentially-sensitive data flowing to a log output. */ @@ -62,7 +61,7 @@ module SensitiveLoggerConfig implements DataFlow::ConfigSig { sanitizer.getType() instanceof TypeType } - predicate isBarrierIn(Node node) { isSource(node) } + predicate isBarrierIn(DataFlow::Node node) { isSource(node) } } module SensitiveLoggerFlow = TaintTracking::Global; diff --git a/java/ql/lib/semmle/code/java/security/UnsafeDeserializationQuery.qll b/java/ql/lib/semmle/code/java/security/UnsafeDeserializationQuery.qll index 7e995e5cbaf..550b778d8db 100644 --- a/java/ql/lib/semmle/code/java/security/UnsafeDeserializationQuery.qll +++ b/java/ql/lib/semmle/code/java/security/UnsafeDeserializationQuery.qll @@ -28,6 +28,20 @@ private class ObjectInputStreamReadObjectMethod extends Method { } } +/** + * A type extending `ObjectInputStream` that makes it safe to deserialize untrusted data. + * + * * See https://commons.apache.org/proper/commons-io/javadocs/api-2.5/org/apache/commons/io/serialization/ValidatingObjectInputStream.html + * * See https://github.com/ikkisoft/SerialKiller + */ +private class SafeObjectInputStreamType extends RefType { + SafeObjectInputStreamType() { + this.getASourceSupertype*() + .hasQualifiedName("org.apache.commons.io.serialization", "ValidatingObjectInputStream") or + this.getASourceSupertype*().hasQualifiedName("org.nibblesec.tools", "SerialKiller") + } +} + private class XmlDecoderReadObjectMethod extends Method { XmlDecoderReadObjectMethod() { this.getDeclaringType().hasQualifiedName("java.beans", "XMLDecoder") and @@ -135,9 +149,7 @@ predicate unsafeDeserialization(MethodAccess ma, Expr sink) { sink = ma.getQualifier() and not exists(DataFlow::ExprNode node | node.getExpr() = sink and - node.getTypeBound() - .(RefType) - .hasQualifiedName("org.apache.commons.io.serialization", "ValidatingObjectInputStream") + node.getTypeBound() instanceof SafeObjectInputStreamType ) or m instanceof XmlDecoderReadObjectMethod and diff --git a/java/ql/lib/upgrades/7cbc85b1f3ecda39661ad4806dedbd0973d2c4c0/old.dbscheme b/java/ql/lib/upgrades/7cbc85b1f3ecda39661ad4806dedbd0973d2c4c0/old.dbscheme new file mode 100644 index 00000000000..7cbc85b1f3e --- /dev/null +++ b/java/ql/lib/upgrades/7cbc85b1f3ecda39661ad4806dedbd0973d2c4c0/old.dbscheme @@ -0,0 +1,1255 @@ +/** + * 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 expanded arguments that were passed to the extractor for a + * compiler invocation. This is similar to `compilation_args`, but + * for a `@@@someFile` argument, it includes the arguments from that + * file, rather than just taking the argument literally. + */ +#keyset[id, num] +compilation_expanded_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_or_interfaces( + unique int id: @classorinterface, + string nodeName: string ref, + int parentid: @package ref, + int sourceid: @classorinterface ref +); + +file_class( + int id: @classorinterface ref +); + +class_object( + unique int id: @classorinterface ref, + unique int instance: @field ref +); + +type_companion_object( + unique int id: @classorinterface ref, + unique int instance: @field ref, + unique int companion_object: @classorinterface 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 + +isInterface( + unique int id: @classorinterface ref +); + +isRecord( + unique int id: @classorinterface 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: @classorinterface ref +); + +isAnnotElem( + int methodid: @method ref +); + +annotValue( + int parentid: @annotation ref, + int id2: @method ref, + unique int value: @expr ref +); + +isEnumType( + int classid: @classorinterface 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: @classorinterface 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: @classorinterface 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 | @classorinterface | @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; + +@classorinterfaceorpackage = @classorinterface | @package; +@classorinterfaceorcallable = @classorinterface | @callable; +@boundedtype = @typevariable | @wildcard; +@reftype = @classorinterface | @array | @boundedtype | @errortype; +@classorarray = @classorinterface | @array; +@type = @primitive | @reftype; +@callable = @method | @constructor; + +/** A program element that has a name. */ +@element = @package | @modifier | @annotation | @errortype | + @locatableElement; + +@locatableElement = @file | @primitive | @classorinterface | @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 = @classorinterface | @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: @classorinterface ref +) diff --git a/java/ql/lib/upgrades/7cbc85b1f3ecda39661ad4806dedbd0973d2c4c0/semmlecode.dbscheme b/java/ql/lib/upgrades/7cbc85b1f3ecda39661ad4806dedbd0973d2c4c0/semmlecode.dbscheme new file mode 100644 index 00000000000..ecfcf050952 --- /dev/null +++ b/java/ql/lib/upgrades/7cbc85b1f3ecda39661ad4806dedbd0973d2c4c0/semmlecode.dbscheme @@ -0,0 +1,1256 @@ +/** + * 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 expanded arguments that were passed to the extractor for a + * compiler invocation. This is similar to `compilation_args`, but + * for a `@@@someFile` argument, it includes the arguments from that + * file, rather than just taking the argument literally. + */ +#keyset[id, num] +compilation_expanded_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_or_interfaces( + unique int id: @classorinterface, + string nodeName: string ref, + int parentid: @package ref, + int sourceid: @classorinterface ref +); + +file_class( + int id: @classorinterface ref +); + +class_object( + unique int id: @classorinterface ref, + unique int instance: @field ref +); + +type_companion_object( + unique int id: @classorinterface ref, + unique int instance: @field ref, + unique int companion_object: @classorinterface 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 + +isInterface( + unique int id: @classorinterface ref +); + +isRecord( + unique int id: @classorinterface 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: @classorinterface ref +); + +isAnnotElem( + int methodid: @method ref +); + +annotValue( + int parentid: @annotation ref, + int id2: @method ref, + unique int value: @expr ref +); + +isEnumType( + int classid: @classorinterface 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: @classorinterface 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: @classorinterface 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 | @classorinterface | @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; + +@classorinterfaceorpackage = @classorinterface | @package; +@classorinterfaceorcallable = @classorinterface | @callable; +@boundedtype = @typevariable | @wildcard; +@reftype = @classorinterface | @array | @boundedtype | @errortype; +@classorarray = @classorinterface | @array; +@type = @primitive | @reftype; +@callable = @method | @constructor; + +/** A program element that has a name. */ +@element = @package | @modifier | @annotation | @errortype | + @locatableElement; + +@locatableElement = @file | @primitive | @classorinterface | @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 = @classorinterface | @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 + // 3: ENUM_ENTRIES +) + +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: @classorinterface ref +) diff --git a/java/ql/lib/upgrades/7cbc85b1f3ecda39661ad4806dedbd0973d2c4c0/upgrade.properties b/java/ql/lib/upgrades/7cbc85b1f3ecda39661ad4806dedbd0973d2c4c0/upgrade.properties new file mode 100644 index 00000000000..47074bcc8ab --- /dev/null +++ b/java/ql/lib/upgrades/7cbc85b1f3ecda39661ad4806dedbd0973d2c4c0/upgrade.properties @@ -0,0 +1,2 @@ +description: Add ENUM_ENTRIES +compatibility: full diff --git a/java/ql/src/CHANGELOG.md b/java/ql/src/CHANGELOG.md index 1e7cebcfca1..4852323b9b8 100644 --- a/java/ql/src/CHANGELOG.md +++ b/java/ql/src/CHANGELOG.md @@ -1,3 +1,9 @@ +## 0.6.3 + +### Minor Analysis Improvements + +* The `java/summary/lines-of-code` query now only counts lines of Java code. The new `java/summary/lines-of-code-kotlin` counts lines of Kotlin code. + ## 0.6.2 ### Minor Analysis Improvements diff --git a/java/ql/src/Security/CWE/CWE-022/ZipSlip.qhelp b/java/ql/src/Security/CWE/CWE-022/ZipSlip.qhelp index adea1b89c49..2caf0ccc8b0 100644 --- a/java/ql/src/Security/CWE/CWE-022/ZipSlip.qhelp +++ b/java/ql/src/Security/CWE/CWE-022/ZipSlip.qhelp @@ -3,17 +3,15 @@ "qhelp.dtd"> -

Extracting files from a malicious zip archive (or another archive format) -without validating that the destination file path -is within the destination directory can cause files outside the destination directory to be -overwritten, due to the possible presence of directory traversal elements (..) in -archive paths.

+

Extracting files from a malicious zip file, or similar type of archive, +is at risk of directory traversal attacks if filenames from the archive are +not properly validated.

Zip archives contain archive entries representing each file in the archive. These entries include a file path for the entry, but these file paths are not restricted and may contain unexpected special elements such as the directory traversal element (..). If these -file paths are used to determine an output file to write the contents of the archive item to, then -the file may be written to an unexpected location. This can result in sensitive information being +file paths are used to create a filesystem path, then a file operation may happen in an +unexpected location. This can result in sensitive information being revealed or deleted, or an attacker being able to influence behavior by modifying unexpected files.

diff --git a/java/ql/src/Security/CWE/CWE-022/ZipSlip.ql b/java/ql/src/Security/CWE/CWE-022/ZipSlip.ql index 3488c97c057..0d165a73521 100644 --- a/java/ql/src/Security/CWE/CWE-022/ZipSlip.ql +++ b/java/ql/src/Security/CWE/CWE-022/ZipSlip.ql @@ -1,8 +1,8 @@ /** - * @name Arbitrary file write during archive extraction ("Zip Slip") - * @description Extracting files from a malicious archive without validating that the - * destination file path is within the destination directory can cause files outside - * the destination directory to be overwritten. + * @name Arbitrary file access during archive extraction ("Zip Slip") + * @description Extracting files from a malicious ZIP file, or similar type of archive, without + * validating that the destination file path is within the destination directory + * can allow an attacker to unexpectedly gain access to resources. * @kind path-problem * @id java/zipslip * @problem.severity error diff --git a/java/ql/src/Security/CWE/CWE-078/ExecTaintedLocal.ql b/java/ql/src/Security/CWE/CWE-078/ExecTaintedLocal.ql index 08c230cb43a..38b79c468cd 100644 --- a/java/ql/src/Security/CWE/CWE-078/ExecTaintedLocal.ql +++ b/java/ql/src/Security/CWE/CWE-078/ExecTaintedLocal.ql @@ -14,11 +14,14 @@ import java import semmle.code.java.security.CommandLineQuery +import semmle.code.java.security.ExternalProcess import LocalUserInputToArgumentToExecFlow::PathGraph from LocalUserInputToArgumentToExecFlow::PathNode source, - LocalUserInputToArgumentToExecFlow::PathNode sink -where LocalUserInputToArgumentToExecFlow::flowPath(source, sink) -select sink.getNode().asExpr(), source, sink, "This command line depends on a $@.", - source.getNode(), "user-provided value" + LocalUserInputToArgumentToExecFlow::PathNode sink, Expr e +where + LocalUserInputToArgumentToExecFlow::flowPath(source, sink) and + argumentToExec(e, sink.getNode()) +select e, source, sink, "This command line depends on a $@.", source.getNode(), + "user-provided value" diff --git a/java/ql/src/Security/CWE/CWE-078/ExecUnescaped.ql b/java/ql/src/Security/CWE/CWE-078/ExecUnescaped.ql index 68e3cc2faa7..d50f583bbfe 100644 --- a/java/ql/src/Security/CWE/CWE-078/ExecUnescaped.ql +++ b/java/ql/src/Security/CWE/CWE-078/ExecUnescaped.ql @@ -14,6 +14,7 @@ import java import semmle.code.java.security.CommandLineQuery +import semmle.code.java.security.ExternalProcess /** * Strings that are known to be sane by some simple local analysis. Such strings diff --git a/java/ql/src/Telemetry/AutomodelApplicationModeCharacteristics.qll b/java/ql/src/Telemetry/AutomodelApplicationModeCharacteristics.qll index 86aff731ba2..51e786eebdc 100644 --- a/java/ql/src/Telemetry/AutomodelApplicationModeCharacteristics.qll +++ b/java/ql/src/Telemetry/AutomodelApplicationModeCharacteristics.qll @@ -59,13 +59,13 @@ module ApplicationCandidatesImpl implements SharedCharacteristics::CandidateSig e.getType() instanceof NumberType ) or - t instanceof AutomodelEndpointTypes::TaintedPathSinkType and + t instanceof AutomodelEndpointTypes::PathInjectionSinkType and e instanceof PathSanitizer::PathInjectionSanitizer } RelatedLocation asLocation(Endpoint e) { result = e.asExpr() } - predicate isKnownKind = AutomodelJavaUtil::isKnownKind/3; + predicate isKnownKind = AutomodelJavaUtil::isKnownKind/2; predicate isSink(Endpoint e, string kind) { exists(string package, string type, string name, string signature, string ext, string input | @@ -79,7 +79,7 @@ module ApplicationCandidatesImpl implements SharedCharacteristics::CandidateSig predicate isNeutral(Endpoint e) { exists(string package, string type, string name, string signature | sinkSpec(e, package, type, name, signature, _, _) and - ExternalFlow::neutralModel(package, type, name, [signature, ""], _, _) + ExternalFlow::neutralModel(package, type, name, [signature, ""], "sink", _) ) } diff --git a/java/ql/src/Telemetry/AutomodelEndpointTypes.qll b/java/ql/src/Telemetry/AutomodelEndpointTypes.qll index 7414837b605..618fa4bc7dd 100644 --- a/java/ql/src/Telemetry/AutomodelEndpointTypes.qll +++ b/java/ql/src/Telemetry/AutomodelEndpointTypes.qll @@ -40,18 +40,18 @@ class NegativeSinkType extends SinkType { } /** A sink relevant to the SQL injection query */ -class SqlSinkType extends SinkType { - SqlSinkType() { this = "sql" } +class SqlInjectionSinkType extends SinkType { + SqlInjectionSinkType() { this = "sql-injection" } } /** A sink relevant to the tainted path injection query. */ -class TaintedPathSinkType extends SinkType { - TaintedPathSinkType() { this = "tainted-path" } +class PathInjectionSinkType extends SinkType { + PathInjectionSinkType() { this = "path-injection" } } /** A sink relevant to the SSRF query. */ class RequestForgerySinkType extends SinkType { - RequestForgerySinkType() { this = "ssrf" } + RequestForgerySinkType() { this = "request-forgery" } } /** A sink relevant to the command injection query. */ diff --git a/java/ql/src/Telemetry/AutomodelFrameworkModeCharacteristics.qll b/java/ql/src/Telemetry/AutomodelFrameworkModeCharacteristics.qll index da37726d8f0..bc5c3b59a91 100644 --- a/java/ql/src/Telemetry/AutomodelFrameworkModeCharacteristics.qll +++ b/java/ql/src/Telemetry/AutomodelFrameworkModeCharacteristics.qll @@ -48,7 +48,7 @@ module FrameworkCandidatesImpl implements SharedCharacteristics::CandidateSig { RelatedLocation asLocation(Endpoint e) { result = e.asParameter() } - predicate isKnownKind = AutomodelJavaUtil::isKnownKind/3; + predicate isKnownKind = AutomodelJavaUtil::isKnownKind/2; predicate isSink(Endpoint e, string kind) { exists(string package, string type, string name, string signature, string ext, string input | @@ -60,7 +60,7 @@ module FrameworkCandidatesImpl implements SharedCharacteristics::CandidateSig { predicate isNeutral(Endpoint e) { exists(string package, string type, string name, string signature | sinkSpec(e, package, type, name, signature, _, _) and - ExternalFlow::neutralModel(package, type, name, [signature, ""], _, _) + ExternalFlow::neutralModel(package, type, name, [signature, ""], "sink", _) ) } diff --git a/java/ql/src/Telemetry/AutomodelJavaUtil.qll b/java/ql/src/Telemetry/AutomodelJavaUtil.qll index 215ab1fca1a..03b73da1015 100644 --- a/java/ql/src/Telemetry/AutomodelJavaUtil.qll +++ b/java/ql/src/Telemetry/AutomodelJavaUtil.qll @@ -27,31 +27,17 @@ class DollarAtString extends string { * Holds for all combinations of MaD kinds (`kind`) and their human readable * descriptions. */ -predicate isKnownKind( - string kind, string humanReadableKind, AutomodelEndpointTypes::EndpointType type -) { - kind = "read-file" and - humanReadableKind = "read file" and - type instanceof AutomodelEndpointTypes::TaintedPathSinkType +predicate isKnownKind(string kind, AutomodelEndpointTypes::EndpointType type) { + kind = "path-injection" and + type instanceof AutomodelEndpointTypes::PathInjectionSinkType or - kind = "create-file" and - humanReadableKind = "create file" and - type instanceof AutomodelEndpointTypes::TaintedPathSinkType + kind = "sql-injection" and + type instanceof AutomodelEndpointTypes::SqlInjectionSinkType or - kind = "sql" and - humanReadableKind = "mad modeled sql" and - type instanceof AutomodelEndpointTypes::SqlSinkType - or - kind = "open-url" and - humanReadableKind = "open url" and - type instanceof AutomodelEndpointTypes::RequestForgerySinkType - or - kind = "jdbc-url" and - humanReadableKind = "jdbc url" and + kind = "request-forgery" and type instanceof AutomodelEndpointTypes::RequestForgerySinkType or kind = "command-injection" and - humanReadableKind = "command injection" and type instanceof AutomodelEndpointTypes::CommandInjectionSinkType } diff --git a/java/ql/src/Telemetry/AutomodelSharedCharacteristics.qll b/java/ql/src/Telemetry/AutomodelSharedCharacteristics.qll index f23340bf34f..b077f77deb9 100644 --- a/java/ql/src/Telemetry/AutomodelSharedCharacteristics.qll +++ b/java/ql/src/Telemetry/AutomodelSharedCharacteristics.qll @@ -50,7 +50,7 @@ signature module CandidateSig { /** * Defines what MaD kinds are known, and what endpoint type they correspond to. */ - predicate isKnownKind(string kind, string humanReadableLabel, EndpointType type); + predicate isKnownKind(string kind, EndpointType type); /** * Holds if `e` is a flow sanitizer, and has type `t`. @@ -276,7 +276,11 @@ module SharedCharacteristics { string madKind; Candidate::EndpointType endpointType; - KnownSinkCharacteristic() { Candidate::isKnownKind(madKind, this, endpointType) } + KnownSinkCharacteristic() { + Candidate::isKnownKind(madKind, endpointType) and + // bind "this" to a unique string differing from that of the SinkType classes + this = madKind + "-characteristic" + } override predicate appliesToEndpoint(Candidate::Endpoint e) { Candidate::isSink(e, madKind) } diff --git a/java/ql/src/change-notes/2023-06-02-unsafe-deserialization-serialkiller.md b/java/ql/src/change-notes/2023-06-02-unsafe-deserialization-serialkiller.md new file mode 100644 index 00000000000..588e83d4795 --- /dev/null +++ b/java/ql/src/change-notes/2023-06-02-unsafe-deserialization-serialkiller.md @@ -0,0 +1,4 @@ +--- +category: minorAnalysis +--- +* The query `java/unsafe-deserialization` has been updated to take into account `SerialKiller`, a library used to prevent deserialization of arbitrary classes. \ No newline at end of file diff --git a/java/ql/src/change-notes/2023-06-16-zipslip-rename.md b/java/ql/src/change-notes/2023-06-16-zipslip-rename.md new file mode 100644 index 00000000000..fa1343317ba --- /dev/null +++ b/java/ql/src/change-notes/2023-06-16-zipslip-rename.md @@ -0,0 +1,4 @@ +--- +category: fix +--- +* The query "Arbitrary file write during archive extraction ("Zip Slip")" (`java/zipslip`) has been renamed to "Arbitrary file access during archive extraction ("Zip Slip")." diff --git a/java/ql/src/change-notes/2023-06-23-apache-commons-lang.md b/java/ql/src/change-notes/2023-06-23-apache-commons-lang.md new file mode 100644 index 00000000000..dc33878d2e5 --- /dev/null +++ b/java/ql/src/change-notes/2023-06-23-apache-commons-lang.md @@ -0,0 +1,4 @@ +--- +category: minorAnalysis +--- +* New models have been added for `org.apache.commons.lang`. diff --git a/java/ql/src/change-notes/2023-06-05-lines-of-code.md b/java/ql/src/change-notes/released/0.6.3.md similarity index 77% rename from java/ql/src/change-notes/2023-06-05-lines-of-code.md rename to java/ql/src/change-notes/released/0.6.3.md index a96c891e506..96665727131 100644 --- a/java/ql/src/change-notes/2023-06-05-lines-of-code.md +++ b/java/ql/src/change-notes/released/0.6.3.md @@ -1,4 +1,5 @@ ---- -category: minorAnalysis ---- +## 0.6.3 + +### Minor Analysis Improvements + * The `java/summary/lines-of-code` query now only counts lines of Java code. The new `java/summary/lines-of-code-kotlin` counts lines of Kotlin code. diff --git a/java/ql/src/codeql-pack.release.yml b/java/ql/src/codeql-pack.release.yml index 5501a2a1cc5..b7dafe32c5d 100644 --- a/java/ql/src/codeql-pack.release.yml +++ b/java/ql/src/codeql-pack.release.yml @@ -1,2 +1,2 @@ --- -lastReleaseVersion: 0.6.2 +lastReleaseVersion: 0.6.3 diff --git a/java/ql/src/experimental/Security/CWE/CWE-078/ExecTainted.ql b/java/ql/src/experimental/Security/CWE/CWE-078/ExecTainted.ql index 4305b9fbabc..5d543d65011 100644 --- a/java/ql/src/experimental/Security/CWE/CWE-078/ExecTainted.ql +++ b/java/ql/src/experimental/Security/CWE/CWE-078/ExecTainted.ql @@ -15,7 +15,11 @@ import java import semmle.code.java.security.CommandLineQuery import RemoteUserInputToArgumentToExecFlow::PathGraph -import JSchOSInjection +private import semmle.code.java.dataflow.ExternalFlow + +private class ActivateModels extends ActiveExperimentalModels { + ActivateModels() { this = "jsch-os-injection" } +} // This is a clone of query `java/command-line-injection` that also includes experimental sinks. from diff --git a/java/ql/src/experimental/Security/CWE/CWE-078/JSchOSInjection.qll b/java/ql/src/experimental/Security/CWE/CWE-078/JSchOSInjection.qll deleted file mode 100644 index ec1f4d0adfa..00000000000 --- a/java/ql/src/experimental/Security/CWE/CWE-078/JSchOSInjection.qll +++ /dev/null @@ -1,20 +0,0 @@ -/** - * Provides classes for JSch OS command injection detection - */ - -import java - -/** The class `com.jcraft.jsch.ChannelExec`. */ -private class JSchChannelExec extends RefType { - JSchChannelExec() { this.hasQualifiedName("com.jcraft.jsch", "ChannelExec") } -} - -/** A method to set an OS Command for the execution. */ -private class ChannelExecSetCommandMethod extends Method, ExecCallable { - ChannelExecSetCommandMethod() { - this.hasName("setCommand") and - this.getDeclaringType() instanceof JSchChannelExec - } - - override int getAnExecutedArgument() { result = 0 } -} diff --git a/java/ql/src/qlpack.yml b/java/ql/src/qlpack.yml index 2da31e822ff..b75aea1c0a0 100644 --- a/java/ql/src/qlpack.yml +++ b/java/ql/src/qlpack.yml @@ -1,5 +1,5 @@ name: codeql/java-queries -version: 0.6.3-dev +version: 0.6.4-dev groups: - java - queries diff --git a/java/ql/src/utils/flowtestcasegenerator/testHeader.qlfrag b/java/ql/src/utils/flowtestcasegenerator/testHeader.qlfrag index 5d91e4e8e26..aca87429a3a 100644 --- a/java/ql/src/utils/flowtestcasegenerator/testHeader.qlfrag +++ b/java/ql/src/utils/flowtestcasegenerator/testHeader.qlfrag @@ -1,2 +1,3 @@ import java import TestUtilities.InlineFlowTest +import DefaultFlowTest diff --git a/java/ql/test/TestUtilities/InlineFlowTest.qll b/java/ql/test/TestUtilities/InlineFlowTest.qll index 5e37770a279..34d7f75ee20 100644 --- a/java/ql/test/TestUtilities/InlineFlowTest.qll +++ b/java/ql/test/TestUtilities/InlineFlowTest.qll @@ -5,6 +5,12 @@ * ```ql * import java * import TestUtilities.InlineFlowTest + * import DefaultFlowTest + * import PathGraph + * + * from PathNode source, PathNode sink + * where flowPath(source, sink) + * select sink, source, sink, "$@", source, source.toString() * ``` * * To declare expectations, you can use the $hasTaintFlow or $hasValueFlow comments within the test source files. @@ -18,22 +24,18 @@ * * public void test() { * Object s = source(); - * sink(s); //$hasValueFlow + * sink(s); // $ hasValueFlow * String t = "foo" + taint(); - * sink(t); //$hasTaintFlow + * sink(t); // $ hasTaintFlow * } * * } * ``` * - * If you're not interested in a specific flow type, you can disable either value or taint flow expectations as follows: - * ```ql - * class HasFlowTest extends InlineFlowTest { - * override DataFlow::Configuration getTaintFlowConfig() { none() } - * - * override DataFlow::Configuration getValueFlowConfig() { none() } - * } - * ``` + * If you are only interested in value flow, then instead of importing `DefaultFlowTest`, you can import + * `ValueFlowTest`. Similarly, if you are only interested in taint flow, then instead of + * importing `DefaultFlowTest`, you can import `TaintFlowTest`. In both cases + * `DefaultFlowConfig` can be replaced by another implementation of `DataFlow::ConfigSig`. * * If you need more fine-grained tuning, consider implementing a test using `InlineExpectationsTest`. */ @@ -43,57 +45,75 @@ import semmle.code.java.dataflow.ExternalFlow import semmle.code.java.dataflow.TaintTracking import TestUtilities.InlineExpectationsTest -private predicate defaultSource(DataFlow::Node src) { - src.asExpr().(MethodAccess).getMethod().getName() = ["source", "taint"] +private predicate defaultSource(DataFlow::Node source) { + source.asExpr().(MethodAccess).getMethod().getName() = ["source", "taint"] +} + +private predicate defaultSink(DataFlow::Node sink) { + exists(MethodAccess ma | ma.getMethod().hasName("sink") | sink.asExpr() = ma.getAnArgument()) } module DefaultFlowConfig implements DataFlow::ConfigSig { - predicate isSource(DataFlow::Node n) { defaultSource(n) } + predicate isSource(DataFlow::Node source) { defaultSource(source) } - predicate isSink(DataFlow::Node n) { - exists(MethodAccess ma | ma.getMethod().hasName("sink") | n.asExpr() = ma.getAnArgument()) - } + predicate isSink(DataFlow::Node sink) { defaultSink(sink) } int fieldFlowBranchLimit() { result = 1000 } } -private module DefaultValueFlow = DataFlow::Global; +private module NoFlowConfig implements DataFlow::ConfigSig { + predicate isSource(DataFlow::Node source) { none() } -private module DefaultTaintFlow = TaintTracking::Global; + predicate isSink(DataFlow::Node sink) { none() } +} private string getSourceArgString(DataFlow::Node src) { defaultSource(src) and src.asExpr().(MethodAccess).getAnArgument().(StringLiteral).getValue() = result } -class InlineFlowTest extends InlineExpectationsTest { - InlineFlowTest() { this = "HasFlowTest" } +module FlowTest { + module ValueFlow = DataFlow::Global; - override string getARelevantTag() { result = ["hasValueFlow", "hasTaintFlow"] } + module TaintFlow = TaintTracking::Global; - override predicate hasActualResult(Location location, string element, string tag, string value) { - tag = "hasValueFlow" and - exists(DataFlow::Node src, DataFlow::Node sink | this.hasValueFlow(src, sink) | - sink.getLocation() = location and - element = sink.toString() and - if exists(getSourceArgString(src)) then value = getSourceArgString(src) else value = "" - ) - or - tag = "hasTaintFlow" and - exists(DataFlow::Node src, DataFlow::Node sink | - this.hasTaintFlow(src, sink) and not this.hasValueFlow(src, sink) - | - sink.getLocation() = location and - element = sink.toString() and - if exists(getSourceArgString(src)) then value = getSourceArgString(src) else value = "" - ) + private module InlineTest implements TestSig { + string getARelevantTag() { result = ["hasValueFlow", "hasTaintFlow"] } + + predicate hasActualResult(Location location, string element, string tag, string value) { + tag = "hasValueFlow" and + exists(DataFlow::Node src, DataFlow::Node sink | ValueFlow::flow(src, sink) | + sink.getLocation() = location and + element = sink.toString() and + if exists(getSourceArgString(src)) then value = getSourceArgString(src) else value = "" + ) + or + tag = "hasTaintFlow" and + exists(DataFlow::Node src, DataFlow::Node sink | + TaintFlow::flow(src, sink) and not ValueFlow::flow(src, sink) + | + sink.getLocation() = location and + element = sink.toString() and + if exists(getSourceArgString(src)) then value = getSourceArgString(src) else value = "" + ) + } } - predicate hasValueFlow(DataFlow::Node src, DataFlow::Node sink) { - DefaultValueFlow::flow(src, sink) - } + import MakeTest + import DataFlow::MergePathGraph - predicate hasTaintFlow(DataFlow::Node src, DataFlow::Node sink) { - DefaultTaintFlow::flow(src, sink) + predicate flowPath(PathNode source, PathNode sink) { + ValueFlow::flowPath(source.asPathNode1(), sink.asPathNode1()) or + TaintFlow::flowPath(source.asPathNode2(), sink.asPathNode2()) } } + +module DefaultFlowTest = FlowTest; + +module ValueFlowTest { + import FlowTest +} + +module TaintFlowTest { + import FlowTest +} diff --git a/java/ql/test/ext/TestModels/test.expected b/java/ql/test/ext/TestModels/test.expected index e69de29bb2d..48de9172b36 100644 --- a/java/ql/test/ext/TestModels/test.expected +++ b/java/ql/test/ext/TestModels/test.expected @@ -0,0 +1,2 @@ +failures +testFailures diff --git a/java/ql/test/ext/TestModels/test.ql b/java/ql/test/ext/TestModels/test.ql index 5d91e4e8e26..aca87429a3a 100644 --- a/java/ql/test/ext/TestModels/test.ql +++ b/java/ql/test/ext/TestModels/test.ql @@ -1,2 +1,3 @@ import java import TestUtilities.InlineFlowTest +import DefaultFlowTest diff --git a/java/ql/test/kotlin/library-tests/dataflow/summaries/test.expected b/java/ql/test/kotlin/library-tests/dataflow/summaries/test.expected index f566914ba89..f72956fded3 100644 --- a/java/ql/test/kotlin/library-tests/dataflow/summaries/test.expected +++ b/java/ql/test/kotlin/library-tests/dataflow/summaries/test.expected @@ -1,3 +1,5 @@ +failures +testFailures | test.kt:28:14:28:21 | getSecond(...) | Unexpected result: hasTaintFlow=a | | test.kt:35:14:35:27 | component1(...) | Unexpected result: hasTaintFlow=d | | test.kt:41:14:41:22 | getSecond(...) | Unexpected result: hasTaintFlow=e | diff --git a/java/ql/test/kotlin/library-tests/dataflow/summaries/test.ql b/java/ql/test/kotlin/library-tests/dataflow/summaries/test.ql index 5d91e4e8e26..aca87429a3a 100644 --- a/java/ql/test/kotlin/library-tests/dataflow/summaries/test.ql +++ b/java/ql/test/kotlin/library-tests/dataflow/summaries/test.ql @@ -1,2 +1,3 @@ import java import TestUtilities.InlineFlowTest +import DefaultFlowTest diff --git a/java/ql/test/library-tests/dataflow/callctx/test.expected b/java/ql/test/library-tests/dataflow/callctx/test.expected index e69de29bb2d..48de9172b36 100644 --- a/java/ql/test/library-tests/dataflow/callctx/test.expected +++ b/java/ql/test/library-tests/dataflow/callctx/test.expected @@ -0,0 +1,2 @@ +failures +testFailures diff --git a/java/ql/test/library-tests/dataflow/callctx/test.ql b/java/ql/test/library-tests/dataflow/callctx/test.ql index 5d91e4e8e26..aca87429a3a 100644 --- a/java/ql/test/library-tests/dataflow/callctx/test.ql +++ b/java/ql/test/library-tests/dataflow/callctx/test.ql @@ -1,2 +1,3 @@ import java import TestUtilities.InlineFlowTest +import DefaultFlowTest diff --git a/java/ql/test/library-tests/dataflow/collections/containerflow.expected b/java/ql/test/library-tests/dataflow/collections/containerflow.expected index e69de29bb2d..48de9172b36 100644 --- a/java/ql/test/library-tests/dataflow/collections/containerflow.expected +++ b/java/ql/test/library-tests/dataflow/collections/containerflow.expected @@ -0,0 +1,2 @@ +failures +testFailures diff --git a/java/ql/test/library-tests/dataflow/collections/containerflow.ql b/java/ql/test/library-tests/dataflow/collections/containerflow.ql index 992971d6dc7..4b1a19535ea 100644 --- a/java/ql/test/library-tests/dataflow/collections/containerflow.ql +++ b/java/ql/test/library-tests/dataflow/collections/containerflow.ql @@ -1,3 +1,4 @@ import java import semmle.code.java.dataflow.DataFlow import TestUtilities.InlineFlowTest +import DefaultFlowTest diff --git a/java/ql/test/library-tests/dataflow/fluent-methods/flow.expected b/java/ql/test/library-tests/dataflow/fluent-methods/flow.expected index e69de29bb2d..48de9172b36 100644 --- a/java/ql/test/library-tests/dataflow/fluent-methods/flow.expected +++ b/java/ql/test/library-tests/dataflow/fluent-methods/flow.expected @@ -0,0 +1,2 @@ +failures +testFailures diff --git a/java/ql/test/library-tests/dataflow/fluent-methods/flow.ql b/java/ql/test/library-tests/dataflow/fluent-methods/flow.ql index 4a9794ab351..944abac0642 100644 --- a/java/ql/test/library-tests/dataflow/fluent-methods/flow.ql +++ b/java/ql/test/library-tests/dataflow/fluent-methods/flow.ql @@ -2,6 +2,7 @@ import java import semmle.code.java.dataflow.DataFlow import semmle.code.java.dataflow.FlowSteps import TestUtilities.InlineFlowTest +import DefaultFlowTest class Model extends FluentMethod { Model() { this.getName() = "modelledFluentMethod" } diff --git a/java/ql/test/library-tests/dataflow/stream-collect/test.expected b/java/ql/test/library-tests/dataflow/stream-collect/test.expected index e69de29bb2d..48de9172b36 100644 --- a/java/ql/test/library-tests/dataflow/stream-collect/test.expected +++ b/java/ql/test/library-tests/dataflow/stream-collect/test.expected @@ -0,0 +1,2 @@ +failures +testFailures diff --git a/java/ql/test/library-tests/dataflow/stream-collect/test.ql b/java/ql/test/library-tests/dataflow/stream-collect/test.ql index c4b63c87071..50e3f8d2f7d 100644 --- a/java/ql/test/library-tests/dataflow/stream-collect/test.ql +++ b/java/ql/test/library-tests/dataflow/stream-collect/test.ql @@ -1 +1,2 @@ import TestUtilities.InlineFlowTest +import DefaultFlowTest diff --git a/java/ql/test/library-tests/dataflow/synth-global/test.expected b/java/ql/test/library-tests/dataflow/synth-global/test.expected index 81332464f79..ae4c33edb3d 100644 --- a/java/ql/test/library-tests/dataflow/synth-global/test.expected +++ b/java/ql/test/library-tests/dataflow/synth-global/test.expected @@ -1,2 +1,3 @@ failures +testFailures invalidModelRow diff --git a/java/ql/test/library-tests/dataflow/synth-global/test.ql b/java/ql/test/library-tests/dataflow/synth-global/test.ql index 0d9f2265afc..53f8aa6a6f6 100644 --- a/java/ql/test/library-tests/dataflow/synth-global/test.ql +++ b/java/ql/test/library-tests/dataflow/synth-global/test.ql @@ -1,3 +1,4 @@ import java import TestUtilities.InlineFlowTest +import DefaultFlowTest import ModelValidation diff --git a/java/ql/test/library-tests/dataflow/taint-format/test.expected b/java/ql/test/library-tests/dataflow/taint-format/test.expected index e69de29bb2d..48de9172b36 100644 --- a/java/ql/test/library-tests/dataflow/taint-format/test.expected +++ b/java/ql/test/library-tests/dataflow/taint-format/test.expected @@ -0,0 +1,2 @@ +failures +testFailures diff --git a/java/ql/test/library-tests/dataflow/taint-format/test.ql b/java/ql/test/library-tests/dataflow/taint-format/test.ql index 5d91e4e8e26..aca87429a3a 100644 --- a/java/ql/test/library-tests/dataflow/taint-format/test.ql +++ b/java/ql/test/library-tests/dataflow/taint-format/test.ql @@ -1,2 +1,3 @@ import java import TestUtilities.InlineFlowTest +import DefaultFlowTest diff --git a/java/ql/test/library-tests/dataflow/taint-gson/dataFlow.expected b/java/ql/test/library-tests/dataflow/taint-gson/dataFlow.expected index e69de29bb2d..48de9172b36 100644 --- a/java/ql/test/library-tests/dataflow/taint-gson/dataFlow.expected +++ b/java/ql/test/library-tests/dataflow/taint-gson/dataFlow.expected @@ -0,0 +1,2 @@ +failures +testFailures diff --git a/java/ql/test/library-tests/dataflow/taint-gson/dataFlow.ql b/java/ql/test/library-tests/dataflow/taint-gson/dataFlow.ql index 5d91e4e8e26..aca87429a3a 100644 --- a/java/ql/test/library-tests/dataflow/taint-gson/dataFlow.ql +++ b/java/ql/test/library-tests/dataflow/taint-gson/dataFlow.ql @@ -1,2 +1,3 @@ import java import TestUtilities.InlineFlowTest +import DefaultFlowTest diff --git a/java/ql/test/library-tests/dataflow/taint-jackson/dataFlow.expected b/java/ql/test/library-tests/dataflow/taint-jackson/dataFlow.expected index e69de29bb2d..48de9172b36 100644 --- a/java/ql/test/library-tests/dataflow/taint-jackson/dataFlow.expected +++ b/java/ql/test/library-tests/dataflow/taint-jackson/dataFlow.expected @@ -0,0 +1,2 @@ +failures +testFailures diff --git a/java/ql/test/library-tests/dataflow/taint-jackson/dataFlow.ql b/java/ql/test/library-tests/dataflow/taint-jackson/dataFlow.ql index 5d91e4e8e26..aca87429a3a 100644 --- a/java/ql/test/library-tests/dataflow/taint-jackson/dataFlow.ql +++ b/java/ql/test/library-tests/dataflow/taint-jackson/dataFlow.ql @@ -1,2 +1,3 @@ import java import TestUtilities.InlineFlowTest +import DefaultFlowTest diff --git a/java/ql/test/library-tests/dataflow/taintsources/Stapler.java b/java/ql/test/library-tests/dataflow/taintsources/Stapler.java new file mode 100644 index 00000000000..96be00270ba --- /dev/null +++ b/java/ql/test/library-tests/dataflow/taintsources/Stapler.java @@ -0,0 +1,14 @@ +import org.kohsuke.stapler.InjectedParameter; + +public class Stapler { + + @InjectedParameter + private @interface MyInjectedParameter { + } + + private static void sink(Object o) {} + + public static void test(@MyInjectedParameter String src) { + sink(src); // $ hasRemoteValueFlow + } +} diff --git a/java/ql/test/library-tests/dataflow/taintsources/options b/java/ql/test/library-tests/dataflow/taintsources/options index bd30b3e8c95..c8249b05e38 100644 --- a/java/ql/test/library-tests/dataflow/taintsources/options +++ b/java/ql/test/library-tests/dataflow/taintsources/options @@ -1 +1 @@ -//semmle-extractor-options: --javac-args -cp ${testdir}/../../../stubs/servlet-api-2.4:${testdir}/../../../stubs/springframework-5.3.8:${testdir}/../../../stubs/google-android-9.0.0:${testdir}/../../../stubs/playframework-2.6.x:${testdir}/../../../stubs/jackson-databind-2.12:${testdir}/../../../stubs/jackson-core-2.12:${testdir}/../../../stubs/akka-2.6.x:${testdir}/../../../stubs/jwtk-jjwt-0.11.2:${testdir}/../../../stubs/jenkins \ No newline at end of file +//semmle-extractor-options: --javac-args -cp ${testdir}/../../../stubs/servlet-api-2.4:${testdir}/../../../stubs/springframework-5.3.8:${testdir}/../../../stubs/google-android-9.0.0:${testdir}/../../../stubs/playframework-2.6.x:${testdir}/../../../stubs/jackson-databind-2.12:${testdir}/../../../stubs/jackson-core-2.12:${testdir}/../../../stubs/akka-2.6.x:${testdir}/../../../stubs/jwtk-jjwt-0.11.2:${testdir}/../../../stubs/jenkins:${testdir}/../../../stubs/stapler-1.263 \ No newline at end of file diff --git a/java/ql/test/library-tests/frameworks/JaxWs/JaxRsFlow.expected b/java/ql/test/library-tests/frameworks/JaxWs/JaxRsFlow.expected index e69de29bb2d..48de9172b36 100644 --- a/java/ql/test/library-tests/frameworks/JaxWs/JaxRsFlow.expected +++ b/java/ql/test/library-tests/frameworks/JaxWs/JaxRsFlow.expected @@ -0,0 +1,2 @@ +failures +testFailures diff --git a/java/ql/test/library-tests/frameworks/JaxWs/JaxRsFlow.ql b/java/ql/test/library-tests/frameworks/JaxWs/JaxRsFlow.ql index d0ab4274b12..93ab3fe066d 100644 --- a/java/ql/test/library-tests/frameworks/JaxWs/JaxRsFlow.ql +++ b/java/ql/test/library-tests/frameworks/JaxWs/JaxRsFlow.ql @@ -13,16 +13,4 @@ module Config implements DataFlow::ConfigSig { predicate isSink = DefaultFlowConfig::isSink/1; } -module TaintFlow = TaintTracking::Global; - -module ValueFlow = DataFlow::Global; - -class Test extends InlineFlowTest { - override predicate hasTaintFlow(DataFlow::Node source, DataFlow::Node sink) { - TaintFlow::flow(source, sink) - } - - override predicate hasValueFlow(DataFlow::Node source, DataFlow::Node sink) { - ValueFlow::flow(source, sink) - } -} +import FlowTest diff --git a/java/ql/test/library-tests/frameworks/android/asynctask/test.expected b/java/ql/test/library-tests/frameworks/android/asynctask/test.expected index e69de29bb2d..48de9172b36 100644 --- a/java/ql/test/library-tests/frameworks/android/asynctask/test.expected +++ b/java/ql/test/library-tests/frameworks/android/asynctask/test.expected @@ -0,0 +1,2 @@ +failures +testFailures diff --git a/java/ql/test/library-tests/frameworks/android/asynctask/test.ql b/java/ql/test/library-tests/frameworks/android/asynctask/test.ql index 5d91e4e8e26..aca87429a3a 100644 --- a/java/ql/test/library-tests/frameworks/android/asynctask/test.ql +++ b/java/ql/test/library-tests/frameworks/android/asynctask/test.ql @@ -1,2 +1,3 @@ import java import TestUtilities.InlineFlowTest +import DefaultFlowTest diff --git a/java/ql/test/library-tests/frameworks/android/content-provider-summaries/test.expected b/java/ql/test/library-tests/frameworks/android/content-provider-summaries/test.expected index e69de29bb2d..48de9172b36 100644 --- a/java/ql/test/library-tests/frameworks/android/content-provider-summaries/test.expected +++ b/java/ql/test/library-tests/frameworks/android/content-provider-summaries/test.expected @@ -0,0 +1,2 @@ +failures +testFailures diff --git a/java/ql/test/library-tests/frameworks/android/content-provider-summaries/test.ql b/java/ql/test/library-tests/frameworks/android/content-provider-summaries/test.ql index 5d91e4e8e26..aca87429a3a 100644 --- a/java/ql/test/library-tests/frameworks/android/content-provider-summaries/test.ql +++ b/java/ql/test/library-tests/frameworks/android/content-provider-summaries/test.ql @@ -1,2 +1,3 @@ import java import TestUtilities.InlineFlowTest +import DefaultFlowTest diff --git a/java/ql/test/library-tests/frameworks/android/content-provider/test.expected b/java/ql/test/library-tests/frameworks/android/content-provider/test.expected index e69de29bb2d..48de9172b36 100644 --- a/java/ql/test/library-tests/frameworks/android/content-provider/test.expected +++ b/java/ql/test/library-tests/frameworks/android/content-provider/test.expected @@ -0,0 +1,2 @@ +failures +testFailures diff --git a/java/ql/test/library-tests/frameworks/android/content-provider/test.ql b/java/ql/test/library-tests/frameworks/android/content-provider/test.ql index f068b30b0d5..2c6bd09dc40 100644 --- a/java/ql/test/library-tests/frameworks/android/content-provider/test.ql +++ b/java/ql/test/library-tests/frameworks/android/content-provider/test.ql @@ -10,12 +10,4 @@ module ProviderTaintFlowConfig implements DataFlow::ConfigSig { int fieldFlowBranchLimit() { result = DefaultFlowConfig::fieldFlowBranchLimit() } } -module ProviderTaintFlow = TaintTracking::Global; - -class ProviderInlineFlowTest extends InlineFlowTest { - override predicate hasValueFlow(DataFlow::Node src, DataFlow::Node sink) { none() } - - override predicate hasTaintFlow(DataFlow::Node src, DataFlow::Node sink) { - ProviderTaintFlow::flow(src, sink) - } -} +import TaintFlowTest diff --git a/java/ql/test/library-tests/frameworks/android/external-storage/test.expected b/java/ql/test/library-tests/frameworks/android/external-storage/test.expected index e69de29bb2d..48de9172b36 100644 --- a/java/ql/test/library-tests/frameworks/android/external-storage/test.expected +++ b/java/ql/test/library-tests/frameworks/android/external-storage/test.expected @@ -0,0 +1,2 @@ +failures +testFailures diff --git a/java/ql/test/library-tests/frameworks/android/external-storage/test.ql b/java/ql/test/library-tests/frameworks/android/external-storage/test.ql index c73c0a5c6c9..64ff27077df 100644 --- a/java/ql/test/library-tests/frameworks/android/external-storage/test.ql +++ b/java/ql/test/library-tests/frameworks/android/external-storage/test.ql @@ -11,10 +11,4 @@ module Config implements DataFlow::ConfigSig { } } -module Flow = TaintTracking::Global; - -class ExternalStorageTest extends InlineFlowTest { - override predicate hasValueFlow(DataFlow::Node src, DataFlow::Node sink) { none() } - - override predicate hasTaintFlow(DataFlow::Node src, DataFlow::Node sink) { Flow::flow(src, sink) } -} +import TaintFlowTest diff --git a/java/ql/test/library-tests/frameworks/android/flow-steps/test.expected b/java/ql/test/library-tests/frameworks/android/flow-steps/test.expected index e69de29bb2d..48de9172b36 100644 --- a/java/ql/test/library-tests/frameworks/android/flow-steps/test.expected +++ b/java/ql/test/library-tests/frameworks/android/flow-steps/test.expected @@ -0,0 +1,2 @@ +failures +testFailures diff --git a/java/ql/test/library-tests/frameworks/android/flow-steps/test.ql b/java/ql/test/library-tests/frameworks/android/flow-steps/test.ql index 5d91e4e8e26..aca87429a3a 100644 --- a/java/ql/test/library-tests/frameworks/android/flow-steps/test.ql +++ b/java/ql/test/library-tests/frameworks/android/flow-steps/test.ql @@ -1,2 +1,3 @@ import java import TestUtilities.InlineFlowTest +import DefaultFlowTest diff --git a/java/ql/test/library-tests/frameworks/android/intent/test.expected b/java/ql/test/library-tests/frameworks/android/intent/test.expected index e69de29bb2d..48de9172b36 100644 --- a/java/ql/test/library-tests/frameworks/android/intent/test.expected +++ b/java/ql/test/library-tests/frameworks/android/intent/test.expected @@ -0,0 +1,2 @@ +failures +testFailures diff --git a/java/ql/test/library-tests/frameworks/android/intent/test.ql b/java/ql/test/library-tests/frameworks/android/intent/test.ql index 5d91e4e8e26..aca87429a3a 100644 --- a/java/ql/test/library-tests/frameworks/android/intent/test.ql +++ b/java/ql/test/library-tests/frameworks/android/intent/test.ql @@ -1,2 +1,3 @@ import java import TestUtilities.InlineFlowTest +import DefaultFlowTest diff --git a/java/ql/test/library-tests/frameworks/android/notification/test.expected b/java/ql/test/library-tests/frameworks/android/notification/test.expected index e69de29bb2d..48de9172b36 100644 --- a/java/ql/test/library-tests/frameworks/android/notification/test.expected +++ b/java/ql/test/library-tests/frameworks/android/notification/test.expected @@ -0,0 +1,2 @@ +failures +testFailures diff --git a/java/ql/test/library-tests/frameworks/android/notification/test.ql b/java/ql/test/library-tests/frameworks/android/notification/test.ql index f0a60550d4d..07c022b1265 100644 --- a/java/ql/test/library-tests/frameworks/android/notification/test.ql +++ b/java/ql/test/library-tests/frameworks/android/notification/test.ql @@ -1,3 +1,4 @@ import java import semmle.code.java.frameworks.android.Intent import TestUtilities.InlineFlowTest +import DefaultFlowTest diff --git a/java/ql/test/library-tests/frameworks/android/slice/test.expected b/java/ql/test/library-tests/frameworks/android/slice/test.expected index e69de29bb2d..48de9172b36 100644 --- a/java/ql/test/library-tests/frameworks/android/slice/test.expected +++ b/java/ql/test/library-tests/frameworks/android/slice/test.expected @@ -0,0 +1,2 @@ +failures +testFailures diff --git a/java/ql/test/library-tests/frameworks/android/slice/test.ql b/java/ql/test/library-tests/frameworks/android/slice/test.ql index b3c5fe78094..787f93df5a0 100644 --- a/java/ql/test/library-tests/frameworks/android/slice/test.ql +++ b/java/ql/test/library-tests/frameworks/android/slice/test.ql @@ -11,8 +11,6 @@ module SliceValueFlowConfig implements DataFlow::ConfigSig { predicate isSink = DefaultFlowConfig::isSink/1; } -module SliceValueFlow = DataFlow::Global; - module SliceTaintFlowConfig implements DataFlow::ConfigSig { predicate isSource = DefaultFlowConfig::isSource/1; @@ -24,14 +22,4 @@ module SliceTaintFlowConfig implements DataFlow::ConfigSig { } } -module SliceTaintFlow = TaintTracking::Global; - -class SliceFlowTest extends InlineFlowTest { - override predicate hasValueFlow(DataFlow::Node source, DataFlow::Node sink) { - SliceValueFlow::flow(source, sink) - } - - override predicate hasTaintFlow(DataFlow::Node source, DataFlow::Node sink) { - SliceTaintFlow::flow(source, sink) - } -} +import FlowTest diff --git a/java/ql/test/library-tests/frameworks/android/sources/OnActivityResultSourceTest.expected b/java/ql/test/library-tests/frameworks/android/sources/OnActivityResultSourceTest.expected index e69de29bb2d..48de9172b36 100644 --- a/java/ql/test/library-tests/frameworks/android/sources/OnActivityResultSourceTest.expected +++ b/java/ql/test/library-tests/frameworks/android/sources/OnActivityResultSourceTest.expected @@ -0,0 +1,2 @@ +failures +testFailures diff --git a/java/ql/test/library-tests/frameworks/android/sources/OnActivityResultSourceTest.ql b/java/ql/test/library-tests/frameworks/android/sources/OnActivityResultSourceTest.ql index 64c0f48ffa6..5b163a81935 100644 --- a/java/ql/test/library-tests/frameworks/android/sources/OnActivityResultSourceTest.ql +++ b/java/ql/test/library-tests/frameworks/android/sources/OnActivityResultSourceTest.ql @@ -10,12 +10,4 @@ module SourceValueFlowConfig implements DataFlow::ConfigSig { int fieldFlowBranchLimit() { result = DefaultFlowConfig::fieldFlowBranchLimit() } } -module SourceValueFlow = DataFlow::Global; - -class SourceInlineFlowTest extends InlineFlowTest { - override predicate hasValueFlow(DataFlow::Node src, DataFlow::Node sink) { - SourceValueFlow::flow(src, sink) - } - - override predicate hasTaintFlow(DataFlow::Node src, DataFlow::Node sink) { none() } -} +import ValueFlowTest diff --git a/java/ql/test/library-tests/frameworks/android/uri/test.expected b/java/ql/test/library-tests/frameworks/android/uri/test.expected index e69de29bb2d..48de9172b36 100644 --- a/java/ql/test/library-tests/frameworks/android/uri/test.expected +++ b/java/ql/test/library-tests/frameworks/android/uri/test.expected @@ -0,0 +1,2 @@ +failures +testFailures diff --git a/java/ql/test/library-tests/frameworks/android/uri/test.ql b/java/ql/test/library-tests/frameworks/android/uri/test.ql index 5d91e4e8e26..aca87429a3a 100644 --- a/java/ql/test/library-tests/frameworks/android/uri/test.ql +++ b/java/ql/test/library-tests/frameworks/android/uri/test.ql @@ -1,2 +1,3 @@ import java import TestUtilities.InlineFlowTest +import DefaultFlowTest diff --git a/java/ql/test/library-tests/frameworks/android/widget/test.expected b/java/ql/test/library-tests/frameworks/android/widget/test.expected index 8329ef6b41b..5390495c737 100644 --- a/java/ql/test/library-tests/frameworks/android/widget/test.expected +++ b/java/ql/test/library-tests/frameworks/android/widget/test.expected @@ -1,2 +1,3 @@ failures +testFailures valueOf diff --git a/java/ql/test/library-tests/frameworks/android/widget/test.ql b/java/ql/test/library-tests/frameworks/android/widget/test.ql index ddabfd1f27a..3d476b8bd5e 100644 --- a/java/ql/test/library-tests/frameworks/android/widget/test.ql +++ b/java/ql/test/library-tests/frameworks/android/widget/test.ql @@ -1,6 +1,7 @@ import java import semmle.code.java.dataflow.FlowSources import TestUtilities.InlineFlowTest +import DefaultFlowTest query predicate valueOf(MethodAccess ma) { ma.getMethod().hasQualifiedName("java.lang", "String", "valueOf") diff --git a/java/ql/test/library-tests/frameworks/apache-ant/test.expected b/java/ql/test/library-tests/frameworks/apache-ant/test.expected index e69de29bb2d..48de9172b36 100644 --- a/java/ql/test/library-tests/frameworks/apache-ant/test.expected +++ b/java/ql/test/library-tests/frameworks/apache-ant/test.expected @@ -0,0 +1,2 @@ +failures +testFailures diff --git a/java/ql/test/library-tests/frameworks/apache-ant/test.ql b/java/ql/test/library-tests/frameworks/apache-ant/test.ql index 5d91e4e8e26..aca87429a3a 100644 --- a/java/ql/test/library-tests/frameworks/apache-ant/test.ql +++ b/java/ql/test/library-tests/frameworks/apache-ant/test.ql @@ -1,2 +1,3 @@ import java import TestUtilities.InlineFlowTest +import DefaultFlowTest diff --git a/java/ql/test/library-tests/frameworks/apache-collections/test.expected b/java/ql/test/library-tests/frameworks/apache-collections/test.expected index e69de29bb2d..48de9172b36 100644 --- a/java/ql/test/library-tests/frameworks/apache-collections/test.expected +++ b/java/ql/test/library-tests/frameworks/apache-collections/test.expected @@ -0,0 +1,2 @@ +failures +testFailures diff --git a/java/ql/test/library-tests/frameworks/apache-collections/test.ql b/java/ql/test/library-tests/frameworks/apache-collections/test.ql index 5d91e4e8e26..aca87429a3a 100644 --- a/java/ql/test/library-tests/frameworks/apache-collections/test.ql +++ b/java/ql/test/library-tests/frameworks/apache-collections/test.ql @@ -1,2 +1,3 @@ import java import TestUtilities.InlineFlowTest +import DefaultFlowTest diff --git a/java/ql/test/library-tests/frameworks/apache-commons-compress/test.expected b/java/ql/test/library-tests/frameworks/apache-commons-compress/test.expected index e69de29bb2d..48de9172b36 100644 --- a/java/ql/test/library-tests/frameworks/apache-commons-compress/test.expected +++ b/java/ql/test/library-tests/frameworks/apache-commons-compress/test.expected @@ -0,0 +1,2 @@ +failures +testFailures diff --git a/java/ql/test/library-tests/frameworks/apache-commons-compress/test.ql b/java/ql/test/library-tests/frameworks/apache-commons-compress/test.ql index 5d91e4e8e26..aca87429a3a 100644 --- a/java/ql/test/library-tests/frameworks/apache-commons-compress/test.ql +++ b/java/ql/test/library-tests/frameworks/apache-commons-compress/test.ql @@ -1,2 +1,3 @@ import java import TestUtilities.InlineFlowTest +import DefaultFlowTest diff --git a/java/ql/test/library-tests/frameworks/apache-commons-lang3/flow.expected b/java/ql/test/library-tests/frameworks/apache-commons-lang3/flow.expected index e69de29bb2d..48de9172b36 100644 --- a/java/ql/test/library-tests/frameworks/apache-commons-lang3/flow.expected +++ b/java/ql/test/library-tests/frameworks/apache-commons-lang3/flow.expected @@ -0,0 +1,2 @@ +failures +testFailures diff --git a/java/ql/test/library-tests/frameworks/apache-commons-lang3/flow.ql b/java/ql/test/library-tests/frameworks/apache-commons-lang3/flow.ql index 5d91e4e8e26..aca87429a3a 100644 --- a/java/ql/test/library-tests/frameworks/apache-commons-lang3/flow.ql +++ b/java/ql/test/library-tests/frameworks/apache-commons-lang3/flow.ql @@ -1,2 +1,3 @@ import java import TestUtilities.InlineFlowTest +import DefaultFlowTest diff --git a/java/ql/test/library-tests/frameworks/apache-http/flow.expected b/java/ql/test/library-tests/frameworks/apache-http/flow.expected index e69de29bb2d..48de9172b36 100644 --- a/java/ql/test/library-tests/frameworks/apache-http/flow.expected +++ b/java/ql/test/library-tests/frameworks/apache-http/flow.expected @@ -0,0 +1,2 @@ +failures +testFailures diff --git a/java/ql/test/library-tests/frameworks/apache-http/flow.ql b/java/ql/test/library-tests/frameworks/apache-http/flow.ql index 20069103a4a..540b4847ff3 100644 --- a/java/ql/test/library-tests/frameworks/apache-http/flow.ql +++ b/java/ql/test/library-tests/frameworks/apache-http/flow.ql @@ -21,10 +21,4 @@ module Config implements DataFlow::ConfigSig { } } -module Flow = TaintTracking::Global; - -class HasFlowTest extends InlineFlowTest { - override predicate hasValueFlow(DataFlow::Node src, DataFlow::Node sink) { none() } - - override predicate hasTaintFlow(DataFlow::Node src, DataFlow::Node sink) { Flow::flow(src, sink) } -} +import TaintFlowTest diff --git a/java/ql/test/library-tests/frameworks/gson/test.expected b/java/ql/test/library-tests/frameworks/gson/test.expected index e69de29bb2d..48de9172b36 100644 --- a/java/ql/test/library-tests/frameworks/gson/test.expected +++ b/java/ql/test/library-tests/frameworks/gson/test.expected @@ -0,0 +1,2 @@ +failures +testFailures diff --git a/java/ql/test/library-tests/frameworks/gson/test.ql b/java/ql/test/library-tests/frameworks/gson/test.ql index 5d91e4e8e26..aca87429a3a 100644 --- a/java/ql/test/library-tests/frameworks/gson/test.ql +++ b/java/ql/test/library-tests/frameworks/gson/test.ql @@ -1,2 +1,3 @@ import java import TestUtilities.InlineFlowTest +import DefaultFlowTest diff --git a/java/ql/test/library-tests/frameworks/guava/generated/cache/test.expected b/java/ql/test/library-tests/frameworks/guava/generated/cache/test.expected index e69de29bb2d..48de9172b36 100644 --- a/java/ql/test/library-tests/frameworks/guava/generated/cache/test.expected +++ b/java/ql/test/library-tests/frameworks/guava/generated/cache/test.expected @@ -0,0 +1,2 @@ +failures +testFailures diff --git a/java/ql/test/library-tests/frameworks/guava/generated/cache/test.ql b/java/ql/test/library-tests/frameworks/guava/generated/cache/test.ql index 5d91e4e8e26..aca87429a3a 100644 --- a/java/ql/test/library-tests/frameworks/guava/generated/cache/test.ql +++ b/java/ql/test/library-tests/frameworks/guava/generated/cache/test.ql @@ -1,2 +1,3 @@ import java import TestUtilities.InlineFlowTest +import DefaultFlowTest diff --git a/java/ql/test/library-tests/frameworks/guava/generated/collect/test.expected b/java/ql/test/library-tests/frameworks/guava/generated/collect/test.expected index e69de29bb2d..48de9172b36 100644 --- a/java/ql/test/library-tests/frameworks/guava/generated/collect/test.expected +++ b/java/ql/test/library-tests/frameworks/guava/generated/collect/test.expected @@ -0,0 +1,2 @@ +failures +testFailures diff --git a/java/ql/test/library-tests/frameworks/guava/generated/collect/test.ql b/java/ql/test/library-tests/frameworks/guava/generated/collect/test.ql index 5d91e4e8e26..aca87429a3a 100644 --- a/java/ql/test/library-tests/frameworks/guava/generated/collect/test.ql +++ b/java/ql/test/library-tests/frameworks/guava/generated/collect/test.ql @@ -1,2 +1,3 @@ import java import TestUtilities.InlineFlowTest +import DefaultFlowTest diff --git a/java/ql/test/library-tests/frameworks/hudson/test.expected b/java/ql/test/library-tests/frameworks/hudson/test.expected index e69de29bb2d..48de9172b36 100644 --- a/java/ql/test/library-tests/frameworks/hudson/test.expected +++ b/java/ql/test/library-tests/frameworks/hudson/test.expected @@ -0,0 +1,2 @@ +failures +testFailures diff --git a/java/ql/test/library-tests/frameworks/hudson/test.ql b/java/ql/test/library-tests/frameworks/hudson/test.ql index 5d91e4e8e26..aca87429a3a 100644 --- a/java/ql/test/library-tests/frameworks/hudson/test.ql +++ b/java/ql/test/library-tests/frameworks/hudson/test.ql @@ -1,2 +1,3 @@ import java import TestUtilities.InlineFlowTest +import DefaultFlowTest diff --git a/java/ql/test/library-tests/frameworks/jackson/test.expected b/java/ql/test/library-tests/frameworks/jackson/test.expected index e69de29bb2d..48de9172b36 100644 --- a/java/ql/test/library-tests/frameworks/jackson/test.expected +++ b/java/ql/test/library-tests/frameworks/jackson/test.expected @@ -0,0 +1,2 @@ +failures +testFailures diff --git a/java/ql/test/library-tests/frameworks/jackson/test.ql b/java/ql/test/library-tests/frameworks/jackson/test.ql index 5d91e4e8e26..aca87429a3a 100644 --- a/java/ql/test/library-tests/frameworks/jackson/test.ql +++ b/java/ql/test/library-tests/frameworks/jackson/test.ql @@ -1,2 +1,3 @@ import java import TestUtilities.InlineFlowTest +import DefaultFlowTest diff --git a/java/ql/test/library-tests/frameworks/javax-json/test.expected b/java/ql/test/library-tests/frameworks/javax-json/test.expected index e69de29bb2d..48de9172b36 100644 --- a/java/ql/test/library-tests/frameworks/javax-json/test.expected +++ b/java/ql/test/library-tests/frameworks/javax-json/test.expected @@ -0,0 +1,2 @@ +failures +testFailures diff --git a/java/ql/test/library-tests/frameworks/javax-json/test.ql b/java/ql/test/library-tests/frameworks/javax-json/test.ql index 5d91e4e8e26..aca87429a3a 100644 --- a/java/ql/test/library-tests/frameworks/javax-json/test.ql +++ b/java/ql/test/library-tests/frameworks/javax-json/test.ql @@ -1,2 +1,3 @@ import java import TestUtilities.InlineFlowTest +import DefaultFlowTest diff --git a/java/ql/test/library-tests/frameworks/jdk/java.io/test.expected b/java/ql/test/library-tests/frameworks/jdk/java.io/test.expected index e69de29bb2d..48de9172b36 100644 --- a/java/ql/test/library-tests/frameworks/jdk/java.io/test.expected +++ b/java/ql/test/library-tests/frameworks/jdk/java.io/test.expected @@ -0,0 +1,2 @@ +failures +testFailures diff --git a/java/ql/test/library-tests/frameworks/jdk/java.io/test.ql b/java/ql/test/library-tests/frameworks/jdk/java.io/test.ql index 5d91e4e8e26..aca87429a3a 100644 --- a/java/ql/test/library-tests/frameworks/jdk/java.io/test.ql +++ b/java/ql/test/library-tests/frameworks/jdk/java.io/test.ql @@ -1,2 +1,3 @@ import java import TestUtilities.InlineFlowTest +import DefaultFlowTest diff --git a/java/ql/test/library-tests/frameworks/jdk/java.net/test.expected b/java/ql/test/library-tests/frameworks/jdk/java.net/test.expected index e69de29bb2d..48de9172b36 100644 --- a/java/ql/test/library-tests/frameworks/jdk/java.net/test.expected +++ b/java/ql/test/library-tests/frameworks/jdk/java.net/test.expected @@ -0,0 +1,2 @@ +failures +testFailures diff --git a/java/ql/test/library-tests/frameworks/jdk/java.net/test.ql b/java/ql/test/library-tests/frameworks/jdk/java.net/test.ql index 5d91e4e8e26..aca87429a3a 100644 --- a/java/ql/test/library-tests/frameworks/jdk/java.net/test.ql +++ b/java/ql/test/library-tests/frameworks/jdk/java.net/test.ql @@ -1,2 +1,3 @@ import java import TestUtilities.InlineFlowTest +import DefaultFlowTest diff --git a/java/ql/test/library-tests/frameworks/jdk/java.nio.file/test.expected b/java/ql/test/library-tests/frameworks/jdk/java.nio.file/test.expected index e69de29bb2d..48de9172b36 100644 --- a/java/ql/test/library-tests/frameworks/jdk/java.nio.file/test.expected +++ b/java/ql/test/library-tests/frameworks/jdk/java.nio.file/test.expected @@ -0,0 +1,2 @@ +failures +testFailures diff --git a/java/ql/test/library-tests/frameworks/jdk/java.nio.file/test.ql b/java/ql/test/library-tests/frameworks/jdk/java.nio.file/test.ql index 5d91e4e8e26..aca87429a3a 100644 --- a/java/ql/test/library-tests/frameworks/jdk/java.nio.file/test.ql +++ b/java/ql/test/library-tests/frameworks/jdk/java.nio.file/test.ql @@ -1,2 +1,3 @@ import java import TestUtilities.InlineFlowTest +import DefaultFlowTest diff --git a/java/ql/test/library-tests/frameworks/json-java/test.expected b/java/ql/test/library-tests/frameworks/json-java/test.expected index e69de29bb2d..48de9172b36 100644 --- a/java/ql/test/library-tests/frameworks/json-java/test.expected +++ b/java/ql/test/library-tests/frameworks/json-java/test.expected @@ -0,0 +1,2 @@ +failures +testFailures diff --git a/java/ql/test/library-tests/frameworks/json-java/test.ql b/java/ql/test/library-tests/frameworks/json-java/test.ql index 5d91e4e8e26..aca87429a3a 100644 --- a/java/ql/test/library-tests/frameworks/json-java/test.ql +++ b/java/ql/test/library-tests/frameworks/json-java/test.ql @@ -1,2 +1,3 @@ import java import TestUtilities.InlineFlowTest +import DefaultFlowTest diff --git a/java/ql/test/library-tests/frameworks/netty/generated/test.expected b/java/ql/test/library-tests/frameworks/netty/generated/test.expected index e69de29bb2d..48de9172b36 100644 --- a/java/ql/test/library-tests/frameworks/netty/generated/test.expected +++ b/java/ql/test/library-tests/frameworks/netty/generated/test.expected @@ -0,0 +1,2 @@ +failures +testFailures diff --git a/java/ql/test/library-tests/frameworks/netty/generated/test.ql b/java/ql/test/library-tests/frameworks/netty/generated/test.ql index 5d91e4e8e26..aca87429a3a 100644 --- a/java/ql/test/library-tests/frameworks/netty/generated/test.ql +++ b/java/ql/test/library-tests/frameworks/netty/generated/test.ql @@ -1,2 +1,3 @@ import java import TestUtilities.InlineFlowTest +import DefaultFlowTest diff --git a/java/ql/test/library-tests/frameworks/netty/manual/test.expected b/java/ql/test/library-tests/frameworks/netty/manual/test.expected index e69de29bb2d..48de9172b36 100644 --- a/java/ql/test/library-tests/frameworks/netty/manual/test.expected +++ b/java/ql/test/library-tests/frameworks/netty/manual/test.expected @@ -0,0 +1,2 @@ +failures +testFailures diff --git a/java/ql/test/library-tests/frameworks/netty/manual/test.ql b/java/ql/test/library-tests/frameworks/netty/manual/test.ql index 111db20f792..c67cf1835fa 100644 --- a/java/ql/test/library-tests/frameworks/netty/manual/test.ql +++ b/java/ql/test/library-tests/frameworks/netty/manual/test.ql @@ -13,10 +13,4 @@ module Config implements DataFlow::ConfigSig { predicate isSink = DefaultFlowConfig::isSink/1; } -module Flow = TaintTracking::Global; - -class Test extends InlineFlowTest { - override predicate hasTaintFlow(DataFlow::Node source, DataFlow::Node sink) { - Flow::flow(source, sink) - } -} +import FlowTest diff --git a/java/ql/test/library-tests/frameworks/okhttp/test.expected b/java/ql/test/library-tests/frameworks/okhttp/test.expected index e69de29bb2d..48de9172b36 100644 --- a/java/ql/test/library-tests/frameworks/okhttp/test.expected +++ b/java/ql/test/library-tests/frameworks/okhttp/test.expected @@ -0,0 +1,2 @@ +failures +testFailures diff --git a/java/ql/test/library-tests/frameworks/okhttp/test.ql b/java/ql/test/library-tests/frameworks/okhttp/test.ql index 52e8a47132a..04cb3e7f7ac 100644 --- a/java/ql/test/library-tests/frameworks/okhttp/test.ql +++ b/java/ql/test/library-tests/frameworks/okhttp/test.ql @@ -10,10 +10,4 @@ module OkHttpFlowConfig implements DataFlow::ConfigSig { } } -module OkHttpFlow = DataFlow::Global; - -class OkHttpTest extends InlineFlowTest { - override predicate hasValueFlow(DataFlow::Node src, DataFlow::Node sink) { - OkHttpFlow::flow(src, sink) - } -} +import FlowTest diff --git a/java/ql/test/library-tests/frameworks/play/test.expected b/java/ql/test/library-tests/frameworks/play/test.expected index e69de29bb2d..48de9172b36 100644 --- a/java/ql/test/library-tests/frameworks/play/test.expected +++ b/java/ql/test/library-tests/frameworks/play/test.expected @@ -0,0 +1,2 @@ +failures +testFailures diff --git a/java/ql/test/library-tests/frameworks/play/test.ql b/java/ql/test/library-tests/frameworks/play/test.ql index 5d91e4e8e26..aca87429a3a 100644 --- a/java/ql/test/library-tests/frameworks/play/test.ql +++ b/java/ql/test/library-tests/frameworks/play/test.ql @@ -1,2 +1,3 @@ import java import TestUtilities.InlineFlowTest +import DefaultFlowTest diff --git a/java/ql/test/library-tests/frameworks/rabbitmq/FlowTest.expected b/java/ql/test/library-tests/frameworks/rabbitmq/FlowTest.expected index e69de29bb2d..48de9172b36 100644 --- a/java/ql/test/library-tests/frameworks/rabbitmq/FlowTest.expected +++ b/java/ql/test/library-tests/frameworks/rabbitmq/FlowTest.expected @@ -0,0 +1,2 @@ +failures +testFailures diff --git a/java/ql/test/library-tests/frameworks/rabbitmq/FlowTest.ql b/java/ql/test/library-tests/frameworks/rabbitmq/FlowTest.ql index 47cc6b07ad2..0adb5a87783 100644 --- a/java/ql/test/library-tests/frameworks/rabbitmq/FlowTest.ql +++ b/java/ql/test/library-tests/frameworks/rabbitmq/FlowTest.ql @@ -11,10 +11,4 @@ module Config implements DataFlow::ConfigSig { } } -module Flow = TaintTracking::Global; - -class HasFlowTest extends InlineFlowTest { - override predicate hasValueFlow(DataFlow::Node src, DataFlow::Node sink) { none() } - - override predicate hasTaintFlow(DataFlow::Node src, DataFlow::Node sink) { Flow::flow(src, sink) } -} +import TaintFlowTest diff --git a/java/ql/test/library-tests/frameworks/ratpack/flow.expected b/java/ql/test/library-tests/frameworks/ratpack/flow.expected index e69de29bb2d..48de9172b36 100644 --- a/java/ql/test/library-tests/frameworks/ratpack/flow.expected +++ b/java/ql/test/library-tests/frameworks/ratpack/flow.expected @@ -0,0 +1,2 @@ +failures +testFailures diff --git a/java/ql/test/library-tests/frameworks/ratpack/flow.ql b/java/ql/test/library-tests/frameworks/ratpack/flow.ql index dae21e78f7c..eab631f0589 100644 --- a/java/ql/test/library-tests/frameworks/ratpack/flow.ql +++ b/java/ql/test/library-tests/frameworks/ratpack/flow.ql @@ -15,12 +15,4 @@ module Config implements DataFlow::ConfigSig { } } -module Flow = TaintTracking::Global; - -class HasFlowTest extends InlineFlowTest { - HasFlowTest() { this = "HasFlowTest" } - - override predicate hasValueFlow(DataFlow::Node src, DataFlow::Node sink) { none() } - - override predicate hasTaintFlow(DataFlow::Node src, DataFlow::Node sink) { Flow::flow(src, sink) } -} +import TaintFlowTest diff --git a/java/ql/test/library-tests/frameworks/retrofit/test.expected b/java/ql/test/library-tests/frameworks/retrofit/test.expected index e69de29bb2d..48de9172b36 100644 --- a/java/ql/test/library-tests/frameworks/retrofit/test.expected +++ b/java/ql/test/library-tests/frameworks/retrofit/test.expected @@ -0,0 +1,2 @@ +failures +testFailures diff --git a/java/ql/test/library-tests/frameworks/retrofit/test.ql b/java/ql/test/library-tests/frameworks/retrofit/test.ql index e09f1ed41d7..0abbff1f958 100644 --- a/java/ql/test/library-tests/frameworks/retrofit/test.ql +++ b/java/ql/test/library-tests/frameworks/retrofit/test.ql @@ -10,8 +10,4 @@ module FlowConfig implements DataFlow::ConfigSig { } } -module Flow = DataFlow::Global; - -class RetrofitFlowTest extends InlineFlowTest { - override predicate hasValueFlow(DataFlow::Node src, DataFlow::Node sink) { Flow::flow(src, sink) } -} +import FlowTest diff --git a/java/ql/test/library-tests/frameworks/spring/beans/test.expected b/java/ql/test/library-tests/frameworks/spring/beans/test.expected index e69de29bb2d..48de9172b36 100644 --- a/java/ql/test/library-tests/frameworks/spring/beans/test.expected +++ b/java/ql/test/library-tests/frameworks/spring/beans/test.expected @@ -0,0 +1,2 @@ +failures +testFailures diff --git a/java/ql/test/library-tests/frameworks/spring/beans/test.ql b/java/ql/test/library-tests/frameworks/spring/beans/test.ql index 5d91e4e8e26..aca87429a3a 100644 --- a/java/ql/test/library-tests/frameworks/spring/beans/test.ql +++ b/java/ql/test/library-tests/frameworks/spring/beans/test.ql @@ -1,2 +1,3 @@ import java import TestUtilities.InlineFlowTest +import DefaultFlowTest diff --git a/java/ql/test/library-tests/frameworks/spring/cache/test.expected b/java/ql/test/library-tests/frameworks/spring/cache/test.expected index e69de29bb2d..48de9172b36 100644 --- a/java/ql/test/library-tests/frameworks/spring/cache/test.expected +++ b/java/ql/test/library-tests/frameworks/spring/cache/test.expected @@ -0,0 +1,2 @@ +failures +testFailures diff --git a/java/ql/test/library-tests/frameworks/spring/cache/test.ql b/java/ql/test/library-tests/frameworks/spring/cache/test.ql index 5d91e4e8e26..aca87429a3a 100644 --- a/java/ql/test/library-tests/frameworks/spring/cache/test.ql +++ b/java/ql/test/library-tests/frameworks/spring/cache/test.ql @@ -1,2 +1,3 @@ import java import TestUtilities.InlineFlowTest +import DefaultFlowTest diff --git a/java/ql/test/library-tests/frameworks/spring/context/flow.expected b/java/ql/test/library-tests/frameworks/spring/context/flow.expected index e69de29bb2d..48de9172b36 100644 --- a/java/ql/test/library-tests/frameworks/spring/context/flow.expected +++ b/java/ql/test/library-tests/frameworks/spring/context/flow.expected @@ -0,0 +1,2 @@ +failures +testFailures diff --git a/java/ql/test/library-tests/frameworks/spring/context/flow.ql b/java/ql/test/library-tests/frameworks/spring/context/flow.ql index 5d91e4e8e26..aca87429a3a 100644 --- a/java/ql/test/library-tests/frameworks/spring/context/flow.ql +++ b/java/ql/test/library-tests/frameworks/spring/context/flow.ql @@ -1,2 +1,3 @@ import java import TestUtilities.InlineFlowTest +import DefaultFlowTest diff --git a/java/ql/test/library-tests/frameworks/spring/controller/test.expected b/java/ql/test/library-tests/frameworks/spring/controller/test.expected index e69de29bb2d..48de9172b36 100644 --- a/java/ql/test/library-tests/frameworks/spring/controller/test.expected +++ b/java/ql/test/library-tests/frameworks/spring/controller/test.expected @@ -0,0 +1,2 @@ +failures +testFailures diff --git a/java/ql/test/library-tests/frameworks/spring/controller/test.ql b/java/ql/test/library-tests/frameworks/spring/controller/test.ql index b6beb8e1e75..35b3d064e5a 100644 --- a/java/ql/test/library-tests/frameworks/spring/controller/test.ql +++ b/java/ql/test/library-tests/frameworks/spring/controller/test.ql @@ -10,10 +10,4 @@ module ValueFlowConfig implements DataFlow::ConfigSig { } } -module ValueFlow = DataFlow::Global; - -class Test extends InlineFlowTest { - override predicate hasValueFlow(DataFlow::Node src, DataFlow::Node sink) { - ValueFlow::flow(src, sink) - } -} +import FlowTest diff --git a/java/ql/test/library-tests/frameworks/spring/data/test.expected b/java/ql/test/library-tests/frameworks/spring/data/test.expected index e69de29bb2d..48de9172b36 100644 --- a/java/ql/test/library-tests/frameworks/spring/data/test.expected +++ b/java/ql/test/library-tests/frameworks/spring/data/test.expected @@ -0,0 +1,2 @@ +failures +testFailures diff --git a/java/ql/test/library-tests/frameworks/spring/data/test.ql b/java/ql/test/library-tests/frameworks/spring/data/test.ql index 5d91e4e8e26..aca87429a3a 100644 --- a/java/ql/test/library-tests/frameworks/spring/data/test.ql +++ b/java/ql/test/library-tests/frameworks/spring/data/test.ql @@ -1,2 +1,3 @@ import java import TestUtilities.InlineFlowTest +import DefaultFlowTest diff --git a/java/ql/test/library-tests/frameworks/spring/http/flow.expected b/java/ql/test/library-tests/frameworks/spring/http/flow.expected index e69de29bb2d..48de9172b36 100644 --- a/java/ql/test/library-tests/frameworks/spring/http/flow.expected +++ b/java/ql/test/library-tests/frameworks/spring/http/flow.expected @@ -0,0 +1,2 @@ +failures +testFailures diff --git a/java/ql/test/library-tests/frameworks/spring/http/flow.ql b/java/ql/test/library-tests/frameworks/spring/http/flow.ql index 5d91e4e8e26..aca87429a3a 100644 --- a/java/ql/test/library-tests/frameworks/spring/http/flow.ql +++ b/java/ql/test/library-tests/frameworks/spring/http/flow.ql @@ -1,2 +1,3 @@ import java import TestUtilities.InlineFlowTest +import DefaultFlowTest diff --git a/java/ql/test/library-tests/frameworks/spring/ui/test.expected b/java/ql/test/library-tests/frameworks/spring/ui/test.expected index e69de29bb2d..48de9172b36 100644 --- a/java/ql/test/library-tests/frameworks/spring/ui/test.expected +++ b/java/ql/test/library-tests/frameworks/spring/ui/test.expected @@ -0,0 +1,2 @@ +failures +testFailures diff --git a/java/ql/test/library-tests/frameworks/spring/ui/test.ql b/java/ql/test/library-tests/frameworks/spring/ui/test.ql index 5d91e4e8e26..aca87429a3a 100644 --- a/java/ql/test/library-tests/frameworks/spring/ui/test.ql +++ b/java/ql/test/library-tests/frameworks/spring/ui/test.ql @@ -1,2 +1,3 @@ import java import TestUtilities.InlineFlowTest +import DefaultFlowTest diff --git a/java/ql/test/library-tests/frameworks/spring/util/test.expected b/java/ql/test/library-tests/frameworks/spring/util/test.expected index e69de29bb2d..48de9172b36 100644 --- a/java/ql/test/library-tests/frameworks/spring/util/test.expected +++ b/java/ql/test/library-tests/frameworks/spring/util/test.expected @@ -0,0 +1,2 @@ +failures +testFailures diff --git a/java/ql/test/library-tests/frameworks/spring/util/test.ql b/java/ql/test/library-tests/frameworks/spring/util/test.ql index 5d91e4e8e26..aca87429a3a 100644 --- a/java/ql/test/library-tests/frameworks/spring/util/test.ql +++ b/java/ql/test/library-tests/frameworks/spring/util/test.ql @@ -1,2 +1,3 @@ import java import TestUtilities.InlineFlowTest +import DefaultFlowTest diff --git a/java/ql/test/library-tests/frameworks/spring/validation/test.expected b/java/ql/test/library-tests/frameworks/spring/validation/test.expected index e69de29bb2d..48de9172b36 100644 --- a/java/ql/test/library-tests/frameworks/spring/validation/test.expected +++ b/java/ql/test/library-tests/frameworks/spring/validation/test.expected @@ -0,0 +1,2 @@ +failures +testFailures diff --git a/java/ql/test/library-tests/frameworks/spring/validation/test.ql b/java/ql/test/library-tests/frameworks/spring/validation/test.ql index 5d91e4e8e26..aca87429a3a 100644 --- a/java/ql/test/library-tests/frameworks/spring/validation/test.ql +++ b/java/ql/test/library-tests/frameworks/spring/validation/test.ql @@ -1,2 +1,3 @@ import java import TestUtilities.InlineFlowTest +import DefaultFlowTest diff --git a/java/ql/test/library-tests/frameworks/spring/webmultipart/test.expected b/java/ql/test/library-tests/frameworks/spring/webmultipart/test.expected index e69de29bb2d..48de9172b36 100644 --- a/java/ql/test/library-tests/frameworks/spring/webmultipart/test.expected +++ b/java/ql/test/library-tests/frameworks/spring/webmultipart/test.expected @@ -0,0 +1,2 @@ +failures +testFailures diff --git a/java/ql/test/library-tests/frameworks/spring/webmultipart/test.ql b/java/ql/test/library-tests/frameworks/spring/webmultipart/test.ql index 5d91e4e8e26..aca87429a3a 100644 --- a/java/ql/test/library-tests/frameworks/spring/webmultipart/test.ql +++ b/java/ql/test/library-tests/frameworks/spring/webmultipart/test.ql @@ -1,2 +1,3 @@ import java import TestUtilities.InlineFlowTest +import DefaultFlowTest diff --git a/java/ql/test/library-tests/frameworks/spring/webutil/test.expected b/java/ql/test/library-tests/frameworks/spring/webutil/test.expected index e69de29bb2d..48de9172b36 100644 --- a/java/ql/test/library-tests/frameworks/spring/webutil/test.expected +++ b/java/ql/test/library-tests/frameworks/spring/webutil/test.expected @@ -0,0 +1,2 @@ +failures +testFailures diff --git a/java/ql/test/library-tests/frameworks/spring/webutil/test.ql b/java/ql/test/library-tests/frameworks/spring/webutil/test.ql index 5d91e4e8e26..aca87429a3a 100644 --- a/java/ql/test/library-tests/frameworks/spring/webutil/test.ql +++ b/java/ql/test/library-tests/frameworks/spring/webutil/test.ql @@ -1,2 +1,3 @@ import java import TestUtilities.InlineFlowTest +import DefaultFlowTest diff --git a/java/ql/test/library-tests/frameworks/stapler/DataBoundPostConstructTest.java b/java/ql/test/library-tests/frameworks/stapler/DataBoundPostConstructTest.java new file mode 100644 index 00000000000..efab6d956d2 --- /dev/null +++ b/java/ql/test/library-tests/frameworks/stapler/DataBoundPostConstructTest.java @@ -0,0 +1,42 @@ +import javax.annotation.PostConstruct; +import net.sf.json.JSONObject; +import org.kohsuke.stapler.DataBoundConstructor; +import org.kohsuke.stapler.DataBoundResolvable; +import org.kohsuke.stapler.DataBoundSetter; +import org.kohsuke.stapler.StaplerRequest; + +public class DataBoundPostConstructTest implements DataBoundResolvable { + + static Object source(String label) { + return null; + } + + static void sink(Object o) {} + + static void test() { + new DataBoundPostConstructTest(source("constructor")); + new DataBoundPostConstructTest(null).setField(source("setter")); + } + + private Object field; + + @DataBoundConstructor + public DataBoundPostConstructTest(Object field) { + this.field = field; + } + + @DataBoundSetter + public void setField(Object field) { + this.field = field; + } + + private Object bindResolve(StaplerRequest request, JSONObject src) { + sink(this.field); // $ hasValueFlow=constructor hasValueFlow=setter + return null; + } + + @PostConstruct + private void post() { + sink(this.field); // $ hasValueFlow=constructor hasValueFlow=setter + } +} diff --git a/java/ql/test/library-tests/frameworks/stapler/HttpResponseTest.java b/java/ql/test/library-tests/frameworks/stapler/HttpResponseTest.java new file mode 100644 index 00000000000..d437d1a7de3 --- /dev/null +++ b/java/ql/test/library-tests/frameworks/stapler/HttpResponseTest.java @@ -0,0 +1,26 @@ +import hudson.model.Descriptor; +import org.kohsuke.stapler.HttpResponse; +import org.kohsuke.stapler.StaplerRequest; +import org.kohsuke.stapler.StaplerResponse; + +public class HttpResponseTest { + + Object source() { + return null; + } + + void sink(Object o) {} + + private class MyDescriptor extends Descriptor { + public HttpResponse doTest() { + return (MyHttpResponse) source(); + } + } + + private class MyHttpResponse implements HttpResponse { + @Override + public void generateResponse(StaplerRequest p0, StaplerResponse p1, Object p2) { + sink(this); // $ hasValueFlow + } + } +} diff --git a/java/ql/test/library-tests/frameworks/stapler/options b/java/ql/test/library-tests/frameworks/stapler/options index 5b75976846a..52f4c738a88 100644 --- a/java/ql/test/library-tests/frameworks/stapler/options +++ b/java/ql/test/library-tests/frameworks/stapler/options @@ -1 +1 @@ -//semmle-extractor-options: --javac-args -cp ${testdir}/../../../stubs/stapler-1.263:${testdir}/../../../stubs/javax-servlet-2.5:${testdir}/../../../stubs/apache-commons-jelly-1.0.1:${testdir}/../../../stubs/apache-commons-fileupload-1.4:${testdir}/../../../stubs/saxon-xqj-9.x:${testdir}/../../../stubs/apache-commons-beanutils:${testdir}/../../../stubs/dom4j-2.1.1:${testdir}/../../../stubs/apache-commons-lang:${testdir}/../../../stubs/jaxen-1.2.0 \ No newline at end of file +//semmle-extractor-options: --javac-args -cp ${testdir}/../../../stubs/stapler-1.263:${testdir}/../../../stubs/javax-servlet-2.5:${testdir}/../../../stubs/apache-commons-jelly-1.0.1:${testdir}/../../../stubs/apache-commons-fileupload-1.4:${testdir}/../../../stubs/saxon-xqj-9.x:${testdir}/../../../stubs/apache-commons-beanutils:${testdir}/../../../stubs/dom4j-2.1.1:${testdir}/../../../stubs/apache-commons-lang:${testdir}/../../../stubs/jaxen-1.2.0:${testdir}/../../../stubs/jenkins:${testdir}/../../../stubs/javax-annotation-api-1.3.2 \ No newline at end of file diff --git a/java/ql/test/library-tests/frameworks/stapler/test.expected b/java/ql/test/library-tests/frameworks/stapler/test.expected index e69de29bb2d..48de9172b36 100644 --- a/java/ql/test/library-tests/frameworks/stapler/test.expected +++ b/java/ql/test/library-tests/frameworks/stapler/test.expected @@ -0,0 +1,2 @@ +failures +testFailures diff --git a/java/ql/test/library-tests/frameworks/stapler/test.ql b/java/ql/test/library-tests/frameworks/stapler/test.ql index 5d91e4e8e26..aca87429a3a 100644 --- a/java/ql/test/library-tests/frameworks/stapler/test.ql +++ b/java/ql/test/library-tests/frameworks/stapler/test.ql @@ -1,2 +1,3 @@ import java import TestUtilities.InlineFlowTest +import DefaultFlowTest diff --git a/java/ql/test/library-tests/frameworks/stream/test.expected b/java/ql/test/library-tests/frameworks/stream/test.expected index e69de29bb2d..48de9172b36 100644 --- a/java/ql/test/library-tests/frameworks/stream/test.expected +++ b/java/ql/test/library-tests/frameworks/stream/test.expected @@ -0,0 +1,2 @@ +failures +testFailures diff --git a/java/ql/test/library-tests/frameworks/stream/test.ql b/java/ql/test/library-tests/frameworks/stream/test.ql index 5d91e4e8e26..aca87429a3a 100644 --- a/java/ql/test/library-tests/frameworks/stream/test.ql +++ b/java/ql/test/library-tests/frameworks/stream/test.ql @@ -1,2 +1,3 @@ import java import TestUtilities.InlineFlowTest +import DefaultFlowTest diff --git a/java/ql/test/library-tests/frameworks/thymeleaf/test.expected b/java/ql/test/library-tests/frameworks/thymeleaf/test.expected index e69de29bb2d..48de9172b36 100644 --- a/java/ql/test/library-tests/frameworks/thymeleaf/test.expected +++ b/java/ql/test/library-tests/frameworks/thymeleaf/test.expected @@ -0,0 +1,2 @@ +failures +testFailures diff --git a/java/ql/test/library-tests/frameworks/thymeleaf/test.ql b/java/ql/test/library-tests/frameworks/thymeleaf/test.ql index 5d91e4e8e26..aca87429a3a 100644 --- a/java/ql/test/library-tests/frameworks/thymeleaf/test.ql +++ b/java/ql/test/library-tests/frameworks/thymeleaf/test.ql @@ -1,2 +1,3 @@ import java import TestUtilities.InlineFlowTest +import DefaultFlowTest diff --git a/java/ql/test/library-tests/logging/test.expected b/java/ql/test/library-tests/logging/test.expected index e69de29bb2d..48de9172b36 100644 --- a/java/ql/test/library-tests/logging/test.expected +++ b/java/ql/test/library-tests/logging/test.expected @@ -0,0 +1,2 @@ +failures +testFailures diff --git a/java/ql/test/library-tests/logging/test.ql b/java/ql/test/library-tests/logging/test.ql index 5d91e4e8e26..aca87429a3a 100644 --- a/java/ql/test/library-tests/logging/test.ql +++ b/java/ql/test/library-tests/logging/test.ql @@ -1,2 +1,3 @@ import java import TestUtilities.InlineFlowTest +import DefaultFlowTest diff --git a/java/ql/test/library-tests/optional/test.expected b/java/ql/test/library-tests/optional/test.expected index e69de29bb2d..48de9172b36 100644 --- a/java/ql/test/library-tests/optional/test.expected +++ b/java/ql/test/library-tests/optional/test.expected @@ -0,0 +1,2 @@ +failures +testFailures diff --git a/java/ql/test/library-tests/optional/test.ql b/java/ql/test/library-tests/optional/test.ql index 5d91e4e8e26..aca87429a3a 100644 --- a/java/ql/test/library-tests/optional/test.ql +++ b/java/ql/test/library-tests/optional/test.ql @@ -1,2 +1,3 @@ import java import TestUtilities.InlineFlowTest +import DefaultFlowTest diff --git a/java/ql/test/library-tests/paths/test.expected b/java/ql/test/library-tests/paths/test.expected index e69de29bb2d..48de9172b36 100644 --- a/java/ql/test/library-tests/paths/test.expected +++ b/java/ql/test/library-tests/paths/test.expected @@ -0,0 +1,2 @@ +failures +testFailures diff --git a/java/ql/test/library-tests/paths/test.ql b/java/ql/test/library-tests/paths/test.ql index 5d91e4e8e26..aca87429a3a 100644 --- a/java/ql/test/library-tests/paths/test.ql +++ b/java/ql/test/library-tests/paths/test.ql @@ -1,2 +1,3 @@ import java import TestUtilities.InlineFlowTest +import DefaultFlowTest diff --git a/java/ql/test/library-tests/pathsanitizer/test.expected b/java/ql/test/library-tests/pathsanitizer/test.expected index e69de29bb2d..48de9172b36 100644 --- a/java/ql/test/library-tests/pathsanitizer/test.expected +++ b/java/ql/test/library-tests/pathsanitizer/test.expected @@ -0,0 +1,2 @@ +failures +testFailures diff --git a/java/ql/test/library-tests/pathsanitizer/test.ql b/java/ql/test/library-tests/pathsanitizer/test.ql index cefce3276e6..0a20ad012b9 100644 --- a/java/ql/test/library-tests/pathsanitizer/test.ql +++ b/java/ql/test/library-tests/pathsanitizer/test.ql @@ -10,12 +10,4 @@ module PathSanitizerConfig implements DataFlow::ConfigSig { predicate isBarrier(DataFlow::Node sanitizer) { sanitizer instanceof PathInjectionSanitizer } } -module PathSanitizerFlow = TaintTracking::Global; - -class Test extends InlineFlowTest { - override predicate hasValueFlow(DataFlow::Node src, DataFlow::Node sink) { none() } - - override predicate hasTaintFlow(DataFlow::Node src, DataFlow::Node sink) { - PathSanitizerFlow::flow(src, sink) - } -} +import TaintFlowTest diff --git a/java/ql/test/library-tests/regex/test.expected b/java/ql/test/library-tests/regex/test.expected index e69de29bb2d..48de9172b36 100644 --- a/java/ql/test/library-tests/regex/test.expected +++ b/java/ql/test/library-tests/regex/test.expected @@ -0,0 +1,2 @@ +failures +testFailures diff --git a/java/ql/test/library-tests/regex/test.ql b/java/ql/test/library-tests/regex/test.ql index 5d91e4e8e26..aca87429a3a 100644 --- a/java/ql/test/library-tests/regex/test.ql +++ b/java/ql/test/library-tests/regex/test.ql @@ -1,2 +1,3 @@ import java import TestUtilities.InlineFlowTest +import DefaultFlowTest diff --git a/java/ql/test/library-tests/scanner/test.expected b/java/ql/test/library-tests/scanner/test.expected index e69de29bb2d..48de9172b36 100644 --- a/java/ql/test/library-tests/scanner/test.expected +++ b/java/ql/test/library-tests/scanner/test.expected @@ -0,0 +1,2 @@ +failures +testFailures diff --git a/java/ql/test/library-tests/scanner/test.ql b/java/ql/test/library-tests/scanner/test.ql index 5d91e4e8e26..aca87429a3a 100644 --- a/java/ql/test/library-tests/scanner/test.ql +++ b/java/ql/test/library-tests/scanner/test.ql @@ -1,2 +1,3 @@ import java import TestUtilities.InlineFlowTest +import DefaultFlowTest diff --git a/java/ql/test/query-tests/security/CWE-078/ExecTaintedLocal.expected b/java/ql/test/query-tests/security/CWE-078/ExecTaintedLocal.expected index 05dd12f9a5b..fd2c8fb4d5c 100644 --- a/java/ql/test/query-tests/security/CWE-078/ExecTaintedLocal.expected +++ b/java/ql/test/query-tests/security/CWE-078/ExecTaintedLocal.expected @@ -1,22 +1,28 @@ edges -| Test.java:6:35:6:44 | arg : String | Test.java:7:44:7:69 | ... + ... | +| Test.java:6:35:6:44 | arg : String | Test.java:7:44:7:69 | ... + ... : String | | Test.java:6:35:6:44 | arg : String | Test.java:10:61:10:73 | ... + ... : String | | Test.java:6:35:6:44 | arg : String | Test.java:16:13:16:25 | ... + ... : String | | Test.java:6:35:6:44 | arg : String | Test.java:22:15:22:27 | ... + ... : String | +| Test.java:7:25:7:70 | new ..[] { .. } : String[] [[]] : String | Test.java:7:25:7:70 | new ..[] { .. } | +| Test.java:7:44:7:69 | ... + ... : String | Test.java:7:25:7:70 | new ..[] { .. } : String[] [[]] : String | | Test.java:10:29:10:74 | {...} : String[] [[]] : String | Test.java:10:29:10:74 | new String[] | | Test.java:10:61:10:73 | ... + ... : String | Test.java:10:29:10:74 | {...} : String[] [[]] : String | | Test.java:16:5:16:7 | cmd [post update] : ArrayList [] : String | Test.java:18:29:18:31 | cmd | | Test.java:16:13:16:25 | ... + ... : String | Test.java:16:5:16:7 | cmd [post update] : ArrayList [] : String | | Test.java:22:5:22:8 | cmd1 [post update] : String[] [[]] : String | Test.java:24:29:24:32 | cmd1 | | Test.java:22:15:22:27 | ... + ... : String | Test.java:22:5:22:8 | cmd1 [post update] : String[] [[]] : String | -| Test.java:28:38:28:47 | arg : String | Test.java:29:44:29:64 | ... + ... | +| Test.java:28:38:28:47 | arg : String | Test.java:29:44:29:64 | ... + ... : String | +| Test.java:29:25:29:65 | new ..[] { .. } : String[] [[]] : String | Test.java:29:25:29:65 | new ..[] { .. } | +| Test.java:29:44:29:64 | ... + ... : String | Test.java:29:25:29:65 | new ..[] { .. } : String[] [[]] : String | | Test.java:57:27:57:39 | args : String[] | Test.java:60:20:60:22 | arg : String | | Test.java:57:27:57:39 | args : String[] | Test.java:61:23:61:25 | arg : String | | Test.java:60:20:60:22 | arg : String | Test.java:6:35:6:44 | arg : String | | Test.java:61:23:61:25 | arg : String | Test.java:28:38:28:47 | arg : String | nodes | Test.java:6:35:6:44 | arg : String | semmle.label | arg : String | -| Test.java:7:44:7:69 | ... + ... | semmle.label | ... + ... | +| Test.java:7:25:7:70 | new ..[] { .. } | semmle.label | new ..[] { .. } | +| Test.java:7:25:7:70 | new ..[] { .. } : String[] [[]] : String | semmle.label | new ..[] { .. } : String[] [[]] : String | +| Test.java:7:44:7:69 | ... + ... : String | semmle.label | ... + ... : String | | Test.java:10:29:10:74 | new String[] | semmle.label | new String[] | | Test.java:10:29:10:74 | {...} : String[] [[]] : String | semmle.label | {...} : String[] [[]] : String | | Test.java:10:61:10:73 | ... + ... : String | semmle.label | ... + ... : String | @@ -27,14 +33,16 @@ nodes | Test.java:22:15:22:27 | ... + ... : String | semmle.label | ... + ... : String | | Test.java:24:29:24:32 | cmd1 | semmle.label | cmd1 | | Test.java:28:38:28:47 | arg : String | semmle.label | arg : String | -| Test.java:29:44:29:64 | ... + ... | semmle.label | ... + ... | +| Test.java:29:25:29:65 | new ..[] { .. } | semmle.label | new ..[] { .. } | +| Test.java:29:25:29:65 | new ..[] { .. } : String[] [[]] : String | semmle.label | new ..[] { .. } : String[] [[]] : String | +| Test.java:29:44:29:64 | ... + ... : String | semmle.label | ... + ... : String | | Test.java:57:27:57:39 | args : String[] | semmle.label | args : String[] | | Test.java:60:20:60:22 | arg : String | semmle.label | arg : String | | Test.java:61:23:61:25 | arg : String | semmle.label | arg : String | subpaths #select -| Test.java:7:44:7:69 | ... + ... | Test.java:57:27:57:39 | args : String[] | Test.java:7:44:7:69 | ... + ... | This command line depends on a $@. | Test.java:57:27:57:39 | args | user-provided value | +| Test.java:7:44:7:69 | ... + ... | Test.java:57:27:57:39 | args : String[] | Test.java:7:25:7:70 | new ..[] { .. } | This command line depends on a $@. | Test.java:57:27:57:39 | args | user-provided value | | Test.java:10:29:10:74 | new String[] | Test.java:57:27:57:39 | args : String[] | Test.java:10:29:10:74 | new String[] | This command line depends on a $@. | Test.java:57:27:57:39 | args | user-provided value | | Test.java:18:29:18:31 | cmd | Test.java:57:27:57:39 | args : String[] | Test.java:18:29:18:31 | cmd | This command line depends on a $@. | Test.java:57:27:57:39 | args | user-provided value | | Test.java:24:29:24:32 | cmd1 | Test.java:57:27:57:39 | args : String[] | Test.java:24:29:24:32 | cmd1 | This command line depends on a $@. | Test.java:57:27:57:39 | args | user-provided value | -| Test.java:29:44:29:64 | ... + ... | Test.java:57:27:57:39 | args : String[] | Test.java:29:44:29:64 | ... + ... | This command line depends on a $@. | Test.java:57:27:57:39 | args | user-provided value | +| Test.java:29:44:29:64 | ... + ... | Test.java:57:27:57:39 | args : String[] | Test.java:29:25:29:65 | new ..[] { .. } | This command line depends on a $@. | Test.java:57:27:57:39 | args | user-provided value | diff --git a/java/ql/test/query-tests/security/CWE-117/LogInjectionTest.expected b/java/ql/test/query-tests/security/CWE-117/LogInjectionTest.expected index e69de29bb2d..48de9172b36 100644 --- a/java/ql/test/query-tests/security/CWE-117/LogInjectionTest.expected +++ b/java/ql/test/query-tests/security/CWE-117/LogInjectionTest.expected @@ -0,0 +1,2 @@ +failures +testFailures diff --git a/java/ql/test/query-tests/security/CWE-117/LogInjectionTest.ql b/java/ql/test/query-tests/security/CWE-117/LogInjectionTest.ql index 73a41b1bd8e..a2707b5df44 100644 --- a/java/ql/test/query-tests/security/CWE-117/LogInjectionTest.ql +++ b/java/ql/test/query-tests/security/CWE-117/LogInjectionTest.ql @@ -8,10 +8,4 @@ private class TestSource extends RemoteFlowSource { override string getSourceType() { result = "test source" } } -private class LogInjectionTest extends InlineFlowTest { - override predicate hasValueFlow(DataFlow::Node src, DataFlow::Node sink) { none() } - - override predicate hasTaintFlow(DataFlow::Node src, DataFlow::Node sink) { - LogInjectionFlow::flow(src, sink) - } -} +import TaintFlowTest diff --git a/java/ql/test/query-tests/security/CWE-266/IntentUriPermissionManipulationTest.expected b/java/ql/test/query-tests/security/CWE-266/IntentUriPermissionManipulationTest.expected index e69de29bb2d..48de9172b36 100644 --- a/java/ql/test/query-tests/security/CWE-266/IntentUriPermissionManipulationTest.expected +++ b/java/ql/test/query-tests/security/CWE-266/IntentUriPermissionManipulationTest.expected @@ -0,0 +1,2 @@ +failures +testFailures diff --git a/java/ql/test/query-tests/security/CWE-266/IntentUriPermissionManipulationTest.ql b/java/ql/test/query-tests/security/CWE-266/IntentUriPermissionManipulationTest.ql index d90039cf920..86feb7843ce 100644 --- a/java/ql/test/query-tests/security/CWE-266/IntentUriPermissionManipulationTest.ql +++ b/java/ql/test/query-tests/security/CWE-266/IntentUriPermissionManipulationTest.ql @@ -1,11 +1,4 @@ import java import TestUtilities.InlineFlowTest import semmle.code.java.security.IntentUriPermissionManipulationQuery - -class IntentUriPermissionManipulationTest extends InlineFlowTest { - override predicate hasValueFlow(DataFlow::Node src, DataFlow::Node sink) { none() } - - override predicate hasTaintFlow(DataFlow::Node src, DataFlow::Node sink) { - IntentUriPermissionManipulationFlow::flow(src, sink) - } -} +import TaintFlowTest diff --git a/java/ql/test/query-tests/security/CWE-441/UnsafeContentUriResolutionTest.expected b/java/ql/test/query-tests/security/CWE-441/UnsafeContentUriResolutionTest.expected index e69de29bb2d..48de9172b36 100644 --- a/java/ql/test/query-tests/security/CWE-441/UnsafeContentUriResolutionTest.expected +++ b/java/ql/test/query-tests/security/CWE-441/UnsafeContentUriResolutionTest.expected @@ -0,0 +1,2 @@ +failures +testFailures diff --git a/java/ql/test/query-tests/security/CWE-441/UnsafeContentUriResolutionTest.ql b/java/ql/test/query-tests/security/CWE-441/UnsafeContentUriResolutionTest.ql index 55c07bbd301..ae1258a66c5 100644 --- a/java/ql/test/query-tests/security/CWE-441/UnsafeContentUriResolutionTest.ql +++ b/java/ql/test/query-tests/security/CWE-441/UnsafeContentUriResolutionTest.ql @@ -1,11 +1,4 @@ import java import TestUtilities.InlineFlowTest import semmle.code.java.security.UnsafeContentUriResolutionQuery - -class Test extends InlineFlowTest { - override predicate hasValueFlow(DataFlow::Node src, DataFlow::Node sink) { none() } - - override predicate hasTaintFlow(DataFlow::Node src, DataFlow::Node sink) { - UnsafeContentResolutionFlow::flow(src, sink) - } -} +import TaintFlowTest diff --git a/java/ql/test/query-tests/security/CWE-470/FragmentInjectionTest.expected b/java/ql/test/query-tests/security/CWE-470/FragmentInjectionTest.expected index e69de29bb2d..48de9172b36 100644 --- a/java/ql/test/query-tests/security/CWE-470/FragmentInjectionTest.expected +++ b/java/ql/test/query-tests/security/CWE-470/FragmentInjectionTest.expected @@ -0,0 +1,2 @@ +failures +testFailures diff --git a/java/ql/test/query-tests/security/CWE-470/FragmentInjectionTest.ql b/java/ql/test/query-tests/security/CWE-470/FragmentInjectionTest.ql index 2771dd3af90..a1cff04f4c6 100644 --- a/java/ql/test/query-tests/security/CWE-470/FragmentInjectionTest.ql +++ b/java/ql/test/query-tests/security/CWE-470/FragmentInjectionTest.ql @@ -1,11 +1,4 @@ import java import semmle.code.java.security.FragmentInjectionQuery import TestUtilities.InlineFlowTest - -class Test extends InlineFlowTest { - override predicate hasValueFlow(DataFlow::Node src, DataFlow::Node sink) { none() } - - override predicate hasTaintFlow(DataFlow::Node src, DataFlow::Node sink) { - FragmentInjectionTaintFlow::flow(src, sink) - } -} +import TaintFlowTest diff --git a/java/ql/test/query-tests/security/CWE-489/webview-debugging/WebviewDebuggingEnabled.expected b/java/ql/test/query-tests/security/CWE-489/webview-debugging/WebviewDebuggingEnabled.expected index e69de29bb2d..48de9172b36 100644 --- a/java/ql/test/query-tests/security/CWE-489/webview-debugging/WebviewDebuggingEnabled.expected +++ b/java/ql/test/query-tests/security/CWE-489/webview-debugging/WebviewDebuggingEnabled.expected @@ -0,0 +1,2 @@ +failures +testFailures diff --git a/java/ql/test/query-tests/security/CWE-489/webview-debugging/WebviewDebuggingEnabled.ql b/java/ql/test/query-tests/security/CWE-489/webview-debugging/WebviewDebuggingEnabled.ql index 5bd19fb5b9e..99ac3d4e03c 100644 --- a/java/ql/test/query-tests/security/CWE-489/webview-debugging/WebviewDebuggingEnabled.ql +++ b/java/ql/test/query-tests/security/CWE-489/webview-debugging/WebviewDebuggingEnabled.ql @@ -1,11 +1,4 @@ import java import TestUtilities.InlineFlowTest import semmle.code.java.security.WebviewDebuggingEnabledQuery - -class HasFlowTest extends InlineFlowTest { - override predicate hasTaintFlow(DataFlow::Node src, DataFlow::Node sink) { none() } - - override predicate hasValueFlow(DataFlow::Node src, DataFlow::Node sink) { - WebviewDebugEnabledFlow::flow(src, sink) - } -} +import ValueFlowTest diff --git a/java/ql/test/query-tests/security/CWE-502/A.java b/java/ql/test/query-tests/security/CWE-502/A.java index e95f15bff4f..f3bd633f880 100644 --- a/java/ql/test/query-tests/security/CWE-502/A.java +++ b/java/ql/test/query-tests/security/CWE-502/A.java @@ -7,6 +7,7 @@ import com.esotericsoftware.kryo.io.Input; import org.yaml.snakeyaml.constructor.SafeConstructor; import org.yaml.snakeyaml.constructor.Constructor; import org.yaml.snakeyaml.Yaml; +import org.nibblesec.tools.SerialKiller; public class A { public Object deserialize1(Socket sock) throws java.io.IOException, ClassNotFoundException { @@ -21,6 +22,12 @@ public class A { return in.readUnshared(); // $unsafeDeserialization } + public Object deserializeWithSerialKiller(Socket sock) throws java.io.IOException, ClassNotFoundException { + InputStream inputStream = sock.getInputStream(); + ObjectInputStream in = new SerialKiller(inputStream, "/etc/serialkiller.conf"); + return in.readUnshared(); // OK + } + public Object deserialize3(Socket sock) throws java.io.IOException { InputStream inputStream = sock.getInputStream(); XMLDecoder d = new XMLDecoder(inputStream); diff --git a/java/ql/test/query-tests/security/CWE-502/options b/java/ql/test/query-tests/security/CWE-502/options index 0e9cab2d100..47e25fe74fb 100644 --- a/java/ql/test/query-tests/security/CWE-502/options +++ b/java/ql/test/query-tests/security/CWE-502/options @@ -1 +1 @@ -//semmle-extractor-options: --javac-args -cp ${testdir}/../../../stubs/snakeyaml-1.21:${testdir}/../../../stubs/xstream-1.4.10:${testdir}/../../../stubs/kryo-4.0.2:${testdir}/../../../stubs/jsr311-api-1.1.1:${testdir}/../../../stubs/fastjson-1.2.74:${testdir}/../../../stubs/springframework-5.3.8:${testdir}/../../../stubs/servlet-api-2.4:${testdir}/../../../stubs/jyaml-1.3:${testdir}/../../../stubs/json-io-4.10.0:${testdir}/../../../stubs/yamlbeans-1.09:${testdir}/../../../stubs/hessian-4.0.38:${testdir}/../../../stubs/castor-1.4.1:${testdir}/../../../stubs/jackson-databind-2.12:${testdir}/../../../stubs/jackson-core-2.12:${testdir}/../../../stubs/jabsorb-1.3.2:${testdir}/../../../stubs/json-java-20210307:${testdir}/../../../stubs/joddjson-6.0.3:${testdir}/../../../stubs/flexjson-2.1:${testdir}/../../../stubs/gson-2.8.6:${testdir}/../../../stubs/google-android-9.0.0 +//semmle-extractor-options: --javac-args -cp ${testdir}/../../../stubs/snakeyaml-1.21:${testdir}/../../../stubs/xstream-1.4.10:${testdir}/../../../stubs/kryo-4.0.2:${testdir}/../../../stubs/jsr311-api-1.1.1:${testdir}/../../../stubs/fastjson-1.2.74:${testdir}/../../../stubs/springframework-5.3.8:${testdir}/../../../stubs/servlet-api-2.4:${testdir}/../../../stubs/jyaml-1.3:${testdir}/../../../stubs/json-io-4.10.0:${testdir}/../../../stubs/yamlbeans-1.09:${testdir}/../../../stubs/hessian-4.0.38:${testdir}/../../../stubs/castor-1.4.1:${testdir}/../../../stubs/jackson-databind-2.12:${testdir}/../../../stubs/jackson-core-2.12:${testdir}/../../../stubs/jabsorb-1.3.2:${testdir}/../../../stubs/json-java-20210307:${testdir}/../../../stubs/joddjson-6.0.3:${testdir}/../../../stubs/flexjson-2.1:${testdir}/../../../stubs/gson-2.8.6:${testdir}/../../../stubs/google-android-9.0.0:${testdir}/../../../stubs/serialkiller-4.0.0 diff --git a/java/ql/test/query-tests/security/CWE-532/SensitiveLogInfo.expected b/java/ql/test/query-tests/security/CWE-532/SensitiveLogInfo.expected index e69de29bb2d..48de9172b36 100644 --- a/java/ql/test/query-tests/security/CWE-532/SensitiveLogInfo.expected +++ b/java/ql/test/query-tests/security/CWE-532/SensitiveLogInfo.expected @@ -0,0 +1,2 @@ +failures +testFailures diff --git a/java/ql/test/query-tests/security/CWE-532/SensitiveLogInfo.ql b/java/ql/test/query-tests/security/CWE-532/SensitiveLogInfo.ql index 5de153a9e35..389cff934a9 100644 --- a/java/ql/test/query-tests/security/CWE-532/SensitiveLogInfo.ql +++ b/java/ql/test/query-tests/security/CWE-532/SensitiveLogInfo.ql @@ -1,11 +1,4 @@ import java import TestUtilities.InlineFlowTest import semmle.code.java.security.SensitiveLoggingQuery - -class HasFlowTest extends InlineFlowTest { - override predicate hasTaintFlow(DataFlow::Node src, DataFlow::Node sink) { - SensitiveLoggerFlow::flow(src, sink) - } - - override predicate hasValueFlow(DataFlow::Node src, DataFlow::Node sink) { none() } -} +import TaintFlowTest diff --git a/java/ql/test/query-tests/security/CWE-611/XXE.expected b/java/ql/test/query-tests/security/CWE-611/XXE.expected index e69de29bb2d..48de9172b36 100644 --- a/java/ql/test/query-tests/security/CWE-611/XXE.expected +++ b/java/ql/test/query-tests/security/CWE-611/XXE.expected @@ -0,0 +1,2 @@ +failures +testFailures diff --git a/java/ql/test/query-tests/security/CWE-611/XXE.ql b/java/ql/test/query-tests/security/CWE-611/XXE.ql index f1463f561f3..ed12823a6bb 100644 --- a/java/ql/test/query-tests/security/CWE-611/XXE.ql +++ b/java/ql/test/query-tests/security/CWE-611/XXE.ql @@ -1,11 +1,4 @@ import java import TestUtilities.InlineFlowTest import semmle.code.java.security.XxeRemoteQuery - -class HasFlowTest extends InlineFlowTest { - override predicate hasTaintFlow(DataFlow::Node src, DataFlow::Node sink) { - XxeFlow::flow(src, sink) - } - - override predicate hasValueFlow(DataFlow::Node src, DataFlow::Node sink) { none() } -} +import TaintFlowTest diff --git a/java/ql/test/query-tests/security/CWE-780/RsaWithoutOaepTest.expected b/java/ql/test/query-tests/security/CWE-780/RsaWithoutOaepTest.expected index e69de29bb2d..48de9172b36 100644 --- a/java/ql/test/query-tests/security/CWE-780/RsaWithoutOaepTest.expected +++ b/java/ql/test/query-tests/security/CWE-780/RsaWithoutOaepTest.expected @@ -0,0 +1,2 @@ +failures +testFailures diff --git a/java/ql/test/query-tests/security/CWE-780/RsaWithoutOaepTest.ql b/java/ql/test/query-tests/security/CWE-780/RsaWithoutOaepTest.ql index 01af77284f0..c1bc2049c76 100644 --- a/java/ql/test/query-tests/security/CWE-780/RsaWithoutOaepTest.ql +++ b/java/ql/test/query-tests/security/CWE-780/RsaWithoutOaepTest.ql @@ -2,11 +2,4 @@ import java import TestUtilities.InlineExpectationsTest import TestUtilities.InlineFlowTest import semmle.code.java.security.RsaWithoutOaepQuery - -class HasFlowTest extends InlineFlowTest { - override predicate hasValueFlow(DataFlow::Node src, DataFlow::Node sink) { none() } - - override predicate hasTaintFlow(DataFlow::Node src, DataFlow::Node sink) { - RsaWithoutOaepFlow::flow(src, sink) - } -} +import TaintFlowTest diff --git a/java/ql/test/query-tests/security/CWE-927/SensitiveCommunication.expected b/java/ql/test/query-tests/security/CWE-927/SensitiveCommunication.expected index e69de29bb2d..48de9172b36 100644 --- a/java/ql/test/query-tests/security/CWE-927/SensitiveCommunication.expected +++ b/java/ql/test/query-tests/security/CWE-927/SensitiveCommunication.expected @@ -0,0 +1,2 @@ +failures +testFailures diff --git a/java/ql/test/query-tests/security/CWE-927/SensitiveCommunication.ql b/java/ql/test/query-tests/security/CWE-927/SensitiveCommunication.ql index 0f1864398b4..cf7e46b6e8e 100644 --- a/java/ql/test/query-tests/security/CWE-927/SensitiveCommunication.ql +++ b/java/ql/test/query-tests/security/CWE-927/SensitiveCommunication.ql @@ -2,11 +2,4 @@ import java import semmle.code.java.security.AndroidSensitiveCommunicationQuery import TestUtilities.InlineExpectationsTest import TestUtilities.InlineFlowTest - -class HasFlowTest extends InlineFlowTest { - override predicate hasValueFlow(DataFlow::Node src, DataFlow::Node sink) { none() } - - override predicate hasTaintFlow(DataFlow::Node src, DataFlow::Node sink) { - SensitiveCommunicationFlow::flow(src, sink) - } -} +import TaintFlowTest diff --git a/java/ql/test/stubs/javax-annotation-api-1.3.2/javax/annotation/PostConstruct.java b/java/ql/test/stubs/javax-annotation-api-1.3.2/javax/annotation/PostConstruct.java new file mode 100644 index 00000000000..a7fde6ae23b --- /dev/null +++ b/java/ql/test/stubs/javax-annotation-api-1.3.2/javax/annotation/PostConstruct.java @@ -0,0 +1,13 @@ +package javax.annotation; + +import static java.lang.annotation.ElementType.METHOD; +import static java.lang.annotation.RetentionPolicy.RUNTIME; +import java.lang.annotation.Documented; +import java.lang.annotation.Retention; +import java.lang.annotation.Target; + +@Documented +@Retention(RUNTIME) +@Target(METHOD) +public @interface PostConstruct { +} diff --git a/java/ql/test/stubs/jenkins/hudson/model/Descriptor.java b/java/ql/test/stubs/jenkins/hudson/model/Descriptor.java new file mode 100644 index 00000000000..613ed31f665 --- /dev/null +++ b/java/ql/test/stubs/jenkins/hudson/model/Descriptor.java @@ -0,0 +1,5 @@ +package hudson.model; + +public abstract class Descriptor { + +} diff --git a/java/ql/test/stubs/serialkiller-4.0.0/org/nibblesec/tools/SerialKiller.java b/java/ql/test/stubs/serialkiller-4.0.0/org/nibblesec/tools/SerialKiller.java new file mode 100644 index 00000000000..fc59ccd538d --- /dev/null +++ b/java/ql/test/stubs/serialkiller-4.0.0/org/nibblesec/tools/SerialKiller.java @@ -0,0 +1,23 @@ +/* + * SerialKiller.java + * + * Copyright (c) 2015-2016 Luca Carettoni + * + * SerialKiller is an easy-to-use look-ahead Java deserialization library + * to secure application from untrusted input. When Java serialization is + * used to exchange information between a client and a server, attackers + * can replace the legitimate serialized stream with malicious data. + * SerialKiller inspects Java classes during naming resolution and allows + * a combination of blacklisting/whitelisting to secure your application. + * + * Dual-Licensed Software: Apache v2.0 and GPL v2.0 + */ +package org.nibblesec.tools; + +import java.io.ObjectInputStream; +import java.io.InputStream; +import java.io.IOException; + +public class SerialKiller extends ObjectInputStream { + public SerialKiller(InputStream inputStream, String configFile) throws IOException {} +} \ No newline at end of file diff --git a/java/ql/test/stubs/stapler-1.263/org/kohsuke/stapler/DataBoundConstructor.java b/java/ql/test/stubs/stapler-1.263/org/kohsuke/stapler/DataBoundConstructor.java new file mode 100644 index 00000000000..6909171def8 --- /dev/null +++ b/java/ql/test/stubs/stapler-1.263/org/kohsuke/stapler/DataBoundConstructor.java @@ -0,0 +1,13 @@ +package org.kohsuke.stapler; + +import static java.lang.annotation.ElementType.CONSTRUCTOR; +import static java.lang.annotation.RetentionPolicy.RUNTIME; +import java.lang.annotation.Documented; +import java.lang.annotation.Retention; +import java.lang.annotation.Target; + +@Retention(RUNTIME) +@Target(CONSTRUCTOR) +@Documented +public @interface DataBoundConstructor { +} diff --git a/java/ql/test/stubs/stapler-1.263/org/kohsuke/stapler/DataBoundResolvable.java b/java/ql/test/stubs/stapler-1.263/org/kohsuke/stapler/DataBoundResolvable.java new file mode 100644 index 00000000000..0fc9cffd0aa --- /dev/null +++ b/java/ql/test/stubs/stapler-1.263/org/kohsuke/stapler/DataBoundResolvable.java @@ -0,0 +1,4 @@ +package org.kohsuke.stapler; + +public interface DataBoundResolvable { +} diff --git a/java/ql/test/stubs/stapler-1.263/org/kohsuke/stapler/DataBoundSetter.java b/java/ql/test/stubs/stapler-1.263/org/kohsuke/stapler/DataBoundSetter.java new file mode 100644 index 00000000000..ff11084fcbd --- /dev/null +++ b/java/ql/test/stubs/stapler-1.263/org/kohsuke/stapler/DataBoundSetter.java @@ -0,0 +1,14 @@ +package org.kohsuke.stapler; + +import static java.lang.annotation.ElementType.FIELD; +import static java.lang.annotation.ElementType.METHOD; +import static java.lang.annotation.RetentionPolicy.RUNTIME; +import java.lang.annotation.Documented; +import java.lang.annotation.Retention; +import java.lang.annotation.Target; + +@Retention(RUNTIME) +@Target({METHOD, FIELD}) +@Documented +public @interface DataBoundSetter { +} diff --git a/java/ql/test/stubs/stapler-1.263/org/kohsuke/stapler/InjectedParameter.java b/java/ql/test/stubs/stapler-1.263/org/kohsuke/stapler/InjectedParameter.java new file mode 100644 index 00000000000..1674e667233 --- /dev/null +++ b/java/ql/test/stubs/stapler-1.263/org/kohsuke/stapler/InjectedParameter.java @@ -0,0 +1,13 @@ +package org.kohsuke.stapler; + +import java.lang.annotation.Documented; +import java.lang.annotation.Retention; +import java.lang.annotation.Target; +import static java.lang.annotation.RetentionPolicy.RUNTIME; +import static java.lang.annotation.ElementType.ANNOTATION_TYPE;; + +@Retention(RUNTIME) +@Target(ANNOTATION_TYPE) +@Documented +public @interface InjectedParameter { +} diff --git a/javascript/downgrades/qlpack.yml b/javascript/downgrades/qlpack.yml index b23d7602f30..2ef368fed0b 100644 --- a/javascript/downgrades/qlpack.yml +++ b/javascript/downgrades/qlpack.yml @@ -2,3 +2,4 @@ name: codeql/javascript-downgrades groups: javascript downgrades: . library: true +warnOnImplicitThis: true diff --git a/javascript/ql/examples/qlpack.yml b/javascript/ql/examples/qlpack.yml index 9bd7a0e73a9..75f3cbcb2cd 100644 --- a/javascript/ql/examples/qlpack.yml +++ b/javascript/ql/examples/qlpack.yml @@ -4,3 +4,4 @@ groups: - examples dependencies: codeql/javascript-all: ${workspace} +warnOnImplicitThis: true diff --git a/javascript/ql/experimental/adaptivethreatmodeling/lib/qlpack.yml b/javascript/ql/experimental/adaptivethreatmodeling/lib/qlpack.yml index addde82c591..cda69768254 100644 --- a/javascript/ql/experimental/adaptivethreatmodeling/lib/qlpack.yml +++ b/javascript/ql/experimental/adaptivethreatmodeling/lib/qlpack.yml @@ -8,3 +8,4 @@ groups: - experimental dependencies: codeql/javascript-all: ${workspace} +warnOnImplicitThis: true diff --git a/javascript/ql/experimental/adaptivethreatmodeling/model/qlpack.yml b/javascript/ql/experimental/adaptivethreatmodeling/model/qlpack.yml index e37547ed938..affda0e0b16 100644 --- a/javascript/ql/experimental/adaptivethreatmodeling/model/qlpack.yml +++ b/javascript/ql/experimental/adaptivethreatmodeling/model/qlpack.yml @@ -6,3 +6,4 @@ groups: - experimental mlModels: - "resources/*.codeqlmodel" +warnOnImplicitThis: true diff --git a/javascript/ql/experimental/adaptivethreatmodeling/modelbuilding/qlpack.yml b/javascript/ql/experimental/adaptivethreatmodeling/modelbuilding/qlpack.yml index a42eb4067ac..cdf0b9d5aab 100644 --- a/javascript/ql/experimental/adaptivethreatmodeling/modelbuilding/qlpack.yml +++ b/javascript/ql/experimental/adaptivethreatmodeling/modelbuilding/qlpack.yml @@ -8,3 +8,4 @@ groups: dependencies: codeql/javascript-experimental-atm-lib: ${workspace} codeql/javascript-experimental-atm-model: "0.3.1-2023-03-01-12h42m43s.strong-turtle-1xp3dqvv.ecb17d40286d14132b481c065a43459a7f0ba9059015b7a49c909c9f9ce5fec5" +warnOnImplicitThis: true diff --git a/javascript/ql/experimental/adaptivethreatmodeling/src/qlpack.yml b/javascript/ql/experimental/adaptivethreatmodeling/src/qlpack.yml index dec06084ef1..6c2930991e4 100644 --- a/javascript/ql/experimental/adaptivethreatmodeling/src/qlpack.yml +++ b/javascript/ql/experimental/adaptivethreatmodeling/src/qlpack.yml @@ -10,3 +10,4 @@ groups: dependencies: codeql/javascript-experimental-atm-lib: ${workspace} codeql/javascript-experimental-atm-model: "0.3.1-2023-03-01-12h42m43s.strong-turtle-1xp3dqvv.ecb17d40286d14132b481c065a43459a7f0ba9059015b7a49c909c9f9ce5fec5" +warnOnImplicitThis: true diff --git a/javascript/ql/experimental/adaptivethreatmodeling/test/qlpack.yml b/javascript/ql/experimental/adaptivethreatmodeling/test/qlpack.yml index 987b0ef55c4..5a7612bd33c 100644 --- a/javascript/ql/experimental/adaptivethreatmodeling/test/qlpack.yml +++ b/javascript/ql/experimental/adaptivethreatmodeling/test/qlpack.yml @@ -2,3 +2,4 @@ name: codeql/javascript-experimental-atm-tests extractor: javascript dependencies: codeql/javascript-experimental-atm-model-building: ${workspace} +warnOnImplicitThis: true diff --git a/javascript/ql/integration-tests/all-platforms/qlpack.yml b/javascript/ql/integration-tests/all-platforms/qlpack.yml index f4bc24850b4..9f076584585 100644 --- a/javascript/ql/integration-tests/all-platforms/qlpack.yml +++ b/javascript/ql/integration-tests/all-platforms/qlpack.yml @@ -1,3 +1,4 @@ dependencies: codeql/javascript-all: '*' codeql/javascript-queries: '*' +warnOnImplicitThis: true diff --git a/javascript/ql/lib/CHANGELOG.md b/javascript/ql/lib/CHANGELOG.md index 3ac3bc23481..47c4130c3af 100644 --- a/javascript/ql/lib/CHANGELOG.md +++ b/javascript/ql/lib/CHANGELOG.md @@ -1,3 +1,23 @@ +## 0.6.3 + +### Major Analysis Improvements + +* Added support for TypeScript 5.1. + +### Minor Analysis Improvements + +* Deleted many deprecated predicates and classes with uppercase `XML`, `JSON`, `URL`, `API`, etc. in their names. Use the PascalCased versions instead. +* Deleted the deprecated `localTaintStep` predicate from `DataFlow.qll`. +* Deleted the deprecated `stringStep`, and `localTaintStep` predicates from `TaintTracking.qll`. +* Deleted many modules that started with a lowercase letter. Use the versions that start with an uppercase letter instead. +* Deleted the deprecated `HtmlInjectionConfiguration` and `JQueryHtmlOrSelectorInjectionConfiguration` classes from `DomBasedXssQuery.qll`, use `Configuration` instead. +* Deleted the deprecated `DefiningIdentifier` class and the `Definitions.qll` file it was in. Use `SsaDefinition` instead. +* Deleted the deprecated `definitionReaches`, `localDefinitionReaches`, `getAPseudoDefinitionInput`, `nextDefAfter`, and `localDefinitionOverwrites` predicates from `DefUse.qll`. +* Updated the following JavaScript sink kind names. Any custom data extensions that use these sink kinds will need to be updated accordingly in order to continue working. + * `command-line-injection` to `command-injection` + * `credentials[kind]` to `credentials-kind` +* Added a support of sub modules in `node_modules`. + ## 0.6.2 ### Minor Analysis Improvements diff --git a/javascript/ql/lib/change-notes/2023-04-19-typescript-5-1.md b/javascript/ql/lib/change-notes/2023-04-19-typescript-5-1.md deleted file mode 100644 index 7260bd3d389..00000000000 --- a/javascript/ql/lib/change-notes/2023-04-19-typescript-5-1.md +++ /dev/null @@ -1,4 +0,0 @@ ---- -category: majorAnalysis ---- -* Added support for TypeScript 5.1. \ No newline at end of file diff --git a/javascript/ql/lib/change-notes/2023-04-30-npm-submodule.md b/javascript/ql/lib/change-notes/2023-04-30-npm-submodule.md deleted file mode 100644 index 5ef95cf7d58..00000000000 --- a/javascript/ql/lib/change-notes/2023-04-30-npm-submodule.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -category: minorAnalysis ---- - -- Added a support of sub modules in `node_modules`. diff --git a/javascript/ql/lib/change-notes/2023-05-12-update-js-sink-kinds.md b/javascript/ql/lib/change-notes/2023-05-12-update-js-sink-kinds.md deleted file mode 100644 index 9d215924623..00000000000 --- a/javascript/ql/lib/change-notes/2023-05-12-update-js-sink-kinds.md +++ /dev/null @@ -1,6 +0,0 @@ ---- -category: minorAnalysis ---- -* Updated the following JavaScript sink kind names. Any custom data extensions that use these sink kinds will need to be updated accordingly in order to continue working. - * `command-line-injection` to `command-injection` - * `credentials[kind]` to `credentials-kind` diff --git a/javascript/ql/lib/change-notes/2023-06-02-delete-deps.md b/javascript/ql/lib/change-notes/released/0.6.3.md similarity index 64% rename from javascript/ql/lib/change-notes/2023-06-02-delete-deps.md rename to javascript/ql/lib/change-notes/released/0.6.3.md index 9edbce9771e..c87e2deb626 100644 --- a/javascript/ql/lib/change-notes/2023-06-02-delete-deps.md +++ b/javascript/ql/lib/change-notes/released/0.6.3.md @@ -1,10 +1,19 @@ ---- -category: minorAnalysis ---- +## 0.6.3 + +### Major Analysis Improvements + +* Added support for TypeScript 5.1. + +### Minor Analysis Improvements + * Deleted many deprecated predicates and classes with uppercase `XML`, `JSON`, `URL`, `API`, etc. in their names. Use the PascalCased versions instead. * Deleted the deprecated `localTaintStep` predicate from `DataFlow.qll`. * Deleted the deprecated `stringStep`, and `localTaintStep` predicates from `TaintTracking.qll`. * Deleted many modules that started with a lowercase letter. Use the versions that start with an uppercase letter instead. * Deleted the deprecated `HtmlInjectionConfiguration` and `JQueryHtmlOrSelectorInjectionConfiguration` classes from `DomBasedXssQuery.qll`, use `Configuration` instead. * Deleted the deprecated `DefiningIdentifier` class and the `Definitions.qll` file it was in. Use `SsaDefinition` instead. -* Deleted the deprecated `definitionReaches`, `localDefinitionReaches`, `getAPseudoDefinitionInput`, `nextDefAfter`, and `localDefinitionOverwrites` predicates from `DefUse.qll`. \ No newline at end of file +* Deleted the deprecated `definitionReaches`, `localDefinitionReaches`, `getAPseudoDefinitionInput`, `nextDefAfter`, and `localDefinitionOverwrites` predicates from `DefUse.qll`. +* Updated the following JavaScript sink kind names. Any custom data extensions that use these sink kinds will need to be updated accordingly in order to continue working. + * `command-line-injection` to `command-injection` + * `credentials[kind]` to `credentials-kind` +* Added a support of sub modules in `node_modules`. diff --git a/javascript/ql/lib/codeql-pack.release.yml b/javascript/ql/lib/codeql-pack.release.yml index 5501a2a1cc5..b7dafe32c5d 100644 --- a/javascript/ql/lib/codeql-pack.release.yml +++ b/javascript/ql/lib/codeql-pack.release.yml @@ -1,2 +1,2 @@ --- -lastReleaseVersion: 0.6.2 +lastReleaseVersion: 0.6.3 diff --git a/javascript/ql/lib/qlpack.yml b/javascript/ql/lib/qlpack.yml index 52962f549b0..bd3f17d627f 100644 --- a/javascript/ql/lib/qlpack.yml +++ b/javascript/ql/lib/qlpack.yml @@ -1,11 +1,12 @@ name: codeql/javascript-all -version: 0.6.3-dev +version: 0.6.4-dev groups: javascript dbscheme: semmlecode.javascript.dbscheme extractor: javascript library: true upgrades: upgrades dependencies: + codeql/mad: ${workspace} codeql/regex: ${workspace} codeql/tutorial: ${workspace} codeql/util: ${workspace} diff --git a/javascript/ql/lib/semmle/javascript/frameworks/data/internal/ApiGraphModels.qll b/javascript/ql/lib/semmle/javascript/frameworks/data/internal/ApiGraphModels.qll index 227f4ea22fb..2e598711fcc 100644 --- a/javascript/ql/lib/semmle/javascript/frameworks/data/internal/ApiGraphModels.qll +++ b/javascript/ql/lib/semmle/javascript/frameworks/data/internal/ApiGraphModels.qll @@ -643,6 +643,15 @@ module ModelOutput { baseNode = getInvocationFromPath(type, path) } + /** + * Holds if a `baseNode` is a callable identified by the `type,path` part of a summary row. + */ + cached + predicate resolvedSummaryRefBase(string type, string path, API::Node baseNode) { + summaryModel(type, path, _, _, _) and + baseNode = getNodeFromPath(type, path) + } + /** * Holds if `node` is seen as an instance of `type` due to a type definition * contributed by a CSV model. @@ -653,6 +662,17 @@ module ModelOutput { import Cached import Specific::ModelOutputSpecific + private import codeql.mad.ModelValidation as SharedModelVal + + private module KindValConfig implements SharedModelVal::KindValidationConfigSig { + predicate summaryKind(string kind) { summaryModel(_, _, _, _, kind) } + + predicate sinkKind(string kind) { sinkModel(_, _, kind) } + + predicate sourceKind(string kind) { sourceModel(_, _, kind) } + } + + private module KindVal = SharedModelVal::KindValidation; /** * Gets an error message relating to an invalid CSV row in a model. @@ -698,5 +718,8 @@ module ModelOutput { not isValidNoArgumentTokenInIdentifyingAccessPath(token.getName()) and result = "Invalid token '" + token + "' is missing its arguments, in access path: " + path ) + or + // Check for invalid model kinds + result = KindVal::getInvalidModelKind() } } diff --git a/javascript/ql/src/CHANGELOG.md b/javascript/ql/src/CHANGELOG.md index eb914577876..0194f6f1c4a 100644 --- a/javascript/ql/src/CHANGELOG.md +++ b/javascript/ql/src/CHANGELOG.md @@ -1,3 +1,11 @@ +## 0.6.3 + +### Minor Analysis Improvements + +* Fixed an issue where calls to a method named `search` would lead to false positive alerts related to regular expressions. + This happened when the call was incorrectly seen as a call to `String.prototype.search`, since this function converts its first argument + to a regular expression. The analysis is now more restrictive about when to treat `search` calls as regular expression sinks. + ## 0.6.2 ### Major Analysis Improvements diff --git a/javascript/ql/src/Security/CWE-022/ZipSlip.qhelp b/javascript/ql/src/Security/CWE-022/ZipSlip.qhelp index d9dc0fb67a8..2318ad859f0 100644 --- a/javascript/ql/src/Security/CWE-022/ZipSlip.qhelp +++ b/javascript/ql/src/Security/CWE-022/ZipSlip.qhelp @@ -4,16 +4,16 @@ -

Extracting files from a malicious zip archive without validating that the destination file path -is within the destination directory can cause files outside the destination directory to be -overwritten, due to the possible presence of directory traversal elements (..) in +

Extracting files from a malicious zip file, or similar type of archive, +is at risk of directory traversal attacks if filenames from the archive are +not properly validated. archive paths.

Zip archives contain archive entries representing each file in the archive. These entries include a file path for the entry, but these file paths are not restricted and may contain unexpected special elements such as the directory traversal element (..). If these -file paths are used to determine an output file to write the contents of the archive item to, then -the file may be written to an unexpected location. This can result in sensitive information being +file paths are used to create a filesystem path, then a file operation may happen in an +unexpected location. This can result in sensitive information being revealed or deleted, or an attacker being able to influence behavior by modifying unexpected files.

diff --git a/javascript/ql/src/Security/CWE-022/ZipSlip.ql b/javascript/ql/src/Security/CWE-022/ZipSlip.ql index 0380031ad66..aef13830eb1 100644 --- a/javascript/ql/src/Security/CWE-022/ZipSlip.ql +++ b/javascript/ql/src/Security/CWE-022/ZipSlip.ql @@ -1,8 +1,8 @@ /** - * @name Arbitrary file write during zip extraction ("Zip Slip") - * @description Extracting files from a malicious zip archive without validating that the - * destination file path is within the destination directory can cause files outside - * the destination directory to be overwritten. + * @name Arbitrary file access during archive extraction ("Zip Slip") + * @description Extracting files from a malicious ZIP file, or similar type of archive, without + * validating that the destination file path is within the destination directory + * can allow an attacker to unexpectedly gain access to resources. * @kind path-problem * @id js/zipslip * @problem.severity error diff --git a/javascript/ql/src/Security/CWE-094/examples/comment_issue_good.yml b/javascript/ql/src/Security/CWE-094/examples/comment_issue_good.yml index 22d6554076c..07254a8b204 100644 --- a/javascript/ql/src/Security/CWE-094/examples/comment_issue_good.yml +++ b/javascript/ql/src/Security/CWE-094/examples/comment_issue_good.yml @@ -7,4 +7,4 @@ jobs: - env: BODY: ${{ github.event.issue.body }} run: | - echo '$BODY' \ No newline at end of file + echo "$BODY" diff --git a/javascript/ql/src/Security/CWE-798/HardcodedCredentials.qhelp b/javascript/ql/src/Security/CWE-798/HardcodedCredentials.qhelp index 3fd37c3245a..adcd6fc4715 100644 --- a/javascript/ql/src/Security/CWE-798/HardcodedCredentials.qhelp +++ b/javascript/ql/src/Security/CWE-798/HardcodedCredentials.qhelp @@ -21,6 +21,23 @@

+ +

+ The following code example connects to an HTTP request using an hard-codes authentication header: +

+ + + +

+ Instead, user name and password can be supplied through the environment variables + username and password, which can be set externally without hard-coding + credentials in the source code. +

+ + + +
+

The following code example connects to a Postgres database using the pg package diff --git a/javascript/ql/src/Security/CWE-798/examples/HardcodedCredentialsHttpRequest.js b/javascript/ql/src/Security/CWE-798/examples/HardcodedCredentialsHttpRequest.js new file mode 100644 index 00000000000..097e9f7b1a6 --- /dev/null +++ b/javascript/ql/src/Security/CWE-798/examples/HardcodedCredentialsHttpRequest.js @@ -0,0 +1,18 @@ +let base64 = require('base-64'); + +let url = 'http://example.org/auth'; +let username = 'user'; +let password = 'passwd'; + +let headers = new Headers(); + +headers.append('Content-Type', 'text/json'); +headers.append('Authorization', 'Basic' + base64.encode(username + ":" + password)); + +fetch(url, { + method:'GET', + headers: headers + }) +.then(response => response.json()) +.then(json => console.log(json)) +.done(); diff --git a/javascript/ql/src/Security/CWE-798/examples/HardcodedCredentialsHttpRequestFixed.js b/javascript/ql/src/Security/CWE-798/examples/HardcodedCredentialsHttpRequestFixed.js new file mode 100644 index 00000000000..1747d460dde --- /dev/null +++ b/javascript/ql/src/Security/CWE-798/examples/HardcodedCredentialsHttpRequestFixed.js @@ -0,0 +1,18 @@ +let base64 = require('base-64'); + +let url = 'http://example.org/auth'; +let username = process.env.USERNAME; +let password = process.env.PASSWORD; + +let headers = new Headers(); + +headers.append('Content-Type', 'text/json'); +headers.append('Authorization', 'Basic' + base64.encode(username + ":" + password)); + +fetch(url, { + method:'GET', + headers: headers + }) +.then(response => response.json()) +.then(json => console.log(json)) +.done(); diff --git a/javascript/ql/src/change-notes/2023-06-16-zipslip-rename.md b/javascript/ql/src/change-notes/2023-06-16-zipslip-rename.md new file mode 100644 index 00000000000..3a0654e642e --- /dev/null +++ b/javascript/ql/src/change-notes/2023-06-16-zipslip-rename.md @@ -0,0 +1,4 @@ +--- +category: fix +--- +* The query "Arbitrary file write during zip extraction ("Zip Slip")" (`js/zipslip`) has been renamed to "Arbitrary file access during archive extraction ("Zip Slip")." diff --git a/javascript/ql/src/change-notes/2023-06-01-restrict-regex-search-function.md b/javascript/ql/src/change-notes/released/0.6.3.md similarity index 90% rename from javascript/ql/src/change-notes/2023-06-01-restrict-regex-search-function.md rename to javascript/ql/src/change-notes/released/0.6.3.md index a43aebff717..3b5d43026f8 100644 --- a/javascript/ql/src/change-notes/2023-06-01-restrict-regex-search-function.md +++ b/javascript/ql/src/change-notes/released/0.6.3.md @@ -1,6 +1,7 @@ ---- -category: minorAnalysis ---- +## 0.6.3 + +### Minor Analysis Improvements + * Fixed an issue where calls to a method named `search` would lead to false positive alerts related to regular expressions. This happened when the call was incorrectly seen as a call to `String.prototype.search`, since this function converts its first argument to a regular expression. The analysis is now more restrictive about when to treat `search` calls as regular expression sinks. diff --git a/javascript/ql/src/codeql-pack.release.yml b/javascript/ql/src/codeql-pack.release.yml index 5501a2a1cc5..b7dafe32c5d 100644 --- a/javascript/ql/src/codeql-pack.release.yml +++ b/javascript/ql/src/codeql-pack.release.yml @@ -1,2 +1,2 @@ --- -lastReleaseVersion: 0.6.2 +lastReleaseVersion: 0.6.3 diff --git a/javascript/ql/src/qlpack.yml b/javascript/ql/src/qlpack.yml index 10e071e417c..a1a1ed2b4f2 100644 --- a/javascript/ql/src/qlpack.yml +++ b/javascript/ql/src/qlpack.yml @@ -1,5 +1,5 @@ name: codeql/javascript-queries -version: 0.6.3-dev +version: 0.6.4-dev groups: - javascript - queries diff --git a/misc/suite-helpers/CHANGELOG.md b/misc/suite-helpers/CHANGELOG.md index 46787616efa..9571c393549 100644 --- a/misc/suite-helpers/CHANGELOG.md +++ b/misc/suite-helpers/CHANGELOG.md @@ -1,3 +1,7 @@ +## 0.5.3 + +No user-facing changes. + ## 0.5.2 No user-facing changes. diff --git a/misc/suite-helpers/change-notes/released/0.5.3.md b/misc/suite-helpers/change-notes/released/0.5.3.md new file mode 100644 index 00000000000..e97503053f0 --- /dev/null +++ b/misc/suite-helpers/change-notes/released/0.5.3.md @@ -0,0 +1,3 @@ +## 0.5.3 + +No user-facing changes. diff --git a/misc/suite-helpers/codeql-pack.release.yml b/misc/suite-helpers/codeql-pack.release.yml index 2d9d3f587f8..2164e038a5d 100644 --- a/misc/suite-helpers/codeql-pack.release.yml +++ b/misc/suite-helpers/codeql-pack.release.yml @@ -1,2 +1,2 @@ --- -lastReleaseVersion: 0.5.2 +lastReleaseVersion: 0.5.3 diff --git a/misc/suite-helpers/qlpack.yml b/misc/suite-helpers/qlpack.yml index b6fbcda7201..f07f050124a 100644 --- a/misc/suite-helpers/qlpack.yml +++ b/misc/suite-helpers/qlpack.yml @@ -1,3 +1,3 @@ name: codeql/suite-helpers -version: 0.5.3-dev +version: 0.5.4-dev groups: shared diff --git a/python/downgrades/qlpack.yml b/python/downgrades/qlpack.yml index 12755ccb199..0c96e12e7cf 100644 --- a/python/downgrades/qlpack.yml +++ b/python/downgrades/qlpack.yml @@ -2,3 +2,4 @@ name: codeql/python-downgrades groups: python downgrades: . library: true +warnOnImplicitThis: true diff --git a/python/ql/consistency-queries/qlpack.yml b/python/ql/consistency-queries/qlpack.yml index c3433e9fcd6..f74d964dd91 100644 --- a/python/ql/consistency-queries/qlpack.yml +++ b/python/ql/consistency-queries/qlpack.yml @@ -3,3 +3,4 @@ groups: [python, test, consistency-queries] dependencies: codeql/python-all: ${workspace} extractor: python +warnOnImplicitThis: true diff --git a/python/ql/examples/qlpack.yml b/python/ql/examples/qlpack.yml index 96edb7bd844..b3e268d26d6 100644 --- a/python/ql/examples/qlpack.yml +++ b/python/ql/examples/qlpack.yml @@ -4,3 +4,4 @@ groups: - examples dependencies: codeql/python-all: ${workspace} +warnOnImplicitThis: true diff --git a/python/ql/lib/CHANGELOG.md b/python/ql/lib/CHANGELOG.md index 91f53df486b..3bfc2ddf115 100644 --- a/python/ql/lib/CHANGELOG.md +++ b/python/ql/lib/CHANGELOG.md @@ -1,3 +1,7 @@ +## 0.9.3 + +No user-facing changes. + ## 0.9.2 ### Minor Analysis Improvements diff --git a/python/ql/lib/change-notes/2023-05-30-typetracking-via-flow-summaries.md b/python/ql/lib/change-notes/2023-05-30-typetracking-via-flow-summaries.md new file mode 100644 index 00000000000..11c01629987 --- /dev/null +++ b/python/ql/lib/change-notes/2023-05-30-typetracking-via-flow-summaries.md @@ -0,0 +1,4 @@ +--- +category: minorAnalysis +--- +* Type tracking is now aware of flow summaries. This leads to a richer API graph, and may lead to more results in some queries. diff --git a/python/ql/lib/change-notes/2023-06-14-delete-deps.md b/python/ql/lib/change-notes/2023-06-14-delete-deps.md new file mode 100644 index 00000000000..16946163f5e --- /dev/null +++ b/python/ql/lib/change-notes/2023-06-14-delete-deps.md @@ -0,0 +1,4 @@ +--- +category: minorAnalysis +--- +* Deleted many models that used the old dataflow library, the new models can be found in the `python/ql/lib/semmle/python/frameworks` folder. \ No newline at end of file diff --git a/python/ql/lib/change-notes/2023-06-20-summaries-from-models.md b/python/ql/lib/change-notes/2023-06-20-summaries-from-models.md new file mode 100644 index 00000000000..feded1bb6c5 --- /dev/null +++ b/python/ql/lib/change-notes/2023-06-20-summaries-from-models.md @@ -0,0 +1,4 @@ +--- +category: feature +--- +* It is now possible to specify flow summaries in the format "MyPkg;Member[list_map];Argument[1].ListElement;Argument[0].Parameter[0];value" diff --git a/python/ql/lib/change-notes/released/0.9.3.md b/python/ql/lib/change-notes/released/0.9.3.md new file mode 100644 index 00000000000..1c859ebb6b3 --- /dev/null +++ b/python/ql/lib/change-notes/released/0.9.3.md @@ -0,0 +1,3 @@ +## 0.9.3 + +No user-facing changes. diff --git a/python/ql/lib/codeql-pack.release.yml b/python/ql/lib/codeql-pack.release.yml index e1eda519435..7af7247cbb0 100644 --- a/python/ql/lib/codeql-pack.release.yml +++ b/python/ql/lib/codeql-pack.release.yml @@ -1,2 +1,2 @@ --- -lastReleaseVersion: 0.9.2 +lastReleaseVersion: 0.9.3 diff --git a/python/ql/lib/qlpack.yml b/python/ql/lib/qlpack.yml index 9d4522d5f58..5701aac8cbf 100644 --- a/python/ql/lib/qlpack.yml +++ b/python/ql/lib/qlpack.yml @@ -1,11 +1,12 @@ name: codeql/python-all -version: 0.9.3-dev +version: 0.9.4-dev groups: python dbscheme: semmlecode.python.dbscheme extractor: python library: true upgrades: upgrades dependencies: + codeql/mad: ${workspace} codeql/regex: ${workspace} codeql/tutorial: ${workspace} codeql/util: ${workspace} diff --git a/python/ql/lib/semmle/python/dataflow/new/FlowSummary.qll b/python/ql/lib/semmle/python/dataflow/new/FlowSummary.qll index 5e82700bd0e..8b80e13d06d 100644 --- a/python/ql/lib/semmle/python/dataflow/new/FlowSummary.qll +++ b/python/ql/lib/semmle/python/dataflow/new/FlowSummary.qll @@ -90,39 +90,32 @@ abstract class SummarizedCallable extends LibraryCallable, Impl::Public::Summari } class RequiredSummaryComponentStack = Impl::Public::RequiredSummaryComponentStack; -// // This gives access to getNodeFromPath, which is not constrained to `CallNode`s -// // as `resolvedSummaryBase` is. -// private import semmle.python.frameworks.data.internal.ApiGraphModels as AGM -// -// private class SummarizedCallableFromModel extends SummarizedCallable { -// string package; -// string type; -// string path; -// SummarizedCallableFromModel() { -// ModelOutput::relevantSummaryModel(package, type, path, _, _, _) and -// this = package + ";" + type + ";" + path -// } -// override CallCfgNode getACall() { -// exists(API::CallNode base | -// ModelOutput::resolvedSummaryBase(package, type, path, base) and -// result = base.getACall() -// ) -// } -// override ArgumentNode getACallback() { -// exists(API::Node base | -// base = AGM::getNodeFromPath(package, type, path) and -// result = base.getAValueReachableFromSource() -// ) -// } -// override predicate propagatesFlowExt(string input, string output, boolean preservesValue) { -// exists(string kind | -// ModelOutput::relevantSummaryModel(package, type, path, input, output, kind) -// | -// kind = "value" and -// preservesValue = true -// or -// kind = "taint" and -// preservesValue = false -// ) -// } -// } + +private class SummarizedCallableFromModel extends SummarizedCallable { + string type; + string path; + + SummarizedCallableFromModel() { + ModelOutput::relevantSummaryModel(type, path, _, _, _) and + this = type + ";" + path + } + + override CallCfgNode getACall() { ModelOutput::resolvedSummaryBase(type, path, result) } + + override ArgumentNode getACallback() { + exists(API::Node base | + ModelOutput::resolvedSummaryRefBase(type, path, base) and + result = base.getAValueReachableFromSource() + ) + } + + override predicate propagatesFlowExt(string input, string output, boolean preservesValue) { + exists(string kind | ModelOutput::relevantSummaryModel(type, path, input, output, kind) | + kind = "value" and + preservesValue = true + or + kind = "taint" and + preservesValue = false + ) + } +} diff --git a/python/ql/lib/semmle/python/dataflow/new/internal/DataFlowDispatch.qll b/python/ql/lib/semmle/python/dataflow/new/internal/DataFlowDispatch.qll index 3084983a605..ba451a21fdf 100644 --- a/python/ql/lib/semmle/python/dataflow/new/internal/DataFlowDispatch.qll +++ b/python/ql/lib/semmle/python/dataflow/new/internal/DataFlowDispatch.qll @@ -251,6 +251,9 @@ abstract class LibraryCallable extends string { /** Gets a call to this library callable. */ abstract CallCfgNode getACall(); + /** Same as `getACall` but without referring to the call graph or API graph. */ + CallCfgNode getACallSimple() { none() } + /** Gets a data-flow node, where this library callable is used as a call-back. */ abstract ArgumentNode getACallback(); } diff --git a/python/ql/lib/semmle/python/dataflow/new/internal/DataFlowImpl.qll b/python/ql/lib/semmle/python/dataflow/new/internal/DataFlowImpl.qll index 984c5ae2018..284fff191ae 100644 --- a/python/ql/lib/semmle/python/dataflow/new/internal/DataFlowImpl.qll +++ b/python/ql/lib/semmle/python/dataflow/new/internal/DataFlowImpl.qll @@ -2021,7 +2021,8 @@ module Impl { FlowCheckNode() { castNode(this.asNode()) or clearsContentCached(this.asNode(), _) or - expectsContentCached(this.asNode(), _) + expectsContentCached(this.asNode(), _) or + neverSkipInPathGraph(this.asNode()) } } diff --git a/python/ql/lib/semmle/python/dataflow/new/internal/DataFlowPrivate.qll b/python/ql/lib/semmle/python/dataflow/new/internal/DataFlowPrivate.qll index 7f907ca84e8..29504b6aa38 100644 --- a/python/ql/lib/semmle/python/dataflow/new/internal/DataFlowPrivate.qll +++ b/python/ql/lib/semmle/python/dataflow/new/internal/DataFlowPrivate.qll @@ -486,6 +486,14 @@ class DataFlowType extends TDataFlowType { /** A node that performs a type cast. */ class CastNode extends Node { + CastNode() { none() } +} + +/** + * Holds if `n` should never be skipped over in the `PathGraph` and in path + * explanations. + */ +predicate neverSkipInPathGraph(Node n) { // We include read- and store steps here to force them to be // shown in path explanations. // This hack is necessary, because we have included some of these @@ -494,7 +502,7 @@ class CastNode extends Node { // We should revert this once, we can remove this steps from the // default taint steps; this should be possible once we have // implemented flow summaries and recursive content. - CastNode() { readStep(_, _, this) or storeStep(_, _, this) } + readStep(_, _, n) or storeStep(_, _, n) } /** diff --git a/python/ql/lib/semmle/python/dataflow/new/internal/SummaryTypeTracker.qll b/python/ql/lib/semmle/python/dataflow/new/internal/SummaryTypeTracker.qll new file mode 100644 index 00000000000..9c6f841651d --- /dev/null +++ b/python/ql/lib/semmle/python/dataflow/new/internal/SummaryTypeTracker.qll @@ -0,0 +1,391 @@ +/** + * Provides the implementation of type tracking steps through flow summaries. + * To use this, you must implement the `Input` signature. You can then use the predicates in the `Output` + * signature to implement the predicates of the same names inside `TypeTrackerSpecific.qll`. + */ + +/** The classes and predicates needed to generate type-tracking steps from summaries. */ +signature module Input { + // Dataflow nodes + class Node; + + // Content + class TypeTrackerContent; + + class TypeTrackerContentFilter; + + // Relating content and filters + /** + * Gets a content filter to use for a `WithoutContent[content]` step, (data is not allowed to be stored in `content`) + * or has no result if + * the step should be treated as ordinary flow. + * + * `WithoutContent` is often used to perform strong updates on individual collection elements, but for + * type-tracking this is rarely beneficial and quite expensive. However, `WithoutContent` can be quite useful + * for restricting the type of an object, and in these cases we translate it to a filter. + */ + TypeTrackerContentFilter getFilterFromWithoutContentStep(TypeTrackerContent content); + + /** + * Gets a content filter to use for a `WithContent[content]` step, (data must be stored in `content`) + * or has no result if + * the step cannot be handled by type-tracking. + * + * `WithContent` is often used to perform strong updates on individual collection elements (or rather + * to preserve those that didn't get updated). But for type-tracking this is rarely beneficial and quite expensive. + * However, `WithContent` can be quite useful for restricting the type of an object, and in these cases we translate it to a filter. + */ + TypeTrackerContentFilter getFilterFromWithContentStep(TypeTrackerContent content); + + // Summaries and their stacks + class SummaryComponent; + + class SummaryComponentStack { + SummaryComponent head(); + } + + /** Gets a singleton stack containing `component`. */ + SummaryComponentStack singleton(SummaryComponent component); + + /** + * Gets the stack obtained by pushing `head` onto `tail`. + */ + SummaryComponentStack push(SummaryComponent head, SummaryComponentStack tail); + + /** Gets a singleton stack representing a return. */ + SummaryComponent return(); + + // Relating content to summaries + /** Gets a summary component for content `c`. */ + SummaryComponent content(TypeTrackerContent contents); + + /** Gets a summary component where data is not allowed to be stored in `contents`. */ + SummaryComponent withoutContent(TypeTrackerContent contents); + + /** Gets a summary component where data must be stored in `contents`. */ + SummaryComponent withContent(TypeTrackerContent contents); + + // Callables + class SummarizedCallable { + predicate propagatesFlow( + SummaryComponentStack input, SummaryComponentStack output, boolean preservesValue + ); + } + + // Relating nodes to summaries + /** Gets a dataflow node respresenting the argument of `call` indicated by `arg`. */ + Node argumentOf(Node call, SummaryComponent arg); + + /** Gets a dataflow node respresenting the parameter of `callable` indicated by `param`. */ + Node parameterOf(Node callable, SummaryComponent param); + + /** Gets a dataflow node respresenting the return of `callable` indicated by `return`. */ + Node returnOf(Node callable, SummaryComponent return); + + // Relating callables to nodes + /** Gets a dataflow node respresenting a call to `callable`. */ + Node callTo(SummarizedCallable callable); +} + +/** + * The predicates provided by a summary type tracker. + * These are meant to be used in `TypeTrackerSpecific.qll` + * inside the predicates of the same names. + */ +signature module Output { + /** + * Holds if there is a level step from `nodeFrom` to `nodeTo`, which does not depend on the call graph. + */ + predicate levelStepNoCall(I::Node nodeFrom, I::Node nodeTo); + + /** + * Holds if `nodeTo` is the result of accessing the `content` content of `nodeFrom`. + */ + predicate basicLoadStep(I::Node nodeFrom, I::Node nodeTo, I::TypeTrackerContent content); + + /** + * Holds if `nodeFrom` is being written to the `content` content of the object in `nodeTo`. + */ + predicate basicStoreStep(I::Node nodeFrom, I::Node nodeTo, I::TypeTrackerContent content); + + /** + * Holds if the `loadContent` of `nodeFrom` is stored in the `storeContent` of `nodeTo`. + */ + predicate basicLoadStoreStep( + I::Node nodeFrom, I::Node nodeTo, I::TypeTrackerContent loadContent, + I::TypeTrackerContent storeContent + ); + + /** + * Holds if type-tracking should step from `nodeFrom` to `nodeTo` but block flow of contents matched by `filter` through here. + */ + predicate basicWithoutContentStep( + I::Node nodeFrom, I::Node nodeTo, I::TypeTrackerContentFilter filter + ); + + /** + * Holds if type-tracking should step from `nodeFrom` to `nodeTo` if inside a content matched by `filter`. + */ + predicate basicWithContentStep( + I::Node nodeFrom, I::Node nodeTo, I::TypeTrackerContentFilter filter + ); +} + +/** + * Implementation of the summary type tracker, that is type tracking through flow summaries. + */ +module SummaryFlow implements Output { + pragma[nomagic] + private predicate isNonLocal(I::SummaryComponent component) { + component = I::content(_) + or + component = I::withContent(_) + } + + pragma[nomagic] + private predicate hasLoadSummary( + I::SummarizedCallable callable, I::TypeTrackerContent contents, I::SummaryComponentStack input, + I::SummaryComponentStack output + ) { + callable.propagatesFlow(I::push(I::content(contents), input), output, true) and + not isNonLocal(input.head()) and + not isNonLocal(output.head()) + } + + pragma[nomagic] + private predicate hasStoreSummary( + I::SummarizedCallable callable, I::TypeTrackerContent contents, I::SummaryComponentStack input, + I::SummaryComponentStack output + ) { + not isNonLocal(input.head()) and + not isNonLocal(output.head()) and + ( + callable.propagatesFlow(input, I::push(I::content(contents), output), true) + or + // Allow the input to start with an arbitrary WithoutContent[X]. + // Since type-tracking only tracks one content deep, and we're about to store into another content, + // we're already preventing the input from being in a content. + callable + .propagatesFlow(I::push(I::withoutContent(_), input), + I::push(I::content(contents), output), true) + ) + } + + pragma[nomagic] + private predicate hasLoadStoreSummary( + I::SummarizedCallable callable, I::TypeTrackerContent loadContents, + I::TypeTrackerContent storeContents, I::SummaryComponentStack input, + I::SummaryComponentStack output + ) { + callable + .propagatesFlow(I::push(I::content(loadContents), input), + I::push(I::content(storeContents), output), true) and + not isNonLocal(input.head()) and + not isNonLocal(output.head()) + } + + pragma[nomagic] + private predicate hasWithoutContentSummary( + I::SummarizedCallable callable, I::TypeTrackerContentFilter filter, + I::SummaryComponentStack input, I::SummaryComponentStack output + ) { + exists(I::TypeTrackerContent content | + callable.propagatesFlow(I::push(I::withoutContent(content), input), output, true) and + filter = I::getFilterFromWithoutContentStep(content) and + not isNonLocal(input.head()) and + not isNonLocal(output.head()) and + input != output + ) + } + + pragma[nomagic] + private predicate hasWithContentSummary( + I::SummarizedCallable callable, I::TypeTrackerContentFilter filter, + I::SummaryComponentStack input, I::SummaryComponentStack output + ) { + exists(I::TypeTrackerContent content | + callable.propagatesFlow(I::push(I::withContent(content), input), output, true) and + filter = I::getFilterFromWithContentStep(content) and + not isNonLocal(input.head()) and + not isNonLocal(output.head()) and + input != output + ) + } + + private predicate componentLevelStep(I::SummaryComponent component) { + exists(I::TypeTrackerContent content | + component = I::withoutContent(content) and + not exists(I::getFilterFromWithoutContentStep(content)) + ) + } + + /** + * Gets a data flow `I::Node` corresponding an argument or return value of `call`, + * as specified by `component`. + */ + bindingset[call, component] + private I::Node evaluateSummaryComponentLocal(I::Node call, I::SummaryComponent component) { + result = I::argumentOf(call, component) + or + component = I::return() and + result = call + } + + /** + * Holds if `callable` is relevant for type-tracking and we therefore want `stack` to + * be evaluated locally at its call sites. + */ + pragma[nomagic] + private predicate dependsOnSummaryComponentStack( + I::SummarizedCallable callable, I::SummaryComponentStack stack + ) { + exists(I::callTo(callable)) and + ( + callable.propagatesFlow(stack, _, true) + or + callable.propagatesFlow(_, stack, true) + or + // include store summaries as they may skip an initial step at the input + hasStoreSummary(callable, _, stack, _) + ) + or + dependsOnSummaryComponentStackCons(callable, _, stack) + } + + pragma[nomagic] + private predicate dependsOnSummaryComponentStackCons( + I::SummarizedCallable callable, I::SummaryComponent head, I::SummaryComponentStack tail + ) { + dependsOnSummaryComponentStack(callable, I::push(head, tail)) + } + + pragma[nomagic] + private predicate dependsOnSummaryComponentStackConsLocal( + I::SummarizedCallable callable, I::SummaryComponent head, I::SummaryComponentStack tail + ) { + dependsOnSummaryComponentStackCons(callable, head, tail) and + not isNonLocal(head) + } + + pragma[nomagic] + private predicate dependsOnSummaryComponentStackLeaf( + I::SummarizedCallable callable, I::SummaryComponent leaf + ) { + dependsOnSummaryComponentStack(callable, I::singleton(leaf)) + } + + /** + * Gets a data flow I::Node corresponding to the local input or output of `call` + * identified by `stack`, if possible. + */ + pragma[nomagic] + private I::Node evaluateSummaryComponentStackLocal( + I::SummarizedCallable callable, I::Node call, I::SummaryComponentStack stack + ) { + exists(I::SummaryComponent component | + dependsOnSummaryComponentStackLeaf(callable, component) and + stack = I::singleton(component) and + call = I::callTo(callable) and + result = evaluateSummaryComponentLocal(call, component) + ) + or + exists(I::Node prev, I::SummaryComponent head, I::SummaryComponentStack tail | + prev = evaluateSummaryComponentStackLocal(callable, call, tail) and + dependsOnSummaryComponentStackConsLocal(callable, pragma[only_bind_into](head), + pragma[only_bind_out](tail)) and + stack = I::push(pragma[only_bind_out](head), pragma[only_bind_out](tail)) + | + result = I::parameterOf(prev, head) + or + result = I::returnOf(prev, head) + or + componentLevelStep(head) and + result = prev + ) + } + + // Implement Output + predicate levelStepNoCall(I::Node nodeFrom, I::Node nodeTo) { + exists( + I::SummarizedCallable callable, I::Node call, I::SummaryComponentStack input, + I::SummaryComponentStack output + | + callable.propagatesFlow(input, output, true) and + call = I::callTo(callable) and + nodeFrom = evaluateSummaryComponentStackLocal(callable, call, input) and + nodeTo = evaluateSummaryComponentStackLocal(callable, call, output) + ) + } + + predicate basicLoadStep(I::Node nodeFrom, I::Node nodeTo, I::TypeTrackerContent content) { + exists( + I::SummarizedCallable callable, I::Node call, I::SummaryComponentStack input, + I::SummaryComponentStack output + | + hasLoadSummary(callable, content, pragma[only_bind_into](input), + pragma[only_bind_into](output)) and + call = I::callTo(callable) and + nodeFrom = evaluateSummaryComponentStackLocal(callable, call, input) and + nodeTo = evaluateSummaryComponentStackLocal(callable, call, output) + ) + } + + predicate basicStoreStep(I::Node nodeFrom, I::Node nodeTo, I::TypeTrackerContent content) { + exists( + I::SummarizedCallable callable, I::Node call, I::SummaryComponentStack input, + I::SummaryComponentStack output + | + hasStoreSummary(callable, content, pragma[only_bind_into](input), + pragma[only_bind_into](output)) and + call = I::callTo(callable) and + nodeFrom = evaluateSummaryComponentStackLocal(callable, call, input) and + nodeTo = evaluateSummaryComponentStackLocal(callable, call, output) + ) + } + + predicate basicLoadStoreStep( + I::Node nodeFrom, I::Node nodeTo, I::TypeTrackerContent loadContent, + I::TypeTrackerContent storeContent + ) { + exists( + I::SummarizedCallable callable, I::Node call, I::SummaryComponentStack input, + I::SummaryComponentStack output + | + hasLoadStoreSummary(callable, loadContent, storeContent, pragma[only_bind_into](input), + pragma[only_bind_into](output)) and + call = I::callTo(callable) and + nodeFrom = evaluateSummaryComponentStackLocal(callable, call, input) and + nodeTo = evaluateSummaryComponentStackLocal(callable, call, output) + ) + } + + predicate basicWithoutContentStep( + I::Node nodeFrom, I::Node nodeTo, I::TypeTrackerContentFilter filter + ) { + exists( + I::SummarizedCallable callable, I::Node call, I::SummaryComponentStack input, + I::SummaryComponentStack output + | + hasWithoutContentSummary(callable, filter, pragma[only_bind_into](input), + pragma[only_bind_into](output)) and + call = I::callTo(callable) and + nodeFrom = evaluateSummaryComponentStackLocal(callable, call, input) and + nodeTo = evaluateSummaryComponentStackLocal(callable, call, output) + ) + } + + predicate basicWithContentStep( + I::Node nodeFrom, I::Node nodeTo, I::TypeTrackerContentFilter filter + ) { + exists( + I::SummarizedCallable callable, I::Node call, I::SummaryComponentStack input, + I::SummaryComponentStack output + | + hasWithContentSummary(callable, filter, pragma[only_bind_into](input), + pragma[only_bind_into](output)) and + call = I::callTo(callable) and + nodeFrom = evaluateSummaryComponentStackLocal(callable, call, input) and + nodeTo = evaluateSummaryComponentStackLocal(callable, call, output) + ) + } +} diff --git a/python/ql/lib/semmle/python/dataflow/new/internal/TypeTracker.qll b/python/ql/lib/semmle/python/dataflow/new/internal/TypeTracker.qll index 25521f5f1a5..001375b4dc5 100644 --- a/python/ql/lib/semmle/python/dataflow/new/internal/TypeTracker.qll +++ b/python/ql/lib/semmle/python/dataflow/new/internal/TypeTracker.qll @@ -55,10 +55,9 @@ private module Cached { ) } - pragma[nomagic] - private TypeTracker noContentTypeTracker(boolean hasCall) { - result = MkTypeTracker(hasCall, noContent()) - } + /** Gets a type tracker with no content and the call bit set to the given value. */ + cached + TypeTracker noContentTypeTracker(boolean hasCall) { result = MkTypeTracker(hasCall, noContent()) } /** Gets the summary resulting from appending `step` to type-tracking summary `tt`. */ cached @@ -235,17 +234,6 @@ private predicate stepProj(TypeTrackingNode nodeFrom, StepSummary summary) { step(nodeFrom, _, summary) } -bindingset[nodeFrom, t] -pragma[inline_late] -pragma[noopt] -private TypeTracker stepInlineLate(TypeTracker t, TypeTrackingNode nodeFrom, TypeTrackingNode nodeTo) { - exists(StepSummary summary | - stepProj(nodeFrom, summary) and - result = t.append(summary) and - step(nodeFrom, nodeTo, summary) - ) -} - private predicate smallstep(Node nodeFrom, TypeTrackingNode nodeTo, StepSummary summary) { smallstepNoCall(nodeFrom, nodeTo, summary) or @@ -257,17 +245,6 @@ private predicate smallstepProj(Node nodeFrom, StepSummary summary) { smallstep(nodeFrom, _, summary) } -bindingset[nodeFrom, t] -pragma[inline_late] -pragma[noopt] -private TypeTracker smallstepInlineLate(TypeTracker t, Node nodeFrom, Node nodeTo) { - exists(StepSummary summary | - smallstepProj(nodeFrom, summary) and - result = t.append(summary) and - smallstep(nodeFrom, nodeTo, summary) - ) -} - /** * Holds if `nodeFrom` is being written to the `content` of the object in `nodeTo`. * @@ -340,6 +317,8 @@ class StepSummary extends TStepSummary { /** Provides predicates for updating step summaries (`StepSummary`s). */ module StepSummary { + predicate append = Cached::append/2; + /** * Gets the summary that corresponds to having taken a forwards * inter-procedural step from `nodeFrom` to `nodeTo`. @@ -400,6 +379,35 @@ module StepSummary { } deprecated predicate localSourceStoreStep = flowsToStoreStep/3; + + /** Gets the step summary for a level step. */ + StepSummary levelStep() { result = LevelStep() } + + /** Gets the step summary for a call step. */ + StepSummary callStep() { result = CallStep() } + + /** Gets the step summary for a return step. */ + StepSummary returnStep() { result = ReturnStep() } + + /** Gets the step summary for storing into `content`. */ + StepSummary storeStep(TypeTrackerContent content) { result = StoreStep(content) } + + /** Gets the step summary for loading from `content`. */ + StepSummary loadStep(TypeTrackerContent content) { result = LoadStep(content) } + + /** Gets the step summary for loading from `load` and then storing into `store`. */ + StepSummary loadStoreStep(TypeTrackerContent load, TypeTrackerContent store) { + result = LoadStoreStep(load, store) + } + + /** Gets the step summary for a step that only permits contents matched by `filter`. */ + StepSummary withContent(ContentFilter filter) { result = WithContent(filter) } + + /** Gets the step summary for a step that blocks contents matched by `filter`. */ + StepSummary withoutContent(ContentFilter filter) { result = WithoutContent(filter) } + + /** Gets the step summary for a jump step. */ + StepSummary jumpStep() { result = JumpStep() } } /** @@ -501,9 +509,26 @@ class TypeTracker extends TTypeTracker { * Gets the summary that corresponds to having taken a forwards * heap and/or inter-procedural step from `nodeFrom` to `nodeTo`. */ - pragma[inline] + bindingset[nodeFrom, this] + pragma[inline_late] + pragma[noopt] TypeTracker step(TypeTrackingNode nodeFrom, TypeTrackingNode nodeTo) { - result = stepInlineLate(this, nodeFrom, nodeTo) + exists(StepSummary summary | + stepProj(nodeFrom, summary) and + result = this.append(summary) and + step(nodeFrom, nodeTo, summary) + ) + } + + bindingset[nodeFrom, this] + pragma[inline_late] + pragma[noopt] + private TypeTracker smallstepNoSimpleLocalFlowStep(Node nodeFrom, Node nodeTo) { + exists(StepSummary summary | + smallstepProj(nodeFrom, summary) and + result = this.append(summary) and + smallstep(nodeFrom, nodeTo, summary) + ) } /** @@ -532,7 +557,7 @@ class TypeTracker extends TTypeTracker { */ pragma[inline] TypeTracker smallstep(Node nodeFrom, Node nodeTo) { - result = smallstepInlineLate(this, nodeFrom, nodeTo) + result = this.smallstepNoSimpleLocalFlowStep(nodeFrom, nodeTo) or simpleLocalFlowStep(nodeFrom, nodeTo) and result = this @@ -545,6 +570,13 @@ module TypeTracker { * Gets a valid end point of type tracking. */ TypeTracker end() { result.end() } + + /** + * INTERNAL USE ONLY. + * + * Gets a valid end point of type tracking with the call bit set to the given value. + */ + predicate end = Cached::noContentTypeTracker/1; } pragma[nomagic] @@ -552,34 +584,10 @@ private predicate backStepProj(TypeTrackingNode nodeTo, StepSummary summary) { step(_, nodeTo, summary) } -bindingset[nodeTo, t] -pragma[inline_late] -pragma[noopt] -private TypeBackTracker backStepInlineLate( - TypeBackTracker t, TypeTrackingNode nodeFrom, TypeTrackingNode nodeTo -) { - exists(StepSummary summary | - backStepProj(nodeTo, summary) and - result = t.prepend(summary) and - step(nodeFrom, nodeTo, summary) - ) -} - private predicate backSmallstepProj(TypeTrackingNode nodeTo, StepSummary summary) { smallstep(_, nodeTo, summary) } -bindingset[nodeTo, t] -pragma[inline_late] -pragma[noopt] -private TypeBackTracker backSmallstepInlineLate(TypeBackTracker t, Node nodeFrom, Node nodeTo) { - exists(StepSummary summary | - backSmallstepProj(nodeTo, summary) and - result = t.prepend(summary) and - smallstep(nodeFrom, nodeTo, summary) - ) -} - /** * A summary of the steps needed to back-track a use of a value to a given dataflow node. * @@ -661,9 +669,26 @@ class TypeBackTracker extends TTypeBackTracker { * Gets the summary that corresponds to having taken a backwards * heap and/or inter-procedural step from `nodeTo` to `nodeFrom`. */ - pragma[inline] + bindingset[nodeTo, result] + pragma[inline_late] + pragma[noopt] TypeBackTracker step(TypeTrackingNode nodeFrom, TypeTrackingNode nodeTo) { - this = backStepInlineLate(result, nodeFrom, nodeTo) + exists(StepSummary summary | + backStepProj(nodeTo, summary) and + this = result.prepend(summary) and + step(nodeFrom, nodeTo, summary) + ) + } + + bindingset[nodeTo, result] + pragma[inline_late] + pragma[noopt] + private TypeBackTracker smallstepNoSimpleLocalFlowStep(Node nodeFrom, Node nodeTo) { + exists(StepSummary summary | + backSmallstepProj(nodeTo, summary) and + this = result.prepend(summary) and + smallstep(nodeFrom, nodeTo, summary) + ) } /** @@ -692,7 +717,7 @@ class TypeBackTracker extends TTypeBackTracker { */ pragma[inline] TypeBackTracker smallstep(Node nodeFrom, Node nodeTo) { - this = backSmallstepInlineLate(result, nodeFrom, nodeTo) + this = this.smallstepNoSimpleLocalFlowStep(nodeFrom, nodeTo) or simpleLocalFlowStep(nodeFrom, nodeTo) and this = result diff --git a/python/ql/lib/semmle/python/dataflow/new/internal/TypeTrackerSpecific.qll b/python/ql/lib/semmle/python/dataflow/new/internal/TypeTrackerSpecific.qll index 9e05b7869c5..bac194aae9e 100644 --- a/python/ql/lib/semmle/python/dataflow/new/internal/TypeTrackerSpecific.qll +++ b/python/ql/lib/semmle/python/dataflow/new/internal/TypeTrackerSpecific.qll @@ -61,7 +61,9 @@ predicate capturedJumpStep(Node nodeFrom, Node nodeTo) { predicate levelStepCall(Node nodeFrom, Node nodeTo) { none() } /** Holds if there is a level step from `nodeFrom` to `nodeTo`, which does not depend on the call graph. */ -predicate levelStepNoCall(Node nodeFrom, Node nodeTo) { none() } +predicate levelStepNoCall(Node nodeFrom, Node nodeTo) { + TypeTrackerSummaryFlow::levelStepNoCall(nodeFrom, nodeTo) +} /** * Gets the name of a possible piece of content. For Python, this is currently only attribute names, @@ -108,6 +110,12 @@ predicate basicStoreStep(Node nodeFrom, Node nodeTo, string content) { nodeFrom = a.getValue() and nodeTo = a.getObject() ) + or + exists(DataFlowPublic::ContentSet contents | + contents.(DataFlowPublic::AttributeContent).getAttribute() = content + | + TypeTrackerSummaryFlow::basicStoreStep(nodeFrom, nodeTo, contents) + ) } /** @@ -119,13 +127,24 @@ predicate basicLoadStep(Node nodeFrom, Node nodeTo, string content) { nodeFrom = a.getObject() and nodeTo = a ) + or + exists(DataFlowPublic::ContentSet contents | + contents.(DataFlowPublic::AttributeContent).getAttribute() = content + | + TypeTrackerSummaryFlow::basicLoadStep(nodeFrom, nodeTo, contents) + ) } /** * Holds if the `loadContent` of `nodeFrom` is stored in the `storeContent` of `nodeTo`. */ predicate basicLoadStoreStep(Node nodeFrom, Node nodeTo, string loadContent, string storeContent) { - none() + exists(DataFlowPublic::ContentSet loadContents, DataFlowPublic::ContentSet storeContents | + loadContents.(DataFlowPublic::AttributeContent).getAttribute() = loadContent and + storeContents.(DataFlowPublic::AttributeContent).getAttribute() = storeContent + | + TypeTrackerSummaryFlow::basicLoadStoreStep(nodeFrom, nodeTo, loadContents, storeContents) + ) } /** @@ -144,3 +163,93 @@ predicate basicWithContentStep(Node nodeFrom, Node nodeTo, ContentFilter filter) class Boolean extends boolean { Boolean() { this = true or this = false } } + +private import SummaryTypeTracker as SummaryTypeTracker +private import semmle.python.dataflow.new.FlowSummary as FlowSummary +private import semmle.python.dataflow.new.internal.DataFlowDispatch as DataFlowDispatch + +pragma[noinline] +private predicate argumentPositionMatch( + DataFlowPublic::CallCfgNode call, DataFlowPublic::Node arg, + DataFlowDispatch::ParameterPosition ppos +) { + exists(DataFlowDispatch::ArgumentPosition apos | + DataFlowDispatch::parameterMatch(ppos, apos) and + DataFlowDispatch::normalCallArg(call.getNode(), arg, apos) + ) +} + +private module SummaryTypeTrackerInput implements SummaryTypeTracker::Input { + // Dataflow nodes + class Node = DataFlowPublic::Node; + + // Content + class TypeTrackerContent = DataFlowPublic::ContentSet; + + class TypeTrackerContentFilter = ContentFilter; + + TypeTrackerContentFilter getFilterFromWithoutContentStep(TypeTrackerContent content) { none() } + + TypeTrackerContentFilter getFilterFromWithContentStep(TypeTrackerContent content) { none() } + + // Callables + class SummarizedCallable = FlowSummary::SummarizedCallable; + + // Summaries and their stacks + class SummaryComponent = FlowSummary::SummaryComponent; + + class SummaryComponentStack = FlowSummary::SummaryComponentStack; + + predicate singleton = FlowSummary::SummaryComponentStack::singleton/1; + + predicate push = FlowSummary::SummaryComponentStack::push/2; + + // Relating content to summaries + predicate content = FlowSummary::SummaryComponent::content/1; + + SummaryComponent withoutContent(TypeTrackerContent contents) { none() } + + SummaryComponent withContent(TypeTrackerContent contents) { none() } + + predicate return = FlowSummary::SummaryComponent::return/0; + + // Relating nodes to summaries + Node argumentOf(Node call, SummaryComponent arg) { + exists(DataFlowDispatch::ParameterPosition pos | + arg = FlowSummary::SummaryComponent::argument(pos) and + argumentPositionMatch(call, result, pos) + ) + } + + Node parameterOf(Node callable, SummaryComponent param) { + exists( + DataFlowDispatch::ArgumentPosition apos, DataFlowDispatch::ParameterPosition ppos, Parameter p + | + param = FlowSummary::SummaryComponent::parameter(apos) and + DataFlowDispatch::parameterMatch(ppos, apos) and + // pick the SsaNode rather than the CfgNode + result.asVar().getDefinition().(ParameterDefinition).getParameter() = p and + ( + exists(int i | ppos.isPositional(i) | + p = callable.getALocalSource().asExpr().(CallableExpr).getInnerScope().getArg(i) + ) + or + exists(string name | ppos.isKeyword(name) | + p = callable.getALocalSource().asExpr().(CallableExpr).getInnerScope().getArgByName(name) + ) + ) + ) + } + + Node returnOf(Node callable, SummaryComponent return) { + return = FlowSummary::SummaryComponent::return() and + // `result` should be the return value of a callable expression (lambda or function) referenced by `callable` + result.asCfgNode() = + callable.getALocalSource().asExpr().(CallableExpr).getInnerScope().getAReturnValueFlowNode() + } + + // Relating callables to nodes + Node callTo(SummarizedCallable callable) { result = callable.getACallSimple() } +} + +private module TypeTrackerSummaryFlow = SummaryTypeTracker::SummaryFlow; diff --git a/python/ql/lib/semmle/python/dataflow/old/TaintTracking.qll b/python/ql/lib/semmle/python/dataflow/old/TaintTracking.qll index 0ce4bc27790..d0293522404 100644 --- a/python/ql/lib/semmle/python/dataflow/old/TaintTracking.qll +++ b/python/ql/lib/semmle/python/dataflow/old/TaintTracking.qll @@ -664,6 +664,14 @@ module DataFlow { } } +deprecated private class DataFlowType extends TaintKind { + // this only exists to avoid an empty recursion error in the type checker + DataFlowType() { + this = "Data flow" and + 1 = 2 + } +} + pragma[noinline] private predicate dict_construct(ControlFlowNode itemnode, ControlFlowNode dictnode) { dictnode.(DictNode).getAValue() = itemnode diff --git a/python/ql/lib/semmle/python/frameworks/data/internal/ApiGraphModels.qll b/python/ql/lib/semmle/python/frameworks/data/internal/ApiGraphModels.qll index 227f4ea22fb..2e598711fcc 100644 --- a/python/ql/lib/semmle/python/frameworks/data/internal/ApiGraphModels.qll +++ b/python/ql/lib/semmle/python/frameworks/data/internal/ApiGraphModels.qll @@ -643,6 +643,15 @@ module ModelOutput { baseNode = getInvocationFromPath(type, path) } + /** + * Holds if a `baseNode` is a callable identified by the `type,path` part of a summary row. + */ + cached + predicate resolvedSummaryRefBase(string type, string path, API::Node baseNode) { + summaryModel(type, path, _, _, _) and + baseNode = getNodeFromPath(type, path) + } + /** * Holds if `node` is seen as an instance of `type` due to a type definition * contributed by a CSV model. @@ -653,6 +662,17 @@ module ModelOutput { import Cached import Specific::ModelOutputSpecific + private import codeql.mad.ModelValidation as SharedModelVal + + private module KindValConfig implements SharedModelVal::KindValidationConfigSig { + predicate summaryKind(string kind) { summaryModel(_, _, _, _, kind) } + + predicate sinkKind(string kind) { sinkModel(_, _, kind) } + + predicate sourceKind(string kind) { sourceModel(_, _, kind) } + } + + private module KindVal = SharedModelVal::KindValidation; /** * Gets an error message relating to an invalid CSV row in a model. @@ -698,5 +718,8 @@ module ModelOutput { not isValidNoArgumentTokenInIdentifyingAccessPath(token.getName()) and result = "Invalid token '" + token + "' is missing its arguments, in access path: " + path ) + or + // Check for invalid model kinds + result = KindVal::getInvalidModelKind() } } diff --git a/python/ql/lib/semmle/python/security/Exceptions.qll b/python/ql/lib/semmle/python/security/Exceptions.qll deleted file mode 100644 index a335d4e3c35..00000000000 --- a/python/ql/lib/semmle/python/security/Exceptions.qll +++ /dev/null @@ -1,103 +0,0 @@ -/** - * Provides classes and predicates for tracking exceptions and information - * associated with exceptions. - */ - -import python -import semmle.python.dataflow.TaintTracking -import semmle.python.security.strings.Basic - -deprecated private Value traceback_function(string name) { - result = Module::named("traceback").attr(name) -} - -/** - * This represents information relating to an exception, for instance the - * message, arguments or parts of the exception traceback. - */ -deprecated class ExceptionInfo extends StringKind { - ExceptionInfo() { this = "exception.info" } - - override string repr() { result = "exception info" } -} - -/** - * A class representing sources of information about - * execution state exposed in tracebacks and the like. - */ -abstract deprecated class ErrorInfoSource extends TaintSource { } - -/** - * This kind represents exceptions themselves. - */ -deprecated class ExceptionKind extends TaintKind { - ExceptionKind() { this = "exception.kind" } - - override string repr() { result = "exception" } - - override TaintKind getTaintOfAttribute(string name) { - name = "args" and result instanceof ExceptionInfoSequence - or - name = "message" and result instanceof ExceptionInfo - } -} - -/** - * A source of exception objects, either explicitly created, or captured by an - * `except` statement. - */ -deprecated class ExceptionSource extends ErrorInfoSource { - ExceptionSource() { - exists(ClassValue cls | - cls.getASuperType() = ClassValue::baseException() and - this.(ControlFlowNode).pointsTo().getClass() = cls - ) - or - this = any(ExceptStmt s).getName().getAFlowNode() - } - - override string toString() { result = "exception.source" } - - override predicate isSourceOf(TaintKind kind) { kind instanceof ExceptionKind } -} - -/** - * Represents a sequence of pieces of information relating to an exception, - * for instance the contents of the `args` attribute, or the stack trace. - */ -deprecated class ExceptionInfoSequence extends SequenceKind { - ExceptionInfoSequence() { this.getItem() instanceof ExceptionInfo } -} - -/** - * Represents calls to functions in the `traceback` module that return - * sequences of exception information. - */ -deprecated class CallToTracebackFunction extends ErrorInfoSource { - CallToTracebackFunction() { - exists(string name | - name in [ - "extract_tb", "extract_stack", "format_list", "format_exception_only", "format_exception", - "format_tb", "format_stack" - ] - | - this = traceback_function(name).getACall() - ) - } - - override string toString() { result = "exception.info.sequence.source" } - - override predicate isSourceOf(TaintKind kind) { kind instanceof ExceptionInfoSequence } -} - -/** - * Represents calls to functions in the `traceback` module that return a single - * string of information about an exception. - */ -deprecated class FormattedTracebackSource extends ErrorInfoSource { - FormattedTracebackSource() { this = traceback_function("format_exc").getACall() } - - override string toString() { result = "exception.info.source" } - - override predicate isSourceOf(TaintKind kind) { kind instanceof ExceptionInfo } -} diff --git a/python/ql/lib/semmle/python/security/SQL.qll b/python/ql/lib/semmle/python/security/SQL.qll index 4d2ef6218cc..6485402b78a 100644 --- a/python/ql/lib/semmle/python/security/SQL.qll +++ b/python/ql/lib/semmle/python/security/SQL.qll @@ -1,4 +1,4 @@ import python import semmle.python.dataflow.TaintTracking -abstract class SqlInjectionSink extends TaintSink { } +abstract deprecated class SqlInjectionSink extends TaintSink { } diff --git a/python/ql/lib/semmle/python/security/injection/Command.qll b/python/ql/lib/semmle/python/security/injection/Command.qll deleted file mode 100644 index b8ae8b94563..00000000000 --- a/python/ql/lib/semmle/python/security/injection/Command.qll +++ /dev/null @@ -1,263 +0,0 @@ -/** - * Provides class and predicates to track external data that - * may represent malicious OS commands. - * - * This module is intended to be imported into a taint-tracking query - * to extend `TaintKind` and `TaintSink`. - */ - -import python -import semmle.python.dataflow.TaintTracking -import semmle.python.security.strings.Untrusted - -/** Abstract taint sink that is potentially vulnerable to malicious shell commands. */ -abstract deprecated class CommandSink extends TaintSink { } - -deprecated private ModuleObject osOrPopenModule() { result.getName() = ["os", "popen2"] } - -deprecated private Object makeOsCall() { - exists(string name | result = ModuleObject::named("subprocess").attr(name) | - name = ["Popen", "call", "check_call", "check_output", "run"] - ) -} - -/**Special case for first element in sequence. */ -deprecated class FirstElementKind extends TaintKind { - FirstElementKind() { this = "sequence[" + any(ExternalStringKind key) + "][0]" } - - override string repr() { result = "first item in sequence of " + this.getItem().repr() } - - /** Gets the taint kind for item in this sequence. */ - ExternalStringKind getItem() { this = "sequence[" + result + "][0]" } -} - -deprecated class FirstElementFlow extends DataFlowExtension::DataFlowNode { - FirstElementFlow() { this = any(SequenceNode s).getElement(0) } - - override ControlFlowNode getASuccessorNode(TaintKind fromkind, TaintKind tokind) { - result.(SequenceNode).getElement(0) = this and tokind.(FirstElementKind).getItem() = fromkind - } -} - -/** - * A taint sink that is potentially vulnerable to malicious shell commands. - * The `vuln` in `subprocess.call(shell=vuln)` and similar calls. - */ -deprecated class ShellCommand extends CommandSink { - override string toString() { result = "shell command" } - - ShellCommand() { - exists(CallNode call, Object istrue | - call.getFunction().refersTo(makeOsCall()) and - call.getAnArg() = this and - call.getArgByName("shell").refersTo(istrue) and - istrue.booleanValue() = true - ) - or - exists(CallNode call, string name | - call.getAnArg() = this and - call.getFunction().refersTo(osOrPopenModule().attr(name)) - | - name = ["system", "popen"] or - name.matches("popen_") - ) - or - exists(CallNode call | - call.getAnArg() = this and - call.getFunction().refersTo(ModuleObject::named("commands")) - ) - } - - override predicate sinks(TaintKind kind) { - /* Tainted string command */ - kind instanceof ExternalStringKind - or - /* List (or tuple) containing a tainted string command */ - kind instanceof ExternalStringSequenceKind - } -} - -/** - * A taint sink that is potentially vulnerable to malicious shell commands. - * The `vuln` in `subprocess.call(vuln, ...)` and similar calls. - */ -deprecated class OsCommandFirstArgument extends CommandSink { - override string toString() { result = "OS command first argument" } - - OsCommandFirstArgument() { - not this instanceof ShellCommand and - exists(CallNode call | - call.getFunction().refersTo(makeOsCall()) and - call.getArg(0) = this - ) - } - - override predicate sinks(TaintKind kind) { - /* Tainted string command */ - kind instanceof ExternalStringKind - or - /* List (or tuple) whose first element is tainted */ - kind instanceof FirstElementKind - } -} - -// -------------------------------------------------------------------------- // -// Modeling of the 'invoke' package and 'fabric' package (v 2.x) -// -// Since fabric build so closely upon invoke, we model them together to avoid -// duplication -// -------------------------------------------------------------------------- // -/** - * A taint sink that is potentially vulnerable to malicious shell commands. - * The `vuln` in `invoke.run(vuln, ...)` and similar calls. - */ -deprecated class InvokeRun extends CommandSink { - InvokeRun() { - this = Value::named("invoke.run").(FunctionValue).getArgumentForCall(_, 0) - or - this = Value::named("invoke.sudo").(FunctionValue).getArgumentForCall(_, 0) - } - - override string toString() { result = "InvokeRun" } - - override predicate sinks(TaintKind kind) { kind instanceof ExternalStringKind } -} - -/** - * Internal TaintKind to track the invoke.Context instance passed to functions - * marked with @invoke.task - */ -deprecated private class InvokeContextArg extends TaintKind { - InvokeContextArg() { this = "InvokeContextArg" } -} - -/** Internal TaintSource to track the context passed to functions marked with @invoke.task */ -deprecated private class InvokeContextArgSource extends TaintSource { - InvokeContextArgSource() { - exists(Function f, Expr decorator | - count(f.getADecorator()) = 1 and - ( - decorator = f.getADecorator() and not decorator instanceof Call - or - decorator = f.getADecorator().(Call).getFunc() - ) and - ( - decorator.pointsTo(Value::named("invoke.task")) - or - decorator.pointsTo(Value::named("fabric.task")) - ) - | - this.(ControlFlowNode).getNode() = f.getArg(0) - ) - } - - override predicate isSourceOf(TaintKind kind) { kind instanceof InvokeContextArg } -} - -/** - * A taint sink that is potentially vulnerable to malicious shell commands. - * The `vuln` in `invoke.Context().run(vuln, ...)` and similar calls. - */ -deprecated class InvokeContextRun extends CommandSink { - InvokeContextRun() { - exists(CallNode call | - any(InvokeContextArg k).taints(call.getFunction().(AttrNode).getObject("run")) - or - call = Value::named("invoke.Context").(ClassValue).lookup("run").getACall() - or - // fabric.connection.Connection is a subtype of invoke.context.Context - // since fabric.Connection.run has a decorator, it doesn't work with FunctionValue :| - // and `Value::named("fabric.Connection").(ClassValue).lookup("run").getACall()` returned no results, - // so here is the hacky solution that works :\ - call.getFunction().(AttrNode).getObject("run").pointsTo().getClass() = - Value::named("fabric.Connection") - | - this = call.getArg(0) - or - this = call.getArgByName("command") - ) - } - - override string toString() { result = "InvokeContextRun" } - - override predicate sinks(TaintKind kind) { kind instanceof ExternalStringKind } -} - -/** - * A taint sink that is potentially vulnerable to malicious shell commands. - * The `vuln` in `fabric.Group().run(vuln, ...)` and similar calls. - */ -deprecated class FabricGroupRun extends CommandSink { - FabricGroupRun() { - exists(ClassValue cls | - cls.getASuperType() = Value::named("fabric.Group") and - this = cls.lookup("run").(FunctionValue).getArgumentForCall(_, 1) - ) - } - - override string toString() { result = "FabricGroupRun" } - - override predicate sinks(TaintKind kind) { kind instanceof ExternalStringKind } -} - -// -------------------------------------------------------------------------- // -// Modeling of the 'invoke' package and 'fabric' package (v 1.x) -// -------------------------------------------------------------------------- // -deprecated class FabricV1Commands extends CommandSink { - FabricV1Commands() { - // since `run` and `sudo` are decorated, we can't use FunctionValue's :( - exists(CallNode call | - call = Value::named("fabric.api.local").getACall() - or - call = Value::named("fabric.api.run").getACall() - or - call = Value::named("fabric.api.sudo").getACall() - | - this = call.getArg(0) - or - this = call.getArgByName("command") - ) - } - - override string toString() { result = "FabricV1Commands" } - - override predicate sinks(TaintKind kind) { kind instanceof ExternalStringKind } -} - -/** - * An extension that propagates taint from the arguments of `fabric.api.execute(func, arg0, arg1, ...)` - * to the parameters of `func`, since this will call `func(arg0, arg1, ...)`. - */ -deprecated class FabricExecuteExtension extends DataFlowExtension::DataFlowNode { - CallNode call; - - FabricExecuteExtension() { - call = Value::named("fabric.api.execute").getACall() and - ( - this = call.getArg(any(int i | i > 0)) - or - this = call.getArgByName(any(string s | not s = "task")) - ) - } - - override ControlFlowNode getASuccessorNode(TaintKind fromkind, TaintKind tokind) { - tokind = fromkind and - exists(CallableValue func | - ( - call.getArg(0).pointsTo(func) - or - call.getArgByName("task").pointsTo(func) - ) and - exists(int i | - // execute(func, arg0, arg1) => func(arg0, arg1) - this = call.getArg(i) and - result = func.getParameter(i - 1) - ) - or - exists(string name | - this = call.getArgByName(name) and - result = func.getParameterByName(name) - ) - ) - } -} diff --git a/python/ql/lib/semmle/python/security/injection/Sql.qll b/python/ql/lib/semmle/python/security/injection/Sql.qll deleted file mode 100644 index b2e2cd47715..00000000000 --- a/python/ql/lib/semmle/python/security/injection/Sql.qll +++ /dev/null @@ -1,83 +0,0 @@ -/** - * Provides class and predicates to track external data that - * may represent malicious SQL queries or parts of queries. - * - * This module is intended to be imported into a taint-tracking query - * to extend `TaintKind` and `TaintSink`. - */ - -import python -import semmle.python.dataflow.TaintTracking -import semmle.python.security.strings.Untrusted -import semmle.python.security.SQL - -deprecated private StringObject first_part(ControlFlowNode command) { - command.(BinaryExprNode).getOp() instanceof Add and - command.(BinaryExprNode).getLeft().refersTo(result) - or - exists(CallNode call, SequenceObject seq | call = command | - call = theStrType().lookupAttribute("join") and - call.getArg(0).refersTo(seq) and - seq.getInferredElement(0) = result - ) - or - command.(BinaryExprNode).getOp() instanceof Mod and - command.getNode().(StrConst).getLiteralObject() = result -} - -/** Holds if `command` appears to be a SQL command string of which `inject` is a part. */ -deprecated predicate probable_sql_command(ControlFlowNode command, ControlFlowNode inject) { - exists(string prefix | - inject = command.getAChild*() and - first_part(command).getText().regexpMatch(" *" + prefix + ".*") - | - prefix = "CREATE" or prefix = "SELECT" - ) -} - -/** - * A taint kind representing a DB cursor. - * This will be overridden to provide specific kinds of DB cursor. - */ -abstract deprecated class DbCursor extends TaintKind { - bindingset[this] - DbCursor() { any() } - - string getExecuteMethodName() { result = "execute" } -} - -/** - * A part of a string that appears to be a SQL command and is thus - * vulnerable to malicious input. - */ -deprecated class SimpleSqlStringInjection extends SqlInjectionSink { - override string toString() { result = "simple SQL string injection" } - - SimpleSqlStringInjection() { probable_sql_command(_, this) } - - override predicate sinks(TaintKind kind) { kind instanceof ExternalStringKind } -} - -/** - * A taint source representing sources of DB connections. - * This will be overridden to provide specific kinds of DB connection sources. - */ -abstract deprecated class DbConnectionSource extends TaintSource { } - -/** - * A taint sink that is vulnerable to malicious SQL queries. - * The `vuln` in `db.connection.execute(vuln)` and similar. - */ -deprecated class DbConnectionExecuteArgument extends SqlInjectionSink { - override string toString() { result = "db.connection.execute" } - - DbConnectionExecuteArgument() { - exists(CallNode call, DbCursor cursor, string name | - cursor.taints(call.getFunction().(AttrNode).getObject(name)) and - cursor.getExecuteMethodName() = name and - call.getArg(0) = this - ) - } - - override predicate sinks(TaintKind kind) { kind instanceof ExternalStringKind } -} diff --git a/python/ql/lib/semmle/python/web/ClientHttpRequest.qll b/python/ql/lib/semmle/python/web/ClientHttpRequest.qll deleted file mode 100644 index edcd236e031..00000000000 --- a/python/ql/lib/semmle/python/web/ClientHttpRequest.qll +++ /dev/null @@ -1,2 +0,0 @@ -import semmle.python.web.client.StdLib -import semmle.python.web.client.Requests diff --git a/python/ql/lib/semmle/python/web/HttpRedirect.qll b/python/ql/lib/semmle/python/web/HttpRedirect.qll deleted file mode 100644 index cfbe35f30f1..00000000000 --- a/python/ql/lib/semmle/python/web/HttpRedirect.qll +++ /dev/null @@ -1,7 +0,0 @@ -import python -import semmle.python.security.strings.Basic -import semmle.python.web.django.Redirect -import semmle.python.web.flask.Redirect -import semmle.python.web.tornado.Redirect -import semmle.python.web.pyramid.Redirect -import semmle.python.web.bottle.Redirect diff --git a/python/ql/lib/semmle/python/web/HttpResponse.qll b/python/ql/lib/semmle/python/web/HttpResponse.qll deleted file mode 100644 index 25a4221ce68..00000000000 --- a/python/ql/lib/semmle/python/web/HttpResponse.qll +++ /dev/null @@ -1,10 +0,0 @@ -import semmle.python.web.django.Response -import semmle.python.web.flask.Response -import semmle.python.web.pyramid.Response -import semmle.python.web.tornado.Response -import semmle.python.web.twisted.Response -import semmle.python.web.bottle.Response -import semmle.python.web.turbogears.Response -import semmle.python.web.falcon.Response -import semmle.python.web.cherrypy.Response -import semmle.python.web.stdlib.Response diff --git a/python/ql/lib/semmle/python/web/bottle/Redirect.qll b/python/ql/lib/semmle/python/web/bottle/Redirect.qll deleted file mode 100644 index 6525cc058eb..00000000000 --- a/python/ql/lib/semmle/python/web/bottle/Redirect.qll +++ /dev/null @@ -1,28 +0,0 @@ -/** - * Provides class representing the `bottle.redirect` function. - * This module is intended to be imported into a taint-tracking query - * to extend `TaintSink`. - */ - -import python -import semmle.python.dataflow.TaintTracking -import semmle.python.security.strings.Basic -import semmle.python.web.bottle.General - -deprecated FunctionValue bottle_redirect() { result = theBottleModule().attr("redirect") } - -/** - * An argument to the `bottle.redirect` function. - */ -deprecated class BottleRedirect extends TaintSink { - override string toString() { result = "bottle.redirect" } - - BottleRedirect() { - exists(CallNode call | - bottle_redirect().getACall() = call and - this = call.getAnArg() - ) - } - - override predicate sinks(TaintKind kind) { kind instanceof StringKind } -} diff --git a/python/ql/lib/semmle/python/web/bottle/Response.qll b/python/ql/lib/semmle/python/web/bottle/Response.qll deleted file mode 100644 index f027c6be1a5..00000000000 --- a/python/ql/lib/semmle/python/web/bottle/Response.qll +++ /dev/null @@ -1,52 +0,0 @@ -import python -import semmle.python.dataflow.TaintTracking -import semmle.python.security.strings.Untrusted -import semmle.python.web.Http -import semmle.python.web.bottle.General - -/** - * A bottle.Response object - * This isn't really a "taint", but we use the value tracking machinery to - * track the flow of response objects. - */ -deprecated class BottleResponse extends TaintKind { - BottleResponse() { this = "bottle.response" } -} - -deprecated private Value theBottleResponseObject() { result = theBottleModule().attr("response") } - -deprecated class BottleResponseBodyAssignment extends HttpResponseTaintSink { - BottleResponseBodyAssignment() { - exists(DefinitionNode lhs | - lhs.getValue() = this and - lhs.(AttrNode).getObject("body").pointsTo(theBottleResponseObject()) - ) - } - - override predicate sinks(TaintKind kind) { kind instanceof StringKind } -} - -deprecated class BottleHandlerFunctionResult extends HttpResponseTaintSink { - BottleHandlerFunctionResult() { - exists(BottleRoute route, Return ret | - ret.getScope() = route.getFunction() and - ret.getValue().getAFlowNode() = this - ) - } - - override predicate sinks(TaintKind kind) { kind instanceof StringKind } - - override string toString() { result = "bottle handler function result" } -} - -deprecated class BottleCookieSet extends CookieSet, CallNode { - BottleCookieSet() { - any(BottleResponse r).taints(this.getFunction().(AttrNode).getObject("set_cookie")) - } - - override string toString() { result = CallNode.super.toString() } - - override ControlFlowNode getKey() { result = this.getArg(0) } - - override ControlFlowNode getValue() { result = this.getArg(1) } -} diff --git a/python/ql/lib/semmle/python/web/cherrypy/Response.qll b/python/ql/lib/semmle/python/web/cherrypy/Response.qll deleted file mode 100644 index 124a9f9b7d0..00000000000 --- a/python/ql/lib/semmle/python/web/cherrypy/Response.qll +++ /dev/null @@ -1,18 +0,0 @@ -import python -import semmle.python.dataflow.TaintTracking -import semmle.python.security.strings.Untrusted -import semmle.python.web.Http -import semmle.python.web.cherrypy.General - -deprecated class CherryPyExposedFunctionResult extends HttpResponseTaintSink { - CherryPyExposedFunctionResult() { - exists(Return ret | - ret.getScope() instanceof CherryPyExposedFunction and - ret.getValue().getAFlowNode() = this - ) - } - - override predicate sinks(TaintKind kind) { kind instanceof StringKind } - - override string toString() { result = "cherrypy handler function result" } -} diff --git a/python/ql/lib/semmle/python/web/client/Requests.qll b/python/ql/lib/semmle/python/web/client/Requests.qll deleted file mode 100644 index 9adf2c8120f..00000000000 --- a/python/ql/lib/semmle/python/web/client/Requests.qll +++ /dev/null @@ -1,22 +0,0 @@ -/** - * Modeling outgoing HTTP requests using the `requests` package - * https://pypi.org/project/requests/ - */ - -import python -private import semmle.python.web.Http - -deprecated class RequestsHttpRequest extends Client::HttpRequest, CallNode { - CallableValue func; - string method; - - RequestsHttpRequest() { - method = httpVerbLower() and - func = Module::named("requests").attr(method) and - this = func.getACall() - } - - override ControlFlowNode getAUrlPart() { result = func.getNamedArgumentForCall(this, "url") } - - override string getMethodUpper() { result = method.toUpperCase() } -} diff --git a/python/ql/lib/semmle/python/web/client/StdLib.qll b/python/ql/lib/semmle/python/web/client/StdLib.qll deleted file mode 100644 index b8a533c410f..00000000000 --- a/python/ql/lib/semmle/python/web/client/StdLib.qll +++ /dev/null @@ -1,55 +0,0 @@ -import python -private import semmle.python.web.Http - -deprecated ClassValue httpConnectionClass() { - // Python 2 - result = Value::named("httplib.HTTPConnection") - or - result = Value::named("httplib.HTTPSConnection") - or - // Python 3 - result = Value::named("http.client.HTTPConnection") - or - result = Value::named("http.client.HTTPSConnection") - or - // six - result = Value::named("six.moves.http_client.HTTPConnection") - or - result = Value::named("six.moves.http_client.HTTPSConnection") -} - -deprecated class HttpConnectionHttpRequest extends Client::HttpRequest, CallNode { - CallNode constructor_call; - CallableValue func; - - HttpConnectionHttpRequest() { - exists(ClassValue cls, AttrNode call_origin, Value constructor_call_value | - cls = httpConnectionClass() and - func = cls.lookup("request") and - this = func.getACall() and - // since you can do `r = conn.request; r('GET', path)`, we need to find the origin - this.getFunction().pointsTo(_, _, call_origin) and - // Since HTTPSConnection is a subtype of HTTPConnection, up until this point, `cls` could be either class, - // because `HTTPSConnection.request == HTTPConnection.request`. To avoid generating 2 results, we filter - // on the actual class used as the constructor - call_origin.getObject().pointsTo(_, constructor_call_value, constructor_call) and - cls = constructor_call_value.getClass() and - constructor_call = cls.getACall() - ) - } - - override ControlFlowNode getAUrlPart() { - result = func.getNamedArgumentForCall(this, "url") - or - result = constructor_call.getArg(0) - or - result = constructor_call.getArgByName("host") - } - - override string getMethodUpper() { - exists(string method | - result = method.toUpperCase() and - func.getNamedArgumentForCall(this, "method").pointsTo(Value::forString(method)) - ) - } -} diff --git a/python/ql/lib/semmle/python/web/django/Db.qll b/python/ql/lib/semmle/python/web/django/Db.qll deleted file mode 100644 index bb089f5f0fe..00000000000 --- a/python/ql/lib/semmle/python/web/django/Db.qll +++ /dev/null @@ -1,50 +0,0 @@ -import python -import semmle.python.security.injection.Sql - -/** - * A taint kind representing a django cursor object. - */ -deprecated class DjangoDbCursor extends DbCursor { - DjangoDbCursor() { this = "django.db.connection.cursor" } -} - -deprecated private Value theDjangoConnectionObject() { - result = Value::named("django.db.connection") -} - -/** - * A kind of taint source representing sources of django cursor objects. - */ -deprecated class DjangoDbCursorSource extends DbConnectionSource { - DjangoDbCursorSource() { - exists(AttrNode cursor | - this.(CallNode).getFunction() = cursor and - cursor.getObject("cursor").pointsTo(theDjangoConnectionObject()) - ) - } - - override string toString() { result = "django.db.connection.cursor" } - - override predicate isSourceOf(TaintKind kind) { kind instanceof DjangoDbCursor } -} - -deprecated ClassValue theDjangoRawSqlClass() { - result = Value::named("django.db.models.expressions.RawSQL") -} - -/** - * A sink of taint on calls to `django.db.models.expressions.RawSQL`. This - * allows arbitrary SQL statements to be executed, which is a security risk. - */ -deprecated class DjangoRawSqlSink extends SqlInjectionSink { - DjangoRawSqlSink() { - exists(CallNode call | - call = theDjangoRawSqlClass().getACall() and - this = call.getArg(0) - ) - } - - override predicate sinks(TaintKind kind) { kind instanceof ExternalStringKind } - - override string toString() { result = "django.db.models.expressions.RawSQL(sink,...)" } -} diff --git a/python/ql/lib/semmle/python/web/django/Model.qll b/python/ql/lib/semmle/python/web/django/Model.qll deleted file mode 100644 index 944eec4f799..00000000000 --- a/python/ql/lib/semmle/python/web/django/Model.qll +++ /dev/null @@ -1,69 +0,0 @@ -import python -import semmle.python.dataflow.TaintTracking -import semmle.python.security.strings.Basic -import semmle.python.web.Http -import semmle.python.security.injection.Sql - -/** A django model class */ -deprecated class DjangoModel extends ClassValue { - DjangoModel() { Value::named("django.db.models.Model") = this.getASuperType() } -} - -/** A "taint" for django database tables */ -deprecated class DjangoDbTableObjects extends TaintKind { - DjangoDbTableObjects() { this = "django.db.models.Model.objects" } - - override TaintKind getTaintOfMethodResult(string name) { - result = this and - name in [ - "filter", "exclude", "none", "all", "union", "intersection", "difference", "select_related", - "prefetch_related", "extra", "defer", "only", "annotate", "using", "select_for_update", - "raw", "order_by", "reverse", "distinct", "values", "values_list", "dates", "datetimes" - ] - } -} - -/** Django model objects, which are sources of django database table "taint" */ -deprecated class DjangoModelObjects extends TaintSource { - DjangoModelObjects() { - this.(AttrNode).isLoad() and this.(AttrNode).getObject("objects").pointsTo(any(DjangoModel m)) - } - - override predicate isSourceOf(TaintKind kind) { kind instanceof DjangoDbTableObjects } - - override string toString() { result = "django.db.models.Model.objects" } -} - -/** - * A call to the `raw` method on a django model. This allows a raw SQL query - * to be sent to the database, which is a security risk. - */ -deprecated class DjangoModelRawCall extends SqlInjectionSink { - DjangoModelRawCall() { - exists(CallNode raw_call, ControlFlowNode queryset | this = raw_call.getArg(0) | - raw_call.getFunction().(AttrNode).getObject("raw") = queryset and - any(DjangoDbTableObjects objs).taints(queryset) - ) - } - - override predicate sinks(TaintKind kind) { kind instanceof ExternalStringKind } - - override string toString() { result = "django.models.QuerySet.raw(sink,...)" } -} - -/** - * A call to the `extra` method on a django model. This allows a raw SQL query - * to be sent to the database, which is a security risk. - */ -deprecated class DjangoModelExtraCall extends SqlInjectionSink { - DjangoModelExtraCall() { - exists(CallNode extra_call, ControlFlowNode queryset | this = extra_call.getArg(0) | - extra_call.getFunction().(AttrNode).getObject("extra") = queryset and - any(DjangoDbTableObjects objs).taints(queryset) - ) - } - - override predicate sinks(TaintKind kind) { kind instanceof ExternalStringKind } - - override string toString() { result = "django.models.QuerySet.extra(sink,...)" } -} diff --git a/python/ql/lib/semmle/python/web/django/Redirect.qll b/python/ql/lib/semmle/python/web/django/Redirect.qll deleted file mode 100644 index 0319f18190f..00000000000 --- a/python/ql/lib/semmle/python/web/django/Redirect.qll +++ /dev/null @@ -1,37 +0,0 @@ -/** - * Provides class representing the `django.redirect` function. - * This module is intended to be imported into a taint-tracking query - * to extend `TaintSink`. - */ - -import python -import semmle.python.dataflow.TaintTracking -import semmle.python.security.strings.Basic -private import semmle.python.web.django.Shared -private import semmle.python.web.Http - -/** - * The URL argument for a call to the `django.shortcuts.redirect` function. - */ -deprecated class DjangoShortcutsRedirectSink extends HttpRedirectTaintSink { - override string toString() { result = "DjangoShortcutsRedirectSink" } - - DjangoShortcutsRedirectSink() { - this = Value::named("django.shortcuts.redirect").(FunctionValue).getArgumentForCall(_, 0) - } -} - -/** - * The URL argument when instantiating a Django Redirect Response. - */ -deprecated class DjangoRedirectResponseSink extends HttpRedirectTaintSink { - DjangoRedirectResponseSink() { - exists(CallNode call | call = any(DjangoRedirectResponseClass cls).getACall() | - this = call.getArg(0) - or - this = call.getArgByName("redirect_to") - ) - } - - override string toString() { result = "DjangoRedirectResponseSink" } -} diff --git a/python/ql/lib/semmle/python/web/django/Response.qll b/python/ql/lib/semmle/python/web/django/Response.qll deleted file mode 100644 index 3444884dd32..00000000000 --- a/python/ql/lib/semmle/python/web/django/Response.qll +++ /dev/null @@ -1,79 +0,0 @@ -import python -import semmle.python.dataflow.TaintTracking -import semmle.python.security.strings.Basic -private import semmle.python.web.django.Shared -private import semmle.python.web.Http - -/** INTERNAL class used for tracking a django response object. */ -deprecated private class DjangoResponseKind extends TaintKind { - DjangoResponseKind() { this = "django.response.HttpResponse" } -} - -/** INTERNAL taint-source used for tracking a django response object. */ -deprecated private class DjangoResponseSource extends TaintSource { - DjangoResponseSource() { exists(DjangoContentResponseClass cls | cls.getACall() = this) } - - override predicate isSourceOf(TaintKind kind) { kind instanceof DjangoResponseKind } - - override string toString() { result = "django.http.response.HttpResponse" } -} - -/** A write to a django response, which is vulnerable to external data (xss) */ -deprecated class DjangoResponseWrite extends HttpResponseTaintSink { - DjangoResponseWrite() { - exists(AttrNode meth, CallNode call | - call.getFunction() = meth and - any(DjangoResponseKind response).taints(meth.getObject("write")) and - this = call.getArg(0) - ) - } - - override predicate sinks(TaintKind kind) { kind instanceof StringKind } - - override string toString() { result = "django.Response.write(...)" } -} - -/** - * An argument to initialization of a django response. - */ -deprecated class DjangoResponseContent extends HttpResponseTaintSink { - DjangoContentResponseClass cls; - CallNode call; - - DjangoResponseContent() { - call = cls.getACall() and - this = cls.getContentArg(call) - } - - override predicate sinks(TaintKind kind) { kind instanceof StringKind } - - override string toString() { result = "django.Response(...)" } -} - -/** - * An argument to initialization of a django response, which is vulnerable to external data (XSS). - */ -deprecated class DjangoResponseContentXSSVulnerable extends DjangoResponseContent { - override DjangoXSSVulnerableResponseClass cls; - - DjangoResponseContentXSSVulnerable() { - not exists(cls.getContentTypeArg(call)) - or - exists(StringValue s | - cls.getContentTypeArg(call).pointsTo(s) and - s.getText().matches("text/html%") - ) - } -} - -deprecated class DjangoCookieSet extends CookieSet, CallNode { - DjangoCookieSet() { - any(DjangoResponseKind r).taints(this.getFunction().(AttrNode).getObject("set_cookie")) - } - - override string toString() { result = CallNode.super.toString() } - - override ControlFlowNode getKey() { result = this.getArg(0) } - - override ControlFlowNode getValue() { result = this.getArg(1) } -} diff --git a/python/ql/lib/semmle/python/web/django/Sanitizers.qll b/python/ql/lib/semmle/python/web/django/Sanitizers.qll deleted file mode 100644 index e1694a1c481..00000000000 --- a/python/ql/lib/semmle/python/web/django/Sanitizers.qll +++ /dev/null @@ -1,6 +0,0 @@ -import python -/* - * Sanitizers - * No django sanitizers implemented yet. - */ - diff --git a/python/ql/lib/semmle/python/web/django/Shared.qll b/python/ql/lib/semmle/python/web/django/Shared.qll deleted file mode 100644 index bc156249d25..00000000000 --- a/python/ql/lib/semmle/python/web/django/Shared.qll +++ /dev/null @@ -1,72 +0,0 @@ -import python - -/** A class that is a Django Redirect Response (subclass of `django.http.HttpResponseRedirectBase`). */ -deprecated class DjangoRedirectResponseClass extends ClassValue { - DjangoRedirectResponseClass() { - exists(ClassValue redirect_base | - // version 1.x - redirect_base = Value::named("django.http.response.HttpResponseRedirectBase") - or - // version 2.x and 3.x - redirect_base = Value::named("django.http.HttpResponseRedirectBase") - | - this.getASuperType() = redirect_base - ) - } -} - -/** - * A class that is a Django Response, which can contain content. - * A subclass of `django.http.HttpResponse` that is not a `DjangoRedirectResponseClass`. - */ -deprecated class DjangoContentResponseClass extends ClassValue { - ClassValue base; - - DjangoContentResponseClass() { - ( - // version 1.x - base = Value::named("django.http.response.HttpResponse") - or - // version 2.x and 3.x - // https://docs.djangoproject.com/en/2.2/ref/request-response/#httpresponse-objects - base = Value::named("django.http.HttpResponse") - ) and - this.getASuperType() = base - } - - // The reason these two methods are defined in this class (and not in the Sink - // definition that uses this class), is that if we were to add support for - // `django.http.response.HttpResponseNotAllowed` it would make much more sense to add - // the custom logic in this class (or subclass), than to handle all of it in the sink - // definition. - /** Gets the `content` argument of a `call` to the constructor */ - ControlFlowNode getContentArg(CallNode call) { none() } - - /** Gets the `content_type` argument of a `call` to the constructor */ - ControlFlowNode getContentTypeArg(CallNode call) { none() } -} - -/** A class that is a Django Response, and is vulnerable to XSS. */ -deprecated class DjangoXSSVulnerableResponseClass extends DjangoContentResponseClass { - DjangoXSSVulnerableResponseClass() { - // We want to avoid FPs on subclasses that are not exposed to XSS, for example `JsonResponse`. - // The easiest way is to disregard any subclass that has a special `__init__` method. - // It's not guaranteed to remove all FPs, or not to generate FNs, but compared to our - // previous implementation that would treat 0-th argument to _any_ subclass as a sink, - // this gets us much closer to reality. - this.lookup("__init__") = base.lookup("__init__") and - not this instanceof DjangoRedirectResponseClass - } - - override ControlFlowNode getContentArg(CallNode call) { - result = call.getArg(0) - or - result = call.getArgByName("content") - } - - override ControlFlowNode getContentTypeArg(CallNode call) { - result = call.getArg(1) - or - result = call.getArgByName("content_type") - } -} diff --git a/python/ql/lib/semmle/python/web/falcon/Response.qll b/python/ql/lib/semmle/python/web/falcon/Response.qll deleted file mode 100644 index ae5562b6432..00000000000 --- a/python/ql/lib/semmle/python/web/falcon/Response.qll +++ /dev/null @@ -1,28 +0,0 @@ -import python -import semmle.python.dataflow.TaintTracking -import semmle.python.web.Http -import semmle.python.web.falcon.General - -/** https://falcon.readthedocs.io/en/stable/api/request_and_response.html */ -deprecated class FalconResponse extends TaintKind { - FalconResponse() { this = "falcon.response" } -} - -/** Only used internally to track the response parameter */ -deprecated private class FalconResponseParameter extends TaintSource { - FalconResponseParameter() { - exists(FalconHandlerFunction f | f.getResponse() = this.(ControlFlowNode).getNode()) - } - - override predicate isSourceOf(TaintKind k) { k instanceof FalconResponse } -} - -deprecated class FalconResponseBodySink extends HttpResponseTaintSink { - FalconResponseBodySink() { - exists(AttrNode attr | any(FalconResponse f).taints(attr.getObject("body")) | - attr.(DefinitionNode).getValue() = this - ) - } - - override predicate sinks(TaintKind kind) { kind instanceof StringKind } -} diff --git a/python/ql/lib/semmle/python/web/flask/Redirect.qll b/python/ql/lib/semmle/python/web/flask/Redirect.qll deleted file mode 100644 index f178c2520ea..00000000000 --- a/python/ql/lib/semmle/python/web/flask/Redirect.qll +++ /dev/null @@ -1,26 +0,0 @@ -/** - * Provides class representing the `flask.redirect` function. - * This module is intended to be imported into a taint-tracking query - * to extend `TaintSink`. - */ - -import python -import semmle.python.dataflow.TaintTracking -import semmle.python.security.strings.Basic -import semmle.python.web.flask.General - -deprecated FunctionValue flask_redirect() { result = Value::named("flask.redirect") } - -/** - * Represents an argument to the `flask.redirect` function. - */ -deprecated class FlaskRedirect extends HttpRedirectTaintSink { - override string toString() { result = "flask.redirect" } - - FlaskRedirect() { - exists(CallNode call | - flask_redirect().getACall() = call and - this = call.getAnArg() - ) - } -} diff --git a/python/ql/lib/semmle/python/web/pyramid/Redirect.qll b/python/ql/lib/semmle/python/web/pyramid/Redirect.qll deleted file mode 100644 index 852df8cf422..00000000000 --- a/python/ql/lib/semmle/python/web/pyramid/Redirect.qll +++ /dev/null @@ -1,33 +0,0 @@ -/** - * Provides class representing the `pyramid.redirect` function. - * This module is intended to be imported into a taint-tracking query - * to extend `TaintSink`. - */ - -import python -import semmle.python.dataflow.TaintTracking -import semmle.python.security.strings.Basic -import semmle.python.web.Http - -deprecated private ClassValue redirectClass() { - exists(ModuleValue ex | ex.getName() = "pyramid.httpexceptions" | - ex.attr("HTTPFound") = result - or - ex.attr("HTTPTemporaryRedirect") = result - ) -} - -/** - * Represents an argument to the `tornado.redirect` function. - */ -deprecated class PyramidRedirect extends HttpRedirectTaintSink { - override string toString() { result = "pyramid.redirect" } - - PyramidRedirect() { - exists(CallNode call | call.getFunction().pointsTo(redirectClass()) | - call.getArg(0) = this - or - call.getArgByName("location") = this - ) - } -} diff --git a/python/ql/lib/semmle/python/web/pyramid/Response.qll b/python/ql/lib/semmle/python/web/pyramid/Response.qll deleted file mode 100644 index 0512e7e7f4d..00000000000 --- a/python/ql/lib/semmle/python/web/pyramid/Response.qll +++ /dev/null @@ -1,37 +0,0 @@ -import python -import semmle.python.dataflow.TaintTracking -import semmle.python.security.strings.Basic -import semmle.python.web.Http -private import semmle.python.web.pyramid.View - -/** - * A pyramid response, which is vulnerable to any sort of - * http response malice. - */ -deprecated class PyramidRoutedResponse extends HttpResponseTaintSink { - PyramidRoutedResponse() { - exists(PythonFunctionValue view | - is_pyramid_view_function(view.getScope()) and - this = view.getAReturnedNode() - ) - } - - override predicate sinks(TaintKind kind) { kind instanceof StringKind } - - override string toString() { result = "pyramid.routed.response" } -} - -deprecated class PyramidCookieSet extends CookieSet, CallNode { - PyramidCookieSet() { - exists(ControlFlowNode f | - f = this.getFunction().(AttrNode).getObject("set_cookie") and - f.pointsTo().getClass() = Value::named("pyramid.response.Response") - ) - } - - override string toString() { result = CallNode.super.toString() } - - override ControlFlowNode getKey() { result = this.getArg(0) } - - override ControlFlowNode getValue() { result = this.getArg(1) } -} diff --git a/python/ql/lib/semmle/python/web/stdlib/Response.qll b/python/ql/lib/semmle/python/web/stdlib/Response.qll deleted file mode 100644 index 651201e204d..00000000000 --- a/python/ql/lib/semmle/python/web/stdlib/Response.qll +++ /dev/null @@ -1,43 +0,0 @@ -/** - * Provides the sinks for HTTP servers defined with standard library (stdlib). - */ - -import python -import semmle.python.dataflow.TaintTracking -import semmle.python.web.Http - -deprecated private predicate is_wfile(AttrNode wfile) { - exists(ClassValue cls | - // Python 2 - cls.getABaseType+() = Value::named("BaseHTTPServer.BaseHTTPRequestHandler") - or - // Python 3 - cls.getABaseType+() = Value::named("http.server.BaseHTTPRequestHandler") - | - wfile.getObject("wfile").pointsTo().getClass() = cls - ) -} - -/** Sink for `h.wfile.write` where `h` is an instance of BaseHttpRequestHandler. */ -deprecated class StdLibWFileWriteSink extends HttpResponseTaintSink { - StdLibWFileWriteSink() { - exists(CallNode call | - is_wfile(call.getFunction().(AttrNode).getObject("write")) and - call.getArg(0) = this - ) - } - - override predicate sinks(TaintKind kind) { kind instanceof ExternalStringKind } -} - -/** Sink for `h.wfile.writelines` where `h` is an instance of BaseHttpRequestHandler. */ -deprecated class StdLibWFileWritelinesSink extends HttpResponseTaintSink { - StdLibWFileWritelinesSink() { - exists(CallNode call | - is_wfile(call.getFunction().(AttrNode).getObject("writelines")) and - call.getArg(0) = this - ) - } - - override predicate sinks(TaintKind kind) { kind instanceof ExternalStringSequenceKind } -} diff --git a/python/ql/lib/semmle/python/web/tornado/Redirect.qll b/python/ql/lib/semmle/python/web/tornado/Redirect.qll deleted file mode 100644 index 87965a9be23..00000000000 --- a/python/ql/lib/semmle/python/web/tornado/Redirect.qll +++ /dev/null @@ -1,28 +0,0 @@ -/** - * Provides class representing the `tornado.redirect` function. - * This module is intended to be imported into a taint-tracking query - * to extend `TaintSink`. - */ - -import python -import semmle.python.dataflow.TaintTracking -import semmle.python.security.strings.Basic -import semmle.python.web.Http -import Tornado - -/** - * Represents an argument to the `tornado.redirect` function. - */ -deprecated class TornadoHttpRequestHandlerRedirect extends HttpRedirectTaintSink { - override string toString() { result = "tornado.HttpRequestHandler.redirect" } - - TornadoHttpRequestHandlerRedirect() { - exists(CallNode call, ControlFlowNode node | - node = call.getFunction().(AttrNode).getObject("redirect") and - isTornadoRequestHandlerInstance(node) and - this = call.getArg(0) - ) - } - - override predicate sinks(TaintKind kind) { kind instanceof StringKind } -} diff --git a/python/ql/lib/semmle/python/web/tornado/Response.qll b/python/ql/lib/semmle/python/web/tornado/Response.qll deleted file mode 100644 index fed57b14a02..00000000000 --- a/python/ql/lib/semmle/python/web/tornado/Response.qll +++ /dev/null @@ -1,47 +0,0 @@ -import python -import semmle.python.dataflow.TaintTracking -import semmle.python.security.strings.Basic -private import semmle.python.web.Http -import Tornado - -deprecated class TornadoConnection extends TaintKind { - TornadoConnection() { this = "tornado.http.connection" } -} - -deprecated class TornadoConnectionSource extends TaintSource { - TornadoConnectionSource() { - isTornadoRequestHandlerInstance(this.(AttrNode).getObject("connection")) - } - - override string toString() { result = "Tornado http connection source" } - - override predicate isSourceOf(TaintKind kind) { kind instanceof TornadoConnection } -} - -deprecated class TornadoConnectionWrite extends HttpResponseTaintSink { - override string toString() { result = "tornado.connection.write" } - - TornadoConnectionWrite() { - exists(CallNode call, ControlFlowNode conn | - conn = call.getFunction().(AttrNode).getObject("write") and - this = call.getAnArg() and - exists(TornadoConnection tc | tc.taints(conn)) - ) - } - - override predicate sinks(TaintKind kind) { kind instanceof StringKind } -} - -deprecated class TornadoHttpRequestHandlerWrite extends HttpResponseTaintSink { - override string toString() { result = "tornado.HttpRequestHandler.write" } - - TornadoHttpRequestHandlerWrite() { - exists(CallNode call, ControlFlowNode node | - node = call.getFunction().(AttrNode).getObject("write") and - this = call.getAnArg() and - isTornadoRequestHandlerInstance(node) - ) - } - - override predicate sinks(TaintKind kind) { kind instanceof StringKind } -} diff --git a/python/ql/lib/semmle/python/web/turbogears/Response.qll b/python/ql/lib/semmle/python/web/turbogears/Response.qll deleted file mode 100644 index 331de6bce48..00000000000 --- a/python/ql/lib/semmle/python/web/turbogears/Response.qll +++ /dev/null @@ -1,31 +0,0 @@ -import python -import semmle.python.dataflow.TaintTracking -import semmle.python.security.strings.Basic -import semmle.python.web.Http -import TurboGears - -deprecated class ControllerMethodReturnValue extends HttpResponseTaintSink { - override string toString() { result = "TurboGears ControllerMethodReturnValue" } - - ControllerMethodReturnValue() { - exists(TurboGearsControllerMethod m | - m.getAReturnValueFlowNode() = this and - not m.isTemplated() - ) - } - - override predicate sinks(TaintKind kind) { kind instanceof StringKind } -} - -deprecated class ControllerMethodTemplatedReturnValue extends HttpResponseTaintSink { - override string toString() { result = "TurboGears ControllerMethodTemplatedReturnValue" } - - ControllerMethodTemplatedReturnValue() { - exists(TurboGearsControllerMethod m | - m.getAReturnValueFlowNode() = this and - m.isTemplated() - ) - } - - override predicate sinks(TaintKind kind) { kind instanceof ExternalStringDictKind } -} diff --git a/python/ql/lib/semmle/python/web/twisted/Response.qll b/python/ql/lib/semmle/python/web/twisted/Response.qll deleted file mode 100644 index 65313ab5280..00000000000 --- a/python/ql/lib/semmle/python/web/twisted/Response.qll +++ /dev/null @@ -1,45 +0,0 @@ -import python -import semmle.python.dataflow.TaintTracking -import semmle.python.web.Http -import semmle.python.security.strings.Basic -import Twisted -import Request - -deprecated class TwistedResponse extends HttpResponseTaintSink { - TwistedResponse() { - exists(PythonFunctionValue func, string name | - isKnownRequestHandlerMethodName(name) and - name = func.getName() and - func = getTwistedRequestHandlerMethod(name) and - this = func.getAReturnedNode() - ) - } - - override predicate sinks(TaintKind kind) { kind instanceof ExternalStringKind } - - override string toString() { result = "Twisted response" } -} - -/** - * A sink of taint in the form of a "setter" method on a twisted request - * object, which affects the properties of the subsequent response sent to this - * request. - */ -deprecated class TwistedRequestSetter extends HttpResponseTaintSink { - TwistedRequestSetter() { - exists(CallNode call, ControlFlowNode node, string name | - ( - name = "setHeader" or - name = "addCookie" or - name = "write" - ) and - any(TwistedRequest t).taints(node) and - node = call.getFunction().(AttrNode).getObject(name) and - this = call.getAnArg() - ) - } - - override predicate sinks(TaintKind kind) { kind instanceof ExternalStringKind } - - override string toString() { result = "Twisted request setter" } -} diff --git a/python/ql/src/CHANGELOG.md b/python/ql/src/CHANGELOG.md index 712de670fdc..655914b4a32 100644 --- a/python/ql/src/CHANGELOG.md +++ b/python/ql/src/CHANGELOG.md @@ -1,3 +1,9 @@ +## 0.7.3 + +### Bug Fixes + +* The display name (`@name`) of the `py/unsafe-deserialization` query has been updated in favor of consistency with other languages. + ## 0.7.2 No user-facing changes. diff --git a/python/ql/src/Security/CWE-798/HardcodedCredentials.ql b/python/ql/src/Security/CWE-798/HardcodedCredentials.ql index 4c9818e91f8..d1d29a78ff5 100644 --- a/python/ql/src/Security/CWE-798/HardcodedCredentials.ql +++ b/python/ql/src/Security/CWE-798/HardcodedCredentials.ql @@ -13,13 +13,10 @@ */ import python -import semmle.python.security.Paths -import semmle.python.dataflow.TaintTracking +import semmle.python.dataflow.new.DataFlow +import semmle.python.dataflow.new.TaintTracking import semmle.python.filters.Tests - -class HardcodedValue extends TaintKind { - HardcodedValue() { this = "hard coded value" } -} +import DataFlow::PathGraph bindingset[char, fraction] predicate fewer_characters_than(StrConst str, string char, float fraction) { @@ -78,31 +75,27 @@ predicate maybeCredential(ControlFlowNode f) { ) } -class HardcodedValueSource extends TaintSource { - HardcodedValueSource() { maybeCredential(this) } - - override predicate isSourceOf(TaintKind kind) { kind instanceof HardcodedValue } +class HardcodedValueSource extends DataFlow::Node { + HardcodedValueSource() { maybeCredential(this.asCfgNode()) } } -class CredentialSink extends TaintSink { +class CredentialSink extends DataFlow::Node { CredentialSink() { exists(string name | name.regexpMatch(getACredentialRegex()) and not name.matches("%file") | - any(FunctionValue func).getNamedArgumentForCall(_, name) = this + any(FunctionValue func).getNamedArgumentForCall(_, name) = this.asCfgNode() or - exists(Keyword k | k.getArg() = name and k.getValue().getAFlowNode() = this) + exists(Keyword k | k.getArg() = name and k.getValue().getAFlowNode() = this.asCfgNode()) or exists(CompareNode cmp, NameNode n | n.getId() = name | - cmp.operands(this, any(Eq eq), n) + cmp.operands(this.asCfgNode(), any(Eq eq), n) or - cmp.operands(n, any(Eq eq), this) + cmp.operands(n, any(Eq eq), this.asCfgNode()) ) ) } - - override predicate sinks(TaintKind kind) { kind instanceof HardcodedValue } } /** @@ -118,16 +111,14 @@ private string getACredentialRegex() { class HardcodedCredentialsConfiguration extends TaintTracking::Configuration { HardcodedCredentialsConfiguration() { this = "Hardcoded credentials configuration" } - override predicate isSource(TaintTracking::Source source) { - source instanceof HardcodedValueSource - } + override predicate isSource(DataFlow::Node source) { source instanceof HardcodedValueSource } - override predicate isSink(TaintTracking::Sink sink) { sink instanceof CredentialSink } + override predicate isSink(DataFlow::Node sink) { sink instanceof CredentialSink } } -from HardcodedCredentialsConfiguration config, TaintedPathSource src, TaintedPathSink sink +from HardcodedCredentialsConfiguration config, DataFlow::PathNode src, DataFlow::PathNode sink where config.hasFlowPath(src, sink) and - not any(TestScope test).contains(src.getAstNode()) -select src.getSource(), src, sink, "This hardcoded value is $@.", sink.getNode(), + not any(TestScope test).contains(src.getNode().asCfgNode().getNode()) +select src.getNode(), src, sink, "This hardcoded value is $@.", sink.getNode(), "used as credentials" diff --git a/python/ql/src/change-notes/2023-06-16-zipslip-rename.md b/python/ql/src/change-notes/2023-06-16-zipslip-rename.md new file mode 100644 index 00000000000..4d4d4db15c3 --- /dev/null +++ b/python/ql/src/change-notes/2023-06-16-zipslip-rename.md @@ -0,0 +1,4 @@ +--- +category: fix +--- +* The query "Arbitrary file write during archive extraction ("Zip Slip")" (`py/zipslip`) has been renamed to "Arbitrary file access during archive extraction ("Zip Slip")." diff --git a/python/ql/src/change-notes/2023-06-02-unsafe-deserialization-name-update.md b/python/ql/src/change-notes/released/0.7.3.md similarity index 81% rename from python/ql/src/change-notes/2023-06-02-unsafe-deserialization-name-update.md rename to python/ql/src/change-notes/released/0.7.3.md index d786e9dc14d..2f9c3725fb0 100644 --- a/python/ql/src/change-notes/2023-06-02-unsafe-deserialization-name-update.md +++ b/python/ql/src/change-notes/released/0.7.3.md @@ -1,4 +1,5 @@ ---- -category: fix ---- -* The display name (`@name`) of the `py/unsafe-deserialization` query has been updated in favor of consistency with other languages. \ No newline at end of file +## 0.7.3 + +### Bug Fixes + +* The display name (`@name`) of the `py/unsafe-deserialization` query has been updated in favor of consistency with other languages. diff --git a/python/ql/src/codeql-pack.release.yml b/python/ql/src/codeql-pack.release.yml index fee171e9685..a4ea9c8de17 100644 --- a/python/ql/src/codeql-pack.release.yml +++ b/python/ql/src/codeql-pack.release.yml @@ -1,2 +1,2 @@ --- -lastReleaseVersion: 0.7.2 +lastReleaseVersion: 0.7.3 diff --git a/python/ql/src/experimental/Security/CWE-022/ZipSlip.qhelp b/python/ql/src/experimental/Security/CWE-022/ZipSlip.qhelp index 89260db7bd7..4afb7cc9a2a 100644 --- a/python/ql/src/experimental/Security/CWE-022/ZipSlip.qhelp +++ b/python/ql/src/experimental/Security/CWE-022/ZipSlip.qhelp @@ -4,16 +4,15 @@ -

Extracting files from a malicious zip archive without validating that the destination file path -is within the destination directory can cause files outside the destination directory to be -overwritten, due to the possible presence of directory traversal elements (..) in -archive paths.

+

Extracting files from a malicious zip file, or similar type of archive, +is at risk of directory traversal attacks if filenames from the archive are +not properly validated.

Zip archives contain archive entries representing each file in the archive. These entries include a file path for the entry, but these file paths are not restricted and may contain unexpected special elements such as the directory traversal element (..). If these -file paths are used to determine an output file to write the contents of the archive item to, then -the file may be written to an unexpected location. This can result in sensitive information being +file paths are used to create a filesystem path, then a file operation may happen in an +unexpected location. This can result in sensitive information being revealed or deleted, or an attacker being able to influence behavior by modifying unexpected files.

diff --git a/python/ql/src/experimental/Security/CWE-022/ZipSlip.ql b/python/ql/src/experimental/Security/CWE-022/ZipSlip.ql index aea193dde36..eba8da087b3 100644 --- a/python/ql/src/experimental/Security/CWE-022/ZipSlip.ql +++ b/python/ql/src/experimental/Security/CWE-022/ZipSlip.ql @@ -1,8 +1,8 @@ /** - * @name Arbitrary file write during archive extraction ("Zip Slip") - * @description Extracting files from a malicious archive without validating that the - * destination file path is within the destination directory can cause files outside - * the destination directory to be overwritten. + * @name Arbitrary file access during archive extraction ("Zip Slip") + * @description Extracting files from a malicious ZIP file, or similar type of archive, without + * validating that the destination file path is within the destination directory + * can allow an attacker to unexpectedly gain access to resources. * @kind path-problem * @id py/zipslip * @problem.severity error diff --git a/python/ql/src/qlpack.yml b/python/ql/src/qlpack.yml index eb327c2e42e..7dd13516d8b 100644 --- a/python/ql/src/qlpack.yml +++ b/python/ql/src/qlpack.yml @@ -1,5 +1,5 @@ name: codeql/python-queries -version: 0.7.3-dev +version: 0.7.4-dev groups: - python - queries diff --git a/python/ql/test/3/library-tests/taint/strings/Taint.qll b/python/ql/test/3/library-tests/taint/strings/Taint.qll deleted file mode 100644 index 3368a1c4f70..00000000000 --- a/python/ql/test/3/library-tests/taint/strings/Taint.qll +++ /dev/null @@ -1,44 +0,0 @@ -import python -import semmle.python.dataflow.TaintTracking -import semmle.python.security.strings.Untrusted -import semmle.python.security.Exceptions - -class SimpleSource extends TaintSource { - SimpleSource() { this.(NameNode).getId() = "TAINTED_STRING" } - - override predicate isSourceOf(TaintKind kind) { kind instanceof ExternalStringKind } - - override string toString() { result = "taint source" } -} - -class ListSource extends TaintSource { - ListSource() { this.(NameNode).getId() = "TAINTED_LIST" } - - override predicate isSourceOf(TaintKind kind) { kind instanceof ExternalStringSequenceKind } - - override string toString() { result = "list taint source" } -} - -class DictSource extends TaintSource { - DictSource() { this.(NameNode).getId() = "TAINTED_DICT" } - - override predicate isSourceOf(TaintKind kind) { kind instanceof ExternalStringDictKind } - - override string toString() { result = "dict taint source" } -} - -class ExceptionInfoSource extends TaintSource { - ExceptionInfoSource() { this.(NameNode).getId() = "TAINTED_EXCEPTION_INFO" } - - override predicate isSourceOf(TaintKind kind) { kind instanceof ExceptionInfo } - - override string toString() { result = "Exception info source" } -} - -class ExternalFileObjectSource extends TaintSource { - ExternalFileObjectSource() { this.(NameNode).getId() = "TAINTED_FILE" } - - override predicate isSourceOf(TaintKind kind) { kind instanceof ExternalFileObject } - - override string toString() { result = "Tainted file source" } -} diff --git a/python/ql/test/3/library-tests/taint/strings/TestTaint.expected b/python/ql/test/3/library-tests/taint/strings/TestTaint.expected deleted file mode 100644 index 3e5e988f825..00000000000 --- a/python/ql/test/3/library-tests/taint/strings/TestTaint.expected +++ /dev/null @@ -1 +0,0 @@ -| test.py:4 | ok | fstring | Fstring | externally controlled string | diff --git a/python/ql/test/3/library-tests/taint/strings/TestTaint.ql b/python/ql/test/3/library-tests/taint/strings/TestTaint.ql deleted file mode 100644 index 7b9c6025c9d..00000000000 --- a/python/ql/test/3/library-tests/taint/strings/TestTaint.ql +++ /dev/null @@ -1,33 +0,0 @@ -import python -import semmle.python.security.TaintTracking -import semmle.python.web.HttpRequest -import semmle.python.security.strings.Untrusted -import Taint - -from - Call call, Expr arg, boolean expected_taint, boolean has_taint, string test_res, - string taint_string -where - call.getLocation().getFile().getShortName() = "test.py" and - ( - call.getFunc().(Name).getId() = "ensure_tainted" and - expected_taint = true - or - call.getFunc().(Name).getId() = "ensure_not_tainted" and - expected_taint = false - ) and - arg = call.getAnArg() and - ( - not exists(TaintedNode tainted | tainted.getAstNode() = arg) and - taint_string = "" and - has_taint = false - or - exists(TaintedNode tainted | tainted.getAstNode() = arg | - taint_string = tainted.getTaintKind().toString() - ) and - has_taint = true - ) and - if expected_taint = has_taint then test_res = "ok " else test_res = "fail" -// if expected_taint = has_taint then test_res = "✓" else test_res = "✕" -select arg.getLocation().toString(), test_res, call.getScope().(Function).getName(), arg.toString(), - taint_string diff --git a/python/ql/test/3/library-tests/taint/strings/test.py b/python/ql/test/3/library-tests/taint/strings/test.py deleted file mode 100644 index e1c25b5dccc..00000000000 --- a/python/ql/test/3/library-tests/taint/strings/test.py +++ /dev/null @@ -1,5 +0,0 @@ -def fstring(): - tainted_string = TAINTED_STRING - ensure_tainted( - f"foo {tainted_string} bar" - ) diff --git a/python/ql/test/3/library-tests/taint/unpacking/Taint.qll b/python/ql/test/3/library-tests/taint/unpacking/Taint.qll deleted file mode 100644 index 010b9738c5c..00000000000 --- a/python/ql/test/3/library-tests/taint/unpacking/Taint.qll +++ /dev/null @@ -1,27 +0,0 @@ -import python -import semmle.python.dataflow.TaintTracking -import semmle.python.security.strings.Untrusted - -class SimpleSource extends TaintSource { - SimpleSource() { this.(NameNode).getId() = "TAINTED_STRING" } - - override predicate isSourceOf(TaintKind kind) { kind instanceof ExternalStringKind } - - override string toString() { result = "taint source" } -} - -class ListSource extends TaintSource { - ListSource() { this.(NameNode).getId() = "TAINTED_LIST" } - - override predicate isSourceOf(TaintKind kind) { kind instanceof ExternalStringSequenceKind } - - override string toString() { result = "list taint source" } -} - -class DictSource extends TaintSource { - DictSource() { this.(NameNode).getId() = "TAINTED_DICT" } - - override predicate isSourceOf(TaintKind kind) { kind instanceof ExternalStringDictKind } - - override string toString() { result = "dict taint source" } -} diff --git a/python/ql/test/3/library-tests/taint/unpacking/TestTaint.expected b/python/ql/test/3/library-tests/taint/unpacking/TestTaint.expected deleted file mode 100644 index b6aacc7d670..00000000000 --- a/python/ql/test/3/library-tests/taint/unpacking/TestTaint.expected +++ /dev/null @@ -1,9 +0,0 @@ -| test.py:11 | extended_unpacking | first | externally controlled string | -| test.py:11 | extended_unpacking | last | externally controlled string | -| test.py:11 | extended_unpacking | rest | [externally controlled string] | -| test.py:16 | also_allowed | a | [externally controlled string] | -| test.py:24 | also_allowed | b | NO TAINT | -| test.py:24 | also_allowed | c | NO TAINT | -| test.py:31 | nested | x | externally controlled string | -| test.py:31 | nested | xs | [externally controlled string] | -| test.py:31 | nested | ys | [externally controlled string] | diff --git a/python/ql/test/3/library-tests/taint/unpacking/TestTaint.ql b/python/ql/test/3/library-tests/taint/unpacking/TestTaint.ql deleted file mode 100644 index 47883578516..00000000000 --- a/python/ql/test/3/library-tests/taint/unpacking/TestTaint.ql +++ /dev/null @@ -1,19 +0,0 @@ -import python -import semmle.python.dataflow.TaintTracking -import Taint - -from Call call, Expr arg, string taint_string -where - call.getLocation().getFile().getShortName() = "test.py" and - call.getFunc().(Name).getId() = "test" and - arg = call.getAnArg() and - ( - not exists(TaintedNode tainted | tainted.getAstNode() = arg) and - taint_string = "NO TAINT" - or - exists(TaintedNode tainted | tainted.getAstNode() = arg | - taint_string = tainted.getTaintKind().toString() - ) - ) -select arg.getLocation().toString(), call.getScope().(Function).getName(), arg.toString(), - taint_string diff --git a/python/ql/test/3/library-tests/taint/unpacking/test.py b/python/ql/test/3/library-tests/taint/unpacking/test.py deleted file mode 100644 index 6807ddebee8..00000000000 --- a/python/ql/test/3/library-tests/taint/unpacking/test.py +++ /dev/null @@ -1,31 +0,0 @@ -# Extended Iterable Unpacking -- PEP 3132 -# https://www.python.org/dev/peps/pep-3132/ - - -def test(*args): - pass - - -def extended_unpacking(): - first, *rest, last = TAINTED_LIST - test(first, rest, last) - - -def also_allowed(): - *a, = TAINTED_LIST - test(a) - - # for b, *c in [(1, 2, 3), (4, 5, 6, 7)]: - # print(c) - # i=0; c=[2,3] - # i=1; c=[5,6,7] - - for b, *c in [TAINTED_LIST, TAINTED_LIST]: - test(b, c) # TODO: mark `c` as [taint] - -def nested(): - l = TAINTED_LIST - ll = [l,l] - - [[x, *xs], ys] = ll - test(x, xs, ys) diff --git a/python/ql/test/experimental/dataflow/TestUtil/DataflowQueryTest.qll b/python/ql/test/experimental/dataflow/TestUtil/DataflowQueryTest.qll index 1c9259038bc..74a43bb4cc4 100644 --- a/python/ql/test/experimental/dataflow/TestUtil/DataflowQueryTest.qll +++ b/python/ql/test/experimental/dataflow/TestUtil/DataflowQueryTest.qll @@ -3,12 +3,10 @@ import semmle.python.dataflow.new.DataFlow import TestUtilities.InlineExpectationsTest private import semmle.python.dataflow.new.internal.PrintNode -class DataFlowQueryTest extends InlineExpectationsTest { - DataFlowQueryTest() { this = "DataFlowQueryTest" } +module DataFlowQueryTest implements TestSig { + string getARelevantTag() { result = "result" } - override string getARelevantTag() { result = "result" } - - override predicate hasActualResult(Location location, string element, string tag, string value) { + predicate hasActualResult(Location location, string element, string tag, string value) { exists(DataFlow::Configuration cfg, DataFlow::Node sink | cfg.hasFlowTo(sink) | location = sink.getLocation() and tag = "result" and @@ -22,7 +20,7 @@ class DataFlowQueryTest extends InlineExpectationsTest { // Sometimes a line contains both an alert and a safe sink. // In this situation, the annotation form `OK(safe sink)` // can be useful. - override predicate hasOptionalResult(Location location, string element, string tag, string value) { + predicate hasOptionalResult(Location location, string element, string tag, string value) { exists(DataFlow::Configuration cfg, DataFlow::Node sink | cfg.isSink(sink) or cfg.isSink(sink, _) | @@ -34,6 +32,8 @@ class DataFlowQueryTest extends InlineExpectationsTest { } } +import MakeTest + query predicate missingAnnotationOnSink(Location location, string error, string element) { error = "ERROR, you should add `# $ MISSING: result=BAD` or `result=OK` annotation" and exists(DataFlow::Node sink | @@ -42,13 +42,13 @@ query predicate missingAnnotationOnSink(Location location, string error, string location = sink.getLocation() and element = prettyExpr(sink.asExpr()) and not exists(DataFlow::Configuration cfg | cfg.hasFlowTo(sink)) and - not exists(FalseNegativeExpectation missingResult | + not exists(FalseNegativeTestExpectation missingResult | missingResult.getTag() = "result" and missingResult.getValue() = "BAD" and missingResult.getLocation().getFile() = location.getFile() and missingResult.getLocation().getStartLine() = location.getStartLine() ) and - not exists(GoodExpectation okResult | + not exists(GoodTestExpectation okResult | okResult.getTag() = "result" and okResult.getValue() in ["OK", "OK(" + prettyNode(sink) + ")"] and okResult.getLocation().getFile() = location.getFile() and diff --git a/python/ql/test/experimental/dataflow/TestUtil/FlowTest.qll b/python/ql/test/experimental/dataflow/TestUtil/FlowTest.qll index 2f5d7de5952..e6abf741b36 100644 --- a/python/ql/test/experimental/dataflow/TestUtil/FlowTest.qll +++ b/python/ql/test/experimental/dataflow/TestUtil/FlowTest.qll @@ -3,22 +3,21 @@ import semmle.python.dataflow.new.DataFlow import TestUtilities.InlineExpectationsTest private import semmle.python.dataflow.new.internal.PrintNode -abstract class FlowTest extends InlineExpectationsTest { - bindingset[this] - FlowTest() { any() } +signature module FlowTestSig { + string flowTag(); - abstract string flowTag(); + predicate relevantFlow(DataFlow::Node fromNode, DataFlow::Node toNode); +} - abstract predicate relevantFlow(DataFlow::Node fromNode, DataFlow::Node toNode); +private module FlowTest implements TestSig { + string getARelevantTag() { result = Impl::flowTag() } - override string getARelevantTag() { result = this.flowTag() } - - override predicate hasActualResult(Location location, string element, string tag, string value) { - exists(DataFlow::Node fromNode, DataFlow::Node toNode | this.relevantFlow(fromNode, toNode) | + predicate hasActualResult(Location location, string element, string tag, string value) { + exists(DataFlow::Node fromNode, DataFlow::Node toNode | Impl::relevantFlow(fromNode, toNode) | location = toNode.getLocation() and - tag = this.flowTag() and + tag = Impl::flowTag() and value = - "\"" + prettyNode(fromNode).replaceAll("\"", "'") + this.lineStr(fromNode, toNode) + " -> " + + "\"" + prettyNode(fromNode).replaceAll("\"", "'") + lineStr(fromNode, toNode) + " -> " + prettyNode(toNode).replaceAll("\"", "'") + "\"" and element = toNode.toString() ) @@ -38,3 +37,11 @@ abstract class FlowTest extends InlineExpectationsTest { ) } } + +module MakeFlowTest { + import MakeTest> +} + +module MakeFlowTest2 { + import MakeTest, FlowTest>> +} diff --git a/python/ql/test/experimental/dataflow/TestUtil/LocalFlowStepTest.qll b/python/ql/test/experimental/dataflow/TestUtil/LocalFlowStepTest.qll index c2c180627ec..6cbfe917fd4 100644 --- a/python/ql/test/experimental/dataflow/TestUtil/LocalFlowStepTest.qll +++ b/python/ql/test/experimental/dataflow/TestUtil/LocalFlowStepTest.qll @@ -2,12 +2,12 @@ import python import semmle.python.dataflow.new.DataFlow import FlowTest -class LocalFlowStepTest extends FlowTest { - LocalFlowStepTest() { this = "LocalFlowStepTest" } +module LocalFlowStepTest implements FlowTestSig { + string flowTag() { result = "step" } - override string flowTag() { result = "step" } - - override predicate relevantFlow(DataFlow::Node fromNode, DataFlow::Node toNode) { + predicate relevantFlow(DataFlow::Node fromNode, DataFlow::Node toNode) { DataFlow::localFlowStep(fromNode, toNode) } } + +import MakeFlowTest diff --git a/python/ql/test/experimental/dataflow/TestUtil/MaximalFlowTest.qll b/python/ql/test/experimental/dataflow/TestUtil/MaximalFlowTest.qll index 6615afb9247..681e51ca604 100644 --- a/python/ql/test/experimental/dataflow/TestUtil/MaximalFlowTest.qll +++ b/python/ql/test/experimental/dataflow/TestUtil/MaximalFlowTest.qll @@ -3,25 +3,23 @@ import semmle.python.dataflow.new.DataFlow private import semmle.python.dataflow.new.internal.DataFlowPrivate import FlowTest -class MaximalFlowTest extends FlowTest { - MaximalFlowTest() { this = "MaximalFlowTest" } +module MaximalFlowTest implements FlowTestSig { + string flowTag() { result = "flow" } - override string flowTag() { result = "flow" } - - override predicate relevantFlow(DataFlow::Node source, DataFlow::Node sink) { + predicate relevantFlow(DataFlow::Node source, DataFlow::Node sink) { source != sink and - exists(MaximalFlowsConfig cfg | cfg.hasFlow(source, sink)) + MaximalFlows::flow(source, sink) } } +import MakeFlowTest + /** * A configuration to find all "maximal" flows. * To be used on small programs. */ -class MaximalFlowsConfig extends DataFlow::Configuration { - MaximalFlowsConfig() { this = "MaximalFlowsConfig" } - - override predicate isSource(DataFlow::Node node) { +module MaximalFlowsConfig implements DataFlow::ConfigSig { + predicate isSource(DataFlow::Node node) { exists(node.getLocation().getFile().getRelativePath()) and not node.asCfgNode() instanceof CallNode and not node.asCfgNode().getNode() instanceof Return and @@ -32,7 +30,7 @@ class MaximalFlowsConfig extends DataFlow::Configuration { not DataFlow::localFlowStep(_, node) } - override predicate isSink(DataFlow::Node node) { + predicate isSink(DataFlow::Node node) { exists(node.getLocation().getFile().getRelativePath()) and not any(CallNode c).getArg(_) = node.asCfgNode() and not node instanceof DataFlow::ArgumentNode and @@ -40,3 +38,5 @@ class MaximalFlowsConfig extends DataFlow::Configuration { not DataFlow::localFlowStep(node, _) } } + +module MaximalFlows = DataFlow::Global; diff --git a/python/ql/test/experimental/dataflow/TestUtil/NormalDataflowTest.qll b/python/ql/test/experimental/dataflow/TestUtil/NormalDataflowTest.qll index f526a1f43ae..a327886fedd 100644 --- a/python/ql/test/experimental/dataflow/TestUtil/NormalDataflowTest.qll +++ b/python/ql/test/experimental/dataflow/TestUtil/NormalDataflowTest.qll @@ -3,20 +3,20 @@ import experimental.dataflow.TestUtil.FlowTest import experimental.dataflow.testConfig private import semmle.python.dataflow.new.internal.PrintNode -class DataFlowTest extends FlowTest { - DataFlowTest() { this = "DataFlowTest" } +module DataFlowTest implements FlowTestSig { + string flowTag() { result = "flow" } - override string flowTag() { result = "flow" } - - override predicate relevantFlow(DataFlow::Node source, DataFlow::Node sink) { - exists(TestConfiguration cfg | cfg.hasFlow(source, sink)) + predicate relevantFlow(DataFlow::Node source, DataFlow::Node sink) { + TestFlow::flow(source, sink) } } +import MakeFlowTest + query predicate missingAnnotationOnSink(Location location, string error, string element) { error = "ERROR, you should add `# $ MISSING: flow` annotation" and exists(DataFlow::Node sink | - any(TestConfiguration config).isSink(sink) and + TestConfig::isSink(sink) and // note: we only care about `SINK` and not `SINK_F`, so we have to reconstruct manually. exists(DataFlow::CallCfgNode call | call.getFunction().asCfgNode().(NameNode).getId() = "SINK" and @@ -24,8 +24,8 @@ query predicate missingAnnotationOnSink(Location location, string error, string ) and location = sink.getLocation() and element = prettyExpr(sink.asExpr()) and - not any(TestConfiguration config).hasFlow(_, sink) and - not exists(FalseNegativeExpectation missingResult | + not TestFlow::flowTo(sink) and + not exists(FalseNegativeTestExpectation missingResult | missingResult.getTag() = "flow" and missingResult.getLocation().getFile() = location.getFile() and missingResult.getLocation().getStartLine() = location.getStartLine() diff --git a/python/ql/test/experimental/dataflow/TestUtil/NormalTaintTrackingTest.qll b/python/ql/test/experimental/dataflow/TestUtil/NormalTaintTrackingTest.qll index 9619679da03..4a07dc4d2d6 100644 --- a/python/ql/test/experimental/dataflow/TestUtil/NormalTaintTrackingTest.qll +++ b/python/ql/test/experimental/dataflow/TestUtil/NormalTaintTrackingTest.qll @@ -3,16 +3,16 @@ import experimental.dataflow.TestUtil.FlowTest import experimental.dataflow.testTaintConfig private import semmle.python.dataflow.new.internal.PrintNode -class DataFlowTest extends FlowTest { - DataFlowTest() { this = "DataFlowTest" } +module DataFlowTest implements FlowTestSig { + string flowTag() { result = "flow" } - override string flowTag() { result = "flow" } - - override predicate relevantFlow(DataFlow::Node source, DataFlow::Node sink) { - exists(TestConfiguration cfg | cfg.hasFlow(source, sink)) + predicate relevantFlow(DataFlow::Node source, DataFlow::Node sink) { + TestFlow::flow(source, sink) } } +import MakeFlowTest + query predicate missingAnnotationOnSink(Location location, string error, string element) { error = "ERROR, you should add `# $ MISSING: flow` annotation" and exists(DataFlow::Node sink | @@ -23,8 +23,8 @@ query predicate missingAnnotationOnSink(Location location, string error, string ) and location = sink.getLocation() and element = prettyExpr(sink.asExpr()) and - not any(TestConfiguration config).hasFlow(_, sink) and - not exists(FalseNegativeExpectation missingResult | + not TestFlow::flowTo(sink) and + not exists(FalseNegativeTestExpectation missingResult | missingResult.getTag() = "flow" and missingResult.getLocation().getFile() = location.getFile() and missingResult.getLocation().getStartLine() = location.getStartLine() diff --git a/python/ql/test/experimental/dataflow/TestUtil/UnresolvedCalls.qll b/python/ql/test/experimental/dataflow/TestUtil/UnresolvedCalls.qll index 003d02ba530..9b26d8c9175 100644 --- a/python/ql/test/experimental/dataflow/TestUtil/UnresolvedCalls.qll +++ b/python/ql/test/experimental/dataflow/TestUtil/UnresolvedCalls.qll @@ -4,11 +4,11 @@ private import semmle.python.dataflow.new.internal.DataFlowPrivate as DataFlowPr private import semmle.python.ApiGraphs import TestUtilities.InlineExpectationsTest -class UnresolvedCallExpectations extends InlineExpectationsTest { - UnresolvedCallExpectations() { this = "UnresolvedCallExpectations" } - - override string getARelevantTag() { result = "unresolved_call" } +signature module UnresolvedCallExpectationsSig { + predicate unresolvedCall(CallNode call); +} +module DefaultUnresolvedCallExpectations implements UnresolvedCallExpectationsSig { predicate unresolvedCall(CallNode call) { not exists(DataFlowPrivate::DataFlowCall dfc | exists(dfc.getCallable()) and dfc.getNode() = call @@ -16,14 +16,22 @@ class UnresolvedCallExpectations extends InlineExpectationsTest { not DataFlowPrivate::resolveClassCall(call, _) and not call = API::builtin(_).getACall().asCfgNode() } - - override predicate hasActualResult(Location location, string element, string tag, string value) { - exists(location.getFile().getRelativePath()) and - exists(CallNode call | this.unresolvedCall(call) | - location = call.getLocation() and - tag = "unresolved_call" and - value = prettyExpr(call.getNode()) and - element = call.toString() - ) - } +} + +module MakeUnresolvedCallExpectations { + private module UnresolvedCallExpectations implements TestSig { + string getARelevantTag() { result = "unresolved_call" } + + predicate hasActualResult(Location location, string element, string tag, string value) { + exists(location.getFile().getRelativePath()) and + exists(CallNode call | Impl::unresolvedCall(call) | + location = call.getLocation() and + tag = "unresolved_call" and + value = prettyExpr(call.getNode()) and + element = call.toString() + ) + } + } + + import MakeTest } diff --git a/python/ql/test/experimental/dataflow/basic/localFlowStepTest.expected b/python/ql/test/experimental/dataflow/basic/localFlowStepTest.expected index e69de29bb2d..48de9172b36 100644 --- a/python/ql/test/experimental/dataflow/basic/localFlowStepTest.expected +++ b/python/ql/test/experimental/dataflow/basic/localFlowStepTest.expected @@ -0,0 +1,2 @@ +failures +testFailures diff --git a/python/ql/test/experimental/dataflow/basic/maximalFlowTest.expected b/python/ql/test/experimental/dataflow/basic/maximalFlowTest.expected index e69de29bb2d..48de9172b36 100644 --- a/python/ql/test/experimental/dataflow/basic/maximalFlowTest.expected +++ b/python/ql/test/experimental/dataflow/basic/maximalFlowTest.expected @@ -0,0 +1,2 @@ +failures +testFailures diff --git a/python/ql/test/experimental/dataflow/coverage/NormalDataflowTest.expected b/python/ql/test/experimental/dataflow/coverage/NormalDataflowTest.expected index 3875da4e143..04431311999 100644 --- a/python/ql/test/experimental/dataflow/coverage/NormalDataflowTest.expected +++ b/python/ql/test/experimental/dataflow/coverage/NormalDataflowTest.expected @@ -1,2 +1,3 @@ missingAnnotationOnSink failures +testFailures diff --git a/python/ql/test/experimental/dataflow/exceptions/NormalDataflowTest.expected b/python/ql/test/experimental/dataflow/exceptions/NormalDataflowTest.expected index 3875da4e143..04431311999 100644 --- a/python/ql/test/experimental/dataflow/exceptions/NormalDataflowTest.expected +++ b/python/ql/test/experimental/dataflow/exceptions/NormalDataflowTest.expected @@ -1,2 +1,3 @@ missingAnnotationOnSink failures +testFailures diff --git a/python/ql/test/experimental/dataflow/fieldflow/NormalDataflowTest.expected b/python/ql/test/experimental/dataflow/fieldflow/NormalDataflowTest.expected index 3875da4e143..04431311999 100644 --- a/python/ql/test/experimental/dataflow/fieldflow/NormalDataflowTest.expected +++ b/python/ql/test/experimental/dataflow/fieldflow/NormalDataflowTest.expected @@ -1,2 +1,3 @@ missingAnnotationOnSink failures +testFailures diff --git a/python/ql/test/experimental/dataflow/fieldflow/UnresolvedCalls.expected b/python/ql/test/experimental/dataflow/fieldflow/UnresolvedCalls.expected index e69de29bb2d..48de9172b36 100644 --- a/python/ql/test/experimental/dataflow/fieldflow/UnresolvedCalls.expected +++ b/python/ql/test/experimental/dataflow/fieldflow/UnresolvedCalls.expected @@ -0,0 +1,2 @@ +failures +testFailures diff --git a/python/ql/test/experimental/dataflow/fieldflow/UnresolvedCalls.ql b/python/ql/test/experimental/dataflow/fieldflow/UnresolvedCalls.ql index af73ca552fc..3c7498bd651 100644 --- a/python/ql/test/experimental/dataflow/fieldflow/UnresolvedCalls.ql +++ b/python/ql/test/experimental/dataflow/fieldflow/UnresolvedCalls.ql @@ -2,11 +2,13 @@ import python import experimental.dataflow.TestUtil.UnresolvedCalls private import semmle.python.dataflow.new.DataFlow -class IgnoreDictMethod extends UnresolvedCallExpectations { - override predicate unresolvedCall(CallNode call) { - super.unresolvedCall(call) and +module IgnoreDictMethod implements UnresolvedCallExpectationsSig { + predicate unresolvedCall(CallNode call) { + DefaultUnresolvedCallExpectations::unresolvedCall(call) and not any(DataFlow::MethodCallNode methodCall | methodCall.getMethodName() in ["get", "setdefault"] ).asCfgNode() = call } } + +import MakeUnresolvedCallExpectations diff --git a/python/ql/test/experimental/dataflow/match/NormalDataflowTest.expected b/python/ql/test/experimental/dataflow/match/NormalDataflowTest.expected index 3875da4e143..04431311999 100644 --- a/python/ql/test/experimental/dataflow/match/NormalDataflowTest.expected +++ b/python/ql/test/experimental/dataflow/match/NormalDataflowTest.expected @@ -1,2 +1,3 @@ missingAnnotationOnSink failures +testFailures diff --git a/python/ql/test/experimental/dataflow/model-summaries/InlineTaintTest.expected b/python/ql/test/experimental/dataflow/model-summaries/InlineTaintTest.expected new file mode 100644 index 00000000000..4a72c551661 --- /dev/null +++ b/python/ql/test/experimental/dataflow/model-summaries/InlineTaintTest.expected @@ -0,0 +1,4 @@ +failures +argumentToEnsureNotTaintedNotMarkedAsSpurious +untaintedArgumentToEnsureTaintedNotMarkedAsMissing +testFailures diff --git a/python/ql/test/experimental/dataflow/model-summaries/InlineTaintTest.ql b/python/ql/test/experimental/dataflow/model-summaries/InlineTaintTest.ql new file mode 100644 index 00000000000..551266d7455 --- /dev/null +++ b/python/ql/test/experimental/dataflow/model-summaries/InlineTaintTest.ql @@ -0,0 +1,4 @@ +import python +private import TestSummaries +import experimental.meta.InlineTaintTest +import MakeInlineTaintTest diff --git a/python/ql/test/experimental/dataflow/model-summaries/NormalDataflowTest.expected b/python/ql/test/experimental/dataflow/model-summaries/NormalDataflowTest.expected new file mode 100644 index 00000000000..04431311999 --- /dev/null +++ b/python/ql/test/experimental/dataflow/model-summaries/NormalDataflowTest.expected @@ -0,0 +1,3 @@ +missingAnnotationOnSink +failures +testFailures diff --git a/python/ql/test/experimental/dataflow/model-summaries/NormalDataflowTest.ql b/python/ql/test/experimental/dataflow/model-summaries/NormalDataflowTest.ql new file mode 100644 index 00000000000..3e311335e14 --- /dev/null +++ b/python/ql/test/experimental/dataflow/model-summaries/NormalDataflowTest.ql @@ -0,0 +1,3 @@ +import python +private import TestSummaries +import experimental.dataflow.TestUtil.NormalDataflowTest diff --git a/python/ql/test/experimental/dataflow/model-summaries/TestSummaries.qll b/python/ql/test/experimental/dataflow/model-summaries/TestSummaries.qll new file mode 100644 index 00000000000..5f1e0a1f90b --- /dev/null +++ b/python/ql/test/experimental/dataflow/model-summaries/TestSummaries.qll @@ -0,0 +1,25 @@ +private import python +private import semmle.python.dataflow.new.FlowSummary +private import semmle.python.frameworks.data.ModelsAsData +private import semmle.python.ApiGraphs + +private class StepsFromModel extends ModelInput::SummaryModelCsv { + override predicate row(string row) { + row = + [ + "foo;Member[MS_identity];Argument[0];ReturnValue;value", + "foo;Member[MS_apply_lambda];Argument[1];Argument[0].Parameter[0];value", + "foo;Member[MS_apply_lambda];Argument[0].ReturnValue;ReturnValue;value", + "foo;Member[MS_reversed];Argument[0].ListElement;ReturnValue.ListElement;value", + "foo;Member[MS_reversed];Argument[0];ReturnValue;taint", + "foo;Member[MS_list_map];Argument[1].ListElement;Argument[0].Parameter[0];value", + "foo;Member[MS_list_map];Argument[0].ReturnValue;ReturnValue.ListElement;value", + "foo;Member[MS_list_map];Argument[1];ReturnValue;taint", + "foo;Member[MS_append_to_list];Argument[0].ListElement;ReturnValue.ListElement;value", + "foo;Member[MS_append_to_list];Argument[1];ReturnValue.ListElement;value", + "foo;Member[MS_append_to_list];Argument[0];ReturnValue;taint", + "foo;Member[MS_append_to_list];Argument[1];ReturnValue;taint", + "json;Member[MS_loads];Argument[0];ReturnValue;taint" + ] + } +} diff --git a/python/ql/test/experimental/dataflow/model-summaries/model_summaries.py b/python/ql/test/experimental/dataflow/model-summaries/model_summaries.py new file mode 100644 index 00000000000..ee02918b079 --- /dev/null +++ b/python/ql/test/experimental/dataflow/model-summaries/model_summaries.py @@ -0,0 +1,122 @@ + +import sys +import os + +sys.path.append(os.path.dirname(os.path.dirname((__file__)))) +from testlib import expects + +# These are defined so that we can evaluate the test code. +NONSOURCE = "not a source" +SOURCE = "source" + + +def is_source(x): + return x == "source" or x == b"source" or x == 42 or x == 42.0 or x == 42j + + +def SINK(x): + if is_source(x): + print("OK") + else: + print("Unexpected flow", x) + + +def SINK_F(x): + if is_source(x): + print("Unexpected flow", x) + else: + print("OK") + +ensure_tainted = ensure_not_tainted = print +TAINTED_STRING = "TAINTED_STRING" + +from foo import MS_identity, MS_apply_lambda, MS_reversed, MS_list_map, MS_append_to_list + +# Simple summary +via_identity = MS_identity(SOURCE) +SINK(via_identity) # $ flow="SOURCE, l:-1 -> via_identity" + +# Lambda summary +via_lambda = MS_apply_lambda(lambda x: [x], SOURCE) +SINK(via_lambda[0]) # $ flow="SOURCE, l:-1 -> via_lambda[0]" + +# A lambda that breaks the flow +not_via_lambda = MS_apply_lambda(lambda x: 1, SOURCE) +SINK_F(not_via_lambda) + + +# Collection summaries +via_reversed = MS_reversed([SOURCE]) +SINK(via_reversed[0]) # $ flow="SOURCE, l:-1 -> via_reversed[0]" + +tainted_list = MS_reversed(TAINTED_LIST) +ensure_tainted( + tainted_list, # $ tainted + tainted_list[0], # $ tainted +) + +# Complex summaries +def box(x): + return [x] + +via_map = MS_list_map(box, [SOURCE]) +SINK(via_map[0][0]) # $ flow="SOURCE, l:-1 -> via_map[0][0]" + +tainted_mapped = MS_list_map(box, TAINTED_LIST) +ensure_tainted( + tainted_mapped, # $ tainted + tainted_mapped[0][0], # $ tainted +) + +def explicit_identity(x): + return x + +via_map_explicit = MS_list_map(explicit_identity, [SOURCE]) +SINK(via_map_explicit[0]) # $ flow="SOURCE, l:-1 -> via_map_explicit[0]" + +tainted_mapped_explicit = MS_list_map(explicit_identity, TAINTED_LIST) +ensure_tainted( + tainted_mapped_explicit, # $ tainted + tainted_mapped_explicit[0], # $ tainted +) + +via_map_summary = MS_list_map(MS_identity, [SOURCE]) +SINK(via_map_summary[0]) # $ flow="SOURCE, l:-1 -> via_map_summary[0]" + +tainted_mapped_summary = MS_list_map(MS_identity, TAINTED_LIST) +ensure_tainted( + tainted_mapped_summary, # $ tainted + tainted_mapped_summary[0], # $ tainted +) + +via_append_el = MS_append_to_list([], SOURCE) +SINK(via_append_el[0]) # $ flow="SOURCE, l:-1 -> via_append_el[0]" + +tainted_list_el = MS_append_to_list([], TAINTED_STRING) +ensure_tainted( + tainted_list_el, # $ tainted + tainted_list_el[0], # $ tainted +) + +via_append = MS_append_to_list([SOURCE], NONSOURCE) +SINK(via_append[0]) # $ flow="SOURCE, l:-1 -> via_append[0]" + +tainted_list_implicit = MS_append_to_list(TAINTED_LIST, NONSOURCE) +ensure_tainted( + tainted_list, # $ tainted + tainted_list[0], # $ tainted +) + +# Modeled flow-summary is not value preserving +from json import MS_loads as json_loads + +# so no data-flow +SINK_F(json_loads(SOURCE)) +SINK_F(json_loads(SOURCE)[0]) + +# but has taint-flow +tainted_resultlist = json_loads(TAINTED_STRING) +ensure_tainted( + tainted_resultlist, # $ tainted + tainted_resultlist[0], # $ tainted +) diff --git a/python/ql/test/experimental/dataflow/module-initialization/localFlow.expected b/python/ql/test/experimental/dataflow/module-initialization/localFlow.expected index e69de29bb2d..48de9172b36 100644 --- a/python/ql/test/experimental/dataflow/module-initialization/localFlow.expected +++ b/python/ql/test/experimental/dataflow/module-initialization/localFlow.expected @@ -0,0 +1,2 @@ +failures +testFailures diff --git a/python/ql/test/experimental/dataflow/module-initialization/localFlow.ql b/python/ql/test/experimental/dataflow/module-initialization/localFlow.ql index 635902e7045..8ef3860955d 100644 --- a/python/ql/test/experimental/dataflow/module-initialization/localFlow.ql +++ b/python/ql/test/experimental/dataflow/module-initialization/localFlow.ql @@ -4,12 +4,10 @@ import experimental.dataflow.TestUtil.FlowTest private import semmle.python.dataflow.new.internal.PrintNode private import semmle.python.dataflow.new.internal.DataFlowPrivate as DP -class ImportTimeLocalFlowTest extends FlowTest { - ImportTimeLocalFlowTest() { this = "ImportTimeLocalFlowTest" } +module ImportTimeLocalFlowTest implements FlowTestSig { + string flowTag() { result = "importTimeFlow" } - override string flowTag() { result = "importTimeFlow" } - - override predicate relevantFlow(DataFlow::Node nodeFrom, DataFlow::Node nodeTo) { + predicate relevantFlow(DataFlow::Node nodeFrom, DataFlow::Node nodeTo) { nodeFrom.getLocation().getFile().getBaseName() = "multiphase.py" and // results are displayed next to `nodeTo`, so we need a line to write on nodeTo.getLocation().getStartLine() > 0 and @@ -18,12 +16,10 @@ class ImportTimeLocalFlowTest extends FlowTest { } } -class RuntimeLocalFlowTest extends FlowTest { - RuntimeLocalFlowTest() { this = "RuntimeLocalFlowTest" } +module RuntimeLocalFlowTest implements FlowTestSig { + string flowTag() { result = "runtimeFlow" } - override string flowTag() { result = "runtimeFlow" } - - override predicate relevantFlow(DataFlow::Node nodeFrom, DataFlow::Node nodeTo) { + predicate relevantFlow(DataFlow::Node nodeFrom, DataFlow::Node nodeTo) { nodeFrom.getLocation().getFile().getBaseName() = "multiphase.py" and // results are displayed next to `nodeTo`, so we need a line to write on nodeTo.getLocation().getStartLine() > 0 and @@ -34,3 +30,5 @@ class RuntimeLocalFlowTest extends FlowTest { DP::runtimeJumpStep(nodeFrom, nodeTo) } } + +import MakeFlowTest2 diff --git a/python/ql/test/experimental/dataflow/regression/dataflow.ql b/python/ql/test/experimental/dataflow/regression/dataflow.ql index c95b2f2111f..39763fa4814 100644 --- a/python/ql/test/experimental/dataflow/regression/dataflow.ql +++ b/python/ql/test/experimental/dataflow/regression/dataflow.ql @@ -9,5 +9,5 @@ import python import experimental.dataflow.testConfig from DataFlow::Node source, DataFlow::Node sink -where exists(TestConfiguration cfg | cfg.hasFlow(source, sink)) +where TestFlow::flow(source, sink) select source, sink diff --git a/python/ql/test/experimental/dataflow/summaries/NormalTaintTrackingTest.expected b/python/ql/test/experimental/dataflow/summaries/NormalTaintTrackingTest.expected index 3875da4e143..04431311999 100644 --- a/python/ql/test/experimental/dataflow/summaries/NormalTaintTrackingTest.expected +++ b/python/ql/test/experimental/dataflow/summaries/NormalTaintTrackingTest.expected @@ -1,2 +1,3 @@ missingAnnotationOnSink failures +testFailures diff --git a/python/ql/test/experimental/dataflow/summaries/TestSummaries.qll b/python/ql/test/experimental/dataflow/summaries/TestSummaries.qll index 971a653c469..cdd61420bbb 100644 --- a/python/ql/test/experimental/dataflow/summaries/TestSummaries.qll +++ b/python/ql/test/experimental/dataflow/summaries/TestSummaries.qll @@ -60,7 +60,7 @@ private class SummarizedCallableApplyLambda extends SummarizedCallable { } private class SummarizedCallableReversed extends SummarizedCallable { - SummarizedCallableReversed() { this = "reversed" } + SummarizedCallableReversed() { this = "list_reversed" } override DataFlow::CallCfgNode getACall() { result.getFunction().asCfgNode().(NameNode).getId() = this diff --git a/python/ql/test/experimental/dataflow/summaries/summaries.py b/python/ql/test/experimental/dataflow/summaries/summaries.py index 1532a5393d8..806b39f6dc8 100644 --- a/python/ql/test/experimental/dataflow/summaries/summaries.py +++ b/python/ql/test/experimental/dataflow/summaries/summaries.py @@ -66,3 +66,21 @@ SINK(tainted_list[0]) # $ flow="SOURCE, l:-1 -> tainted_list[0]" from json import loads as json_loads tainted_resultlist = json_loads(SOURCE) SINK(tainted_resultlist[0]) # $ flow="SOURCE, l:-1 -> tainted_resultlist[0]" + + +# Class methods are not handled right now + +class MyClass: + @staticmethod + def foo(x): + return x + + def bar(self, x): + return x + +through_staticmethod = apply_lambda(MyClass.foo, SOURCE) +through_staticmethod # $ MISSING: flow + +mc = MyClass() +through_method = apply_lambda(mc.bar, SOURCE) +through_method # $ MISSING: flow diff --git a/python/ql/test/experimental/dataflow/summaries/summaries.ql b/python/ql/test/experimental/dataflow/summaries/summaries.ql index f2c0a522279..d3c0206d41f 100644 --- a/python/ql/test/experimental/dataflow/summaries/summaries.ql +++ b/python/ql/test/experimental/dataflow/summaries/summaries.ql @@ -4,7 +4,7 @@ import python import semmle.python.dataflow.new.FlowSummary -import DataFlow::PathGraph +import TestFlow::PathGraph import semmle.python.dataflow.new.TaintTracking import semmle.python.dataflow.new.internal.FlowSummaryImpl import semmle.python.ApiGraphs @@ -16,6 +16,6 @@ query predicate invalidSpecComponent(SummarizedCallable sc, string s, string c) Private::External::invalidSpecComponent(s, c) } -from DataFlow::PathNode source, DataFlow::PathNode sink, TestConfiguration conf -where conf.hasFlowPath(source, sink) +from TestFlow::PathNode source, TestFlow::PathNode sink +where TestFlow::flowPath(source, sink) select sink, source, sink, "$@", source, source.toString() diff --git a/python/ql/test/experimental/dataflow/tainttracking/commonSanitizer/InlineTaintTest.expected b/python/ql/test/experimental/dataflow/tainttracking/commonSanitizer/InlineTaintTest.expected index 79d760d87f4..4a72c551661 100644 --- a/python/ql/test/experimental/dataflow/tainttracking/commonSanitizer/InlineTaintTest.expected +++ b/python/ql/test/experimental/dataflow/tainttracking/commonSanitizer/InlineTaintTest.expected @@ -1,3 +1,4 @@ +failures argumentToEnsureNotTaintedNotMarkedAsSpurious untaintedArgumentToEnsureTaintedNotMarkedAsMissing -failures +testFailures diff --git a/python/ql/test/experimental/dataflow/tainttracking/commonSanitizer/InlineTaintTest.ql b/python/ql/test/experimental/dataflow/tainttracking/commonSanitizer/InlineTaintTest.ql index 048d530dd41..46263250a9b 100644 --- a/python/ql/test/experimental/dataflow/tainttracking/commonSanitizer/InlineTaintTest.ql +++ b/python/ql/test/experimental/dataflow/tainttracking/commonSanitizer/InlineTaintTest.ql @@ -1,6 +1,12 @@ import experimental.meta.InlineTaintTest import semmle.python.dataflow.new.BarrierGuards -class CustomSanitizerOverrides extends TestTaintTrackingConfiguration { - override predicate isSanitizer(DataFlow::Node node) { node instanceof StringConstCompareBarrier } +module CustomSanitizerOverridesConfig implements DataFlow::ConfigSig { + predicate isSource = TestTaintTrackingConfig::isSource/1; + + predicate isSink = TestTaintTrackingConfig::isSink/1; + + predicate isBarrier(DataFlow::Node node) { node instanceof StringConstCompareBarrier } } + +import MakeInlineTaintTest diff --git a/python/ql/test/experimental/dataflow/tainttracking/customSanitizer/InlineTaintTest.expected b/python/ql/test/experimental/dataflow/tainttracking/customSanitizer/InlineTaintTest.expected index fdad063534b..6e4a1c072bc 100644 --- a/python/ql/test/experimental/dataflow/tainttracking/customSanitizer/InlineTaintTest.expected +++ b/python/ql/test/experimental/dataflow/tainttracking/customSanitizer/InlineTaintTest.expected @@ -1,25 +1,26 @@ +failures argumentToEnsureNotTaintedNotMarkedAsSpurious untaintedArgumentToEnsureTaintedNotMarkedAsMissing -failures +testFailures isSanitizer -| TestTaintTrackingConfiguration | test.py:21:39:21:39 | ControlFlowNode for s | -| TestTaintTrackingConfiguration | test.py:34:39:34:39 | ControlFlowNode for s | -| TestTaintTrackingConfiguration | test.py:52:28:52:28 | ControlFlowNode for s | -| TestTaintTrackingConfiguration | test.py:66:10:66:29 | ControlFlowNode for emulated_escaping() | -| TestTaintTrackingConfiguration | test_logical.py:33:28:33:28 | ControlFlowNode for s | -| TestTaintTrackingConfiguration | test_logical.py:40:28:40:28 | ControlFlowNode for s | -| TestTaintTrackingConfiguration | test_logical.py:48:28:48:28 | ControlFlowNode for s | -| TestTaintTrackingConfiguration | test_logical.py:53:28:53:28 | ControlFlowNode for s | -| TestTaintTrackingConfiguration | test_logical.py:92:28:92:28 | ControlFlowNode for s | -| TestTaintTrackingConfiguration | test_logical.py:103:28:103:28 | ControlFlowNode for s | -| TestTaintTrackingConfiguration | test_logical.py:111:28:111:28 | ControlFlowNode for s | -| TestTaintTrackingConfiguration | test_logical.py:130:28:130:28 | ControlFlowNode for s | -| TestTaintTrackingConfiguration | test_logical.py:137:28:137:28 | ControlFlowNode for s | -| TestTaintTrackingConfiguration | test_logical.py:148:28:148:28 | ControlFlowNode for s | -| TestTaintTrackingConfiguration | test_logical.py:151:28:151:28 | ControlFlowNode for s | -| TestTaintTrackingConfiguration | test_logical.py:158:28:158:28 | ControlFlowNode for s | -| TestTaintTrackingConfiguration | test_logical.py:167:24:167:24 | ControlFlowNode for s | -| TestTaintTrackingConfiguration | test_logical.py:176:24:176:24 | ControlFlowNode for s | -| TestTaintTrackingConfiguration | test_logical.py:185:24:185:24 | ControlFlowNode for s | -| TestTaintTrackingConfiguration | test_logical.py:193:24:193:24 | ControlFlowNode for s | -| TestTaintTrackingConfiguration | test_reference.py:31:28:31:28 | ControlFlowNode for s | +| test.py:21:39:21:39 | ControlFlowNode for s | +| test.py:34:39:34:39 | ControlFlowNode for s | +| test.py:52:28:52:28 | ControlFlowNode for s | +| test.py:66:10:66:29 | ControlFlowNode for emulated_escaping() | +| test_logical.py:33:28:33:28 | ControlFlowNode for s | +| test_logical.py:40:28:40:28 | ControlFlowNode for s | +| test_logical.py:48:28:48:28 | ControlFlowNode for s | +| test_logical.py:53:28:53:28 | ControlFlowNode for s | +| test_logical.py:92:28:92:28 | ControlFlowNode for s | +| test_logical.py:103:28:103:28 | ControlFlowNode for s | +| test_logical.py:111:28:111:28 | ControlFlowNode for s | +| test_logical.py:130:28:130:28 | ControlFlowNode for s | +| test_logical.py:137:28:137:28 | ControlFlowNode for s | +| test_logical.py:148:28:148:28 | ControlFlowNode for s | +| test_logical.py:151:28:151:28 | ControlFlowNode for s | +| test_logical.py:158:28:158:28 | ControlFlowNode for s | +| test_logical.py:167:24:167:24 | ControlFlowNode for s | +| test_logical.py:176:24:176:24 | ControlFlowNode for s | +| test_logical.py:185:24:185:24 | ControlFlowNode for s | +| test_logical.py:193:24:193:24 | ControlFlowNode for s | +| test_reference.py:31:28:31:28 | ControlFlowNode for s | diff --git a/python/ql/test/experimental/dataflow/tainttracking/customSanitizer/InlineTaintTest.ql b/python/ql/test/experimental/dataflow/tainttracking/customSanitizer/InlineTaintTest.ql index 984cf74d036..597f368b02f 100644 --- a/python/ql/test/experimental/dataflow/tainttracking/customSanitizer/InlineTaintTest.ql +++ b/python/ql/test/experimental/dataflow/tainttracking/customSanitizer/InlineTaintTest.ql @@ -12,8 +12,12 @@ predicate isUnsafeCheck(DataFlow::GuardNode g, ControlFlowNode node, boolean bra branch = false } -class CustomSanitizerOverrides extends TestTaintTrackingConfiguration { - override predicate isSanitizer(DataFlow::Node node) { +module CustomSanitizerOverridesConfig implements DataFlow::ConfigSig { + predicate isSource = TestTaintTrackingConfig::isSource/1; + + predicate isSink = TestTaintTrackingConfig::isSink/1; + + predicate isBarrier(DataFlow::Node node) { exists(Call call | call.getFunc().(Name).getId() = "emulated_authentication_check" and call.getArg(0) = node.asExpr() @@ -27,7 +31,9 @@ class CustomSanitizerOverrides extends TestTaintTrackingConfiguration { } } -query predicate isSanitizer(TestTaintTrackingConfiguration conf, DataFlow::Node node) { +import MakeInlineTaintTest + +query predicate isSanitizer(DataFlow::Node node) { exists(node.getLocation().getFile().getRelativePath()) and - conf.isSanitizer(node) + CustomSanitizerOverridesConfig::isBarrier(node) } diff --git a/python/ql/test/experimental/dataflow/tainttracking/defaultAdditionalTaintStep-py3/InlineTaintTest.expected b/python/ql/test/experimental/dataflow/tainttracking/defaultAdditionalTaintStep-py3/InlineTaintTest.expected index 79d760d87f4..4a72c551661 100644 --- a/python/ql/test/experimental/dataflow/tainttracking/defaultAdditionalTaintStep-py3/InlineTaintTest.expected +++ b/python/ql/test/experimental/dataflow/tainttracking/defaultAdditionalTaintStep-py3/InlineTaintTest.expected @@ -1,3 +1,4 @@ +failures argumentToEnsureNotTaintedNotMarkedAsSpurious untaintedArgumentToEnsureTaintedNotMarkedAsMissing -failures +testFailures diff --git a/python/ql/test/experimental/dataflow/tainttracking/defaultAdditionalTaintStep-py3/InlineTaintTest.ql b/python/ql/test/experimental/dataflow/tainttracking/defaultAdditionalTaintStep-py3/InlineTaintTest.ql index 027ad8667be..8524da5fe7d 100644 --- a/python/ql/test/experimental/dataflow/tainttracking/defaultAdditionalTaintStep-py3/InlineTaintTest.ql +++ b/python/ql/test/experimental/dataflow/tainttracking/defaultAdditionalTaintStep-py3/InlineTaintTest.ql @@ -1 +1,2 @@ import experimental.meta.InlineTaintTest +import MakeInlineTaintTest diff --git a/python/ql/test/experimental/dataflow/tainttracking/defaultAdditionalTaintStep/InlineTaintTest.expected b/python/ql/test/experimental/dataflow/tainttracking/defaultAdditionalTaintStep/InlineTaintTest.expected index 79d760d87f4..4a72c551661 100644 --- a/python/ql/test/experimental/dataflow/tainttracking/defaultAdditionalTaintStep/InlineTaintTest.expected +++ b/python/ql/test/experimental/dataflow/tainttracking/defaultAdditionalTaintStep/InlineTaintTest.expected @@ -1,3 +1,4 @@ +failures argumentToEnsureNotTaintedNotMarkedAsSpurious untaintedArgumentToEnsureTaintedNotMarkedAsMissing -failures +testFailures diff --git a/python/ql/test/experimental/dataflow/tainttracking/defaultAdditionalTaintStep/InlineTaintTest.ql b/python/ql/test/experimental/dataflow/tainttracking/defaultAdditionalTaintStep/InlineTaintTest.ql index 027ad8667be..8524da5fe7d 100644 --- a/python/ql/test/experimental/dataflow/tainttracking/defaultAdditionalTaintStep/InlineTaintTest.ql +++ b/python/ql/test/experimental/dataflow/tainttracking/defaultAdditionalTaintStep/InlineTaintTest.ql @@ -1 +1,2 @@ import experimental.meta.InlineTaintTest +import MakeInlineTaintTest diff --git a/python/ql/test/experimental/dataflow/tainttracking/generator-flow/InlineTaintTest.expected b/python/ql/test/experimental/dataflow/tainttracking/generator-flow/InlineTaintTest.expected index 79d760d87f4..4a72c551661 100644 --- a/python/ql/test/experimental/dataflow/tainttracking/generator-flow/InlineTaintTest.expected +++ b/python/ql/test/experimental/dataflow/tainttracking/generator-flow/InlineTaintTest.expected @@ -1,3 +1,4 @@ +failures argumentToEnsureNotTaintedNotMarkedAsSpurious untaintedArgumentToEnsureTaintedNotMarkedAsMissing -failures +testFailures diff --git a/python/ql/test/experimental/dataflow/tainttracking/generator-flow/InlineTaintTest.ql b/python/ql/test/experimental/dataflow/tainttracking/generator-flow/InlineTaintTest.ql index 027ad8667be..8524da5fe7d 100644 --- a/python/ql/test/experimental/dataflow/tainttracking/generator-flow/InlineTaintTest.ql +++ b/python/ql/test/experimental/dataflow/tainttracking/generator-flow/InlineTaintTest.ql @@ -1 +1,2 @@ import experimental.meta.InlineTaintTest +import MakeInlineTaintTest diff --git a/python/ql/test/experimental/dataflow/tainttracking/generator-flow/NormalDataflowTest.expected b/python/ql/test/experimental/dataflow/tainttracking/generator-flow/NormalDataflowTest.expected index 3875da4e143..04431311999 100644 --- a/python/ql/test/experimental/dataflow/tainttracking/generator-flow/NormalDataflowTest.expected +++ b/python/ql/test/experimental/dataflow/tainttracking/generator-flow/NormalDataflowTest.expected @@ -1,2 +1,3 @@ missingAnnotationOnSink failures +testFailures diff --git a/python/ql/test/experimental/dataflow/tainttracking/unwanted-global-flow/InlineTaintTest.expected b/python/ql/test/experimental/dataflow/tainttracking/unwanted-global-flow/InlineTaintTest.expected index 79d760d87f4..4a72c551661 100644 --- a/python/ql/test/experimental/dataflow/tainttracking/unwanted-global-flow/InlineTaintTest.expected +++ b/python/ql/test/experimental/dataflow/tainttracking/unwanted-global-flow/InlineTaintTest.expected @@ -1,3 +1,4 @@ +failures argumentToEnsureNotTaintedNotMarkedAsSpurious untaintedArgumentToEnsureTaintedNotMarkedAsMissing -failures +testFailures diff --git a/python/ql/test/experimental/dataflow/tainttracking/unwanted-global-flow/InlineTaintTest.ql b/python/ql/test/experimental/dataflow/tainttracking/unwanted-global-flow/InlineTaintTest.ql index 027ad8667be..8524da5fe7d 100644 --- a/python/ql/test/experimental/dataflow/tainttracking/unwanted-global-flow/InlineTaintTest.ql +++ b/python/ql/test/experimental/dataflow/tainttracking/unwanted-global-flow/InlineTaintTest.ql @@ -1 +1,2 @@ import experimental.meta.InlineTaintTest +import MakeInlineTaintTest diff --git a/python/ql/test/experimental/dataflow/testConfig.qll b/python/ql/test/experimental/dataflow/testConfig.qll index ab5f125d898..887f9e48e8e 100644 --- a/python/ql/test/experimental/dataflow/testConfig.qll +++ b/python/ql/test/experimental/dataflow/testConfig.qll @@ -23,10 +23,8 @@ private import python import semmle.python.dataflow.new.DataFlow -class TestConfiguration extends DataFlow::Configuration { - TestConfiguration() { this = "TestConfiguration" } - - override predicate isSource(DataFlow::Node node) { +module TestConfig implements DataFlow::ConfigSig { + predicate isSource(DataFlow::Node node) { node.(DataFlow::CfgNode).getNode().(NameNode).getId() = "SOURCE" or node.(DataFlow::CfgNode).getNode().getNode().(StrConst).getS() = "source" @@ -37,7 +35,7 @@ class TestConfiguration extends DataFlow::Configuration { // No support for complex numbers } - override predicate isSink(DataFlow::Node node) { + predicate isSink(DataFlow::Node node) { exists(DataFlow::CallCfgNode call | call.getFunction().asCfgNode().(NameNode).getId() in ["SINK", "SINK_F"] and (node = call.getArg(_) or node = call.getArgByName(_)) and @@ -45,5 +43,7 @@ class TestConfiguration extends DataFlow::Configuration { ) } - override predicate isBarrierIn(DataFlow::Node node) { this.isSource(node) } + predicate isBarrierIn(DataFlow::Node node) { isSource(node) } } + +module TestFlow = DataFlow::Global; diff --git a/python/ql/test/experimental/dataflow/testTaintConfig.qll b/python/ql/test/experimental/dataflow/testTaintConfig.qll index 09496895c9a..89e9593c89f 100644 --- a/python/ql/test/experimental/dataflow/testTaintConfig.qll +++ b/python/ql/test/experimental/dataflow/testTaintConfig.qll @@ -24,10 +24,8 @@ private import python import semmle.python.dataflow.new.DataFlow import semmle.python.dataflow.new.TaintTracking -class TestConfiguration extends TaintTracking::Configuration { - TestConfiguration() { this = "TestConfiguration" } - - override predicate isSource(DataFlow::Node node) { +module TestConfig implements DataFlow::ConfigSig { + predicate isSource(DataFlow::Node node) { node.(DataFlow::CfgNode).getNode().(NameNode).getId() = "SOURCE" or node.(DataFlow::CfgNode).getNode().getNode().(StrConst).getS() = "source" @@ -38,12 +36,14 @@ class TestConfiguration extends TaintTracking::Configuration { // No support for complex numbers } - override predicate isSink(DataFlow::Node node) { + predicate isSink(DataFlow::Node node) { exists(CallNode call | call.getFunction().(NameNode).getId() in ["SINK", "SINK_F"] and node.(DataFlow::CfgNode).getNode() = call.getAnArg() ) } - override predicate isSanitizerIn(DataFlow::Node node) { this.isSource(node) } + predicate isBarrierIn(DataFlow::Node node) { isSource(node) } } + +module TestFlow = TaintTracking::Global; diff --git a/python/ql/test/experimental/dataflow/typetracking-summaries/TestSummaries.qll b/python/ql/test/experimental/dataflow/typetracking-summaries/TestSummaries.qll new file mode 100644 index 00000000000..8d626b332a3 --- /dev/null +++ b/python/ql/test/experimental/dataflow/typetracking-summaries/TestSummaries.qll @@ -0,0 +1,189 @@ +private import python +private import semmle.python.dataflow.new.FlowSummary +private import semmle.python.ApiGraphs + +/** + * This module ensures that the `callStep` predicate in + * our type tracker implementation does not refer to the + * `getACall` predicate on `SummarizedCallable`. + */ +module RecursionGuard { + private import semmle.python.dataflow.new.internal.TypeTrackerSpecific as TT + + private class RecursionGuard extends SummarizedCallable { + RecursionGuard() { this = "TypeTrackingSummariesRecursionGuard" } + + override DataFlow::CallCfgNode getACall() { + result.getFunction().asCfgNode().(NameNode).getId() = this and + (TT::callStep(_, _) implies any()) + } + + override DataFlow::CallCfgNode getACallSimple() { none() } + + override DataFlow::ArgumentNode getACallback() { result.asExpr().(Name).getId() = this } + } + + predicate test(DataFlow::Node nodeFrom, DataFlow::Node nodeTo) { + TT::levelStepNoCall(nodeFrom, nodeTo) + } +} + +private class SummarizedCallableIdentity extends SummarizedCallable { + SummarizedCallableIdentity() { this = "TTS_identity" } + + override DataFlow::CallCfgNode getACall() { none() } + + override DataFlow::CallCfgNode getACallSimple() { + result.getFunction().asCfgNode().(NameNode).getId() = this + } + + override DataFlow::ArgumentNode getACallback() { result.asExpr().(Name).getId() = this } + + override predicate propagatesFlowExt(string input, string output, boolean preservesValue) { + input = "Argument[0]" and + output = "ReturnValue" and + preservesValue = true + } +} + +// For lambda flow to work, implement lambdaCall and lambdaCreation +private class SummarizedCallableApplyLambda extends SummarizedCallable { + SummarizedCallableApplyLambda() { this = "TTS_apply_lambda" } + + override DataFlow::CallCfgNode getACall() { none() } + + override DataFlow::CallCfgNode getACallSimple() { + result.getFunction().asCfgNode().(NameNode).getId() = this + } + + override DataFlow::ArgumentNode getACallback() { result.asExpr().(Name).getId() = this } + + override predicate propagatesFlowExt(string input, string output, boolean preservesValue) { + input = "Argument[1]" and + output = "Argument[0].Parameter[0]" and + preservesValue = true + or + input = "Argument[0].ReturnValue" and + output = "ReturnValue" and + preservesValue = true + } +} + +private class SummarizedCallableReversed extends SummarizedCallable { + SummarizedCallableReversed() { this = "TTS_reversed" } + + override DataFlow::CallCfgNode getACall() { none() } + + override DataFlow::CallCfgNode getACallSimple() { + result.getFunction().asCfgNode().(NameNode).getId() = this + } + + override DataFlow::ArgumentNode getACallback() { result.asExpr().(Name).getId() = this } + + override predicate propagatesFlowExt(string input, string output, boolean preservesValue) { + input = "Argument[0].ListElement" and + output = "ReturnValue.ListElement" and + preservesValue = true + } +} + +private class SummarizedCallableMap extends SummarizedCallable { + SummarizedCallableMap() { this = "TTS_list_map" } + + override DataFlow::CallCfgNode getACall() { none() } + + override DataFlow::CallCfgNode getACallSimple() { + result.getFunction().asCfgNode().(NameNode).getId() = this + } + + override DataFlow::ArgumentNode getACallback() { result.asExpr().(Name).getId() = this } + + override predicate propagatesFlowExt(string input, string output, boolean preservesValue) { + input = "Argument[1].ListElement" and + output = "Argument[0].Parameter[0]" and + preservesValue = true + or + input = "Argument[0].ReturnValue" and + output = "ReturnValue.ListElement" and + preservesValue = true + } +} + +private class SummarizedCallableAppend extends SummarizedCallable { + SummarizedCallableAppend() { this = "TTS_append_to_list" } + + override DataFlow::CallCfgNode getACall() { none() } + + override DataFlow::CallCfgNode getACallSimple() { + result.getFunction().asCfgNode().(NameNode).getId() = this + } + + override DataFlow::ArgumentNode getACallback() { result.asExpr().(Name).getId() = this } + + override predicate propagatesFlowExt(string input, string output, boolean preservesValue) { + input = "Argument[0]" and + output = "ReturnValue" and + preservesValue = false + or + input = "Argument[1]" and + output = "ReturnValue.ListElement" and + preservesValue = true + } +} + +private class SummarizedCallableJsonLoads extends SummarizedCallable { + SummarizedCallableJsonLoads() { this = "TTS_json.loads" } + + override DataFlow::CallCfgNode getACall() { + result = API::moduleImport("json").getMember("loads").getACall() + } + + override DataFlow::CallCfgNode getACallSimple() { none() } + + override DataFlow::ArgumentNode getACallback() { + result = API::moduleImport("json").getMember("loads").getAValueReachableFromSource() + } + + override predicate propagatesFlowExt(string input, string output, boolean preservesValue) { + input = "Argument[0]" and + output = "ReturnValue.ListElement" and + preservesValue = true + } +} + +// read and store +private class SummarizedCallableReadSecret extends SummarizedCallable { + SummarizedCallableReadSecret() { this = "TTS_read_secret" } + + override DataFlow::CallCfgNode getACall() { none() } + + override DataFlow::CallCfgNode getACallSimple() { + result.getFunction().asCfgNode().(NameNode).getId() = this + } + + override DataFlow::ArgumentNode getACallback() { result.asExpr().(Name).getId() = this } + + override predicate propagatesFlowExt(string input, string output, boolean preservesValue) { + input = "Argument[0].Attribute[secret]" and + output = "ReturnValue" and + preservesValue = true + } +} + +private class SummarizedCallableSetSecret extends SummarizedCallable { + SummarizedCallableSetSecret() { this = "TTS_set_secret" } + + override DataFlow::CallCfgNode getACall() { none() } + + override DataFlow::CallCfgNode getACallSimple() { + result.getFunction().asCfgNode().(NameNode).getId() = this + } + + override DataFlow::ArgumentNode getACallback() { result.asExpr().(Name).getId() = this } + + override predicate propagatesFlowExt(string input, string output, boolean preservesValue) { + input = "Argument[1]" and + output = "Argument[0].Attribute[secret]" and + preservesValue = true + } +} diff --git a/python/ql/test/experimental/dataflow/typetracking-summaries/summaries.py b/python/ql/test/experimental/dataflow/typetracking-summaries/summaries.py new file mode 100644 index 00000000000..f838032b063 --- /dev/null +++ b/python/ql/test/experimental/dataflow/typetracking-summaries/summaries.py @@ -0,0 +1,78 @@ +import sys +import os + +# Simple summary +tainted = TTS_identity(tracked) # $ tracked +tainted # $ tracked + +# Lambda summary +# I think the missing result is expected because type tracking +# is not allowed to flow back out of a call. +tainted_lambda = TTS_apply_lambda(lambda x: x, tracked) # $ tracked +tainted_lambda # $ MISSING: tracked + +# A lambda that directly introduces taint +bad_lambda = TTS_apply_lambda(lambda x: tracked, 1) # $ tracked +bad_lambda # $ tracked + +# A lambda that breaks the flow +untainted_lambda = TTS_apply_lambda(lambda x: 1, tracked) # $ tracked +untainted_lambda + +# Collection summaries +tainted_list = TTS_reversed([tracked]) # $ tracked +tl = tainted_list[0] +tl # $ MISSING: tracked + +# Complex summaries +def add_colon(x): + return x + ":" + +tainted_mapped = TTS_list_map(add_colon, [tracked]) # $ tracked +tm = tainted_mapped[0] +tm # $ MISSING: tracked + +def explicit_identity(x): + return x + +tainted_mapped_explicit = TTS_list_map(explicit_identity, [tracked]) # $ tracked +tainted_mapped_explicit[0] # $ MISSING: tracked + +tainted_mapped_summary = TTS_list_map(identity, [tracked]) # $ tracked +tms = tainted_mapped_summary[0] +tms # $ MISSING: tracked + +another_tainted_list = TTS_append_to_list([], tracked) # $ tracked +atl = another_tainted_list[0] +atl # $ MISSING: tracked + +# This will not work, as the call is not found by `getACallSimple`. +from json import loads as json_loads +tainted_resultlist = json_loads(tracked) # $ tracked +tr = tainted_resultlist[0] +tr # $ MISSING: tracked + +x.secret = tracked # $ tracked=secret tracked +r = TTS_read_secret(x) # $ tracked=secret tracked +r # $ tracked + +y # $ tracked=secret +TTS_set_secret(y, tracked) # $ tracked tracked=secret +y.secret # $ tracked tracked=secret + +# Class methods are not handled right now + +class MyClass: + @staticmethod + def foo(x): + return x + + def bar(self, x): + return x + +through_staticmethod = TTS_apply_lambda(MyClass.foo, tracked) # $ tracked +through_staticmethod # $ MISSING: tracked + +mc = MyClass() +through_method = TTS_apply_lambda(mc.bar, tracked) # $ tracked +through_method # $ MISSING: tracked diff --git a/python/ql/test/experimental/dataflow/typetracking-summaries/tracked.expected b/python/ql/test/experimental/dataflow/typetracking-summaries/tracked.expected new file mode 100644 index 00000000000..e69de29bb2d diff --git a/python/ql/test/experimental/dataflow/typetracking-summaries/tracked.ql b/python/ql/test/experimental/dataflow/typetracking-summaries/tracked.ql new file mode 100644 index 00000000000..e5bf62053a0 --- /dev/null +++ b/python/ql/test/experimental/dataflow/typetracking-summaries/tracked.ql @@ -0,0 +1,36 @@ +import python +import semmle.python.dataflow.new.DataFlow +import semmle.python.dataflow.new.TypeTracker +import TestUtilities.InlineExpectationsTest +import semmle.python.ApiGraphs +import TestSummaries + +// ----------------------------------------------------------------------------- +// tracked +// ----------------------------------------------------------------------------- +private DataFlow::TypeTrackingNode tracked(TypeTracker t) { + t.start() and + result.asCfgNode() = any(NameNode n | n.getId() = "tracked") + or + exists(TypeTracker t2 | result = tracked(t2).track(t2, t)) +} + +class TrackedTest extends InlineExpectationsTest { + TrackedTest() { this = "TrackedTest" } + + override string getARelevantTag() { result = "tracked" } + + override predicate hasActualResult(Location location, string element, string tag, string value) { + exists(DataFlow::Node e, TypeTracker t | + exists(e.getLocation().getFile().getRelativePath()) and + e.getLocation().getStartLine() > 0 and + tracked(t).flowsTo(e) and + // Module variables have no sensible location, and hence can't be annotated. + not e instanceof DataFlow::ModuleVariableNode and + tag = "tracked" and + location = e.getLocation() and + value = t.getAttr() and + element = e.toString() + ) + } +} diff --git a/python/ql/test/experimental/dataflow/typetracking/tracked.expected b/python/ql/test/experimental/dataflow/typetracking/tracked.expected index e69de29bb2d..48de9172b36 100644 --- a/python/ql/test/experimental/dataflow/typetracking/tracked.expected +++ b/python/ql/test/experimental/dataflow/typetracking/tracked.expected @@ -0,0 +1,2 @@ +failures +testFailures diff --git a/python/ql/test/experimental/dataflow/typetracking/tracked.ql b/python/ql/test/experimental/dataflow/typetracking/tracked.ql index c0ed62e258f..b6aa9d268d0 100644 --- a/python/ql/test/experimental/dataflow/typetracking/tracked.ql +++ b/python/ql/test/experimental/dataflow/typetracking/tracked.ql @@ -14,12 +14,10 @@ private DataFlow::TypeTrackingNode tracked(TypeTracker t) { exists(TypeTracker t2 | result = tracked(t2).track(t2, t)) } -class TrackedTest extends InlineExpectationsTest { - TrackedTest() { this = "TrackedTest" } +module TrackedTest implements TestSig { + string getARelevantTag() { result = "tracked" } - override string getARelevantTag() { result = "tracked" } - - override predicate hasActualResult(Location location, string element, string tag, string value) { + predicate hasActualResult(Location location, string element, string tag, string value) { exists(DataFlow::Node e, TypeTracker t | tracked(t).flowsTo(e) and // Module variables have no sensible location, and hence can't be annotated. @@ -54,12 +52,10 @@ private DataFlow::TypeTrackingNode string_type(TypeTracker t) { exists(TypeTracker t2 | result = string_type(t2).track(t2, t)) } -class TrackedIntTest extends InlineExpectationsTest { - TrackedIntTest() { this = "TrackedIntTest" } +module TrackedIntTest implements TestSig { + string getARelevantTag() { result = "int" } - override string getARelevantTag() { result = "int" } - - override predicate hasActualResult(Location location, string element, string tag, string value) { + predicate hasActualResult(Location location, string element, string tag, string value) { exists(DataFlow::Node e, TypeTracker t | int_type(t).flowsTo(e) and tag = "int" and @@ -70,12 +66,10 @@ class TrackedIntTest extends InlineExpectationsTest { } } -class TrackedStringTest extends InlineExpectationsTest { - TrackedStringTest() { this = "TrackedStringTest" } +module TrackedStringTest implements TestSig { + string getARelevantTag() { result = "str" } - override string getARelevantTag() { result = "str" } - - override predicate hasActualResult(Location location, string element, string tag, string value) { + predicate hasActualResult(Location location, string element, string tag, string value) { exists(DataFlow::Node e, TypeTracker t | string_type(t).flowsTo(e) and tag = "str" and @@ -100,12 +94,10 @@ private DataFlow::TypeTrackingNode tracked_self(TypeTracker t) { exists(TypeTracker t2 | result = tracked_self(t2).track(t2, t)) } -class TrackedSelfTest extends InlineExpectationsTest { - TrackedSelfTest() { this = "TrackedSelfTest" } +module TrackedSelfTest implements TestSig { + string getARelevantTag() { result = "tracked_self" } - override string getARelevantTag() { result = "tracked_self" } - - override predicate hasActualResult(Location location, string element, string tag, string value) { + predicate hasActualResult(Location location, string element, string tag, string value) { exists(DataFlow::Node e, TypeTracker t | tracked_self(t).flowsTo(e) and // Module variables have no sensible location, and hence can't be annotated. @@ -161,12 +153,10 @@ private DataFlow::TypeTrackingNode foo_bar_baz(DataFlow::TypeTracker t) { /** Gets a reference to `foo.bar.baz` (fictive attribute on `foo.bar` module). */ DataFlow::Node foo_bar_baz() { foo_bar_baz(DataFlow::TypeTracker::end()).flowsTo(result) } -class TrackedFooBarBaz extends InlineExpectationsTest { - TrackedFooBarBaz() { this = "TrackedFooBarBaz" } +module TrackedFooBarBaz implements TestSig { + string getARelevantTag() { result = "tracked_foo_bar_baz" } - override string getARelevantTag() { result = "tracked_foo_bar_baz" } - - override predicate hasActualResult(Location location, string element, string tag, string value) { + predicate hasActualResult(Location location, string element, string tag, string value) { exists(DataFlow::Node e | e = foo_bar_baz() and // Module variables have no sensible location, and hence can't be annotated. @@ -178,3 +168,5 @@ class TrackedFooBarBaz extends InlineExpectationsTest { ) } } + +import MakeTest> diff --git a/python/ql/test/experimental/dataflow/typetracking_imports/tracked.expected b/python/ql/test/experimental/dataflow/typetracking_imports/tracked.expected index e69de29bb2d..48de9172b36 100644 --- a/python/ql/test/experimental/dataflow/typetracking_imports/tracked.expected +++ b/python/ql/test/experimental/dataflow/typetracking_imports/tracked.expected @@ -0,0 +1,2 @@ +failures +testFailures diff --git a/python/ql/test/experimental/dataflow/variable-capture/CaptureTest.ql b/python/ql/test/experimental/dataflow/variable-capture/CaptureTest.ql index 68d15822cf6..a1c754e8ee5 100644 --- a/python/ql/test/experimental/dataflow/variable-capture/CaptureTest.ql +++ b/python/ql/test/experimental/dataflow/variable-capture/CaptureTest.ql @@ -7,7 +7,7 @@ module CaptureTest implements TestSig { string getARelevantTag() { result = "captured" } predicate hasActualResult(Location location, string element, string tag, string value) { - exists(DataFlow::Node sink | exists(TestConfiguration cfg | cfg.hasFlowTo(sink)) | + exists(DataFlow::Node sink | TestFlow::flowTo(sink) | location = sink.getLocation() and tag = "captured" and value = "" and diff --git a/python/ql/test/experimental/meta/ConceptsTest.qll b/python/ql/test/experimental/meta/ConceptsTest.qll index 27c8cb99ab4..48803e11fb4 100644 --- a/python/ql/test/experimental/meta/ConceptsTest.qll +++ b/python/ql/test/experimental/meta/ConceptsTest.qll @@ -4,12 +4,10 @@ import semmle.python.Concepts import TestUtilities.InlineExpectationsTest private import semmle.python.dataflow.new.internal.PrintNode -class SystemCommandExecutionTest extends InlineExpectationsTest { - SystemCommandExecutionTest() { this = "SystemCommandExecutionTest" } +module SystemCommandExecutionTest implements TestSig { + string getARelevantTag() { result = "getCommand" } - override string getARelevantTag() { result = "getCommand" } - - override predicate hasActualResult(Location location, string element, string tag, string value) { + predicate hasActualResult(Location location, string element, string tag, string value) { exists(location.getFile().getRelativePath()) and exists(SystemCommandExecution sce, DataFlow::Node command | command = sce.getCommand() and @@ -21,14 +19,12 @@ class SystemCommandExecutionTest extends InlineExpectationsTest { } } -class DecodingTest extends InlineExpectationsTest { - DecodingTest() { this = "DecodingTest" } - - override string getARelevantTag() { +module DecodingTest implements TestSig { + string getARelevantTag() { result in ["decodeInput", "decodeOutput", "decodeFormat", "decodeMayExecuteInput"] } - override predicate hasActualResult(Location location, string element, string tag, string value) { + predicate hasActualResult(Location location, string element, string tag, string value) { exists(location.getFile().getRelativePath()) and exists(Decoding d | exists(DataFlow::Node data | @@ -61,12 +57,10 @@ class DecodingTest extends InlineExpectationsTest { } } -class EncodingTest extends InlineExpectationsTest { - EncodingTest() { this = "EncodingTest" } +module EncodingTest implements TestSig { + string getARelevantTag() { result in ["encodeInput", "encodeOutput", "encodeFormat"] } - override string getARelevantTag() { result in ["encodeInput", "encodeOutput", "encodeFormat"] } - - override predicate hasActualResult(Location location, string element, string tag, string value) { + predicate hasActualResult(Location location, string element, string tag, string value) { exists(location.getFile().getRelativePath()) and exists(Encoding e | exists(DataFlow::Node data | @@ -93,12 +87,10 @@ class EncodingTest extends InlineExpectationsTest { } } -class LoggingTest extends InlineExpectationsTest { - LoggingTest() { this = "LoggingTest" } +module LoggingTest implements TestSig { + string getARelevantTag() { result = "loggingInput" } - override string getARelevantTag() { result = "loggingInput" } - - override predicate hasActualResult(Location location, string element, string tag, string value) { + predicate hasActualResult(Location location, string element, string tag, string value) { exists(location.getFile().getRelativePath()) and exists(Logging logging, DataFlow::Node data | location = data.getLocation() and @@ -110,12 +102,10 @@ class LoggingTest extends InlineExpectationsTest { } } -class CodeExecutionTest extends InlineExpectationsTest { - CodeExecutionTest() { this = "CodeExecutionTest" } +module CodeExecutionTest implements TestSig { + string getARelevantTag() { result = "getCode" } - override string getARelevantTag() { result = "getCode" } - - override predicate hasActualResult(Location location, string element, string tag, string value) { + predicate hasActualResult(Location location, string element, string tag, string value) { exists(location.getFile().getRelativePath()) and exists(CodeExecution ce, DataFlow::Node code | exists(location.getFile().getRelativePath()) and @@ -128,12 +118,10 @@ class CodeExecutionTest extends InlineExpectationsTest { } } -class SqlConstructionTest extends InlineExpectationsTest { - SqlConstructionTest() { this = "SqlConstructionTest" } +module SqlConstructionTest implements TestSig { + string getARelevantTag() { result = "constructedSql" } - override string getARelevantTag() { result = "constructedSql" } - - override predicate hasActualResult(Location location, string element, string tag, string value) { + predicate hasActualResult(Location location, string element, string tag, string value) { exists(location.getFile().getRelativePath()) and exists(SqlConstruction e, DataFlow::Node sql | exists(location.getFile().getRelativePath()) and @@ -146,12 +134,10 @@ class SqlConstructionTest extends InlineExpectationsTest { } } -class SqlExecutionTest extends InlineExpectationsTest { - SqlExecutionTest() { this = "SqlExecutionTest" } +module SqlExecutionTest implements TestSig { + string getARelevantTag() { result = "getSql" } - override string getARelevantTag() { result = "getSql" } - - override predicate hasActualResult(Location location, string element, string tag, string value) { + predicate hasActualResult(Location location, string element, string tag, string value) { exists(location.getFile().getRelativePath()) and exists(SqlExecution e, DataFlow::Node sql | exists(location.getFile().getRelativePath()) and @@ -164,12 +150,10 @@ class SqlExecutionTest extends InlineExpectationsTest { } } -class XPathConstructionTest extends InlineExpectationsTest { - XPathConstructionTest() { this = "XPathConstructionTest" } +module XPathConstructionTest implements TestSig { + string getARelevantTag() { result = "constructedXPath" } - override string getARelevantTag() { result = "constructedXPath" } - - override predicate hasActualResult(Location location, string element, string tag, string value) { + predicate hasActualResult(Location location, string element, string tag, string value) { exists(location.getFile().getRelativePath()) and exists(XML::XPathConstruction e, DataFlow::Node xpath | exists(location.getFile().getRelativePath()) and @@ -182,12 +166,10 @@ class XPathConstructionTest extends InlineExpectationsTest { } } -class XPathExecutionTest extends InlineExpectationsTest { - XPathExecutionTest() { this = "XPathExecutionTest" } +module XPathExecutionTest implements TestSig { + string getARelevantTag() { result = "getXPath" } - override string getARelevantTag() { result = "getXPath" } - - override predicate hasActualResult(Location location, string element, string tag, string value) { + predicate hasActualResult(Location location, string element, string tag, string value) { exists(location.getFile().getRelativePath()) and exists(XML::XPathExecution e, DataFlow::Node xpath | exists(location.getFile().getRelativePath()) and @@ -200,12 +182,10 @@ class XPathExecutionTest extends InlineExpectationsTest { } } -class EscapingTest extends InlineExpectationsTest { - EscapingTest() { this = "EscapingTest" } +module EscapingTest implements TestSig { + string getARelevantTag() { result in ["escapeInput", "escapeOutput", "escapeKind"] } - override string getARelevantTag() { result in ["escapeInput", "escapeOutput", "escapeKind"] } - - override predicate hasActualResult(Location location, string element, string tag, string value) { + predicate hasActualResult(Location location, string element, string tag, string value) { exists(location.getFile().getRelativePath()) and exists(Escaping esc | exists(DataFlow::Node data | @@ -232,12 +212,10 @@ class EscapingTest extends InlineExpectationsTest { } } -class HttpServerRouteSetupTest extends InlineExpectationsTest { - HttpServerRouteSetupTest() { this = "HttpServerRouteSetupTest" } +module HttpServerRouteSetupTest implements TestSig { + string getARelevantTag() { result = "routeSetup" } - override string getARelevantTag() { result = "routeSetup" } - - override predicate hasActualResult(Location location, string element, string tag, string value) { + predicate hasActualResult(Location location, string element, string tag, string value) { exists(location.getFile().getRelativePath()) and exists(Http::Server::RouteSetup setup | location = setup.getLocation() and @@ -253,12 +231,10 @@ class HttpServerRouteSetupTest extends InlineExpectationsTest { } } -class HttpServerRequestHandlerTest extends InlineExpectationsTest { - HttpServerRequestHandlerTest() { this = "HttpServerRequestHandlerTest" } +module HttpServerRequestHandlerTest implements TestSig { + string getARelevantTag() { result in ["requestHandler", "routedParameter"] } - override string getARelevantTag() { result in ["requestHandler", "routedParameter"] } - - override predicate hasActualResult(Location location, string element, string tag, string value) { + predicate hasActualResult(Location location, string element, string tag, string value) { exists(location.getFile().getRelativePath()) and ( exists(Http::Server::RequestHandler handler | @@ -330,12 +306,10 @@ class HttpServerHttpResponseTest extends InlineExpectationsTest { } } -class HttpServerHttpRedirectResponseTest extends InlineExpectationsTest { - HttpServerHttpRedirectResponseTest() { this = "HttpServerHttpRedirectResponseTest" } +module HttpServerHttpRedirectResponseTest implements TestSig { + string getARelevantTag() { result in ["HttpRedirectResponse", "redirectLocation"] } - override string getARelevantTag() { result in ["HttpRedirectResponse", "redirectLocation"] } - - override predicate hasActualResult(Location location, string element, string tag, string value) { + predicate hasActualResult(Location location, string element, string tag, string value) { exists(location.getFile().getRelativePath()) and ( exists(Http::Server::HttpRedirectResponse redirect | @@ -355,14 +329,12 @@ class HttpServerHttpRedirectResponseTest extends InlineExpectationsTest { } } -class HttpServerCookieWriteTest extends InlineExpectationsTest { - HttpServerCookieWriteTest() { this = "HttpServerCookieWriteTest" } - - override string getARelevantTag() { +module HttpServerCookieWriteTest implements TestSig { + string getARelevantTag() { result in ["CookieWrite", "CookieRawHeader", "CookieName", "CookieValue"] } - override predicate hasActualResult(Location location, string element, string tag, string value) { + predicate hasActualResult(Location location, string element, string tag, string value) { exists(location.getFile().getRelativePath()) and exists(Http::Server::CookieWrite cookieWrite | location = cookieWrite.getLocation() and @@ -387,12 +359,10 @@ class HttpServerCookieWriteTest extends InlineExpectationsTest { } } -class FileSystemAccessTest extends InlineExpectationsTest { - FileSystemAccessTest() { this = "FileSystemAccessTest" } +module FileSystemAccessTest implements TestSig { + string getARelevantTag() { result = "getAPathArgument" } - override string getARelevantTag() { result = "getAPathArgument" } - - override predicate hasActualResult(Location location, string element, string tag, string value) { + predicate hasActualResult(Location location, string element, string tag, string value) { exists(location.getFile().getRelativePath()) and exists(FileSystemAccess a, DataFlow::Node path | path = a.getAPathArgument() and @@ -404,12 +374,10 @@ class FileSystemAccessTest extends InlineExpectationsTest { } } -class FileSystemWriteAccessTest extends InlineExpectationsTest { - FileSystemWriteAccessTest() { this = "FileSystemWriteAccessTest" } +module FileSystemWriteAccessTest implements TestSig { + string getARelevantTag() { result = "fileWriteData" } - override string getARelevantTag() { result = "fileWriteData" } - - override predicate hasActualResult(Location location, string element, string tag, string value) { + predicate hasActualResult(Location location, string element, string tag, string value) { exists(location.getFile().getRelativePath()) and exists(FileSystemWriteAccess write, DataFlow::Node data | data = write.getADataNode() and @@ -421,12 +389,10 @@ class FileSystemWriteAccessTest extends InlineExpectationsTest { } } -class PathNormalizationTest extends InlineExpectationsTest { - PathNormalizationTest() { this = "PathNormalizationTest" } +module PathNormalizationTest implements TestSig { + string getARelevantTag() { result = "pathNormalization" } - override string getARelevantTag() { result = "pathNormalization" } - - override predicate hasActualResult(Location location, string element, string tag, string value) { + predicate hasActualResult(Location location, string element, string tag, string value) { exists(location.getFile().getRelativePath()) and exists(Path::PathNormalization n | location = n.getLocation() and @@ -437,12 +403,10 @@ class PathNormalizationTest extends InlineExpectationsTest { } } -class SafeAccessCheckTest extends InlineExpectationsTest { - SafeAccessCheckTest() { this = "SafeAccessCheckTest" } +module SafeAccessCheckTest implements TestSig { + string getARelevantTag() { result = "SafeAccessCheck" } - override string getARelevantTag() { result = "SafeAccessCheck" } - - override predicate hasActualResult(Location location, string element, string tag, string value) { + predicate hasActualResult(Location location, string element, string tag, string value) { exists(location.getFile().getRelativePath()) and exists(Path::SafeAccessCheck c | location = c.getLocation() and @@ -453,12 +417,10 @@ class SafeAccessCheckTest extends InlineExpectationsTest { } } -class PublicKeyGenerationTest extends InlineExpectationsTest { - PublicKeyGenerationTest() { this = "PublicKeyGenerationTest" } +module PublicKeyGenerationTest implements TestSig { + string getARelevantTag() { result in ["PublicKeyGeneration", "keySize"] } - override string getARelevantTag() { result in ["PublicKeyGeneration", "keySize"] } - - override predicate hasActualResult(Location location, string element, string tag, string value) { + predicate hasActualResult(Location location, string element, string tag, string value) { exists(location.getFile().getRelativePath()) and exists(Cryptography::PublicKey::KeyGeneration keyGen | location = keyGen.getLocation() and @@ -475,17 +437,15 @@ class PublicKeyGenerationTest extends InlineExpectationsTest { } } -class CryptographicOperationTest extends InlineExpectationsTest { - CryptographicOperationTest() { this = "CryptographicOperationTest" } - - override string getARelevantTag() { +module CryptographicOperationTest implements TestSig { + string getARelevantTag() { result in [ "CryptographicOperation", "CryptographicOperationInput", "CryptographicOperationAlgorithm", "CryptographicOperationBlockMode" ] } - override predicate hasActualResult(Location location, string element, string tag, string value) { + predicate hasActualResult(Location location, string element, string tag, string value) { exists(location.getFile().getRelativePath()) and exists(Cryptography::CryptographicOperation cryptoOperation | location = cryptoOperation.getLocation() and @@ -510,14 +470,12 @@ class CryptographicOperationTest extends InlineExpectationsTest { } } -class HttpClientRequestTest extends InlineExpectationsTest { - HttpClientRequestTest() { this = "HttpClientRequestTest" } - - override string getARelevantTag() { +module HttpClientRequestTest implements TestSig { + string getARelevantTag() { result in ["clientRequestUrlPart", "clientRequestCertValidationDisabled"] } - override predicate hasActualResult(Location location, string element, string tag, string value) { + predicate hasActualResult(Location location, string element, string tag, string value) { exists(location.getFile().getRelativePath()) and exists(Http::Client::Request req, DataFlow::Node url | url = req.getAUrlPart() and @@ -538,12 +496,10 @@ class HttpClientRequestTest extends InlineExpectationsTest { } } -class CsrfProtectionSettingTest extends InlineExpectationsTest { - CsrfProtectionSettingTest() { this = "CsrfProtectionSettingTest" } +module CsrfProtectionSettingTest implements TestSig { + string getARelevantTag() { result = "CsrfProtectionSetting" } - override string getARelevantTag() { result = "CsrfProtectionSetting" } - - override predicate hasActualResult(Location location, string element, string tag, string value) { + predicate hasActualResult(Location location, string element, string tag, string value) { exists(location.getFile().getRelativePath()) and exists(Http::Server::CsrfProtectionSetting setting | location = setting.getLocation() and @@ -554,12 +510,10 @@ class CsrfProtectionSettingTest extends InlineExpectationsTest { } } -class CsrfLocalProtectionSettingTest extends InlineExpectationsTest { - CsrfLocalProtectionSettingTest() { this = "CsrfLocalProtectionSettingTest" } +module CsrfLocalProtectionSettingTest implements TestSig { + string getARelevantTag() { result = "CsrfLocalProtection" + ["Enabled", "Disabled"] } - override string getARelevantTag() { result = "CsrfLocalProtection" + ["Enabled", "Disabled"] } - - override predicate hasActualResult(Location location, string element, string tag, string value) { + predicate hasActualResult(Location location, string element, string tag, string value) { exists(location.getFile().getRelativePath()) and exists(Http::Server::CsrfLocalProtectionSetting p | location = p.getLocation() and @@ -572,12 +526,10 @@ class CsrfLocalProtectionSettingTest extends InlineExpectationsTest { } } -class XmlParsingTest extends InlineExpectationsTest { - XmlParsingTest() { this = "XmlParsingTest" } +module XmlParsingTest implements TestSig { + string getARelevantTag() { result = "xmlVuln" } - override string getARelevantTag() { result = "xmlVuln" } - - override predicate hasActualResult(Location location, string element, string tag, string value) { + predicate hasActualResult(Location location, string element, string tag, string value) { exists(location.getFile().getRelativePath()) and exists(XML::XmlParsing parsing, XML::XmlParsingVulnerabilityKind kind | parsing.vulnerableTo(kind) and @@ -588,3 +540,14 @@ class XmlParsingTest extends InlineExpectationsTest { ) } } + +import MakeTest, + MergeTests5, + MergeTests4, + MergeTests5, + MergeTests5>> diff --git a/python/ql/test/experimental/meta/InlineTaintTest.qll b/python/ql/test/experimental/meta/InlineTaintTest.qll index 9982ec961d4..24f67bcf2a4 100644 --- a/python/ql/test/experimental/meta/InlineTaintTest.qll +++ b/python/ql/test/experimental/meta/InlineTaintTest.qll @@ -33,10 +33,8 @@ DataFlow::Node shouldNotBeTainted() { // this module allows the configuration to be imported in other `.ql` files without the // top level query predicates of this file coming into scope. module Conf { - class TestTaintTrackingConfiguration extends TaintTracking::Configuration { - TestTaintTrackingConfiguration() { this = "TestTaintTrackingConfiguration" } - - override predicate isSource(DataFlow::Node source) { + module TestTaintTrackingConfig implements DataFlow::ConfigSig { + predicate isSource(DataFlow::Node source) { source.asCfgNode().(NameNode).getId() in [ "TAINTED_STRING", "TAINTED_BYTES", "TAINTED_LIST", "TAINTED_DICT" ] @@ -50,7 +48,7 @@ module Conf { source instanceof RemoteFlowSource } - override predicate isSink(DataFlow::Node sink) { + predicate isSink(DataFlow::Node sink) { sink = shouldBeTainted() or sink = shouldNotBeTainted() @@ -60,49 +58,53 @@ module Conf { import Conf -class InlineTaintTest extends InlineExpectationsTest { - InlineTaintTest() { this = "InlineTaintTest" } +module MakeInlineTaintTest { + private module Flow = TaintTracking::Global; - override string getARelevantTag() { result = "tainted" } + private module InlineTaintTest implements TestSig { + string getARelevantTag() { result = "tainted" } - override predicate hasActualResult(Location location, string element, string tag, string value) { - exists(location.getFile().getRelativePath()) and + predicate hasActualResult(Location location, string element, string tag, string value) { + exists(location.getFile().getRelativePath()) and + exists(DataFlow::Node sink | + Flow::flowTo(sink) and + location = sink.getLocation() and + element = prettyExpr(sink.asExpr()) and + value = "" and + tag = "tainted" + ) + } + } + + import MakeTest + + query predicate argumentToEnsureNotTaintedNotMarkedAsSpurious( + Location location, string error, string element + ) { + error = "ERROR, you should add `SPURIOUS:` to this annotation" and + location = shouldNotBeTainted().getLocation() and + InlineTaintTest::hasActualResult(location, element, "tainted", _) and + exists(GoodTestExpectation good, ActualTestResult actualResult | + good.matchesActualResult(actualResult) and + actualResult.getLocation() = location and + actualResult.toString() = element + ) + } + + query predicate untaintedArgumentToEnsureTaintedNotMarkedAsMissing( + Location location, string error, string element + ) { + error = "ERROR, you should add `# $ MISSING: tainted` annotation" and exists(DataFlow::Node sink | - any(TestTaintTrackingConfiguration config).hasFlow(_, sink) and - location = sink.getLocation() and + sink = shouldBeTainted() and element = prettyExpr(sink.asExpr()) and - value = "" and - tag = "tainted" + not Flow::flowTo(sink) and + location = sink.getLocation() and + not exists(FalseNegativeTestExpectation missingResult | + missingResult.getTag() = "tainted" and + missingResult.getLocation().getFile() = location.getFile() and + missingResult.getLocation().getStartLine() = location.getStartLine() + ) ) } } - -query predicate argumentToEnsureNotTaintedNotMarkedAsSpurious( - Location location, string error, string element -) { - error = "ERROR, you should add `SPURIOUS:` to this annotation" and - location = shouldNotBeTainted().getLocation() and - any(InlineTaintTest test).hasActualResult(location, element, "tainted", _) and - exists(GoodExpectation good, ActualResult actualResult | - good.matchesActualResult(actualResult) and - actualResult.getLocation() = location and - actualResult.toString() = element - ) -} - -query predicate untaintedArgumentToEnsureTaintedNotMarkedAsMissing( - Location location, string error, string element -) { - error = "ERROR, you should add `# $ MISSING: tainted` annotation" and - exists(DataFlow::Node sink | - sink = shouldBeTainted() and - element = prettyExpr(sink.asExpr()) and - not any(TestTaintTrackingConfiguration config).hasFlow(_, sink) and - location = sink.getLocation() and - not exists(FalseNegativeExpectation missingResult | - missingResult.getTag() = "tainted" and - missingResult.getLocation().getFile() = location.getFile() and - missingResult.getLocation().getStartLine() = location.getStartLine() - ) - ) -} diff --git a/python/ql/test/experimental/meta/MaDTest.qll b/python/ql/test/experimental/meta/MaDTest.qll index a4b5877f5ea..9b6bd59287a 100644 --- a/python/ql/test/experimental/meta/MaDTest.qll +++ b/python/ql/test/experimental/meta/MaDTest.qll @@ -7,16 +7,14 @@ private import semmle.python.Frameworks // this import needs to be public to get the query predicates propagated to the actual test files import TestUtilities.InlineExpectationsTest -class MadSinkTest extends InlineExpectationsTest { - MadSinkTest() { this = "MadSinkTest" } - - override string getARelevantTag() { +module MadSinkTest implements TestSig { + string getARelevantTag() { exists(string kind | exists(ModelOutput::getASinkNode(kind)) | result = "mad-sink[" + kind + "]" ) } - override predicate hasActualResult(Location location, string element, string tag, string value) { + predicate hasActualResult(Location location, string element, string tag, string value) { exists(location.getFile().getRelativePath()) and exists(DataFlow::Node sink, string kind | sink = ModelOutput::getASinkNode(kind).asSink() and @@ -28,14 +26,12 @@ class MadSinkTest extends InlineExpectationsTest { } } -class MadSourceTest extends InlineExpectationsTest { - MadSourceTest() { this = "MadSourceTest" } - - override string getARelevantTag() { +module MadSourceTest implements TestSig { + string getARelevantTag() { exists(string kind | exists(ModelOutput::getASourceNode(kind)) | result = "mad-source__" + kind) } - override predicate hasActualResult(Location location, string element, string tag, string value) { + predicate hasActualResult(Location location, string element, string tag, string value) { exists(location.getFile().getRelativePath()) and exists(DataFlow::Node source, string kind | source = ModelOutput::getASourceNode(kind).asSource() and @@ -46,3 +42,5 @@ class MadSourceTest extends InlineExpectationsTest { ) } } + +import MakeTest> diff --git a/python/ql/test/experimental/meta/debug/InlineTaintTestPaths.ql b/python/ql/test/experimental/meta/debug/InlineTaintTestPaths.ql index 98ad634484e..3f082f21fa4 100644 --- a/python/ql/test/experimental/meta/debug/InlineTaintTestPaths.ql +++ b/python/ql/test/experimental/meta/debug/InlineTaintTestPaths.ql @@ -13,11 +13,9 @@ import semmle.python.dataflow.new.TaintTracking import experimental.meta.InlineTaintTest::Conf module Config implements DataFlow::ConfigSig { - predicate isSource(DataFlow::Node source) { - any(TestTaintTrackingConfiguration c).isSource(source) - } + predicate isSource(DataFlow::Node source) { TestTaintTrackingConfig::isSource(source) } - predicate isSink(DataFlow::Node source) { any(TestTaintTrackingConfiguration c).isSink(source) } + predicate isSink(DataFlow::Node source) { TestTaintTrackingConfig::isSink(source) } } module Flows = TaintTracking::Global; diff --git a/python/ql/test/experimental/meta/debug/dataflowTestPaths.ql b/python/ql/test/experimental/meta/debug/dataflowTestPaths.ql index 087787f4fc1..3e2d625de77 100644 --- a/python/ql/test/experimental/meta/debug/dataflowTestPaths.ql +++ b/python/ql/test/experimental/meta/debug/dataflowTestPaths.ql @@ -12,9 +12,9 @@ import semmle.python.dataflow.new.DataFlow import experimental.dataflow.testConfig module Config implements DataFlow::ConfigSig { - predicate isSource(DataFlow::Node source) { any(TestConfiguration c).isSource(source) } + predicate isSource(DataFlow::Node source) { TestConfig::isSource(source) } - predicate isSink(DataFlow::Node source) { any(TestConfiguration c).isSink(source) } + predicate isSink(DataFlow::Node source) { TestConfig::isSink(source) } } module Flows = DataFlow::Global; diff --git a/python/ql/test/experimental/meta/inline-taint-test-demo/InlineTaintTest.expected b/python/ql/test/experimental/meta/inline-taint-test-demo/InlineTaintTest.expected index 745561a5e65..511dc50d5ca 100644 --- a/python/ql/test/experimental/meta/inline-taint-test-demo/InlineTaintTest.expected +++ b/python/ql/test/experimental/meta/inline-taint-test-demo/InlineTaintTest.expected @@ -1,7 +1,8 @@ +failures argumentToEnsureNotTaintedNotMarkedAsSpurious | taint_test.py:48:9:48:29 | taint_test.py:48 | ERROR, you should add `SPURIOUS:` to this annotation | should_not_be_tainted | untaintedArgumentToEnsureTaintedNotMarkedAsMissing | taint_test.py:32:9:32:25 | taint_test.py:32 | ERROR, you should add `# $ MISSING: tainted` annotation | should_be_tainted | | taint_test.py:37:24:37:40 | taint_test.py:37 | ERROR, you should add `# $ MISSING: tainted` annotation | should_be_tainted | -failures +testFailures | taint_test.py:41:20:41:21 | ts | Fixed missing result:tainted= | diff --git a/python/ql/test/experimental/meta/inline-taint-test-demo/InlineTaintTest.ql b/python/ql/test/experimental/meta/inline-taint-test-demo/InlineTaintTest.ql index 027ad8667be..8524da5fe7d 100644 --- a/python/ql/test/experimental/meta/inline-taint-test-demo/InlineTaintTest.ql +++ b/python/ql/test/experimental/meta/inline-taint-test-demo/InlineTaintTest.ql @@ -1 +1,2 @@ import experimental.meta.InlineTaintTest +import MakeInlineTaintTest diff --git a/python/ql/test/experimental/query-tests/Security/CWE-022-UnsafeUnpacking/DataflowQueryTest.expected b/python/ql/test/experimental/query-tests/Security/CWE-022-UnsafeUnpacking/DataflowQueryTest.expected index 3875da4e143..04431311999 100644 --- a/python/ql/test/experimental/query-tests/Security/CWE-022-UnsafeUnpacking/DataflowQueryTest.expected +++ b/python/ql/test/experimental/query-tests/Security/CWE-022-UnsafeUnpacking/DataflowQueryTest.expected @@ -1,2 +1,3 @@ missingAnnotationOnSink failures +testFailures diff --git a/python/ql/test/library-tests/examples/custom-sanitizer/SanitizedEdges.expected b/python/ql/test/library-tests/examples/custom-sanitizer/SanitizedEdges.expected deleted file mode 100644 index 22c6f68f1d7..00000000000 --- a/python/ql/test/library-tests/examples/custom-sanitizer/SanitizedEdges.expected +++ /dev/null @@ -1,23 +0,0 @@ -| MySanitizerHandlingNot | externally controlled string | test.py:13 | Pi(s_0) [true] | -| MySanitizerHandlingNot | externally controlled string | test.py:18 | Pi(s_5) [false] | -| MySanitizerHandlingNot | externally controlled string | test.py:28 | Pi(s_0) [true] | -| MySanitizerHandlingNot | externally controlled string | test.py:34 | Pi(s_10) [true] | -| MySanitizerHandlingNot | externally controlled string | test.py:40 | Pi(s_12) [false] | -| MySanitizerHandlingNot | externally controlled string | test.py:50 | Pi(s_0) [true] | -| MySanitizerHandlingNot | externally controlled string | test.py:56 | Pi(s_10) [true] | -| MySanitizerHandlingNot | externally controlled string | test.py:62 | Pi(s_12) [false] | -| MySanitizerHandlingNot | externally controlled string | test.py:76 | Pi(s_3) [true] | -| MySanitizerHandlingNot | externally controlled string | test.py:82 | Pi(s_0) [true] | -| MySanitizerHandlingNot | externally controlled string | test.py:87 | Pi(s_5) [false] | -| MySanitizerHandlingNot | externally controlled string | test.py:97 | Pi(s_0) [true] | -| MySanitizerHandlingNot | externally controlled string | test.py:102 | Pi(s_7) [true] | -| MySanitizerHandlingNot | externally controlled string | test.py:107 | Pi(s_12) [true] | -| MySimpleSanitizer | externally controlled string | test.py:13 | Pi(s_0) [true] | -| MySimpleSanitizer | externally controlled string | test.py:28 | Pi(s_0) [true] | -| MySimpleSanitizer | externally controlled string | test.py:34 | Pi(s_10) [true] | -| MySimpleSanitizer | externally controlled string | test.py:50 | Pi(s_0) [true] | -| MySimpleSanitizer | externally controlled string | test.py:56 | Pi(s_10) [true] | -| MySimpleSanitizer | externally controlled string | test.py:76 | Pi(s_3) [true] | -| MySimpleSanitizer | externally controlled string | test.py:97 | Pi(s_0) [true] | -| MySimpleSanitizer | externally controlled string | test.py:102 | Pi(s_7) [true] | -| MySimpleSanitizer | externally controlled string | test.py:107 | Pi(s_12) [true] | diff --git a/python/ql/test/library-tests/examples/custom-sanitizer/SanitizedEdges.ql b/python/ql/test/library-tests/examples/custom-sanitizer/SanitizedEdges.ql deleted file mode 100644 index d523f79a963..00000000000 --- a/python/ql/test/library-tests/examples/custom-sanitizer/SanitizedEdges.ql +++ /dev/null @@ -1,6 +0,0 @@ -import python -import Taint - -from Sanitizer s, TaintKind taint, PyEdgeRefinement test -where s.sanitizingEdge(taint, test) -select s, taint, test.getTest().getLocation().toString(), test.getRepresentation() diff --git a/python/ql/test/library-tests/examples/custom-sanitizer/Taint.qll b/python/ql/test/library-tests/examples/custom-sanitizer/Taint.qll deleted file mode 100644 index 343caa1131f..00000000000 --- a/python/ql/test/library-tests/examples/custom-sanitizer/Taint.qll +++ /dev/null @@ -1,77 +0,0 @@ -import python -import semmle.python.dataflow.TaintTracking -import semmle.python.security.strings.Untrusted - -class SimpleSource extends TaintSource { - SimpleSource() { this.(NameNode).getId() = "TAINTED_STRING" } - - override predicate isSourceOf(TaintKind kind) { kind instanceof ExternalStringKind } - - override string toString() { result = "taint source" } -} - -class MySimpleSanitizer extends Sanitizer { - MySimpleSanitizer() { this = "MySimpleSanitizer" } - - /* - * The test `if is_safe(arg):` sanitizes `arg` on its `true` edge. - * - * Can't handle `if not is_safe(arg):` :\ that's why it's called MySimpleSanitizer - */ - - override predicate sanitizingEdge(TaintKind taint, PyEdgeRefinement test) { - taint instanceof ExternalStringKind and - exists(CallNode call | test.getTest() = call and test.getSense() = true | - call = Value::named("test.is_safe").getACall() and - test.getInput().getAUse() = call.getAnArg() - ) - } -} - -class MySanitizerHandlingNot extends Sanitizer { - MySanitizerHandlingNot() { this = "MySanitizerHandlingNot" } - - /** Holds if the test `if is_safe(arg):` sanitizes `arg` on its `true` edge. */ - override predicate sanitizingEdge(TaintKind taint, PyEdgeRefinement test) { - taint instanceof ExternalStringKind and - clears_taint_on_true(test.getTest(), test.getSense(), test) - } -} - -/** - * Helper predicate that recurses into any nesting of `not` - * - * To reduce the number of tuples this predicate holds for, we include the `PyEdgeRefinement` and - * ensure that `test` is a part of this `PyEdgeRefinement` (instead of just taking the - * `edge_refinement.getInput().getAUse()` part as a part of the predicate). Without including - * `PyEdgeRefinement` as an argument *any* `CallNode c` to `test.is_safe` would be a result of - * this predicate, since the tuple where `test = c` and `sense = true` would hold. - */ -private predicate clears_taint_on_true( - ControlFlowNode test, boolean sense, PyEdgeRefinement edge_refinement -) { - edge_refinement.getTest().getNode().(Expr).getASubExpression*() = test.getNode() and - ( - test = Value::named("test.is_safe").getACall() and - edge_refinement.getInput().getAUse() = test.(CallNode).getAnArg() and - sense = true - or - test.(UnaryExprNode).getNode().getOp() instanceof Not and - exists(ControlFlowNode nested_test | - nested_test = test.(UnaryExprNode).getOperand() and - clears_taint_on_true(nested_test, sense.booleanNot(), edge_refinement) - ) - ) -} - -class TestConfig extends TaintTracking::Configuration { - TestConfig() { this = "TestConfig" } - - override predicate isSanitizer(Sanitizer sanitizer) { - sanitizer instanceof MySanitizerHandlingNot - } - - override predicate isSource(TaintTracking::Source source) { source instanceof SimpleSource } - - override predicate isSink(TaintTracking::Sink sink) { none() } -} diff --git a/python/ql/test/library-tests/examples/custom-sanitizer/TestTaint.expected b/python/ql/test/library-tests/examples/custom-sanitizer/TestTaint.expected deleted file mode 100644 index 269eb47a37f..00000000000 --- a/python/ql/test/library-tests/examples/custom-sanitizer/TestTaint.expected +++ /dev/null @@ -1,28 +0,0 @@ -| test.py:14 | test_basic | s | | ok | -| test.py:16 | test_basic | s | externally controlled string | ok | -| test.py:19 | test_basic | s | externally controlled string | ok | -| test.py:21 | test_basic | s | | ok | -| test.py:29 | test_or | s | externally controlled string | ok | -| test.py:31 | test_or | s | externally controlled string | ok | -| test.py:35 | test_or | s | externally controlled string | ok | -| test.py:37 | test_or | s | externally controlled string | ok | -| test.py:41 | test_or | s | externally controlled string | ok | -| test.py:43 | test_or | s | externally controlled string | ok | -| test.py:51 | test_and | s | | ok | -| test.py:53 | test_and | s | externally controlled string | ok | -| test.py:57 | test_and | s | externally controlled string | ok | -| test.py:59 | test_and | s | | ok | -| test.py:63 | test_and | s | externally controlled string | ok | -| test.py:65 | test_and | s | | ok | -| test.py:73 | test_tricky | s | externally controlled string | failure | -| test.py:77 | test_tricky | s_ | externally controlled string | failure | -| test.py:83 | test_nesting_not | s | | ok | -| test.py:85 | test_nesting_not | s | externally controlled string | ok | -| test.py:88 | test_nesting_not | s | externally controlled string | ok | -| test.py:90 | test_nesting_not | s | | ok | -| test.py:98 | test_nesting_not_with_and_true | s | externally controlled string | ok | -| test.py:100 | test_nesting_not_with_and_true | s | | ok | -| test.py:103 | test_nesting_not_with_and_true | s | | ok | -| test.py:105 | test_nesting_not_with_and_true | s | externally controlled string | ok | -| test.py:108 | test_nesting_not_with_and_true | s | externally controlled string | ok | -| test.py:110 | test_nesting_not_with_and_true | s | | ok | diff --git a/python/ql/test/library-tests/examples/custom-sanitizer/TestTaint.ql b/python/ql/test/library-tests/examples/custom-sanitizer/TestTaint.ql deleted file mode 100644 index 431e96e4d5d..00000000000 --- a/python/ql/test/library-tests/examples/custom-sanitizer/TestTaint.ql +++ /dev/null @@ -1,31 +0,0 @@ -import python -import semmle.python.dataflow.TaintTracking -import Taint - -from - Call call, Expr arg, boolean expected_taint, boolean has_taint, string test_res, - string taint_string -where - call.getLocation().getFile().getShortName() = "test.py" and - ( - call.getFunc().(Name).getId() = "ensure_tainted" and - expected_taint = true - or - call.getFunc().(Name).getId() = "ensure_not_tainted" and - expected_taint = false - ) and - arg = call.getAnArg() and - ( - not exists(TaintedNode tainted | tainted.getAstNode() = arg) and - taint_string = "" and - has_taint = false - or - exists(TaintedNode tainted | tainted.getAstNode() = arg | - taint_string = tainted.getTaintKind().toString() - ) and - has_taint = true - ) and - if expected_taint = has_taint then test_res = "ok" else test_res = "failure" -// if expected_taint = has_taint then test_res = "✓" else test_res = "✕" -select arg.getLocation().toString(), call.getScope().(Function).getName(), arg.toString(), - taint_string, test_res diff --git a/python/ql/test/library-tests/examples/custom-sanitizer/test.py b/python/ql/test/library-tests/examples/custom-sanitizer/test.py deleted file mode 100644 index e0bf29f4ee6..00000000000 --- a/python/ql/test/library-tests/examples/custom-sanitizer/test.py +++ /dev/null @@ -1,110 +0,0 @@ -def random_choice(): - return bool(GLOBAL_UNKOWN_VAR) - -def is_safe(arg): - return UNKNOWN_FUNC(arg) - -def true_func(): - return True - -def test_basic(): - s = TAINTED_STRING - - if is_safe(s): - ensure_not_tainted(s) - else: - ensure_tainted(s) - - if not is_safe(s): - ensure_tainted(s) - else: - ensure_not_tainted(s) - - -def test_or(): - s = TAINTED_STRING - - # x or y - if is_safe(s) or random_choice(): - ensure_tainted(s) # might be tainted - else: - ensure_tainted(s) # must be tainted - - # not (x or y) - if not(is_safe(s) or random_choice()): - ensure_tainted(s) # must be tainted - else: - ensure_tainted(s) # might be tainted - - # not (x or y) == not x and not y [de Morgan's laws] - if not is_safe(s) and not random_choice(): - ensure_tainted(s) # must be tainted - else: - ensure_tainted(s) # might be tainted - - -def test_and(): - s = TAINTED_STRING - - # x and y - if is_safe(s) and random_choice(): - ensure_not_tainted(s) # must not be tainted - else: - ensure_tainted(s) # might be tainted - - # not (x and y) - if not(is_safe(s) and random_choice()): - ensure_tainted(s) # might be tainted - else: - ensure_not_tainted(s) - - # not (x and y) == not x or not y [de Morgan's laws] - if not is_safe(s) or not random_choice(): - ensure_tainted(s) # might be tainted - else: - ensure_not_tainted(s) - - -def test_tricky(): - s = TAINTED_STRING - - x = is_safe(s) - if x: - ensure_not_tainted(s) # FP - - s_ = s - if is_safe(s): - ensure_not_tainted(s_) # FP - -def test_nesting_not(): - s = TAINTED_STRING - - if not(not(is_safe(s))): - ensure_not_tainted(s) - else: - ensure_tainted(s) - - if not(not(not(is_safe(s)))): - ensure_tainted(s) - else: - ensure_not_tainted(s) - -# Adding `and True` makes the sanitizer trigger when it would otherwise not. See output in -# SanitizedEdges.expected and compare with `test_nesting_not` and `test_basic` -def test_nesting_not_with_and_true(): - s = TAINTED_STRING - - if not(is_safe(s) and True): - ensure_tainted(s) - else: - ensure_not_tainted(s) - - if not(not(is_safe(s) and True)): - ensure_not_tainted(s) - else: - ensure_tainted(s) - - if not(not(not(is_safe(s) and True))): - ensure_tainted(s) - else: - ensure_not_tainted(s) diff --git a/python/ql/test/library-tests/frameworks/aioch/ConceptsTest.expected b/python/ql/test/library-tests/frameworks/aioch/ConceptsTest.expected index e69de29bb2d..48de9172b36 100644 --- a/python/ql/test/library-tests/frameworks/aioch/ConceptsTest.expected +++ b/python/ql/test/library-tests/frameworks/aioch/ConceptsTest.expected @@ -0,0 +1,2 @@ +failures +testFailures diff --git a/python/ql/test/library-tests/frameworks/aiohttp/ConceptsTest.expected b/python/ql/test/library-tests/frameworks/aiohttp/ConceptsTest.expected index e69de29bb2d..48de9172b36 100644 --- a/python/ql/test/library-tests/frameworks/aiohttp/ConceptsTest.expected +++ b/python/ql/test/library-tests/frameworks/aiohttp/ConceptsTest.expected @@ -0,0 +1,2 @@ +failures +testFailures diff --git a/python/ql/test/library-tests/frameworks/aiohttp/InlineTaintTest.expected b/python/ql/test/library-tests/frameworks/aiohttp/InlineTaintTest.expected index 79d760d87f4..4a72c551661 100644 --- a/python/ql/test/library-tests/frameworks/aiohttp/InlineTaintTest.expected +++ b/python/ql/test/library-tests/frameworks/aiohttp/InlineTaintTest.expected @@ -1,3 +1,4 @@ +failures argumentToEnsureNotTaintedNotMarkedAsSpurious untaintedArgumentToEnsureTaintedNotMarkedAsMissing -failures +testFailures diff --git a/python/ql/test/library-tests/frameworks/aiohttp/InlineTaintTest.ql b/python/ql/test/library-tests/frameworks/aiohttp/InlineTaintTest.ql index 027ad8667be..8524da5fe7d 100644 --- a/python/ql/test/library-tests/frameworks/aiohttp/InlineTaintTest.ql +++ b/python/ql/test/library-tests/frameworks/aiohttp/InlineTaintTest.ql @@ -1 +1,2 @@ import experimental.meta.InlineTaintTest +import MakeInlineTaintTest diff --git a/python/ql/test/library-tests/frameworks/aiomysql/ConceptsTest.expected b/python/ql/test/library-tests/frameworks/aiomysql/ConceptsTest.expected index e69de29bb2d..48de9172b36 100644 --- a/python/ql/test/library-tests/frameworks/aiomysql/ConceptsTest.expected +++ b/python/ql/test/library-tests/frameworks/aiomysql/ConceptsTest.expected @@ -0,0 +1,2 @@ +failures +testFailures diff --git a/python/ql/test/library-tests/frameworks/aiopg/ConceptsTest.expected b/python/ql/test/library-tests/frameworks/aiopg/ConceptsTest.expected index e69de29bb2d..48de9172b36 100644 --- a/python/ql/test/library-tests/frameworks/aiopg/ConceptsTest.expected +++ b/python/ql/test/library-tests/frameworks/aiopg/ConceptsTest.expected @@ -0,0 +1,2 @@ +failures +testFailures diff --git a/python/ql/test/library-tests/frameworks/aiosqlite/ConceptsTest.expected b/python/ql/test/library-tests/frameworks/aiosqlite/ConceptsTest.expected index e69de29bb2d..48de9172b36 100644 --- a/python/ql/test/library-tests/frameworks/aiosqlite/ConceptsTest.expected +++ b/python/ql/test/library-tests/frameworks/aiosqlite/ConceptsTest.expected @@ -0,0 +1,2 @@ +failures +testFailures diff --git a/python/ql/test/library-tests/frameworks/asyncpg/ConceptsTest.expected b/python/ql/test/library-tests/frameworks/asyncpg/ConceptsTest.expected index e69de29bb2d..48de9172b36 100644 --- a/python/ql/test/library-tests/frameworks/asyncpg/ConceptsTest.expected +++ b/python/ql/test/library-tests/frameworks/asyncpg/ConceptsTest.expected @@ -0,0 +1,2 @@ +failures +testFailures diff --git a/python/ql/test/library-tests/frameworks/asyncpg/MaDTest.expected b/python/ql/test/library-tests/frameworks/asyncpg/MaDTest.expected index e69de29bb2d..48de9172b36 100644 --- a/python/ql/test/library-tests/frameworks/asyncpg/MaDTest.expected +++ b/python/ql/test/library-tests/frameworks/asyncpg/MaDTest.expected @@ -0,0 +1,2 @@ +failures +testFailures diff --git a/python/ql/test/library-tests/frameworks/cassandra-driver/ConceptsTest.expected b/python/ql/test/library-tests/frameworks/cassandra-driver/ConceptsTest.expected index e69de29bb2d..48de9172b36 100644 --- a/python/ql/test/library-tests/frameworks/cassandra-driver/ConceptsTest.expected +++ b/python/ql/test/library-tests/frameworks/cassandra-driver/ConceptsTest.expected @@ -0,0 +1,2 @@ +failures +testFailures diff --git a/python/ql/test/library-tests/frameworks/clickhouse_driver/ConceptsTest.expected b/python/ql/test/library-tests/frameworks/clickhouse_driver/ConceptsTest.expected index e69de29bb2d..48de9172b36 100644 --- a/python/ql/test/library-tests/frameworks/clickhouse_driver/ConceptsTest.expected +++ b/python/ql/test/library-tests/frameworks/clickhouse_driver/ConceptsTest.expected @@ -0,0 +1,2 @@ +failures +testFailures diff --git a/python/ql/test/library-tests/frameworks/crypto/ConceptsTest.expected b/python/ql/test/library-tests/frameworks/crypto/ConceptsTest.expected index e69de29bb2d..48de9172b36 100644 --- a/python/ql/test/library-tests/frameworks/crypto/ConceptsTest.expected +++ b/python/ql/test/library-tests/frameworks/crypto/ConceptsTest.expected @@ -0,0 +1,2 @@ +failures +testFailures diff --git a/python/ql/test/library-tests/frameworks/cryptodome/ConceptsTest.expected b/python/ql/test/library-tests/frameworks/cryptodome/ConceptsTest.expected index e69de29bb2d..48de9172b36 100644 --- a/python/ql/test/library-tests/frameworks/cryptodome/ConceptsTest.expected +++ b/python/ql/test/library-tests/frameworks/cryptodome/ConceptsTest.expected @@ -0,0 +1,2 @@ +failures +testFailures diff --git a/python/ql/test/library-tests/frameworks/cryptography/ConceptsTest.expected b/python/ql/test/library-tests/frameworks/cryptography/ConceptsTest.expected index e69de29bb2d..48de9172b36 100644 --- a/python/ql/test/library-tests/frameworks/cryptography/ConceptsTest.expected +++ b/python/ql/test/library-tests/frameworks/cryptography/ConceptsTest.expected @@ -0,0 +1,2 @@ +failures +testFailures diff --git a/python/ql/test/library-tests/frameworks/cx_Oracle/ConceptsTest.expected b/python/ql/test/library-tests/frameworks/cx_Oracle/ConceptsTest.expected index e69de29bb2d..48de9172b36 100644 --- a/python/ql/test/library-tests/frameworks/cx_Oracle/ConceptsTest.expected +++ b/python/ql/test/library-tests/frameworks/cx_Oracle/ConceptsTest.expected @@ -0,0 +1,2 @@ +failures +testFailures diff --git a/python/ql/test/library-tests/frameworks/dill/ConceptsTest.expected b/python/ql/test/library-tests/frameworks/dill/ConceptsTest.expected index e69de29bb2d..48de9172b36 100644 --- a/python/ql/test/library-tests/frameworks/dill/ConceptsTest.expected +++ b/python/ql/test/library-tests/frameworks/dill/ConceptsTest.expected @@ -0,0 +1,2 @@ +failures +testFailures diff --git a/python/ql/test/library-tests/frameworks/django-orm/NormalDataflowTest.expected b/python/ql/test/library-tests/frameworks/django-orm/NormalDataflowTest.expected index 3875da4e143..04431311999 100644 --- a/python/ql/test/library-tests/frameworks/django-orm/NormalDataflowTest.expected +++ b/python/ql/test/library-tests/frameworks/django-orm/NormalDataflowTest.expected @@ -1,2 +1,3 @@ missingAnnotationOnSink failures +testFailures diff --git a/python/ql/test/library-tests/frameworks/django-v1/ConceptsTest.expected b/python/ql/test/library-tests/frameworks/django-v1/ConceptsTest.expected index e69de29bb2d..48de9172b36 100644 --- a/python/ql/test/library-tests/frameworks/django-v1/ConceptsTest.expected +++ b/python/ql/test/library-tests/frameworks/django-v1/ConceptsTest.expected @@ -0,0 +1,2 @@ +failures +testFailures diff --git a/python/ql/test/library-tests/frameworks/django-v2-v3/ConceptsTest.expected b/python/ql/test/library-tests/frameworks/django-v2-v3/ConceptsTest.expected index e69de29bb2d..48de9172b36 100644 --- a/python/ql/test/library-tests/frameworks/django-v2-v3/ConceptsTest.expected +++ b/python/ql/test/library-tests/frameworks/django-v2-v3/ConceptsTest.expected @@ -0,0 +1,2 @@ +failures +testFailures diff --git a/python/ql/test/library-tests/frameworks/django-v2-v3/InlineTaintTest.expected b/python/ql/test/library-tests/frameworks/django-v2-v3/InlineTaintTest.expected index 79d760d87f4..4a72c551661 100644 --- a/python/ql/test/library-tests/frameworks/django-v2-v3/InlineTaintTest.expected +++ b/python/ql/test/library-tests/frameworks/django-v2-v3/InlineTaintTest.expected @@ -1,3 +1,4 @@ +failures argumentToEnsureNotTaintedNotMarkedAsSpurious untaintedArgumentToEnsureTaintedNotMarkedAsMissing -failures +testFailures diff --git a/python/ql/test/library-tests/frameworks/django-v2-v3/InlineTaintTest.ql b/python/ql/test/library-tests/frameworks/django-v2-v3/InlineTaintTest.ql index 027ad8667be..8524da5fe7d 100644 --- a/python/ql/test/library-tests/frameworks/django-v2-v3/InlineTaintTest.ql +++ b/python/ql/test/library-tests/frameworks/django-v2-v3/InlineTaintTest.ql @@ -1 +1,2 @@ import experimental.meta.InlineTaintTest +import MakeInlineTaintTest diff --git a/python/ql/test/library-tests/frameworks/django/ConceptsTest.expected b/python/ql/test/library-tests/frameworks/django/ConceptsTest.expected index e69de29bb2d..48de9172b36 100644 --- a/python/ql/test/library-tests/frameworks/django/ConceptsTest.expected +++ b/python/ql/test/library-tests/frameworks/django/ConceptsTest.expected @@ -0,0 +1,2 @@ +failures +testFailures diff --git a/python/ql/test/library-tests/frameworks/fabric/ConceptsTest.expected b/python/ql/test/library-tests/frameworks/fabric/ConceptsTest.expected index e69de29bb2d..48de9172b36 100644 --- a/python/ql/test/library-tests/frameworks/fabric/ConceptsTest.expected +++ b/python/ql/test/library-tests/frameworks/fabric/ConceptsTest.expected @@ -0,0 +1,2 @@ +failures +testFailures diff --git a/python/ql/test/library-tests/frameworks/fabric/InlineTaintTest.expected b/python/ql/test/library-tests/frameworks/fabric/InlineTaintTest.expected index 79d760d87f4..4a72c551661 100644 --- a/python/ql/test/library-tests/frameworks/fabric/InlineTaintTest.expected +++ b/python/ql/test/library-tests/frameworks/fabric/InlineTaintTest.expected @@ -1,3 +1,4 @@ +failures argumentToEnsureNotTaintedNotMarkedAsSpurious untaintedArgumentToEnsureTaintedNotMarkedAsMissing -failures +testFailures diff --git a/python/ql/test/library-tests/frameworks/fabric/InlineTaintTest.ql b/python/ql/test/library-tests/frameworks/fabric/InlineTaintTest.ql index 027ad8667be..8524da5fe7d 100644 --- a/python/ql/test/library-tests/frameworks/fabric/InlineTaintTest.ql +++ b/python/ql/test/library-tests/frameworks/fabric/InlineTaintTest.ql @@ -1 +1,2 @@ import experimental.meta.InlineTaintTest +import MakeInlineTaintTest diff --git a/python/ql/test/library-tests/frameworks/fastapi/ConceptsTest.expected b/python/ql/test/library-tests/frameworks/fastapi/ConceptsTest.expected index e69de29bb2d..48de9172b36 100644 --- a/python/ql/test/library-tests/frameworks/fastapi/ConceptsTest.expected +++ b/python/ql/test/library-tests/frameworks/fastapi/ConceptsTest.expected @@ -0,0 +1,2 @@ +failures +testFailures diff --git a/python/ql/test/library-tests/frameworks/fastapi/InlineTaintTest.expected b/python/ql/test/library-tests/frameworks/fastapi/InlineTaintTest.expected index 79d760d87f4..4a72c551661 100644 --- a/python/ql/test/library-tests/frameworks/fastapi/InlineTaintTest.expected +++ b/python/ql/test/library-tests/frameworks/fastapi/InlineTaintTest.expected @@ -1,3 +1,4 @@ +failures argumentToEnsureNotTaintedNotMarkedAsSpurious untaintedArgumentToEnsureTaintedNotMarkedAsMissing -failures +testFailures diff --git a/python/ql/test/library-tests/frameworks/fastapi/InlineTaintTest.ql b/python/ql/test/library-tests/frameworks/fastapi/InlineTaintTest.ql index 027ad8667be..8524da5fe7d 100644 --- a/python/ql/test/library-tests/frameworks/fastapi/InlineTaintTest.ql +++ b/python/ql/test/library-tests/frameworks/fastapi/InlineTaintTest.ql @@ -1 +1,2 @@ import experimental.meta.InlineTaintTest +import MakeInlineTaintTest diff --git a/python/ql/test/library-tests/frameworks/flask/ConceptsTest.expected b/python/ql/test/library-tests/frameworks/flask/ConceptsTest.expected index e69de29bb2d..48de9172b36 100644 --- a/python/ql/test/library-tests/frameworks/flask/ConceptsTest.expected +++ b/python/ql/test/library-tests/frameworks/flask/ConceptsTest.expected @@ -0,0 +1,2 @@ +failures +testFailures diff --git a/python/ql/test/library-tests/frameworks/flask/InlineTaintTest.expected b/python/ql/test/library-tests/frameworks/flask/InlineTaintTest.expected index 79d760d87f4..4a72c551661 100644 --- a/python/ql/test/library-tests/frameworks/flask/InlineTaintTest.expected +++ b/python/ql/test/library-tests/frameworks/flask/InlineTaintTest.expected @@ -1,3 +1,4 @@ +failures argumentToEnsureNotTaintedNotMarkedAsSpurious untaintedArgumentToEnsureTaintedNotMarkedAsMissing -failures +testFailures diff --git a/python/ql/test/library-tests/frameworks/flask/InlineTaintTest.ql b/python/ql/test/library-tests/frameworks/flask/InlineTaintTest.ql index 027ad8667be..8524da5fe7d 100644 --- a/python/ql/test/library-tests/frameworks/flask/InlineTaintTest.ql +++ b/python/ql/test/library-tests/frameworks/flask/InlineTaintTest.ql @@ -1 +1,2 @@ import experimental.meta.InlineTaintTest +import MakeInlineTaintTest diff --git a/python/ql/test/library-tests/frameworks/flask_admin/ConceptsTest.expected b/python/ql/test/library-tests/frameworks/flask_admin/ConceptsTest.expected index e69de29bb2d..48de9172b36 100644 --- a/python/ql/test/library-tests/frameworks/flask_admin/ConceptsTest.expected +++ b/python/ql/test/library-tests/frameworks/flask_admin/ConceptsTest.expected @@ -0,0 +1,2 @@ +failures +testFailures diff --git a/python/ql/test/library-tests/frameworks/flask_admin/InlineTaintTest.expected b/python/ql/test/library-tests/frameworks/flask_admin/InlineTaintTest.expected index 79d760d87f4..4a72c551661 100644 --- a/python/ql/test/library-tests/frameworks/flask_admin/InlineTaintTest.expected +++ b/python/ql/test/library-tests/frameworks/flask_admin/InlineTaintTest.expected @@ -1,3 +1,4 @@ +failures argumentToEnsureNotTaintedNotMarkedAsSpurious untaintedArgumentToEnsureTaintedNotMarkedAsMissing -failures +testFailures diff --git a/python/ql/test/library-tests/frameworks/flask_admin/InlineTaintTest.ql b/python/ql/test/library-tests/frameworks/flask_admin/InlineTaintTest.ql index 027ad8667be..8524da5fe7d 100644 --- a/python/ql/test/library-tests/frameworks/flask_admin/InlineTaintTest.ql +++ b/python/ql/test/library-tests/frameworks/flask_admin/InlineTaintTest.ql @@ -1 +1,2 @@ import experimental.meta.InlineTaintTest +import MakeInlineTaintTest diff --git a/python/ql/test/library-tests/frameworks/flask_sqlalchemy/ConceptsTest.expected b/python/ql/test/library-tests/frameworks/flask_sqlalchemy/ConceptsTest.expected index e69de29bb2d..48de9172b36 100644 --- a/python/ql/test/library-tests/frameworks/flask_sqlalchemy/ConceptsTest.expected +++ b/python/ql/test/library-tests/frameworks/flask_sqlalchemy/ConceptsTest.expected @@ -0,0 +1,2 @@ +failures +testFailures diff --git a/python/ql/test/library-tests/frameworks/flask_sqlalchemy/InlineTaintTest.expected b/python/ql/test/library-tests/frameworks/flask_sqlalchemy/InlineTaintTest.expected index 79d760d87f4..4a72c551661 100644 --- a/python/ql/test/library-tests/frameworks/flask_sqlalchemy/InlineTaintTest.expected +++ b/python/ql/test/library-tests/frameworks/flask_sqlalchemy/InlineTaintTest.expected @@ -1,3 +1,4 @@ +failures argumentToEnsureNotTaintedNotMarkedAsSpurious untaintedArgumentToEnsureTaintedNotMarkedAsMissing -failures +testFailures diff --git a/python/ql/test/library-tests/frameworks/flask_sqlalchemy/InlineTaintTest.ql b/python/ql/test/library-tests/frameworks/flask_sqlalchemy/InlineTaintTest.ql index 027ad8667be..8524da5fe7d 100644 --- a/python/ql/test/library-tests/frameworks/flask_sqlalchemy/InlineTaintTest.ql +++ b/python/ql/test/library-tests/frameworks/flask_sqlalchemy/InlineTaintTest.ql @@ -1 +1,2 @@ import experimental.meta.InlineTaintTest +import MakeInlineTaintTest diff --git a/python/ql/test/library-tests/frameworks/httpx/ConceptsTest.expected b/python/ql/test/library-tests/frameworks/httpx/ConceptsTest.expected index e69de29bb2d..48de9172b36 100644 --- a/python/ql/test/library-tests/frameworks/httpx/ConceptsTest.expected +++ b/python/ql/test/library-tests/frameworks/httpx/ConceptsTest.expected @@ -0,0 +1,2 @@ +failures +testFailures diff --git a/python/ql/test/library-tests/frameworks/idna/ConceptsTest.expected b/python/ql/test/library-tests/frameworks/idna/ConceptsTest.expected index e69de29bb2d..48de9172b36 100644 --- a/python/ql/test/library-tests/frameworks/idna/ConceptsTest.expected +++ b/python/ql/test/library-tests/frameworks/idna/ConceptsTest.expected @@ -0,0 +1,2 @@ +failures +testFailures diff --git a/python/ql/test/library-tests/frameworks/idna/InlineTaintTest.expected b/python/ql/test/library-tests/frameworks/idna/InlineTaintTest.expected index 79d760d87f4..4a72c551661 100644 --- a/python/ql/test/library-tests/frameworks/idna/InlineTaintTest.expected +++ b/python/ql/test/library-tests/frameworks/idna/InlineTaintTest.expected @@ -1,3 +1,4 @@ +failures argumentToEnsureNotTaintedNotMarkedAsSpurious untaintedArgumentToEnsureTaintedNotMarkedAsMissing -failures +testFailures diff --git a/python/ql/test/library-tests/frameworks/idna/InlineTaintTest.ql b/python/ql/test/library-tests/frameworks/idna/InlineTaintTest.ql index 027ad8667be..8524da5fe7d 100644 --- a/python/ql/test/library-tests/frameworks/idna/InlineTaintTest.ql +++ b/python/ql/test/library-tests/frameworks/idna/InlineTaintTest.ql @@ -1 +1,2 @@ import experimental.meta.InlineTaintTest +import MakeInlineTaintTest diff --git a/python/ql/test/library-tests/frameworks/invoke/ConceptsTest.expected b/python/ql/test/library-tests/frameworks/invoke/ConceptsTest.expected index e69de29bb2d..48de9172b36 100644 --- a/python/ql/test/library-tests/frameworks/invoke/ConceptsTest.expected +++ b/python/ql/test/library-tests/frameworks/invoke/ConceptsTest.expected @@ -0,0 +1,2 @@ +failures +testFailures diff --git a/python/ql/test/library-tests/frameworks/jmespath/ConceptsTest.expected b/python/ql/test/library-tests/frameworks/jmespath/ConceptsTest.expected index e69de29bb2d..48de9172b36 100644 --- a/python/ql/test/library-tests/frameworks/jmespath/ConceptsTest.expected +++ b/python/ql/test/library-tests/frameworks/jmespath/ConceptsTest.expected @@ -0,0 +1,2 @@ +failures +testFailures diff --git a/python/ql/test/library-tests/frameworks/jmespath/InlineTaintTest.expected b/python/ql/test/library-tests/frameworks/jmespath/InlineTaintTest.expected index 79d760d87f4..4a72c551661 100644 --- a/python/ql/test/library-tests/frameworks/jmespath/InlineTaintTest.expected +++ b/python/ql/test/library-tests/frameworks/jmespath/InlineTaintTest.expected @@ -1,3 +1,4 @@ +failures argumentToEnsureNotTaintedNotMarkedAsSpurious untaintedArgumentToEnsureTaintedNotMarkedAsMissing -failures +testFailures diff --git a/python/ql/test/library-tests/frameworks/jmespath/InlineTaintTest.ql b/python/ql/test/library-tests/frameworks/jmespath/InlineTaintTest.ql index 027ad8667be..8524da5fe7d 100644 --- a/python/ql/test/library-tests/frameworks/jmespath/InlineTaintTest.ql +++ b/python/ql/test/library-tests/frameworks/jmespath/InlineTaintTest.ql @@ -1 +1,2 @@ import experimental.meta.InlineTaintTest +import MakeInlineTaintTest diff --git a/python/ql/test/library-tests/frameworks/libtaxii/ConceptsTest.expected b/python/ql/test/library-tests/frameworks/libtaxii/ConceptsTest.expected index e69de29bb2d..48de9172b36 100644 --- a/python/ql/test/library-tests/frameworks/libtaxii/ConceptsTest.expected +++ b/python/ql/test/library-tests/frameworks/libtaxii/ConceptsTest.expected @@ -0,0 +1,2 @@ +failures +testFailures diff --git a/python/ql/test/library-tests/frameworks/lxml/ConceptsTest.expected b/python/ql/test/library-tests/frameworks/lxml/ConceptsTest.expected index e69de29bb2d..48de9172b36 100644 --- a/python/ql/test/library-tests/frameworks/lxml/ConceptsTest.expected +++ b/python/ql/test/library-tests/frameworks/lxml/ConceptsTest.expected @@ -0,0 +1,2 @@ +failures +testFailures diff --git a/python/ql/test/library-tests/frameworks/markupsafe/ConceptsTest.expected b/python/ql/test/library-tests/frameworks/markupsafe/ConceptsTest.expected index e69de29bb2d..48de9172b36 100644 --- a/python/ql/test/library-tests/frameworks/markupsafe/ConceptsTest.expected +++ b/python/ql/test/library-tests/frameworks/markupsafe/ConceptsTest.expected @@ -0,0 +1,2 @@ +failures +testFailures diff --git a/python/ql/test/library-tests/frameworks/markupsafe/InlineTaintTest.expected b/python/ql/test/library-tests/frameworks/markupsafe/InlineTaintTest.expected index 79d760d87f4..4a72c551661 100644 --- a/python/ql/test/library-tests/frameworks/markupsafe/InlineTaintTest.expected +++ b/python/ql/test/library-tests/frameworks/markupsafe/InlineTaintTest.expected @@ -1,3 +1,4 @@ +failures argumentToEnsureNotTaintedNotMarkedAsSpurious untaintedArgumentToEnsureTaintedNotMarkedAsMissing -failures +testFailures diff --git a/python/ql/test/library-tests/frameworks/markupsafe/InlineTaintTest.ql b/python/ql/test/library-tests/frameworks/markupsafe/InlineTaintTest.ql index 993da68784e..8fd0d08c56a 100644 --- a/python/ql/test/library-tests/frameworks/markupsafe/InlineTaintTest.ql +++ b/python/ql/test/library-tests/frameworks/markupsafe/InlineTaintTest.ql @@ -1,7 +1,11 @@ import experimental.meta.InlineTaintTest import semmle.python.Concepts -class HtmlSpecialization extends TestTaintTrackingConfiguration { +module HtmlSpecializationConfig implements DataFlow::ConfigSig { + predicate isSource = TestTaintTrackingConfig::isSource/1; + + predicate isSink = TestTaintTrackingConfig::isSink/1; + // TODO: For now, since there is not an `isSanitizingStep` member-predicate part of a // `TaintTracking::Configuration`, we use treat the output is a taint-sanitizer. This // is slightly imprecise, which you can see in the `m_unsafe + SAFE` test-case in @@ -9,5 +13,7 @@ class HtmlSpecialization extends TestTaintTrackingConfiguration { // // However, it is better than `getAnInput()`. Due to use-use flow, that would remove // the taint-flow to `SINK()` in `some_escape(tainted); SINK(tainted)`. - override predicate isSanitizer(DataFlow::Node node) { node = any(HtmlEscaping esc).getOutput() } + predicate isBarrier(DataFlow::Node node) { node = any(HtmlEscaping esc).getOutput() } } + +import MakeInlineTaintTest diff --git a/python/ql/test/library-tests/frameworks/multidict/ConceptsTest.expected b/python/ql/test/library-tests/frameworks/multidict/ConceptsTest.expected index e69de29bb2d..48de9172b36 100644 --- a/python/ql/test/library-tests/frameworks/multidict/ConceptsTest.expected +++ b/python/ql/test/library-tests/frameworks/multidict/ConceptsTest.expected @@ -0,0 +1,2 @@ +failures +testFailures diff --git a/python/ql/test/library-tests/frameworks/multidict/InlineTaintTest.expected b/python/ql/test/library-tests/frameworks/multidict/InlineTaintTest.expected index 79d760d87f4..4a72c551661 100644 --- a/python/ql/test/library-tests/frameworks/multidict/InlineTaintTest.expected +++ b/python/ql/test/library-tests/frameworks/multidict/InlineTaintTest.expected @@ -1,3 +1,4 @@ +failures argumentToEnsureNotTaintedNotMarkedAsSpurious untaintedArgumentToEnsureTaintedNotMarkedAsMissing -failures +testFailures diff --git a/python/ql/test/library-tests/frameworks/multidict/InlineTaintTest.ql b/python/ql/test/library-tests/frameworks/multidict/InlineTaintTest.ql index 027ad8667be..8524da5fe7d 100644 --- a/python/ql/test/library-tests/frameworks/multidict/InlineTaintTest.ql +++ b/python/ql/test/library-tests/frameworks/multidict/InlineTaintTest.ql @@ -1 +1,2 @@ import experimental.meta.InlineTaintTest +import MakeInlineTaintTest diff --git a/python/ql/test/library-tests/frameworks/mysql-connector-python/ConceptsTest.expected b/python/ql/test/library-tests/frameworks/mysql-connector-python/ConceptsTest.expected index e69de29bb2d..48de9172b36 100644 --- a/python/ql/test/library-tests/frameworks/mysql-connector-python/ConceptsTest.expected +++ b/python/ql/test/library-tests/frameworks/mysql-connector-python/ConceptsTest.expected @@ -0,0 +1,2 @@ +failures +testFailures diff --git a/python/ql/test/library-tests/frameworks/mysqldb/ConceptsTest.expected b/python/ql/test/library-tests/frameworks/mysqldb/ConceptsTest.expected index e69de29bb2d..48de9172b36 100644 --- a/python/ql/test/library-tests/frameworks/mysqldb/ConceptsTest.expected +++ b/python/ql/test/library-tests/frameworks/mysqldb/ConceptsTest.expected @@ -0,0 +1,2 @@ +failures +testFailures diff --git a/python/ql/test/library-tests/frameworks/oracledb/ConceptsTest.expected b/python/ql/test/library-tests/frameworks/oracledb/ConceptsTest.expected index e69de29bb2d..48de9172b36 100644 --- a/python/ql/test/library-tests/frameworks/oracledb/ConceptsTest.expected +++ b/python/ql/test/library-tests/frameworks/oracledb/ConceptsTest.expected @@ -0,0 +1,2 @@ +failures +testFailures diff --git a/python/ql/test/library-tests/frameworks/peewee/ConceptsTest.expected b/python/ql/test/library-tests/frameworks/peewee/ConceptsTest.expected index e69de29bb2d..48de9172b36 100644 --- a/python/ql/test/library-tests/frameworks/peewee/ConceptsTest.expected +++ b/python/ql/test/library-tests/frameworks/peewee/ConceptsTest.expected @@ -0,0 +1,2 @@ +failures +testFailures diff --git a/python/ql/test/library-tests/frameworks/peewee/InlineTaintTest.expected b/python/ql/test/library-tests/frameworks/peewee/InlineTaintTest.expected index 79d760d87f4..4a72c551661 100644 --- a/python/ql/test/library-tests/frameworks/peewee/InlineTaintTest.expected +++ b/python/ql/test/library-tests/frameworks/peewee/InlineTaintTest.expected @@ -1,3 +1,4 @@ +failures argumentToEnsureNotTaintedNotMarkedAsSpurious untaintedArgumentToEnsureTaintedNotMarkedAsMissing -failures +testFailures diff --git a/python/ql/test/library-tests/frameworks/peewee/InlineTaintTest.ql b/python/ql/test/library-tests/frameworks/peewee/InlineTaintTest.ql index 027ad8667be..8524da5fe7d 100644 --- a/python/ql/test/library-tests/frameworks/peewee/InlineTaintTest.ql +++ b/python/ql/test/library-tests/frameworks/peewee/InlineTaintTest.ql @@ -1 +1,2 @@ import experimental.meta.InlineTaintTest +import MakeInlineTaintTest diff --git a/python/ql/test/library-tests/frameworks/phoenixdb/ConceptsTest.expected b/python/ql/test/library-tests/frameworks/phoenixdb/ConceptsTest.expected index e69de29bb2d..48de9172b36 100644 --- a/python/ql/test/library-tests/frameworks/phoenixdb/ConceptsTest.expected +++ b/python/ql/test/library-tests/frameworks/phoenixdb/ConceptsTest.expected @@ -0,0 +1,2 @@ +failures +testFailures diff --git a/python/ql/test/library-tests/frameworks/pycurl/ConceptsTest.expected b/python/ql/test/library-tests/frameworks/pycurl/ConceptsTest.expected index e69de29bb2d..48de9172b36 100644 --- a/python/ql/test/library-tests/frameworks/pycurl/ConceptsTest.expected +++ b/python/ql/test/library-tests/frameworks/pycurl/ConceptsTest.expected @@ -0,0 +1,2 @@ +failures +testFailures diff --git a/python/ql/test/library-tests/frameworks/pymssql/ConceptsTest.expected b/python/ql/test/library-tests/frameworks/pymssql/ConceptsTest.expected index e69de29bb2d..48de9172b36 100644 --- a/python/ql/test/library-tests/frameworks/pymssql/ConceptsTest.expected +++ b/python/ql/test/library-tests/frameworks/pymssql/ConceptsTest.expected @@ -0,0 +1,2 @@ +failures +testFailures diff --git a/python/ql/test/library-tests/frameworks/pymysql/ConceptsTest.expected b/python/ql/test/library-tests/frameworks/pymysql/ConceptsTest.expected index e69de29bb2d..48de9172b36 100644 --- a/python/ql/test/library-tests/frameworks/pymysql/ConceptsTest.expected +++ b/python/ql/test/library-tests/frameworks/pymysql/ConceptsTest.expected @@ -0,0 +1,2 @@ +failures +testFailures diff --git a/python/ql/test/library-tests/frameworks/pyodbc/ConceptsTest.expected b/python/ql/test/library-tests/frameworks/pyodbc/ConceptsTest.expected index e69de29bb2d..48de9172b36 100644 --- a/python/ql/test/library-tests/frameworks/pyodbc/ConceptsTest.expected +++ b/python/ql/test/library-tests/frameworks/pyodbc/ConceptsTest.expected @@ -0,0 +1,2 @@ +failures +testFailures diff --git a/python/ql/test/library-tests/frameworks/requests/ConceptsTest.expected b/python/ql/test/library-tests/frameworks/requests/ConceptsTest.expected index e69de29bb2d..48de9172b36 100644 --- a/python/ql/test/library-tests/frameworks/requests/ConceptsTest.expected +++ b/python/ql/test/library-tests/frameworks/requests/ConceptsTest.expected @@ -0,0 +1,2 @@ +failures +testFailures diff --git a/python/ql/test/library-tests/frameworks/requests/InlineTaintTest.expected b/python/ql/test/library-tests/frameworks/requests/InlineTaintTest.expected index 79d760d87f4..4a72c551661 100644 --- a/python/ql/test/library-tests/frameworks/requests/InlineTaintTest.expected +++ b/python/ql/test/library-tests/frameworks/requests/InlineTaintTest.expected @@ -1,3 +1,4 @@ +failures argumentToEnsureNotTaintedNotMarkedAsSpurious untaintedArgumentToEnsureTaintedNotMarkedAsMissing -failures +testFailures diff --git a/python/ql/test/library-tests/frameworks/requests/InlineTaintTest.ql b/python/ql/test/library-tests/frameworks/requests/InlineTaintTest.ql index 027ad8667be..8524da5fe7d 100644 --- a/python/ql/test/library-tests/frameworks/requests/InlineTaintTest.ql +++ b/python/ql/test/library-tests/frameworks/requests/InlineTaintTest.ql @@ -1 +1,2 @@ import experimental.meta.InlineTaintTest +import MakeInlineTaintTest diff --git a/python/ql/test/library-tests/frameworks/rest_framework/ConceptsTest.expected b/python/ql/test/library-tests/frameworks/rest_framework/ConceptsTest.expected index e69de29bb2d..48de9172b36 100644 --- a/python/ql/test/library-tests/frameworks/rest_framework/ConceptsTest.expected +++ b/python/ql/test/library-tests/frameworks/rest_framework/ConceptsTest.expected @@ -0,0 +1,2 @@ +failures +testFailures diff --git a/python/ql/test/library-tests/frameworks/rest_framework/InlineTaintTest.expected b/python/ql/test/library-tests/frameworks/rest_framework/InlineTaintTest.expected index 79d760d87f4..4a72c551661 100644 --- a/python/ql/test/library-tests/frameworks/rest_framework/InlineTaintTest.expected +++ b/python/ql/test/library-tests/frameworks/rest_framework/InlineTaintTest.expected @@ -1,3 +1,4 @@ +failures argumentToEnsureNotTaintedNotMarkedAsSpurious untaintedArgumentToEnsureTaintedNotMarkedAsMissing -failures +testFailures diff --git a/python/ql/test/library-tests/frameworks/rest_framework/InlineTaintTest.ql b/python/ql/test/library-tests/frameworks/rest_framework/InlineTaintTest.ql index 027ad8667be..8524da5fe7d 100644 --- a/python/ql/test/library-tests/frameworks/rest_framework/InlineTaintTest.ql +++ b/python/ql/test/library-tests/frameworks/rest_framework/InlineTaintTest.ql @@ -1 +1,2 @@ import experimental.meta.InlineTaintTest +import MakeInlineTaintTest diff --git a/python/ql/test/library-tests/frameworks/rsa/ConceptsTest.expected b/python/ql/test/library-tests/frameworks/rsa/ConceptsTest.expected index e69de29bb2d..48de9172b36 100644 --- a/python/ql/test/library-tests/frameworks/rsa/ConceptsTest.expected +++ b/python/ql/test/library-tests/frameworks/rsa/ConceptsTest.expected @@ -0,0 +1,2 @@ +failures +testFailures diff --git a/python/ql/test/library-tests/frameworks/rsa/InlineTaintTest.expected b/python/ql/test/library-tests/frameworks/rsa/InlineTaintTest.expected index 79d760d87f4..4a72c551661 100644 --- a/python/ql/test/library-tests/frameworks/rsa/InlineTaintTest.expected +++ b/python/ql/test/library-tests/frameworks/rsa/InlineTaintTest.expected @@ -1,3 +1,4 @@ +failures argumentToEnsureNotTaintedNotMarkedAsSpurious untaintedArgumentToEnsureTaintedNotMarkedAsMissing -failures +testFailures diff --git a/python/ql/test/library-tests/frameworks/rsa/InlineTaintTest.ql b/python/ql/test/library-tests/frameworks/rsa/InlineTaintTest.ql index 027ad8667be..8524da5fe7d 100644 --- a/python/ql/test/library-tests/frameworks/rsa/InlineTaintTest.ql +++ b/python/ql/test/library-tests/frameworks/rsa/InlineTaintTest.ql @@ -1 +1,2 @@ import experimental.meta.InlineTaintTest +import MakeInlineTaintTest diff --git a/python/ql/test/library-tests/frameworks/ruamel.yaml/ConceptsTest.expected b/python/ql/test/library-tests/frameworks/ruamel.yaml/ConceptsTest.expected index e69de29bb2d..48de9172b36 100644 --- a/python/ql/test/library-tests/frameworks/ruamel.yaml/ConceptsTest.expected +++ b/python/ql/test/library-tests/frameworks/ruamel.yaml/ConceptsTest.expected @@ -0,0 +1,2 @@ +failures +testFailures diff --git a/python/ql/test/library-tests/frameworks/simplejson/ConceptsTest.expected b/python/ql/test/library-tests/frameworks/simplejson/ConceptsTest.expected index e69de29bb2d..48de9172b36 100644 --- a/python/ql/test/library-tests/frameworks/simplejson/ConceptsTest.expected +++ b/python/ql/test/library-tests/frameworks/simplejson/ConceptsTest.expected @@ -0,0 +1,2 @@ +failures +testFailures diff --git a/python/ql/test/library-tests/frameworks/simplejson/InlineTaintTest.expected b/python/ql/test/library-tests/frameworks/simplejson/InlineTaintTest.expected index 79d760d87f4..4a72c551661 100644 --- a/python/ql/test/library-tests/frameworks/simplejson/InlineTaintTest.expected +++ b/python/ql/test/library-tests/frameworks/simplejson/InlineTaintTest.expected @@ -1,3 +1,4 @@ +failures argumentToEnsureNotTaintedNotMarkedAsSpurious untaintedArgumentToEnsureTaintedNotMarkedAsMissing -failures +testFailures diff --git a/python/ql/test/library-tests/frameworks/simplejson/InlineTaintTest.ql b/python/ql/test/library-tests/frameworks/simplejson/InlineTaintTest.ql index 027ad8667be..8524da5fe7d 100644 --- a/python/ql/test/library-tests/frameworks/simplejson/InlineTaintTest.ql +++ b/python/ql/test/library-tests/frameworks/simplejson/InlineTaintTest.ql @@ -1 +1,2 @@ import experimental.meta.InlineTaintTest +import MakeInlineTaintTest diff --git a/python/ql/test/library-tests/frameworks/sqlalchemy/ConceptsTest.expected b/python/ql/test/library-tests/frameworks/sqlalchemy/ConceptsTest.expected index e69de29bb2d..48de9172b36 100644 --- a/python/ql/test/library-tests/frameworks/sqlalchemy/ConceptsTest.expected +++ b/python/ql/test/library-tests/frameworks/sqlalchemy/ConceptsTest.expected @@ -0,0 +1,2 @@ +failures +testFailures diff --git a/python/ql/test/library-tests/frameworks/sqlalchemy/InlineTaintTest.expected b/python/ql/test/library-tests/frameworks/sqlalchemy/InlineTaintTest.expected index 79d760d87f4..4a72c551661 100644 --- a/python/ql/test/library-tests/frameworks/sqlalchemy/InlineTaintTest.expected +++ b/python/ql/test/library-tests/frameworks/sqlalchemy/InlineTaintTest.expected @@ -1,3 +1,4 @@ +failures argumentToEnsureNotTaintedNotMarkedAsSpurious untaintedArgumentToEnsureTaintedNotMarkedAsMissing -failures +testFailures diff --git a/python/ql/test/library-tests/frameworks/sqlalchemy/InlineTaintTest.ql b/python/ql/test/library-tests/frameworks/sqlalchemy/InlineTaintTest.ql index 027ad8667be..8524da5fe7d 100644 --- a/python/ql/test/library-tests/frameworks/sqlalchemy/InlineTaintTest.ql +++ b/python/ql/test/library-tests/frameworks/sqlalchemy/InlineTaintTest.ql @@ -1 +1,2 @@ import experimental.meta.InlineTaintTest +import MakeInlineTaintTest diff --git a/python/ql/test/library-tests/frameworks/stdlib-py2/ConceptsTest.expected b/python/ql/test/library-tests/frameworks/stdlib-py2/ConceptsTest.expected index e69de29bb2d..48de9172b36 100644 --- a/python/ql/test/library-tests/frameworks/stdlib-py2/ConceptsTest.expected +++ b/python/ql/test/library-tests/frameworks/stdlib-py2/ConceptsTest.expected @@ -0,0 +1,2 @@ +failures +testFailures diff --git a/python/ql/test/library-tests/frameworks/stdlib-py3/ConceptsTest.expected b/python/ql/test/library-tests/frameworks/stdlib-py3/ConceptsTest.expected index e69de29bb2d..48de9172b36 100644 --- a/python/ql/test/library-tests/frameworks/stdlib-py3/ConceptsTest.expected +++ b/python/ql/test/library-tests/frameworks/stdlib-py3/ConceptsTest.expected @@ -0,0 +1,2 @@ +failures +testFailures diff --git a/python/ql/test/library-tests/frameworks/stdlib/ConceptsTest.expected b/python/ql/test/library-tests/frameworks/stdlib/ConceptsTest.expected index e69de29bb2d..48de9172b36 100644 --- a/python/ql/test/library-tests/frameworks/stdlib/ConceptsTest.expected +++ b/python/ql/test/library-tests/frameworks/stdlib/ConceptsTest.expected @@ -0,0 +1,2 @@ +failures +testFailures diff --git a/python/ql/test/library-tests/frameworks/stdlib/InlineTaintTest.expected b/python/ql/test/library-tests/frameworks/stdlib/InlineTaintTest.expected index 79d760d87f4..4a72c551661 100644 --- a/python/ql/test/library-tests/frameworks/stdlib/InlineTaintTest.expected +++ b/python/ql/test/library-tests/frameworks/stdlib/InlineTaintTest.expected @@ -1,3 +1,4 @@ +failures argumentToEnsureNotTaintedNotMarkedAsSpurious untaintedArgumentToEnsureTaintedNotMarkedAsMissing -failures +testFailures diff --git a/python/ql/test/library-tests/frameworks/stdlib/InlineTaintTest.ql b/python/ql/test/library-tests/frameworks/stdlib/InlineTaintTest.ql index 027ad8667be..8524da5fe7d 100644 --- a/python/ql/test/library-tests/frameworks/stdlib/InlineTaintTest.ql +++ b/python/ql/test/library-tests/frameworks/stdlib/InlineTaintTest.ql @@ -1 +1,2 @@ import experimental.meta.InlineTaintTest +import MakeInlineTaintTest diff --git a/python/ql/test/library-tests/frameworks/toml/ConceptsTest.expected b/python/ql/test/library-tests/frameworks/toml/ConceptsTest.expected index e69de29bb2d..48de9172b36 100644 --- a/python/ql/test/library-tests/frameworks/toml/ConceptsTest.expected +++ b/python/ql/test/library-tests/frameworks/toml/ConceptsTest.expected @@ -0,0 +1,2 @@ +failures +testFailures diff --git a/python/ql/test/library-tests/frameworks/tornado/ConceptsTest.expected b/python/ql/test/library-tests/frameworks/tornado/ConceptsTest.expected index e69de29bb2d..48de9172b36 100644 --- a/python/ql/test/library-tests/frameworks/tornado/ConceptsTest.expected +++ b/python/ql/test/library-tests/frameworks/tornado/ConceptsTest.expected @@ -0,0 +1,2 @@ +failures +testFailures diff --git a/python/ql/test/library-tests/frameworks/tornado/InlineTaintTest.expected b/python/ql/test/library-tests/frameworks/tornado/InlineTaintTest.expected index 79d760d87f4..4a72c551661 100644 --- a/python/ql/test/library-tests/frameworks/tornado/InlineTaintTest.expected +++ b/python/ql/test/library-tests/frameworks/tornado/InlineTaintTest.expected @@ -1,3 +1,4 @@ +failures argumentToEnsureNotTaintedNotMarkedAsSpurious untaintedArgumentToEnsureTaintedNotMarkedAsMissing -failures +testFailures diff --git a/python/ql/test/library-tests/frameworks/tornado/InlineTaintTest.ql b/python/ql/test/library-tests/frameworks/tornado/InlineTaintTest.ql index 027ad8667be..8524da5fe7d 100644 --- a/python/ql/test/library-tests/frameworks/tornado/InlineTaintTest.ql +++ b/python/ql/test/library-tests/frameworks/tornado/InlineTaintTest.ql @@ -1 +1,2 @@ import experimental.meta.InlineTaintTest +import MakeInlineTaintTest diff --git a/python/ql/test/library-tests/frameworks/twisted/ConceptsTest.expected b/python/ql/test/library-tests/frameworks/twisted/ConceptsTest.expected index e69de29bb2d..48de9172b36 100644 --- a/python/ql/test/library-tests/frameworks/twisted/ConceptsTest.expected +++ b/python/ql/test/library-tests/frameworks/twisted/ConceptsTest.expected @@ -0,0 +1,2 @@ +failures +testFailures diff --git a/python/ql/test/library-tests/frameworks/twisted/InlineTaintTest.expected b/python/ql/test/library-tests/frameworks/twisted/InlineTaintTest.expected index 79d760d87f4..4a72c551661 100644 --- a/python/ql/test/library-tests/frameworks/twisted/InlineTaintTest.expected +++ b/python/ql/test/library-tests/frameworks/twisted/InlineTaintTest.expected @@ -1,3 +1,4 @@ +failures argumentToEnsureNotTaintedNotMarkedAsSpurious untaintedArgumentToEnsureTaintedNotMarkedAsMissing -failures +testFailures diff --git a/python/ql/test/library-tests/frameworks/twisted/InlineTaintTest.ql b/python/ql/test/library-tests/frameworks/twisted/InlineTaintTest.ql index 027ad8667be..8524da5fe7d 100644 --- a/python/ql/test/library-tests/frameworks/twisted/InlineTaintTest.ql +++ b/python/ql/test/library-tests/frameworks/twisted/InlineTaintTest.ql @@ -1 +1,2 @@ import experimental.meta.InlineTaintTest +import MakeInlineTaintTest diff --git a/python/ql/test/library-tests/frameworks/ujson/ConceptsTest.expected b/python/ql/test/library-tests/frameworks/ujson/ConceptsTest.expected index e69de29bb2d..48de9172b36 100644 --- a/python/ql/test/library-tests/frameworks/ujson/ConceptsTest.expected +++ b/python/ql/test/library-tests/frameworks/ujson/ConceptsTest.expected @@ -0,0 +1,2 @@ +failures +testFailures diff --git a/python/ql/test/library-tests/frameworks/ujson/InlineTaintTest.expected b/python/ql/test/library-tests/frameworks/ujson/InlineTaintTest.expected index 79d760d87f4..4a72c551661 100644 --- a/python/ql/test/library-tests/frameworks/ujson/InlineTaintTest.expected +++ b/python/ql/test/library-tests/frameworks/ujson/InlineTaintTest.expected @@ -1,3 +1,4 @@ +failures argumentToEnsureNotTaintedNotMarkedAsSpurious untaintedArgumentToEnsureTaintedNotMarkedAsMissing -failures +testFailures diff --git a/python/ql/test/library-tests/frameworks/ujson/InlineTaintTest.ql b/python/ql/test/library-tests/frameworks/ujson/InlineTaintTest.ql index 027ad8667be..8524da5fe7d 100644 --- a/python/ql/test/library-tests/frameworks/ujson/InlineTaintTest.ql +++ b/python/ql/test/library-tests/frameworks/ujson/InlineTaintTest.ql @@ -1 +1,2 @@ import experimental.meta.InlineTaintTest +import MakeInlineTaintTest diff --git a/python/ql/test/library-tests/frameworks/urllib3/ConceptsTest.expected b/python/ql/test/library-tests/frameworks/urllib3/ConceptsTest.expected index e69de29bb2d..48de9172b36 100644 --- a/python/ql/test/library-tests/frameworks/urllib3/ConceptsTest.expected +++ b/python/ql/test/library-tests/frameworks/urllib3/ConceptsTest.expected @@ -0,0 +1,2 @@ +failures +testFailures diff --git a/python/ql/test/library-tests/frameworks/xmltodict/ConceptsTest.expected b/python/ql/test/library-tests/frameworks/xmltodict/ConceptsTest.expected index e69de29bb2d..48de9172b36 100644 --- a/python/ql/test/library-tests/frameworks/xmltodict/ConceptsTest.expected +++ b/python/ql/test/library-tests/frameworks/xmltodict/ConceptsTest.expected @@ -0,0 +1,2 @@ +failures +testFailures diff --git a/python/ql/test/library-tests/frameworks/yaml/ConceptsTest.expected b/python/ql/test/library-tests/frameworks/yaml/ConceptsTest.expected index e69de29bb2d..48de9172b36 100644 --- a/python/ql/test/library-tests/frameworks/yaml/ConceptsTest.expected +++ b/python/ql/test/library-tests/frameworks/yaml/ConceptsTest.expected @@ -0,0 +1,2 @@ +failures +testFailures diff --git a/python/ql/test/library-tests/frameworks/yarl/ConceptsTest.expected b/python/ql/test/library-tests/frameworks/yarl/ConceptsTest.expected index e69de29bb2d..48de9172b36 100644 --- a/python/ql/test/library-tests/frameworks/yarl/ConceptsTest.expected +++ b/python/ql/test/library-tests/frameworks/yarl/ConceptsTest.expected @@ -0,0 +1,2 @@ +failures +testFailures diff --git a/python/ql/test/library-tests/frameworks/yarl/InlineTaintTest.expected b/python/ql/test/library-tests/frameworks/yarl/InlineTaintTest.expected index 79d760d87f4..4a72c551661 100644 --- a/python/ql/test/library-tests/frameworks/yarl/InlineTaintTest.expected +++ b/python/ql/test/library-tests/frameworks/yarl/InlineTaintTest.expected @@ -1,3 +1,4 @@ +failures argumentToEnsureNotTaintedNotMarkedAsSpurious untaintedArgumentToEnsureTaintedNotMarkedAsMissing -failures +testFailures diff --git a/python/ql/test/library-tests/frameworks/yarl/InlineTaintTest.ql b/python/ql/test/library-tests/frameworks/yarl/InlineTaintTest.ql index 027ad8667be..8524da5fe7d 100644 --- a/python/ql/test/library-tests/frameworks/yarl/InlineTaintTest.ql +++ b/python/ql/test/library-tests/frameworks/yarl/InlineTaintTest.ql @@ -1 +1,2 @@ import experimental.meta.InlineTaintTest +import MakeInlineTaintTest diff --git a/python/ql/test/library-tests/regex/SubstructureTests.expected b/python/ql/test/library-tests/regex/SubstructureTests.expected index e69de29bb2d..48de9172b36 100644 --- a/python/ql/test/library-tests/regex/SubstructureTests.expected +++ b/python/ql/test/library-tests/regex/SubstructureTests.expected @@ -0,0 +1,2 @@ +failures +testFailures diff --git a/python/ql/test/library-tests/regex/SubstructureTests.ql b/python/ql/test/library-tests/regex/SubstructureTests.ql index e189c13b15e..f575670e16a 100644 --- a/python/ql/test/library-tests/regex/SubstructureTests.ql +++ b/python/ql/test/library-tests/regex/SubstructureTests.ql @@ -2,12 +2,10 @@ import python import TestUtilities.InlineExpectationsTest private import semmle.python.regex -class CharacterSetTest extends InlineExpectationsTest { - CharacterSetTest() { this = "CharacterSetTest" } +module CharacterSetTest implements TestSig { + string getARelevantTag() { result = "charSet" } - override string getARelevantTag() { result = "charSet" } - - override predicate hasActualResult(Location location, string element, string tag, string value) { + predicate hasActualResult(Location location, string element, string tag, string value) { exists(location.getFile().getRelativePath()) and location.getFile().getBaseName() = "charSetTest.py" and exists(RegExp re, int start, int end | @@ -20,12 +18,10 @@ class CharacterSetTest extends InlineExpectationsTest { } } -class CharacterRangeTest extends InlineExpectationsTest { - CharacterRangeTest() { this = "CharacterRangeTest" } +module CharacterRangeTest implements TestSig { + string getARelevantTag() { result = "charRange" } - override string getARelevantTag() { result = "charRange" } - - override predicate hasActualResult(Location location, string element, string tag, string value) { + predicate hasActualResult(Location location, string element, string tag, string value) { exists(location.getFile().getRelativePath()) and location.getFile().getBaseName() = "charRangeTest.py" and exists(RegExp re, int start, int lower_end, int upper_start, int end | @@ -38,12 +34,10 @@ class CharacterRangeTest extends InlineExpectationsTest { } } -class EscapeTest extends InlineExpectationsTest { - EscapeTest() { this = "EscapeTest" } +module EscapeTest implements TestSig { + string getARelevantTag() { result = "escapedCharacter" } - override string getARelevantTag() { result = "escapedCharacter" } - - override predicate hasActualResult(Location location, string element, string tag, string value) { + predicate hasActualResult(Location location, string element, string tag, string value) { exists(location.getFile().getRelativePath()) and location.getFile().getBaseName() = "escapedCharacterTest.py" and exists(RegExp re, int start, int end | @@ -56,12 +50,10 @@ class EscapeTest extends InlineExpectationsTest { } } -class GroupTest extends InlineExpectationsTest { - GroupTest() { this = "GroupTest" } +module GroupTest implements TestSig { + string getARelevantTag() { result = "group" } - override string getARelevantTag() { result = "group" } - - override predicate hasActualResult(Location location, string element, string tag, string value) { + predicate hasActualResult(Location location, string element, string tag, string value) { exists(location.getFile().getRelativePath()) and location.getFile().getBaseName() = "groupTest.py" and exists(RegExp re, int start, int end | @@ -73,3 +65,5 @@ class GroupTest extends InlineExpectationsTest { ) } } + +import MakeTest> diff --git a/python/ql/test/library-tests/security/command-execution/CommandSinks.expected b/python/ql/test/library-tests/security/command-execution/CommandSinks.expected deleted file mode 100644 index 425a0471399..00000000000 --- a/python/ql/test/library-tests/security/command-execution/CommandSinks.expected +++ /dev/null @@ -1,18 +0,0 @@ -WARNING: Type CommandSink has been deprecated and may be removed in future (CommandSinks.ql:4,6-17) -| fabric_v1_test.py:8:7:8:28 | FabricV1Commands | externally controlled string | -| fabric_v1_test.py:9:5:9:27 | FabricV1Commands | externally controlled string | -| fabric_v1_test.py:10:6:10:38 | FabricV1Commands | externally controlled string | -| fabric_v2_test.py:10:16:10:25 | InvokeContextRun | externally controlled string | -| fabric_v2_test.py:12:15:12:36 | InvokeContextRun | externally controlled string | -| fabric_v2_test.py:16:45:16:54 | FabricGroupRun | externally controlled string | -| fabric_v2_test.py:21:10:21:13 | FabricGroupRun | externally controlled string | -| fabric_v2_test.py:31:14:31:41 | InvokeContextRun | externally controlled string | -| fabric_v2_test.py:33:15:33:64 | InvokeContextRun | externally controlled string | -| invoke_test.py:8:12:8:21 | InvokeRun | externally controlled string | -| invoke_test.py:9:20:9:40 | InvokeRun | externally controlled string | -| invoke_test.py:12:17:12:24 | InvokeRun | externally controlled string | -| invoke_test.py:13:25:13:32 | InvokeRun | externally controlled string | -| invoke_test.py:17:11:17:40 | InvokeContextRun | externally controlled string | -| invoke_test.py:21:11:21:32 | InvokeContextRun | externally controlled string | -| invoke_test.py:27:11:27:25 | InvokeContextRun | externally controlled string | -| invoke_test.py:32:11:32:25 | InvokeContextRun | externally controlled string | diff --git a/python/ql/test/library-tests/security/command-execution/CommandSinks.ql b/python/ql/test/library-tests/security/command-execution/CommandSinks.ql deleted file mode 100644 index 797b527d568..00000000000 --- a/python/ql/test/library-tests/security/command-execution/CommandSinks.ql +++ /dev/null @@ -1,6 +0,0 @@ -import python -import semmle.python.security.injection.Command - -from CommandSink sink, TaintKind kind -where sink.sinks(kind) -select sink, kind diff --git a/python/ql/test/library-tests/security/command-execution/fabric-LICENSE b/python/ql/test/library-tests/security/command-execution/fabric-LICENSE deleted file mode 100644 index 10e0dcedd55..00000000000 --- a/python/ql/test/library-tests/security/command-execution/fabric-LICENSE +++ /dev/null @@ -1,22 +0,0 @@ -Copyright (c) 2020 Jeff Forcier. -All rights reserved. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions are met: - - * Redistributions of source code must retain the above copyright notice, - this list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above copyright notice, - this list of conditions and the following disclaimer in the documentation - and/or other materials provided with the distribution. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE -FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL -DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR -SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER -CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, -OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/python/ql/test/library-tests/security/command-execution/fabric_v1_test.py b/python/ql/test/library-tests/security/command-execution/fabric_v1_test.py deleted file mode 100644 index 82f34dde6d2..00000000000 --- a/python/ql/test/library-tests/security/command-execution/fabric_v1_test.py +++ /dev/null @@ -1,10 +0,0 @@ -"""tests for the 'fabric' package (v1.x) - -See http://docs.fabfile.org/en/1.14/tutorial.html -""" - -from fabric.api import run, local, sudo - -local('echo local execution') -run('echo remote execution') -sudo('echo remote execution with sudo') diff --git a/python/ql/test/library-tests/security/command-execution/fabric_v2_test.py b/python/ql/test/library-tests/security/command-execution/fabric_v2_test.py deleted file mode 100644 index 0854db3ea0b..00000000000 --- a/python/ql/test/library-tests/security/command-execution/fabric_v2_test.py +++ /dev/null @@ -1,33 +0,0 @@ -"""tests for the 'fabric' package (v2.x) - -Most of these examples are taken from the fabric documentation: http://docs.fabfile.org/en/2.5/getting-started.html -See fabric-LICENSE for its' license. -""" - -from fabric import Connection - -c = Connection('web1') -result = c.run('uname -s') - -c.run(command='echo run with kwargs') - - -from fabric import SerialGroup as Group -results = Group('web1', 'web2', 'mac1').run('uname -s') - - -from fabric import SerialGroup as Group -pool = Group('web1', 'web2', 'web3') -pool.run('ls') - - - -# using the 'fab' command-line tool - -from fabric import task - -@task -def upload_and_unpack(c): - if c.run('test -f /opt/mydata/myfile', warn=True).failed: - c.put('myfiles.tgz', '/opt/mydata') - c.run('tar -C /opt/mydata -xzvf /opt/mydata/myfiles.tgz') diff --git a/python/ql/test/library-tests/security/command-execution/invoke_test.py b/python/ql/test/library-tests/security/command-execution/invoke_test.py deleted file mode 100644 index 9bc2213b617..00000000000 --- a/python/ql/test/library-tests/security/command-execution/invoke_test.py +++ /dev/null @@ -1,32 +0,0 @@ -"""tests for the 'invoke' package - -see https://www.pyinvoke.org/ -""" - -import invoke - -invoke.run('echo run') -invoke.run(command='echo run with kwarg') - -def with_sudo(): - invoke.sudo('whoami') - invoke.sudo(command='whoami') - -def manual_context(): - c = invoke.Context() - c.run('echo run from manual context') -manual_context() - -def foo_helper(c): - c.run('echo from foo_helper') - -# for use with the 'invoke' command-line tool -@invoke.task -def foo(c): - # 'c' is a invoke.context.Context - c.run('echo task foo') - foo_helper(c) - -@invoke.task() -def bar(c): - c.run('echo task bar') diff --git a/python/ql/test/library-tests/security/command-execution/options b/python/ql/test/library-tests/security/command-execution/options deleted file mode 100644 index 1d132442a3b..00000000000 --- a/python/ql/test/library-tests/security/command-execution/options +++ /dev/null @@ -1 +0,0 @@ -semmle-extractor-options: --max-import-depth=2 -p ../../../query-tests/Security/lib/ diff --git a/python/ql/test/library-tests/security/fabric-v1-execute/Taint.qll b/python/ql/test/library-tests/security/fabric-v1-execute/Taint.qll deleted file mode 100644 index 7d1a812be92..00000000000 --- a/python/ql/test/library-tests/security/fabric-v1-execute/Taint.qll +++ /dev/null @@ -1,24 +0,0 @@ -import python -import semmle.python.dataflow.TaintTracking -import semmle.python.security.strings.Untrusted -import semmle.python.security.injection.Command - -class SimpleSource extends TaintSource { - SimpleSource() { this.(NameNode).getId() = "TAINTED_STRING" } - - override predicate isSourceOf(TaintKind kind) { kind instanceof ExternalStringKind } - - override string toString() { result = "taint source" } -} - -class FabricExecuteTestConfiguration extends TaintTracking::Configuration { - FabricExecuteTestConfiguration() { this = "FabricExecuteTestConfiguration" } - - override predicate isSource(TaintTracking::Source source) { source instanceof SimpleSource } - - override predicate isSink(TaintTracking::Sink sink) { sink instanceof CommandSink } - - override predicate isExtension(TaintTracking::Extension extension) { - extension instanceof FabricExecuteExtension - } -} diff --git a/python/ql/test/library-tests/security/fabric-v1-execute/TestTaint.expected b/python/ql/test/library-tests/security/fabric-v1-execute/TestTaint.expected deleted file mode 100644 index edd58bbb75f..00000000000 --- a/python/ql/test/library-tests/security/fabric-v1-execute/TestTaint.expected +++ /dev/null @@ -1,10 +0,0 @@ -| test.py:8 | ok | unsafe | cmd | externally controlled string | -| test.py:8 | ok | unsafe | cmd2 | externally controlled string | -| test.py:9 | ok | unsafe | safe_arg | | -| test.py:9 | ok | unsafe | safe_optional | | -| test.py:16 | ok | unsafe | cmd | externally controlled string | -| test.py:16 | ok | unsafe | cmd2 | externally controlled string | -| test.py:17 | ok | unsafe | safe_arg | | -| test.py:17 | ok | unsafe | safe_optional | | -| test.py:23 | ok | some_http_handler | cmd | externally controlled string | -| test.py:23 | ok | some_http_handler | cmd2 | externally controlled string | diff --git a/python/ql/test/library-tests/security/fabric-v1-execute/TestTaint.ql b/python/ql/test/library-tests/security/fabric-v1-execute/TestTaint.ql deleted file mode 100644 index 7b9c6025c9d..00000000000 --- a/python/ql/test/library-tests/security/fabric-v1-execute/TestTaint.ql +++ /dev/null @@ -1,33 +0,0 @@ -import python -import semmle.python.security.TaintTracking -import semmle.python.web.HttpRequest -import semmle.python.security.strings.Untrusted -import Taint - -from - Call call, Expr arg, boolean expected_taint, boolean has_taint, string test_res, - string taint_string -where - call.getLocation().getFile().getShortName() = "test.py" and - ( - call.getFunc().(Name).getId() = "ensure_tainted" and - expected_taint = true - or - call.getFunc().(Name).getId() = "ensure_not_tainted" and - expected_taint = false - ) and - arg = call.getAnArg() and - ( - not exists(TaintedNode tainted | tainted.getAstNode() = arg) and - taint_string = "" and - has_taint = false - or - exists(TaintedNode tainted | tainted.getAstNode() = arg | - taint_string = tainted.getTaintKind().toString() - ) and - has_taint = true - ) and - if expected_taint = has_taint then test_res = "ok " else test_res = "fail" -// if expected_taint = has_taint then test_res = "✓" else test_res = "✕" -select arg.getLocation().toString(), test_res, call.getScope().(Function).getName(), arg.toString(), - taint_string diff --git a/python/ql/test/library-tests/security/fabric-v1-execute/options b/python/ql/test/library-tests/security/fabric-v1-execute/options deleted file mode 100644 index 1d132442a3b..00000000000 --- a/python/ql/test/library-tests/security/fabric-v1-execute/options +++ /dev/null @@ -1 +0,0 @@ -semmle-extractor-options: --max-import-depth=2 -p ../../../query-tests/Security/lib/ diff --git a/python/ql/test/library-tests/security/fabric-v1-execute/test.py b/python/ql/test/library-tests/security/fabric-v1-execute/test.py deleted file mode 100644 index 7a1cd9ab377..00000000000 --- a/python/ql/test/library-tests/security/fabric-v1-execute/test.py +++ /dev/null @@ -1,28 +0,0 @@ -"""Test that shows fabric.api.execute propagates taint""" - -from fabric.api import run, execute - - -def unsafe(cmd, safe_arg, cmd2=None, safe_optional=5): - run('./venv/bin/activate && {}'.format(cmd)) - ensure_tainted(cmd, cmd2) - ensure_not_tainted(safe_arg, safe_optional) - - -class Foo(object): - - def unsafe(self, cmd, safe_arg, cmd2=None, safe_optional=5): - run('./venv/bin/activate && {}'.format(cmd)) - ensure_tainted(cmd, cmd2) - ensure_not_tainted(safe_arg, safe_optional) - - -def some_http_handler(): - cmd = TAINTED_STRING - cmd2 = TAINTED_STRING - ensure_tainted(cmd, cmd2) - - execute(unsafe, cmd=cmd, safe_arg='safe_arg', cmd2=cmd2) - - foo = Foo() - execute(foo.unsafe, cmd, 'safe_arg', cmd2) diff --git a/python/ql/test/library-tests/taint/collections/Taint.qll b/python/ql/test/library-tests/taint/collections/Taint.qll deleted file mode 100644 index 010b9738c5c..00000000000 --- a/python/ql/test/library-tests/taint/collections/Taint.qll +++ /dev/null @@ -1,27 +0,0 @@ -import python -import semmle.python.dataflow.TaintTracking -import semmle.python.security.strings.Untrusted - -class SimpleSource extends TaintSource { - SimpleSource() { this.(NameNode).getId() = "TAINTED_STRING" } - - override predicate isSourceOf(TaintKind kind) { kind instanceof ExternalStringKind } - - override string toString() { result = "taint source" } -} - -class ListSource extends TaintSource { - ListSource() { this.(NameNode).getId() = "TAINTED_LIST" } - - override predicate isSourceOf(TaintKind kind) { kind instanceof ExternalStringSequenceKind } - - override string toString() { result = "list taint source" } -} - -class DictSource extends TaintSource { - DictSource() { this.(NameNode).getId() = "TAINTED_DICT" } - - override predicate isSourceOf(TaintKind kind) { kind instanceof ExternalStringDictKind } - - override string toString() { result = "dict taint source" } -} diff --git a/python/ql/test/library-tests/taint/collections/TestStep.expected b/python/ql/test/library-tests/taint/collections/TestStep.expected deleted file mode 100644 index a9591db0b9f..00000000000 --- a/python/ql/test/library-tests/taint/collections/TestStep.expected +++ /dev/null @@ -1,63 +0,0 @@ -| Taint [externally controlled string] | test.py:9 | test.py:9:20:9:35 | List | | --> | Taint [externally controlled string] | test.py:14 | test.py:14:14:14:25 | tainted_list | | -| Taint [externally controlled string] | test.py:9 | test.py:9:20:9:35 | List | | --> | Taint [externally controlled string] | test.py:20 | test.py:20:15:20:26 | tainted_list | | -| Taint [externally controlled string] | test.py:9 | test.py:9:20:9:35 | List | | --> | Taint [externally controlled string] | test.py:21 | test.py:21:13:21:24 | tainted_list | | -| Taint [externally controlled string] | test.py:9 | test.py:9:20:9:35 | List | | --> | Taint [externally controlled string] | test.py:22 | test.py:22:19:22:30 | tainted_list | | -| Taint [externally controlled string] | test.py:10 | test.py:10:22:10:36 | Tuple | | --> | Taint [externally controlled string] | test.py:15 | test.py:15:14:15:26 | tainted_tuple | | -| Taint [externally controlled string] | test.py:14 | test.py:14:9:14:26 | list() | | --> | Taint [externally controlled string] | test.py:23 | test.py:23:10:23:10 | a | | -| Taint [externally controlled string] | test.py:14 | test.py:14:14:14:25 | tainted_list | | --> | Taint [externally controlled string] | test.py:14 | test.py:14:9:14:26 | list() | | -| Taint [externally controlled string] | test.py:15 | test.py:15:9:15:27 | list() | | --> | Taint [externally controlled string] | test.py:23 | test.py:23:13:23:13 | b | | -| Taint [externally controlled string] | test.py:15 | test.py:15:14:15:26 | tainted_tuple | | --> | Taint [externally controlled string] | test.py:15 | test.py:15:9:15:27 | list() | | -| Taint [externally controlled string] | test.py:17 | test.py:17:9:17:35 | list() | | --> | Taint [externally controlled string] | test.py:23 | test.py:23:19:23:19 | d | | -| Taint [externally controlled string] | test.py:17 | test.py:17:14:17:34 | Attribute() | | --> | Taint [externally controlled string] | test.py:17 | test.py:17:9:17:35 | list() | | -| Taint [externally controlled string] | test.py:20 | test.py:20:9:20:27 | tuple() | | --> | Taint [externally controlled string] | test.py:23 | test.py:23:25:23:25 | f | | -| Taint [externally controlled string] | test.py:20 | test.py:20:15:20:26 | tainted_list | | --> | Taint [externally controlled string] | test.py:20 | test.py:20:9:20:27 | tuple() | | -| Taint [externally controlled string] | test.py:21 | test.py:21:9:21:25 | set() | | --> | Taint [externally controlled string] | test.py:23 | test.py:23:28:23:28 | g | | -| Taint [externally controlled string] | test.py:21 | test.py:21:13:21:24 | tainted_list | | --> | Taint [externally controlled string] | test.py:21 | test.py:21:9:21:25 | set() | | -| Taint [externally controlled string] | test.py:26 | test.py:26:20:26:31 | TAINTED_LIST | | --> | Taint [externally controlled string] | test.py:27 | test.py:27:9:27:20 | tainted_list | | -| Taint [externally controlled string] | test.py:26 | test.py:26:20:26:31 | TAINTED_LIST | | --> | Taint [externally controlled string] | test.py:28 | test.py:28:9:28:20 | tainted_list | | -| Taint [externally controlled string] | test.py:26 | test.py:26:20:26:31 | TAINTED_LIST | | --> | Taint [externally controlled string] | test.py:29 | test.py:29:9:29:20 | tainted_list | | -| Taint [externally controlled string] | test.py:26 | test.py:26:20:26:31 | TAINTED_LIST | | --> | Taint [externally controlled string] | test.py:30 | test.py:30:9:30:20 | tainted_list | | -| Taint [externally controlled string] | test.py:26 | test.py:26:20:26:31 | TAINTED_LIST | | --> | Taint [externally controlled string] | test.py:31 | test.py:31:15:31:26 | tainted_list | | -| Taint [externally controlled string] | test.py:26 | test.py:26:20:26:31 | TAINTED_LIST | | --> | Taint [externally controlled string] | test.py:33 | test.py:33:14:33:25 | tainted_list | | -| Taint [externally controlled string] | test.py:26 | test.py:26:20:26:31 | TAINTED_LIST | | --> | Taint [externally controlled string] | test.py:35 | test.py:35:23:35:34 | tainted_list | | -| Taint [externally controlled string] | test.py:27 | test.py:27:9:27:20 | tainted_list | | --> | Taint externally controlled string | test.py:27 | test.py:27:9:27:23 | Subscript | | -| Taint [externally controlled string] | test.py:28 | test.py:28:9:28:20 | tainted_list | | --> | Taint externally controlled string | test.py:28 | test.py:28:9:28:23 | Subscript | | -| Taint [externally controlled string] | test.py:29 | test.py:29:9:29:20 | tainted_list | | --> | Taint [externally controlled string] | test.py:29 | test.py:29:9:29:25 | Subscript | | -| Taint [externally controlled string] | test.py:29 | test.py:29:9:29:25 | Subscript | | --> | Taint [externally controlled string] | test.py:32 | test.py:32:16:32:16 | c | | -| Taint [externally controlled string] | test.py:30 | test.py:30:9:30:20 | tainted_list | | --> | Taint [externally controlled string] | test.py:30 | test.py:30:9:30:27 | Attribute() | | -| Taint [externally controlled string] | test.py:30 | test.py:30:9:30:27 | Attribute() | | --> | Taint [externally controlled string] | test.py:32 | test.py:32:19:32:19 | d | | -| Taint [externally controlled string] | test.py:31 | test.py:31:15:31:26 | tainted_list | | --> | Taint externally controlled string | test.py:32 | test.py:32:22:32:22 | e | | -| Taint [externally controlled string] | test.py:31 | test.py:31:15:31:26 | tainted_list | | --> | Taint externally controlled string | test.py:32 | test.py:32:25:32:25 | f | | -| Taint [externally controlled string] | test.py:31 | test.py:31:15:31:26 | tainted_list | | --> | Taint externally controlled string | test.py:32 | test.py:32:28:32:28 | g | | -| Taint [externally controlled string] | test.py:33 | test.py:33:14:33:25 | tainted_list | | --> | Taint externally controlled string | test.py:33 | test.py:33:5:33:26 | For | | -| Taint [externally controlled string] | test.py:35 | test.py:35:14:35:35 | reversed() | | --> | Taint externally controlled string | test.py:35 | test.py:35:5:35:36 | For | | -| Taint [externally controlled string] | test.py:35 | test.py:35:23:35:34 | tainted_list | | --> | Taint [externally controlled string] | test.py:35 | test.py:35:14:35:35 | reversed() | | -| Taint [externally controlled string] | test.py:44 | test.py:44:14:44:34 | Attribute() | | --> | Taint externally controlled string | test.py:44 | test.py:44:5:44:35 | For | | -| Taint externally controlled string | test.py:8 | test.py:8:22:8:35 | TAINTED_STRING | | --> | Taint externally controlled string | test.py:9 | test.py:9:21:9:34 | tainted_string | | -| Taint externally controlled string | test.py:8 | test.py:8:22:8:35 | TAINTED_STRING | | --> | Taint externally controlled string | test.py:10 | test.py:10:22:10:35 | tainted_string | | -| Taint externally controlled string | test.py:8 | test.py:8:22:8:35 | TAINTED_STRING | | --> | Taint externally controlled string | test.py:11 | test.py:11:20:11:33 | tainted_string | | -| Taint externally controlled string | test.py:8 | test.py:8:22:8:35 | TAINTED_STRING | | --> | Taint externally controlled string | test.py:12 | test.py:12:28:12:41 | tainted_string | | -| Taint externally controlled string | test.py:9 | test.py:9:21:9:34 | tainted_string | | --> | Taint [externally controlled string] | test.py:9 | test.py:9:20:9:35 | List | | -| Taint externally controlled string | test.py:10 | test.py:10:22:10:35 | tainted_string | | --> | Taint [externally controlled string] | test.py:10 | test.py:10:22:10:36 | Tuple | | -| Taint externally controlled string | test.py:12 | test.py:12:28:12:41 | tainted_string | | --> | Taint {externally controlled string} | test.py:12 | test.py:12:20:12:42 | Dict | | -| Taint externally controlled string | test.py:27 | test.py:27:9:27:23 | Subscript | | --> | Taint externally controlled string | test.py:32 | test.py:32:10:32:10 | a | | -| Taint externally controlled string | test.py:28 | test.py:28:9:28:23 | Subscript | | --> | Taint externally controlled string | test.py:32 | test.py:32:13:32:13 | b | | -| Taint externally controlled string | test.py:33 | test.py:33:5:33:26 | For | | --> | Taint externally controlled string | test.py:34 | test.py:34:14:34:14 | h | | -| Taint externally controlled string | test.py:35 | test.py:35:5:35:36 | For | | --> | Taint externally controlled string | test.py:36 | test.py:36:14:36:14 | i | | -| Taint externally controlled string | test.py:40 | test.py:40:9:40:28 | Subscript | | --> | Taint externally controlled string | test.py:43 | test.py:43:10:43:10 | a | | -| Taint externally controlled string | test.py:41 | test.py:41:9:41:23 | Subscript | | --> | Taint externally controlled string | test.py:43 | test.py:43:13:43:13 | b | | -| Taint externally controlled string | test.py:44 | test.py:44:5:44:35 | For | | --> | Taint externally controlled string | test.py:45 | test.py:45:14:45:14 | d | | -| Taint externally controlled string | test.py:62 | test.py:62:34:62:47 | TAINTED_STRING | | --> | Taint externally controlled string | test.py:62 | test.py:62:5:62:47 | BinaryExpr | | -| Taint {externally controlled string} | test.py:12 | test.py:12:20:12:42 | Dict | | --> | Taint {externally controlled string} | test.py:17 | test.py:17:14:17:25 | tainted_dict | | -| Taint {externally controlled string} | test.py:12 | test.py:12:20:12:42 | Dict | | --> | Taint {externally controlled string} | test.py:18 | test.py:18:14:18:25 | tainted_dict | | -| Taint {externally controlled string} | test.py:17 | test.py:17:14:17:25 | tainted_dict | | --> | Taint [externally controlled string] | test.py:17 | test.py:17:14:17:34 | Attribute() | | -| Taint {externally controlled string} | test.py:39 | test.py:39:20:39:31 | TAINTED_DICT | | --> | Taint {externally controlled string} | test.py:40 | test.py:40:9:40:20 | tainted_dict | | -| Taint {externally controlled string} | test.py:39 | test.py:39:20:39:31 | TAINTED_DICT | | --> | Taint {externally controlled string} | test.py:41 | test.py:41:9:41:20 | tainted_dict | | -| Taint {externally controlled string} | test.py:39 | test.py:39:20:39:31 | TAINTED_DICT | | --> | Taint {externally controlled string} | test.py:42 | test.py:42:9:42:20 | tainted_dict | | -| Taint {externally controlled string} | test.py:39 | test.py:39:20:39:31 | TAINTED_DICT | | --> | Taint {externally controlled string} | test.py:44 | test.py:44:14:44:25 | tainted_dict | | -| Taint {externally controlled string} | test.py:39 | test.py:39:20:39:31 | TAINTED_DICT | | --> | Taint {externally controlled string} | test.py:46 | test.py:46:17:46:28 | tainted_dict | | -| Taint {externally controlled string} | test.py:40 | test.py:40:9:40:20 | tainted_dict | | --> | Taint externally controlled string | test.py:40 | test.py:40:9:40:28 | Subscript | | -| Taint {externally controlled string} | test.py:41 | test.py:41:9:41:20 | tainted_dict | | --> | Taint externally controlled string | test.py:41 | test.py:41:9:41:23 | Subscript | | -| Taint {externally controlled string} | test.py:42 | test.py:42:9:42:20 | tainted_dict | | --> | Taint {externally controlled string} | test.py:42 | test.py:42:9:42:27 | Attribute() | | -| Taint {externally controlled string} | test.py:42 | test.py:42:9:42:27 | Attribute() | | --> | Taint {externally controlled string} | test.py:43 | test.py:43:16:43:16 | c | | -| Taint {externally controlled string} | test.py:44 | test.py:44:14:44:25 | tainted_dict | | --> | Taint [externally controlled string] | test.py:44 | test.py:44:14:44:34 | Attribute() | | diff --git a/python/ql/test/library-tests/taint/collections/TestStep.ql b/python/ql/test/library-tests/taint/collections/TestStep.ql deleted file mode 100644 index 177edce3498..00000000000 --- a/python/ql/test/library-tests/taint/collections/TestStep.ql +++ /dev/null @@ -1,11 +0,0 @@ -import python -import semmle.python.dataflow.TaintTracking -import Taint - -from TaintedNode n, TaintedNode s -where - n.getLocation().getFile().getShortName() = "test.py" and - s.getLocation().getFile().getShortName() = "test.py" and - s = n.getASuccessor() -select "Taint " + n.getTaintKind(), n.getLocation().toString(), n.getAstNode(), n.getContext(), - " --> ", "Taint " + s.getTaintKind(), s.getLocation().toString(), s.getAstNode(), s.getContext() diff --git a/python/ql/test/library-tests/taint/collections/TestTaint.expected b/python/ql/test/library-tests/taint/collections/TestTaint.expected deleted file mode 100644 index aae51236e6d..00000000000 --- a/python/ql/test/library-tests/taint/collections/TestTaint.expected +++ /dev/null @@ -1,33 +0,0 @@ -| test.py:23 | test_construction | a | [externally controlled string] | -| test.py:23 | test_construction | b | [externally controlled string] | -| test.py:23 | test_construction | c | NO TAINT | -| test.py:23 | test_construction | d | [externally controlled string] | -| test.py:23 | test_construction | e | NO TAINT | -| test.py:23 | test_construction | f | [externally controlled string] | -| test.py:23 | test_construction | g | [externally controlled string] | -| test.py:23 | test_construction | h | NO TAINT | -| test.py:32 | test_access | a | externally controlled string | -| test.py:32 | test_access | b | externally controlled string | -| test.py:32 | test_access | c | [externally controlled string] | -| test.py:32 | test_access | d | [externally controlled string] | -| test.py:32 | test_access | e | externally controlled string | -| test.py:32 | test_access | f | externally controlled string | -| test.py:32 | test_access | g | externally controlled string | -| test.py:34 | test_access | h | externally controlled string | -| test.py:36 | test_access | i | externally controlled string | -| test.py:43 | test_dict_access | a | externally controlled string | -| test.py:43 | test_dict_access | b | externally controlled string | -| test.py:43 | test_dict_access | c | {externally controlled string} | -| test.py:45 | test_dict_access | d | externally controlled string | -| test.py:47 | test_dict_access | e | NO TAINT | -| test.py:58 | test_named_tuple | a | NO TAINT | -| test.py:58 | test_named_tuple | b | NO TAINT | -| test.py:58 | test_named_tuple | c | NO TAINT | -| test.py:58 | test_named_tuple | d | NO TAINT | -| test.py:58 | test_named_tuple | e | NO TAINT | -| test.py:58 | test_named_tuple | f | NO TAINT | -| test.py:67 | test_defaultdict | a | NO TAINT | -| test.py:67 | test_defaultdict | b | NO TAINT | -| test.py:67 | test_defaultdict | c | NO TAINT | -| test.py:69 | test_defaultdict | d | NO TAINT | -| test.py:71 | test_defaultdict | e | NO TAINT | diff --git a/python/ql/test/library-tests/taint/collections/TestTaint.ql b/python/ql/test/library-tests/taint/collections/TestTaint.ql deleted file mode 100644 index 47883578516..00000000000 --- a/python/ql/test/library-tests/taint/collections/TestTaint.ql +++ /dev/null @@ -1,19 +0,0 @@ -import python -import semmle.python.dataflow.TaintTracking -import Taint - -from Call call, Expr arg, string taint_string -where - call.getLocation().getFile().getShortName() = "test.py" and - call.getFunc().(Name).getId() = "test" and - arg = call.getAnArg() and - ( - not exists(TaintedNode tainted | tainted.getAstNode() = arg) and - taint_string = "NO TAINT" - or - exists(TaintedNode tainted | tainted.getAstNode() = arg | - taint_string = tainted.getTaintKind().toString() - ) - ) -select arg.getLocation().toString(), call.getScope().(Function).getName(), arg.toString(), - taint_string diff --git a/python/ql/test/library-tests/taint/collections/test.py b/python/ql/test/library-tests/taint/collections/test.py deleted file mode 100644 index 445effe19ce..00000000000 --- a/python/ql/test/library-tests/taint/collections/test.py +++ /dev/null @@ -1,71 +0,0 @@ -from collections import defaultdict, namedtuple - -# Use to show only interesting results in qltest output -def test(*args): - pass - -def test_construction(): - tainted_string = TAINTED_STRING - tainted_list = [tainted_string] - tainted_tuple = (tainted_string,) - tainted_set = {tainted_string} # TODO: set currently not handled - tainted_dict = {'key': tainted_string} - - a = list(tainted_list) - b = list(tainted_tuple) - c = list(tainted_set) # TODO: set currently not handled - d = list(tainted_dict.values()) - e = list(tainted_dict.items()) # TODO: dict.items() currently not handled - - f = tuple(tainted_list) - g = set(tainted_list) - h = frozenset(tainted_list) # TODO: frozenset constructor currently not handled - test(a, b, c, d, e, f, g, h) - -def test_access(): - tainted_list = TAINTED_LIST - a = tainted_list[0] - b = tainted_list[x] - c = tainted_list[y:z] - d = tainted_list.copy() - e, f, g = tainted_list - test(a, b, c, d, e, f, g) - for h in tainted_list: - test(h) - for i in reversed(tainted_list): - test(i) - -def test_dict_access(x): - tainted_dict = TAINTED_DICT - a = tainted_dict["name"] - b = tainted_dict[x] - c = tainted_dict.copy() - test(a, b, c) - for d in tainted_dict.values(): - test(d) - for _, e in tainted_dict.items(): # TODO: dict.items() currently not handled - test(e) - -def test_named_tuple(): # TODO: namedtuple currently not handled - Point = namedtuple('Point', ['x', 'y']) - point = Point(TAINTED_STRING, 'const') - - a = point[0] - b = point.x - c = point[1] - d = point.y - e, f = point - test(a, b, c, d, e, f) - -def test_defaultdict(key, x): # TODO: defaultdict currently not handled - tainted_default_dict = defaultdict(str) - tainted_default_dict[key] += TAINTED_STRING - - a = tainted_dict["name"] - b = tainted_dict[x] - c = tainted_dict.copy() - test(a, b, c) - for d in tainted_dict.values(): - test(d) - for _, e in tainted_dict.items(): - test(e) diff --git a/python/ql/test/library-tests/taint/config/RockPaperScissors.expected b/python/ql/test/library-tests/taint/config/RockPaperScissors.expected index 0639e4bf227..ae34dcd904e 100644 --- a/python/ql/test/library-tests/taint/config/RockPaperScissors.expected +++ b/python/ql/test/library-tests/taint/config/RockPaperScissors.expected @@ -1,99 +1,3 @@ -edges -| carrier.py:17:9:17:31 | .attr = simple.test | carrier.py:18:10:18:10 | .attr = simple.test | -| carrier.py:17:25:17:30 | simple.test | carrier.py:17:9:17:31 | .attr = simple.test | -| carrier.py:18:10:18:10 | .attr = simple.test | carrier.py:18:10:18:15 | simple.test | -| carrier.py:21:9:21:28 | explicit.carrier | carrier.py:22:10:22:10 | explicit.carrier | -| carrier.py:22:10:22:10 | explicit.carrier | carrier.py:22:10:22:22 | simple.test | -| carrier.py:25:9:25:36 | .attr = simple.test | carrier.py:26:10:26:10 | .attr = simple.test | -| carrier.py:25:13:25:35 | .attr = simple.test | carrier.py:25:9:25:36 | .attr = simple.test | -| carrier.py:25:29:25:34 | simple.test | carrier.py:25:13:25:35 | .attr = simple.test | -| carrier.py:26:10:26:10 | .attr = simple.test | carrier.py:26:10:26:21 | simple.test | -| carrier.py:29:9:29:33 | explicit.carrier | carrier.py:30:10:30:10 | explicit.carrier | -| carrier.py:29:13:29:32 | explicit.carrier | carrier.py:29:9:29:33 | explicit.carrier | -| carrier.py:30:10:30:10 | explicit.carrier | carrier.py:30:10:30:22 | simple.test | -| carrier.py:33:9:33:45 | .attr = explicit.carrier | carrier.py:34:9:34:9 | .attr = explicit.carrier | -| carrier.py:33:25:33:44 | explicit.carrier | carrier.py:33:9:33:45 | .attr = explicit.carrier | -| carrier.py:34:9:34:9 | .attr = explicit.carrier | carrier.py:34:9:34:14 | explicit.carrier | -| carrier.py:34:9:34:14 | explicit.carrier | carrier.py:35:10:35:10 | explicit.carrier | -| carrier.py:35:10:35:10 | explicit.carrier | carrier.py:35:10:35:22 | simple.test | -| deep.py:20:5:20:14 | simple.test | deep.py:22:6:22:6 | simple.test | -| deep.py:20:8:20:13 | simple.test | deep.py:20:5:20:14 | simple.test | -| module.py:3:13:3:18 | simple.test | test.py:85:8:85:13 | .dangerous = simple.test | -| module.py:3:13:3:18 | simple.test | test.py:88:9:88:14 | .dangerous = simple.test | -| module.py:3:13:3:18 | simple.test | test.py:110:11:110:16 | .dangerous = simple.test | -| module.py:3:13:3:18 | simple.test | test.py:115:11:115:16 | .dangerous = simple.test | -| module.py:3:13:3:18 | simple.test | test.py:155:20:155:38 | simple.test | -| module.py:7:12:7:17 | simple.test | test.py:100:9:100:31 | simple.test | -| rockpaperscissors.py:24:9:24:12 | rock | rockpaperscissors.py:25:9:25:9 | rock | -| rockpaperscissors.py:25:9:25:9 | rock | rockpaperscissors.py:25:9:25:16 | scissors | -| rockpaperscissors.py:25:9:25:16 | scissors | rockpaperscissors.py:25:9:25:23 | paper | -| rockpaperscissors.py:25:9:25:23 | paper | rockpaperscissors.py:26:14:26:14 | paper | -| sanitizer.py:9:9:9:20 | SQL injection | sanitizer.py:13:19:13:19 | SQL injection | -| sanitizer.py:16:9:16:20 | Command injection | sanitizer.py:20:20:20:20 | Command injection | -| sanitizer.py:24:9:24:20 | SQL injection | sanitizer.py:26:19:26:19 | SQL injection | -| sanitizer.py:24:9:24:20 | SQL injection | sanitizer.py:28:19:28:19 | SQL injection | -| sanitizer.py:31:9:31:20 | Command injection | sanitizer.py:33:20:33:20 | Command injection | -| sanitizer.py:31:9:31:20 | Command injection | sanitizer.py:35:20:35:20 | Command injection | -| test.py:6:9:6:14 | simple.test | test.py:7:10:7:10 | simple.test | -| test.py:10:12:10:17 | simple.test | test.py:16:9:16:16 | simple.test | -| test.py:10:12:10:17 | simple.test | test.py:24:9:24:16 | simple.test | -| test.py:10:12:10:17 | simple.test | test.py:44:12:44:22 | simple.test | -| test.py:12:10:12:12 | simple.test | test.py:13:10:13:12 | simple.test | -| test.py:16:9:16:16 | simple.test | test.py:17:10:17:10 | simple.test | -| test.py:20:9:20:14 | simple.test | test.py:21:10:21:10 | simple.test | -| test.py:21:10:21:10 | simple.test | test.py:12:10:12:12 | simple.test | -| test.py:24:9:24:16 | simple.test | test.py:25:10:25:10 | simple.test | -| test.py:25:10:25:10 | simple.test | test.py:12:10:12:12 | simple.test | -| test.py:37:13:37:18 | simple.test | test.py:41:14:41:14 | simple.test | -| test.py:44:12:44:22 | simple.test | test.py:54:9:54:17 | simple.test | -| test.py:46:11:46:13 | simple.test | test.py:47:10:47:12 | simple.test | -| test.py:47:10:47:12 | simple.test | test.py:12:10:12:12 | simple.test | -| test.py:49:17:49:19 | simple.test | test.py:51:14:51:16 | simple.test | -| test.py:51:14:51:16 | simple.test | test.py:12:10:12:12 | simple.test | -| test.py:54:9:54:17 | simple.test | test.py:55:11:55:11 | simple.test | -| test.py:55:11:55:11 | simple.test | test.py:46:11:46:13 | simple.test | -| test.py:62:13:62:18 | simple.test | test.py:63:17:63:17 | simple.test | -| test.py:63:17:63:17 | simple.test | test.py:49:17:49:19 | simple.test | -| test.py:67:13:67:18 | simple.test | test.py:70:17:70:17 | simple.test | -| test.py:70:17:70:17 | simple.test | test.py:49:17:49:19 | simple.test | -| test.py:76:9:76:14 | simple.test | test.py:77:13:77:13 | simple.test | -| test.py:77:9:77:14 | simple.test | test.py:78:10:78:10 | simple.test | -| test.py:77:13:77:13 | simple.test | test.py:77:9:77:14 | simple.test | -| test.py:85:8:85:13 | .dangerous = simple.test | test.py:88:9:88:14 | .dangerous = simple.test | -| test.py:85:8:85:13 | .dangerous = simple.test | test.py:110:11:110:16 | .dangerous = simple.test | -| test.py:85:8:85:13 | .dangerous = simple.test | test.py:115:11:115:16 | .dangerous = simple.test | -| test.py:88:9:88:14 | .dangerous = simple.test | test.py:88:9:88:24 | simple.test | -| test.py:88:9:88:24 | simple.test | test.py:89:10:89:10 | simple.test | -| test.py:100:9:100:31 | simple.test | test.py:101:10:101:10 | simple.test | -| test.py:105:12:105:14 | .x = simple.test | test.py:106:10:106:12 | .x = simple.test | -| test.py:106:10:106:12 | .x = simple.test | test.py:106:10:106:14 | simple.test | -| test.py:110:11:110:16 | .dangerous = simple.test | test.py:110:11:110:26 | simple.test | -| test.py:110:11:110:26 | simple.test | test.py:111:10:111:10 | .x = simple.test | -| test.py:111:10:111:10 | .x = simple.test | test.py:111:10:111:12 | simple.test | -| test.py:115:11:115:16 | .dangerous = simple.test | test.py:115:11:115:26 | simple.test | -| test.py:115:11:115:26 | simple.test | test.py:116:13:116:13 | .x = simple.test | -| test.py:116:9:116:14 | .x = simple.test | test.py:117:12:117:12 | .x = simple.test | -| test.py:116:13:116:13 | .x = simple.test | test.py:116:9:116:14 | .x = simple.test | -| test.py:117:12:117:12 | .x = simple.test | test.py:105:12:105:14 | .x = simple.test | -| test.py:126:13:126:25 | simple.test | test.py:130:21:130:21 | simple.test | -| test.py:128:13:128:18 | simple.test | test.py:132:14:132:14 | simple.test | -| test.py:155:20:155:38 | simple.test | test.py:156:6:156:11 | simple.test | -| test.py:159:10:159:15 | simple.test | test.py:160:14:160:14 | simple.test | -| test.py:163:9:163:14 | simple.test | test.py:165:10:165:10 | simple.test | -| test.py:178:9:178:14 | simple.test | test.py:180:14:180:14 | simple.test | -| test.py:178:9:178:14 | simple.test | test.py:186:14:186:14 | simple.test | -| test.py:195:9:195:14 | simple.test | test.py:197:14:197:14 | simple.test | -| test.py:195:9:195:14 | simple.test | test.py:199:14:199:14 | simple.test | -| test.py:208:11:208:18 | sequence of simple.test | test.py:209:14:209:16 | sequence of simple.test | -| test.py:208:12:208:17 | simple.test | test.py:208:11:208:18 | sequence of simple.test | -| test.py:209:5:209:17 | simple.test | test.py:210:15:210:15 | simple.test | -| test.py:209:14:209:16 | sequence of simple.test | test.py:209:5:209:17 | simple.test | -| test.py:210:15:210:15 | simple.test | test.py:213:14:213:32 | iterable.simple | -| test.py:210:15:210:15 | simple.test | test.py:213:14:213:32 | sequence of simple.test | -| test.py:213:5:213:33 | simple.test | test.py:214:14:214:14 | simple.test | -| test.py:213:14:213:32 | iterable.simple | test.py:213:5:213:33 | simple.test | -| test.py:213:14:213:32 | sequence of simple.test | test.py:213:5:213:33 | simple.test | -#select | rockpaperscissors.py:13:10:13:17 | SCISSORS | rockpaperscissors.py:13:10:13:17 | scissors | rockpaperscissors.py:13:10:13:17 | scissors | $@ loses to $@. | rockpaperscissors.py:13:10:13:17 | SCISSORS | scissors | rockpaperscissors.py:13:10:13:17 | SCISSORS | scissors | | rockpaperscissors.py:16:11:16:14 | ROCK | rockpaperscissors.py:16:11:16:14 | rock | rockpaperscissors.py:16:11:16:14 | rock | $@ loses to $@. | rockpaperscissors.py:16:11:16:14 | ROCK | rock | rockpaperscissors.py:16:11:16:14 | ROCK | rock | | rockpaperscissors.py:26:14:26:14 | y | rockpaperscissors.py:24:9:24:12 | rock | rockpaperscissors.py:26:14:26:14 | paper | $@ loses to $@. | rockpaperscissors.py:24:9:24:12 | ROCK | rock | rockpaperscissors.py:26:14:26:14 | y | paper | diff --git a/python/ql/test/library-tests/taint/config/RockPaperScissors.ql b/python/ql/test/library-tests/taint/config/RockPaperScissors.ql index 8d6170351f1..9fc258159ae 100644 --- a/python/ql/test/library-tests/taint/config/RockPaperScissors.ql +++ b/python/ql/test/library-tests/taint/config/RockPaperScissors.ql @@ -5,7 +5,6 @@ import python import semmle.python.dataflow.TaintTracking import TaintLib -import semmle.python.security.Paths from RockPaperScissorConfig config, TaintedPathSource src, TaintedPathSink sink where config.hasFlowPath(src, sink) diff --git a/python/ql/test/library-tests/taint/config/Simple.expected b/python/ql/test/library-tests/taint/config/Simple.expected index 108f0f12015..78a86d7a0c8 100644 --- a/python/ql/test/library-tests/taint/config/Simple.expected +++ b/python/ql/test/library-tests/taint/config/Simple.expected @@ -1,99 +1,3 @@ -edges -| carrier.py:17:9:17:31 | .attr = simple.test | carrier.py:18:10:18:10 | .attr = simple.test | -| carrier.py:17:25:17:30 | simple.test | carrier.py:17:9:17:31 | .attr = simple.test | -| carrier.py:18:10:18:10 | .attr = simple.test | carrier.py:18:10:18:15 | simple.test | -| carrier.py:21:9:21:28 | explicit.carrier | carrier.py:22:10:22:10 | explicit.carrier | -| carrier.py:22:10:22:10 | explicit.carrier | carrier.py:22:10:22:22 | simple.test | -| carrier.py:25:9:25:36 | .attr = simple.test | carrier.py:26:10:26:10 | .attr = simple.test | -| carrier.py:25:13:25:35 | .attr = simple.test | carrier.py:25:9:25:36 | .attr = simple.test | -| carrier.py:25:29:25:34 | simple.test | carrier.py:25:13:25:35 | .attr = simple.test | -| carrier.py:26:10:26:10 | .attr = simple.test | carrier.py:26:10:26:21 | simple.test | -| carrier.py:29:9:29:33 | explicit.carrier | carrier.py:30:10:30:10 | explicit.carrier | -| carrier.py:29:13:29:32 | explicit.carrier | carrier.py:29:9:29:33 | explicit.carrier | -| carrier.py:30:10:30:10 | explicit.carrier | carrier.py:30:10:30:22 | simple.test | -| carrier.py:33:9:33:45 | .attr = explicit.carrier | carrier.py:34:9:34:9 | .attr = explicit.carrier | -| carrier.py:33:25:33:44 | explicit.carrier | carrier.py:33:9:33:45 | .attr = explicit.carrier | -| carrier.py:34:9:34:9 | .attr = explicit.carrier | carrier.py:34:9:34:14 | explicit.carrier | -| carrier.py:34:9:34:14 | explicit.carrier | carrier.py:35:10:35:10 | explicit.carrier | -| carrier.py:35:10:35:10 | explicit.carrier | carrier.py:35:10:35:22 | simple.test | -| deep.py:20:5:20:14 | simple.test | deep.py:22:6:22:6 | simple.test | -| deep.py:20:8:20:13 | simple.test | deep.py:20:5:20:14 | simple.test | -| module.py:3:13:3:18 | simple.test | test.py:85:8:85:13 | .dangerous = simple.test | -| module.py:3:13:3:18 | simple.test | test.py:88:9:88:14 | .dangerous = simple.test | -| module.py:3:13:3:18 | simple.test | test.py:110:11:110:16 | .dangerous = simple.test | -| module.py:3:13:3:18 | simple.test | test.py:115:11:115:16 | .dangerous = simple.test | -| module.py:3:13:3:18 | simple.test | test.py:155:20:155:38 | simple.test | -| module.py:7:12:7:17 | simple.test | test.py:100:9:100:31 | simple.test | -| rockpaperscissors.py:24:9:24:12 | rock | rockpaperscissors.py:25:9:25:9 | rock | -| rockpaperscissors.py:25:9:25:9 | rock | rockpaperscissors.py:25:9:25:16 | scissors | -| rockpaperscissors.py:25:9:25:16 | scissors | rockpaperscissors.py:25:9:25:23 | paper | -| rockpaperscissors.py:25:9:25:23 | paper | rockpaperscissors.py:26:14:26:14 | paper | -| sanitizer.py:9:9:9:20 | SQL injection | sanitizer.py:13:19:13:19 | SQL injection | -| sanitizer.py:16:9:16:20 | Command injection | sanitizer.py:20:20:20:20 | Command injection | -| sanitizer.py:24:9:24:20 | SQL injection | sanitizer.py:26:19:26:19 | SQL injection | -| sanitizer.py:24:9:24:20 | SQL injection | sanitizer.py:28:19:28:19 | SQL injection | -| sanitizer.py:31:9:31:20 | Command injection | sanitizer.py:33:20:33:20 | Command injection | -| sanitizer.py:31:9:31:20 | Command injection | sanitizer.py:35:20:35:20 | Command injection | -| test.py:6:9:6:14 | simple.test | test.py:7:10:7:10 | simple.test | -| test.py:10:12:10:17 | simple.test | test.py:16:9:16:16 | simple.test | -| test.py:10:12:10:17 | simple.test | test.py:24:9:24:16 | simple.test | -| test.py:10:12:10:17 | simple.test | test.py:44:12:44:22 | simple.test | -| test.py:12:10:12:12 | simple.test | test.py:13:10:13:12 | simple.test | -| test.py:16:9:16:16 | simple.test | test.py:17:10:17:10 | simple.test | -| test.py:20:9:20:14 | simple.test | test.py:21:10:21:10 | simple.test | -| test.py:21:10:21:10 | simple.test | test.py:12:10:12:12 | simple.test | -| test.py:24:9:24:16 | simple.test | test.py:25:10:25:10 | simple.test | -| test.py:25:10:25:10 | simple.test | test.py:12:10:12:12 | simple.test | -| test.py:37:13:37:18 | simple.test | test.py:41:14:41:14 | simple.test | -| test.py:44:12:44:22 | simple.test | test.py:54:9:54:17 | simple.test | -| test.py:46:11:46:13 | simple.test | test.py:47:10:47:12 | simple.test | -| test.py:47:10:47:12 | simple.test | test.py:12:10:12:12 | simple.test | -| test.py:49:17:49:19 | simple.test | test.py:51:14:51:16 | simple.test | -| test.py:51:14:51:16 | simple.test | test.py:12:10:12:12 | simple.test | -| test.py:54:9:54:17 | simple.test | test.py:55:11:55:11 | simple.test | -| test.py:55:11:55:11 | simple.test | test.py:46:11:46:13 | simple.test | -| test.py:62:13:62:18 | simple.test | test.py:63:17:63:17 | simple.test | -| test.py:63:17:63:17 | simple.test | test.py:49:17:49:19 | simple.test | -| test.py:67:13:67:18 | simple.test | test.py:70:17:70:17 | simple.test | -| test.py:70:17:70:17 | simple.test | test.py:49:17:49:19 | simple.test | -| test.py:76:9:76:14 | simple.test | test.py:77:13:77:13 | simple.test | -| test.py:77:9:77:14 | simple.test | test.py:78:10:78:10 | simple.test | -| test.py:77:13:77:13 | simple.test | test.py:77:9:77:14 | simple.test | -| test.py:85:8:85:13 | .dangerous = simple.test | test.py:88:9:88:14 | .dangerous = simple.test | -| test.py:85:8:85:13 | .dangerous = simple.test | test.py:110:11:110:16 | .dangerous = simple.test | -| test.py:85:8:85:13 | .dangerous = simple.test | test.py:115:11:115:16 | .dangerous = simple.test | -| test.py:88:9:88:14 | .dangerous = simple.test | test.py:88:9:88:24 | simple.test | -| test.py:88:9:88:24 | simple.test | test.py:89:10:89:10 | simple.test | -| test.py:100:9:100:31 | simple.test | test.py:101:10:101:10 | simple.test | -| test.py:105:12:105:14 | .x = simple.test | test.py:106:10:106:12 | .x = simple.test | -| test.py:106:10:106:12 | .x = simple.test | test.py:106:10:106:14 | simple.test | -| test.py:110:11:110:16 | .dangerous = simple.test | test.py:110:11:110:26 | simple.test | -| test.py:110:11:110:26 | simple.test | test.py:111:10:111:10 | .x = simple.test | -| test.py:111:10:111:10 | .x = simple.test | test.py:111:10:111:12 | simple.test | -| test.py:115:11:115:16 | .dangerous = simple.test | test.py:115:11:115:26 | simple.test | -| test.py:115:11:115:26 | simple.test | test.py:116:13:116:13 | .x = simple.test | -| test.py:116:9:116:14 | .x = simple.test | test.py:117:12:117:12 | .x = simple.test | -| test.py:116:13:116:13 | .x = simple.test | test.py:116:9:116:14 | .x = simple.test | -| test.py:117:12:117:12 | .x = simple.test | test.py:105:12:105:14 | .x = simple.test | -| test.py:126:13:126:25 | simple.test | test.py:130:21:130:21 | simple.test | -| test.py:128:13:128:18 | simple.test | test.py:132:14:132:14 | simple.test | -| test.py:155:20:155:38 | simple.test | test.py:156:6:156:11 | simple.test | -| test.py:159:10:159:15 | simple.test | test.py:160:14:160:14 | simple.test | -| test.py:163:9:163:14 | simple.test | test.py:165:10:165:10 | simple.test | -| test.py:178:9:178:14 | simple.test | test.py:180:14:180:14 | simple.test | -| test.py:178:9:178:14 | simple.test | test.py:186:14:186:14 | simple.test | -| test.py:195:9:195:14 | simple.test | test.py:197:14:197:14 | simple.test | -| test.py:195:9:195:14 | simple.test | test.py:199:14:199:14 | simple.test | -| test.py:208:11:208:18 | sequence of simple.test | test.py:209:14:209:16 | sequence of simple.test | -| test.py:208:12:208:17 | simple.test | test.py:208:11:208:18 | sequence of simple.test | -| test.py:209:5:209:17 | simple.test | test.py:210:15:210:15 | simple.test | -| test.py:209:14:209:16 | sequence of simple.test | test.py:209:5:209:17 | simple.test | -| test.py:210:15:210:15 | simple.test | test.py:213:14:213:32 | iterable.simple | -| test.py:210:15:210:15 | simple.test | test.py:213:14:213:32 | sequence of simple.test | -| test.py:213:5:213:33 | simple.test | test.py:214:14:214:14 | simple.test | -| test.py:213:14:213:32 | iterable.simple | test.py:213:5:213:33 | simple.test | -| test.py:213:14:213:32 | sequence of simple.test | test.py:213:5:213:33 | simple.test | -#select | carrier.py:18:10:18:15 | Attribute | carrier.py:17:25:17:30 | simple.test | carrier.py:18:10:18:15 | simple.test | $@ flows to $@. | carrier.py:17:25:17:30 | SOURCE | simple.test | carrier.py:18:10:18:15 | Attribute | simple.test | | carrier.py:26:10:26:21 | Attribute() | carrier.py:25:29:25:34 | simple.test | carrier.py:26:10:26:21 | simple.test | $@ flows to $@. | carrier.py:25:29:25:34 | SOURCE | simple.test | carrier.py:26:10:26:21 | Attribute() | simple.test | | deep.py:22:6:22:6 | x | deep.py:20:8:20:13 | simple.test | deep.py:22:6:22:6 | simple.test | $@ flows to $@. | deep.py:20:8:20:13 | SOURCE | simple.test | deep.py:22:6:22:6 | x | simple.test | diff --git a/python/ql/test/library-tests/taint/config/Simple.ql b/python/ql/test/library-tests/taint/config/Simple.ql index 9a87a67c9f1..7617ec0a434 100644 --- a/python/ql/test/library-tests/taint/config/Simple.ql +++ b/python/ql/test/library-tests/taint/config/Simple.ql @@ -5,7 +5,6 @@ import python import semmle.python.dataflow.TaintTracking import TaintLib -import semmle.python.security.Paths from SimpleConfig config, TaintedPathSource src, TaintedPathSink sink where config.hasFlowPath(src, sink) diff --git a/python/ql/test/library-tests/taint/example/ExampleConfig.expected b/python/ql/test/library-tests/taint/example/ExampleConfig.expected index 5127ff1e794..ed1bd0fe0ec 100644 --- a/python/ql/test/library-tests/taint/example/ExampleConfig.expected +++ b/python/ql/test/library-tests/taint/example/ExampleConfig.expected @@ -1,29 +1,3 @@ -edges -| example.py:17:14:17:21 | Dilbert | example.py:18:13:18:18 | Dilbert | -| example.py:17:14:17:21 | Wally | example.py:18:13:18:18 | Wally | -| example.py:22:14:22:21 | Dilbert | example.py:23:20:23:25 | Dilbert | -| example.py:23:14:23:26 | Dilbert | example.py:24:13:24:18 | Dilbert | -| example.py:23:20:23:25 | Dilbert | example.py:23:14:23:26 | Dilbert | -| example.py:28:14:28:21 | Dilbert | example.py:29:13:29:18 | Dilbert | -| example.py:28:14:28:21 | Wally | example.py:29:13:29:18 | Wally | -| example.py:33:14:33:21 | Dilbert | example.py:34:24:34:29 | Dilbert | -| example.py:34:12:34:30 | .worker = Dilbert | example.py:37:20:37:23 | .worker = Dilbert | -| example.py:34:24:34:29 | Dilbert | example.py:34:12:34:30 | .worker = Dilbert | -| example.py:37:14:37:31 | Dilbert | example.py:39:13:39:18 | Dilbert | -| example.py:37:20:37:23 | .worker = Dilbert | example.py:37:20:37:30 | Dilbert | -| example.py:37:20:37:30 | Dilbert | example.py:37:14:37:31 | Dilbert | -| example.py:57:14:57:21 | Dilbert | example.py:58:22:58:27 | Dilbert | -| example.py:57:14:57:21 | Wally | example.py:58:22:58:27 | Wally | -| example.py:58:14:58:28 | Dilbert | example.py:60:13:60:18 | Dilbert | -| example.py:58:14:58:28 | Wally | example.py:60:13:60:18 | Wally | -| example.py:58:22:58:27 | Dilbert | example.py:58:14:58:28 | Dilbert | -| example.py:58:22:58:27 | Wally | example.py:58:14:58:28 | Wally | -| example.py:64:14:64:21 | Dilbert | example.py:65:20:65:25 | Dilbert | -| example.py:65:14:65:26 | Dilbert | example.py:66:22:66:27 | Dilbert | -| example.py:65:20:65:25 | Dilbert | example.py:65:14:65:26 | Dilbert | -| example.py:66:14:66:28 | Dilbert | example.py:68:13:68:18 | Dilbert | -| example.py:66:22:66:27 | Dilbert | example.py:66:14:66:28 | Dilbert | -#select | example.py:18:13:18:18 | worker | example.py:17:14:17:21 | Dilbert | example.py:18:13:18:18 | Dilbert | $@ goes to a $@. | example.py:17:14:17:21 | ENGINEER | Dilbert | example.py:18:13:18:18 | worker | meeting | | example.py:18:13:18:18 | worker | example.py:17:14:17:21 | Wally | example.py:18:13:18:18 | Wally | $@ goes to a $@. | example.py:17:14:17:21 | ENGINEER | Wally | example.py:18:13:18:18 | worker | meeting | | example.py:24:13:24:18 | worker | example.py:22:14:22:21 | Dilbert | example.py:24:13:24:18 | Dilbert | $@ goes to a $@. | example.py:22:14:22:21 | ENGINEER | Dilbert | example.py:24:13:24:18 | worker | meeting | diff --git a/python/ql/test/library-tests/taint/example/ExampleConfig.ql b/python/ql/test/library-tests/taint/example/ExampleConfig.ql index e406fc73e21..ea1dd879a95 100644 --- a/python/ql/test/library-tests/taint/example/ExampleConfig.ql +++ b/python/ql/test/library-tests/taint/example/ExampleConfig.ql @@ -7,7 +7,6 @@ import python import DilbertConfig -import semmle.python.security.Paths from DilbertConfig config, TaintedPathSource src, TaintedPathSink sink where config.hasFlowPath(src, sink) diff --git a/python/ql/test/library-tests/taint/exception_traceback/TestNode.expected b/python/ql/test/library-tests/taint/exception_traceback/TestNode.expected deleted file mode 100644 index b0b2a23b14b..00000000000 --- a/python/ql/test/library-tests/taint/exception_traceback/TestNode.expected +++ /dev/null @@ -1,27 +0,0 @@ -| test.py:10:11:10:47 | test.py:10 | MyException() | exception.kind | -| test.py:15:25:15:25 | test.py:15 | e | exception.kind | -| test.py:16:13:16:34 | test.py:16 | Attribute() | exception.info | -| test.py:17:15:17:15 | test.py:17 | s | exception.info | -| test.py:19:13:19:36 | test.py:19 | Attribute() | [exception.info] | -| test.py:20:13:20:37 | test.py:20 | Attribute() | [exception.info] | -| test.py:21:13:21:36 | test.py:21 | Attribute() | [exception.info] | -| test.py:21:35:21:35 | test.py:21 | t | [exception.info] | -| test.py:22:13:22:58 | test.py:22 | Attribute() | [exception.info] | -| test.py:23:13:23:57 | test.py:23 | Attribute() | [exception.info] | -| test.py:24:13:24:35 | test.py:24 | Attribute() | [exception.info] | -| test.py:25:13:25:36 | test.py:25 | Attribute() | [exception.info] | -| test.py:26:25:26:25 | test.py:26 | e | exception.kind | -| test.py:26:25:26:33 | test.py:26 | Attribute | exception.info | -| test.py:26:25:26:41 | test.py:26 | Tuple | [[exception.info]] | -| test.py:26:25:26:41 | test.py:26 | Tuple | [exception.info] | -| test.py:26:36:26:36 | test.py:26 | e | exception.kind | -| test.py:26:36:26:41 | test.py:26 | Attribute | [exception.info] | -| test.py:27:19:27:19 | test.py:27 | t | [exception.info] | -| test.py:27:22:27:22 | test.py:27 | u | [exception.info] | -| test.py:27:25:27:25 | test.py:27 | v | [exception.info] | -| test.py:27:28:27:28 | test.py:27 | w | [exception.info] | -| test.py:27:31:27:31 | test.py:27 | x | [exception.info] | -| test.py:27:34:27:34 | test.py:27 | y | [exception.info] | -| test.py:27:37:27:37 | test.py:27 | z | [exception.info] | -| test.py:27:40:27:46 | test.py:27 | message | exception.info | -| test.py:27:49:27:52 | test.py:27 | args | [exception.info] | diff --git a/python/ql/test/library-tests/taint/exception_traceback/TestNode.ql b/python/ql/test/library-tests/taint/exception_traceback/TestNode.ql deleted file mode 100644 index f3c7e98de94..00000000000 --- a/python/ql/test/library-tests/taint/exception_traceback/TestNode.ql +++ /dev/null @@ -1,7 +0,0 @@ -import python -import semmle.python.security.Exceptions -import semmle.python.web.HttpResponse - -from TaintedNode node -where not node.getLocation().getFile().inStdlib() -select node.getLocation(), node.getNode().asAstNode().toString(), node.getTaintKind() diff --git a/python/ql/test/library-tests/taint/exception_traceback/TestSource.expected b/python/ql/test/library-tests/taint/exception_traceback/TestSource.expected deleted file mode 100644 index e8b2074bc28..00000000000 --- a/python/ql/test/library-tests/taint/exception_traceback/TestSource.expected +++ /dev/null @@ -1,10 +0,0 @@ -| test.py:10 | MyException() | exception.kind | -| test.py:15 | e | exception.kind | -| test.py:16 | Attribute() | exception.info | -| test.py:19 | Attribute() | [exception.info] | -| test.py:20 | Attribute() | [exception.info] | -| test.py:21 | Attribute() | [exception.info] | -| test.py:22 | Attribute() | [exception.info] | -| test.py:23 | Attribute() | [exception.info] | -| test.py:24 | Attribute() | [exception.info] | -| test.py:25 | Attribute() | [exception.info] | diff --git a/python/ql/test/library-tests/taint/exception_traceback/TestSource.ql b/python/ql/test/library-tests/taint/exception_traceback/TestSource.ql deleted file mode 100644 index d892afe9999..00000000000 --- a/python/ql/test/library-tests/taint/exception_traceback/TestSource.ql +++ /dev/null @@ -1,9 +0,0 @@ -import python -import semmle.python.security.Exceptions -import semmle.python.web.HttpResponse - -from TaintSource src, TaintKind kind -where - src.isSourceOf(kind) and - not src.getLocation().getFile().inStdlib() -select src.getLocation().toString(), src.(ControlFlowNode).getNode().toString(), kind diff --git a/python/ql/test/library-tests/taint/exception_traceback/TestStep.expected b/python/ql/test/library-tests/taint/exception_traceback/TestStep.expected deleted file mode 100644 index 196c1e92c2c..00000000000 --- a/python/ql/test/library-tests/taint/exception_traceback/TestStep.expected +++ /dev/null @@ -1,16 +0,0 @@ -| Taint [exception.info] | test.py:19 | Attribute() | | --> | Taint [exception.info] | test.py:21 | t | | -| Taint [exception.info] | test.py:19 | Attribute() | | --> | Taint [exception.info] | test.py:27 | t | | -| Taint [exception.info] | test.py:20 | Attribute() | | --> | Taint [exception.info] | test.py:27 | u | | -| Taint [exception.info] | test.py:21 | Attribute() | | --> | Taint [exception.info] | test.py:27 | v | | -| Taint [exception.info] | test.py:22 | Attribute() | | --> | Taint [exception.info] | test.py:27 | w | | -| Taint [exception.info] | test.py:23 | Attribute() | | --> | Taint [exception.info] | test.py:27 | x | | -| Taint [exception.info] | test.py:24 | Attribute() | | --> | Taint [exception.info] | test.py:27 | y | | -| Taint [exception.info] | test.py:25 | Attribute() | | --> | Taint [exception.info] | test.py:27 | z | | -| Taint [exception.info] | test.py:26 | Attribute | | --> | Taint [[exception.info]] | test.py:26 | Tuple | | -| Taint [exception.info] | test.py:26 | Attribute | | --> | Taint [exception.info] | test.py:27 | args | | -| Taint exception.info | test.py:16 | Attribute() | | --> | Taint exception.info | test.py:17 | s | | -| Taint exception.info | test.py:26 | Attribute | | --> | Taint [exception.info] | test.py:26 | Tuple | | -| Taint exception.info | test.py:26 | Attribute | | --> | Taint exception.info | test.py:27 | message | | -| Taint exception.kind | test.py:15 | e | | --> | Taint exception.kind | test.py:26 | e | | -| Taint exception.kind | test.py:26 | e | | --> | Taint [exception.info] | test.py:26 | Attribute | | -| Taint exception.kind | test.py:26 | e | | --> | Taint exception.info | test.py:26 | Attribute | | diff --git a/python/ql/test/library-tests/taint/exception_traceback/TestStep.ql b/python/ql/test/library-tests/taint/exception_traceback/TestStep.ql deleted file mode 100644 index b1ab12d0f25..00000000000 --- a/python/ql/test/library-tests/taint/exception_traceback/TestStep.ql +++ /dev/null @@ -1,12 +0,0 @@ -import python -import semmle.python.security.Exceptions -import semmle.python.web.HttpResponse - -from TaintedNode n, TaintedNode s -where - s = n.getASuccessor() and - not n.getLocation().getFile().inStdlib() and - not s.getLocation().getFile().inStdlib() -select "Taint " + n.getTaintKind(), n.getLocation().toString(), n.getNode().toString(), - n.getContext(), " --> ", "Taint " + s.getTaintKind(), s.getLocation().toString(), - s.getNode().toString(), s.getContext() diff --git a/python/ql/test/library-tests/taint/exception_traceback/test.py b/python/ql/test/library-tests/taint/exception_traceback/test.py deleted file mode 100644 index 20573aa8f8b..00000000000 --- a/python/ql/test/library-tests/taint/exception_traceback/test.py +++ /dev/null @@ -1,34 +0,0 @@ -from __future__ import print_function - -import traceback -import sys - -class MyException(Exception): - pass - -def raise_secret_exception(): - raise MyException("Message", "secret info") - -def foo(): - try: - raise_secret_exception() - except Exception as e: - s = traceback.format_exc() - print(s) - etype, evalue, tb = sys.exc_info() - t = traceback.extract_tb(tb) - u = traceback.extract_stack() - v = traceback.format_list(t) - w = traceback.format_exception_only(etype, evalue) - x = traceback.format_exception(etype, evalue, tb) - y = traceback.format_tb(tb) - z = traceback.format_stack() - message, args = e.message, e.args - print(tb, t, u, v, w, x, y, z, message, args) - - -foo() - - -#For test to find stdlib -import os diff --git a/python/ql/test/library-tests/taint/flowpath_regression/Config.qll b/python/ql/test/library-tests/taint/flowpath_regression/Config.qll deleted file mode 100644 index ae9d0e3c332..00000000000 --- a/python/ql/test/library-tests/taint/flowpath_regression/Config.qll +++ /dev/null @@ -1,45 +0,0 @@ -import python -import semmle.python.dataflow.TaintTracking -import semmle.python.security.strings.Untrusted - -class FooSource extends TaintSource { - FooSource() { this.(CallNode).getFunction().(NameNode).getId() = "foo_source" } - - override predicate isSourceOf(TaintKind kind) { kind instanceof UntrustedStringKind } - - override string toString() { result = "FooSource" } -} - -class FooSink extends TaintSink { - FooSink() { - exists(CallNode call | - call.getFunction().(NameNode).getId() = "foo_sink" and - call.getAnArg() = this - ) - } - - override predicate sinks(TaintKind kind) { kind instanceof UntrustedStringKind } - - override string toString() { result = "FooSink" } -} - -class FooConfig extends TaintTracking::Configuration { - FooConfig() { this = "FooConfig" } - - override predicate isSource(TaintTracking::Source source) { source instanceof FooSource } - - override predicate isSink(TaintTracking::Sink sink) { sink instanceof FooSink } -} - -class BarSink extends TaintSink { - BarSink() { - exists(CallNode call | - call.getFunction().(NameNode).getId() = "bar_sink" and - call.getAnArg() = this - ) - } - - override predicate sinks(TaintKind kind) { kind instanceof UntrustedStringKind } - - override string toString() { result = "BarSink" } -} diff --git a/python/ql/test/library-tests/taint/flowpath_regression/Path.expected b/python/ql/test/library-tests/taint/flowpath_regression/Path.expected deleted file mode 100644 index b03f12e8b9a..00000000000 --- a/python/ql/test/library-tests/taint/flowpath_regression/Path.expected +++ /dev/null @@ -1 +0,0 @@ -| test.py:16:9:16:20 | foo_source() | test.py:17:14:17:14 | x | diff --git a/python/ql/test/library-tests/taint/flowpath_regression/Path.ql b/python/ql/test/library-tests/taint/flowpath_regression/Path.ql deleted file mode 100644 index 43562780859..00000000000 --- a/python/ql/test/library-tests/taint/flowpath_regression/Path.ql +++ /dev/null @@ -1,6 +0,0 @@ -import python -import Config - -from FooConfig config, TaintedPathSource src, TaintedPathSink sink -where config.hasFlowPath(src, sink) -select src.getSource(), sink.getSink() diff --git a/python/ql/test/library-tests/taint/flowpath_regression/test.py b/python/ql/test/library-tests/taint/flowpath_regression/test.py deleted file mode 100644 index 7287649330b..00000000000 --- a/python/ql/test/library-tests/taint/flowpath_regression/test.py +++ /dev/null @@ -1,22 +0,0 @@ -def foo_source(): - return 'foo' - - -def foo_sink(x): - if x == 'foo': - print('fire the foo missiles') - - -def bar_sink(x): - if x == 'bar': - print('fire the bar missiles') - - -def should_report(): - x = foo_source() - foo_sink(x) - - -def should_not_report(): - x = foo_source() - bar_sink(x) diff --git a/python/ql/test/library-tests/taint/general/ParamSource.ql b/python/ql/test/library-tests/taint/general/ParamSource.ql index 7ac2b7699ef..9ebc909cc28 100644 --- a/python/ql/test/library-tests/taint/general/ParamSource.ql +++ b/python/ql/test/library-tests/taint/general/ParamSource.ql @@ -1,7 +1,5 @@ import python import semmle.python.dataflow.TaintTracking -/* Standard library sink */ -import semmle.python.security.injection.Command class TestKind extends TaintKind { TestKind() { this = "test" } diff --git a/python/ql/test/library-tests/taint/namedtuple/SanitizedEdges.expected b/python/ql/test/library-tests/taint/namedtuple/SanitizedEdges.expected deleted file mode 100644 index 0adf64dfd5d..00000000000 --- a/python/ql/test/library-tests/taint/namedtuple/SanitizedEdges.expected +++ /dev/null @@ -1,7 +0,0 @@ -| UrlsplitUrlparseTempSanitizer | [externally controlled string] | test.py:21 | Pi(urlsplit_res_0) [true] | -| UrlsplitUrlparseTempSanitizer | [externally controlled string] | test.py:24 | Pi(urlsplit_res_3) [true] | -| UrlsplitUrlparseTempSanitizer | [externally controlled string] | test.py:27 | Pi(urlsplit_res_6) [true] | -| UrlsplitUrlparseTempSanitizer | [externally controlled string] | test.py:30 | Pi(urlsplit_res_9) [true] | -| string equality sanitizer | externally controlled string | test.py:21 | Pi(urlsplit_res_0) [true] | -| string equality sanitizer | externally controlled string | test.py:24 | Pi(urlsplit_res_3) [true] | -| string equality sanitizer | externally controlled string | test.py:27 | Pi(urlsplit_res_6) [true] | diff --git a/python/ql/test/library-tests/taint/namedtuple/SanitizedEdges.ql b/python/ql/test/library-tests/taint/namedtuple/SanitizedEdges.ql deleted file mode 100644 index d523f79a963..00000000000 --- a/python/ql/test/library-tests/taint/namedtuple/SanitizedEdges.ql +++ /dev/null @@ -1,6 +0,0 @@ -import python -import Taint - -from Sanitizer s, TaintKind taint, PyEdgeRefinement test -where s.sanitizingEdge(taint, test) -select s, taint, test.getTest().getLocation().toString(), test.getRepresentation() diff --git a/python/ql/test/library-tests/taint/namedtuple/Taint.qll b/python/ql/test/library-tests/taint/namedtuple/Taint.qll deleted file mode 100644 index 0dc3c71ec84..00000000000 --- a/python/ql/test/library-tests/taint/namedtuple/Taint.qll +++ /dev/null @@ -1,45 +0,0 @@ -import python -import semmle.python.dataflow.TaintTracking -import semmle.python.security.strings.Untrusted - -class SimpleSource extends TaintSource { - SimpleSource() { this.(NameNode).getId() = "TAINTED_STRING" } - - override predicate isSourceOf(TaintKind kind) { kind instanceof ExternalStringKind } - - override string toString() { result = "taint source" } -} - -class ListSource extends TaintSource { - ListSource() { this.(NameNode).getId() = "TAINTED_LIST" } - - override predicate isSourceOf(TaintKind kind) { kind instanceof ExternalStringSequenceKind } - - override string toString() { result = "list taint source" } -} - -class DictSource extends TaintSource { - DictSource() { this.(NameNode).getId() = "TAINTED_DICT" } - - override predicate isSourceOf(TaintKind kind) { kind instanceof ExternalStringDictKind } - - override string toString() { result = "dict taint source" } -} - -class TestConfig extends TaintTracking::Configuration { - TestConfig() { this = "TestConfig" } - - override predicate isSanitizer(Sanitizer sanitizer) { - sanitizer instanceof UrlsplitUrlparseTempSanitizer - } - - override predicate isSource(TaintTracking::Source source) { - source instanceof SimpleSource - or - source instanceof ListSource - or - source instanceof DictSource - } - - override predicate isSink(TaintTracking::Sink sink) { none() } -} diff --git a/python/ql/test/library-tests/taint/namedtuple/TestTaint.expected b/python/ql/test/library-tests/taint/namedtuple/TestTaint.expected deleted file mode 100644 index 62b589299dd..00000000000 --- a/python/ql/test/library-tests/taint/namedtuple/TestTaint.expected +++ /dev/null @@ -1,15 +0,0 @@ -| test.py:13 | test_basic | a | externally controlled string | -| test.py:13 | test_basic | b | externally controlled string | -| test.py:13 | test_basic | c | externally controlled string | -| test.py:13 | test_basic | d | externally controlled string | -| test.py:13 | test_basic | urlsplit_res | [externally controlled string] | -| test.py:19 | test_sanitizer | Attribute | externally controlled string | -| test.py:22 | test_sanitizer | Attribute | NO TAINT | -| test.py:25 | test_sanitizer | Subscript | NO TAINT | -| test.py:28 | test_sanitizer | Attribute | NO TAINT | -| test.py:31 | test_sanitizer | Attribute | NO TAINT | -| test.py:34 | test_sanitizer | Attribute | externally controlled string | -| test.py:44 | test_namedtuple | a | NO TAINT | -| test.py:44 | test_namedtuple | b | NO TAINT | -| test.py:44 | test_namedtuple | c | NO TAINT | -| test.py:44 | test_namedtuple | d | NO TAINT | diff --git a/python/ql/test/library-tests/taint/namedtuple/TestTaint.ql b/python/ql/test/library-tests/taint/namedtuple/TestTaint.ql deleted file mode 100644 index 47883578516..00000000000 --- a/python/ql/test/library-tests/taint/namedtuple/TestTaint.ql +++ /dev/null @@ -1,19 +0,0 @@ -import python -import semmle.python.dataflow.TaintTracking -import Taint - -from Call call, Expr arg, string taint_string -where - call.getLocation().getFile().getShortName() = "test.py" and - call.getFunc().(Name).getId() = "test" and - arg = call.getAnArg() and - ( - not exists(TaintedNode tainted | tainted.getAstNode() = arg) and - taint_string = "NO TAINT" - or - exists(TaintedNode tainted | tainted.getAstNode() = arg | - taint_string = tainted.getTaintKind().toString() - ) - ) -select arg.getLocation().toString(), call.getScope().(Function).getName(), arg.toString(), - taint_string diff --git a/python/ql/test/library-tests/taint/namedtuple/test.py b/python/ql/test/library-tests/taint/namedtuple/test.py deleted file mode 100644 index ec6304f5073..00000000000 --- a/python/ql/test/library-tests/taint/namedtuple/test.py +++ /dev/null @@ -1,44 +0,0 @@ -from six.moves.urllib.parse import urlsplit - -# Currently we don't have support for namedtuples in general, but do have special support -# for `urlsplit` (and `urlparse`) - -def test_basic(): - tainted_string = TAINTED_STRING - urlsplit_res = urlsplit(tainted_string) - a = urlsplit_res.netloc # field access - b = urlsplit_res.hostname # property - c = urlsplit_res[3] # indexing - _, _, d, _, _ = urlsplit(tainted_string) # unpacking - test(a, b, c, d, urlsplit_res) - -def test_sanitizer(): - tainted_string = TAINTED_STRING - urlsplit_res = urlsplit(tainted_string) - - test(urlsplit_res.netloc) # should be tainted - - if urlsplit_res.netloc == "OK": - test(urlsplit_res.netloc) - - if urlsplit_res[2] == "OK": - test(urlsplit_res[0]) - - if urlsplit_res.netloc == "OK": - test(urlsplit_res.path) # FN - - if urlsplit_res.netloc in ["OK"]: - test(urlsplit_res.netloc) - - if urlsplit_res.netloc in ["OK", non_constant()]: - test(urlsplit_res.netloc) # should be tainted - -def test_namedtuple(): - tainted_string = TAINTED_STRING - Point = namedtuple('Point', ['x', 'y']) - p = Point('safe', tainted_string) - a = p.x - b = p.y - c = p[0] - d = p[1] - test(a, b, c, d) # TODO: FN, at least p.y and p[1] should be tainted diff --git a/python/ql/test/library-tests/taint/strings/Taint.qll b/python/ql/test/library-tests/taint/strings/Taint.qll deleted file mode 100644 index 3368a1c4f70..00000000000 --- a/python/ql/test/library-tests/taint/strings/Taint.qll +++ /dev/null @@ -1,44 +0,0 @@ -import python -import semmle.python.dataflow.TaintTracking -import semmle.python.security.strings.Untrusted -import semmle.python.security.Exceptions - -class SimpleSource extends TaintSource { - SimpleSource() { this.(NameNode).getId() = "TAINTED_STRING" } - - override predicate isSourceOf(TaintKind kind) { kind instanceof ExternalStringKind } - - override string toString() { result = "taint source" } -} - -class ListSource extends TaintSource { - ListSource() { this.(NameNode).getId() = "TAINTED_LIST" } - - override predicate isSourceOf(TaintKind kind) { kind instanceof ExternalStringSequenceKind } - - override string toString() { result = "list taint source" } -} - -class DictSource extends TaintSource { - DictSource() { this.(NameNode).getId() = "TAINTED_DICT" } - - override predicate isSourceOf(TaintKind kind) { kind instanceof ExternalStringDictKind } - - override string toString() { result = "dict taint source" } -} - -class ExceptionInfoSource extends TaintSource { - ExceptionInfoSource() { this.(NameNode).getId() = "TAINTED_EXCEPTION_INFO" } - - override predicate isSourceOf(TaintKind kind) { kind instanceof ExceptionInfo } - - override string toString() { result = "Exception info source" } -} - -class ExternalFileObjectSource extends TaintSource { - ExternalFileObjectSource() { this.(NameNode).getId() = "TAINTED_FILE" } - - override predicate isSourceOf(TaintKind kind) { kind instanceof ExternalFileObject } - - override string toString() { result = "Tainted file source" } -} diff --git a/python/ql/test/library-tests/taint/strings/TestStep.expected b/python/ql/test/library-tests/taint/strings/TestStep.expected deleted file mode 100644 index 4dc6682ee81..00000000000 --- a/python/ql/test/library-tests/taint/strings/TestStep.expected +++ /dev/null @@ -1,162 +0,0 @@ -| Taint [[externally controlled string]] | test.py:74 | test.py:74:9:74:33 | parse_qsl() | | --> | Taint [[externally controlled string]] | test.py:75 | test.py:75:19:75:19 | d | | -| Taint [externally controlled string] | test.py:71 | test.py:71:9:71:32 | urlsplit() | | --> | Taint [externally controlled string] | test.py:75 | test.py:75:10:75:10 | a | | -| Taint [externally controlled string] | test.py:72 | test.py:72:9:72:32 | urlparse() | | --> | Taint [externally controlled string] | test.py:75 | test.py:75:13:75:13 | b | | -| Taint [externally controlled string] | test.py:104 | test.py:104:9:104:37 | Attribute() | | --> | Taint externally controlled string | test.py:104 | test.py:104:9:104:40 | Subscript | | -| Taint [externally controlled string] | test.py:108 | test.py:108:9:108:38 | Attribute() | | --> | Taint externally controlled string | test.py:108 | test.py:108:9:108:41 | Subscript | | -| Taint [externally controlled string] | test.py:110 | test.py:110:9:110:37 | Attribute() | | --> | Taint externally controlled string | test.py:110 | test.py:110:9:110:41 | Subscript | | -| Taint [externally controlled string] | test.py:113 | test.py:113:9:113:30 | Attribute() | | --> | Taint externally controlled string | test.py:113 | test.py:113:9:113:33 | Subscript | | -| Taint [externally controlled string] | test.py:115 | test.py:115:9:115:35 | Attribute() | | --> | Taint externally controlled string | test.py:115 | test.py:115:9:115:38 | Subscript | | -| Taint exception.info | test.py:45 | test.py:45:22:45:26 | taint | p1 = exception.info | --> | Taint exception.info | test.py:46 | test.py:46:17:46:21 | taint | p1 = exception.info | -| Taint exception.info | test.py:46 | test.py:46:17:46:21 | taint | p1 = exception.info | --> | Taint exception.info | test.py:46 | test.py:46:12:46:22 | func() | p1 = exception.info | -| Taint exception.info | test.py:46 | test.py:46:17:46:21 | taint | p1 = exception.info | --> | Taint exception.info | test.py:53 | test.py:53:19:53:21 | arg | p0 = exception.info | -| Taint exception.info | test.py:49 | test.py:49:12:49:33 | TAINTED_EXCEPTION_INFO | | --> | Taint exception.info | test.py:50 | test.py:50:37:50:40 | info | | -| Taint exception.info | test.py:50 | test.py:50:11:50:41 | cross_over() | | --> | Taint exception.info | test.py:51 | test.py:51:10:51:12 | res | | -| Taint exception.info | test.py:50 | test.py:50:37:50:40 | info | | --> | Taint exception.info | test.py:45 | test.py:45:22:45:26 | taint | p1 = exception.info | -| Taint exception.info | test.py:50 | test.py:50:37:50:40 | info | | --> | Taint exception.info | test.py:50 | test.py:50:11:50:41 | cross_over() | | -| Taint exception.info | test.py:53 | test.py:53:19:53:21 | arg | p0 = exception.info | --> | Taint exception.info | test.py:54 | test.py:54:12:54:14 | arg | p0 = exception.info | -| Taint externally controlled string | test.py:6 | test.py:6:22:6:35 | TAINTED_STRING | | --> | Taint externally controlled string | test.py:7 | test.py:7:31:7:44 | tainted_string | | -| Taint externally controlled string | test.py:7 | test.py:7:31:7:44 | tainted_string | | --> | Taint json[externally controlled string] | test.py:7 | test.py:7:20:7:45 | Attribute() | | -| Taint externally controlled string | test.py:8 | test.py:8:9:8:25 | Subscript | | --> | Taint externally controlled string | test.py:9 | test.py:9:9:9:9 | a | | -| Taint externally controlled string | test.py:8 | test.py:8:9:8:25 | Subscript | | --> | Taint externally controlled string | test.py:11 | test.py:11:10:11:10 | a | | -| Taint externally controlled string | test.py:9 | test.py:9:9:9:18 | Attribute() | | --> | Taint externally controlled string | test.py:10 | test.py:10:9:10:9 | b | | -| Taint externally controlled string | test.py:9 | test.py:9:9:9:18 | Attribute() | | --> | Taint externally controlled string | test.py:11 | test.py:11:13:11:13 | b | | -| Taint externally controlled string | test.py:10 | test.py:10:9:10:14 | Subscript | | --> | Taint externally controlled string | test.py:11 | test.py:11:16:11:16 | c | | -| Taint externally controlled string | test.py:14 | test.py:14:22:14:35 | TAINTED_STRING | | --> | Taint externally controlled string | test.py:15 | test.py:15:9:15:22 | tainted_string | | -| Taint externally controlled string | test.py:14 | test.py:14:22:14:35 | TAINTED_STRING | | --> | Taint externally controlled string | test.py:16 | test.py:16:9:16:22 | tainted_string | | -| Taint externally controlled string | test.py:14 | test.py:14:22:14:35 | TAINTED_STRING | | --> | Taint externally controlled string | test.py:17 | test.py:17:9:17:22 | tainted_string | | -| Taint externally controlled string | test.py:14 | test.py:14:22:14:35 | TAINTED_STRING | | --> | Taint externally controlled string | test.py:18 | test.py:18:9:18:22 | tainted_string | | -| Taint externally controlled string | test.py:14 | test.py:14:22:14:35 | TAINTED_STRING | | --> | Taint externally controlled string | test.py:19 | test.py:19:18:19:31 | tainted_string | | -| Taint externally controlled string | test.py:14 | test.py:14:22:14:35 | TAINTED_STRING | | --> | Taint externally controlled string | test.py:20 | test.py:20:14:20:27 | tainted_string | | -| Taint externally controlled string | test.py:14 | test.py:14:22:14:35 | TAINTED_STRING | | --> | Taint externally controlled string | test.py:21 | test.py:21:9:21:22 | tainted_string | | -| Taint externally controlled string | test.py:15 | test.py:15:9:15:22 | tainted_string | | --> | Taint externally controlled string | test.py:15 | test.py:15:9:15:31 | Attribute() | | -| Taint externally controlled string | test.py:15 | test.py:15:9:15:31 | Attribute() | | --> | Taint externally controlled string | test.py:22 | test.py:22:10:22:10 | a | | -| Taint externally controlled string | test.py:16 | test.py:16:9:16:22 | tainted_string | | --> | Taint externally controlled string | test.py:16 | test.py:16:9:16:29 | Attribute() | | -| Taint externally controlled string | test.py:16 | test.py:16:9:16:29 | Attribute() | | --> | Taint externally controlled string | test.py:22 | test.py:22:13:22:13 | b | | -| Taint externally controlled string | test.py:17 | test.py:17:9:17:22 | tainted_string | | --> | Taint externally controlled string | test.py:17 | test.py:17:9:17:25 | Subscript | | -| Taint externally controlled string | test.py:17 | test.py:17:9:17:25 | Subscript | | --> | Taint externally controlled string | test.py:22 | test.py:22:16:22:16 | c | | -| Taint externally controlled string | test.py:18 | test.py:18:9:18:22 | tainted_string | | --> | Taint externally controlled string | test.py:18 | test.py:18:9:18:27 | Subscript | | -| Taint externally controlled string | test.py:18 | test.py:18:9:18:27 | Subscript | | --> | Taint externally controlled string | test.py:22 | test.py:22:19:22:19 | d | | -| Taint externally controlled string | test.py:19 | test.py:19:9:19:32 | reversed() | | --> | Taint externally controlled string | test.py:22 | test.py:22:22:22:22 | e | | -| Taint externally controlled string | test.py:19 | test.py:19:18:19:31 | tainted_string | | --> | Taint externally controlled string | test.py:19 | test.py:19:9:19:32 | reversed() | | -| Taint externally controlled string | test.py:20 | test.py:20:9:20:28 | copy() | | --> | Taint externally controlled string | test.py:22 | test.py:22:25:22:25 | f | | -| Taint externally controlled string | test.py:20 | test.py:20:14:20:27 | tainted_string | | --> | Taint externally controlled string | test.py:20 | test.py:20:9:20:28 | copy() | | -| Taint externally controlled string | test.py:21 | test.py:21:9:21:22 | tainted_string | | --> | Taint externally controlled string | test.py:21 | test.py:21:9:21:30 | Attribute() | | -| Taint externally controlled string | test.py:21 | test.py:21:9:21:30 | Attribute() | | --> | Taint externally controlled string | test.py:22 | test.py:22:28:22:28 | g | | -| Taint externally controlled string | test.py:25 | test.py:25:22:25:35 | TAINTED_STRING | | --> | Taint externally controlled string | test.py:26 | test.py:26:8:26:21 | tainted_string | | -| Taint externally controlled string | test.py:25 | test.py:25:22:25:35 | TAINTED_STRING | | --> | Taint externally controlled string | test.py:29 | test.py:29:14:29:27 | tainted_string | | -| Taint externally controlled string | test.py:32 | test.py:32:22:32:35 | TAINTED_STRING | | --> | Taint externally controlled string | test.py:33 | test.py:33:8:33:21 | tainted_string | | -| Taint externally controlled string | test.py:32 | test.py:32:22:32:35 | TAINTED_STRING | | --> | Taint externally controlled string | test.py:33 | test.py:33:34:33:47 | tainted_string | | -| Taint externally controlled string | test.py:32 | test.py:32:22:32:35 | TAINTED_STRING | | --> | Taint externally controlled string | test.py:36 | test.py:36:14:36:27 | tainted_string | | -| Taint externally controlled string | test.py:39 | test.py:39:22:39:35 | TAINTED_STRING | | --> | Taint externally controlled string | test.py:40 | test.py:40:13:40:26 | tainted_string | | -| Taint externally controlled string | test.py:39 | test.py:39:22:39:35 | TAINTED_STRING | | --> | Taint externally controlled string | test.py:41 | test.py:41:15:41:28 | tainted_string | | -| Taint externally controlled string | test.py:39 | test.py:39:22:39:35 | TAINTED_STRING | | --> | Taint externally controlled string | test.py:42 | test.py:42:15:42:28 | tainted_string | | -| Taint externally controlled string | test.py:40 | test.py:40:9:40:27 | str() | | --> | Taint externally controlled string | test.py:43 | test.py:43:10:43:10 | a | | -| Taint externally controlled string | test.py:40 | test.py:40:13:40:26 | tainted_string | | --> | Taint externally controlled string | test.py:40 | test.py:40:9:40:27 | str() | | -| Taint externally controlled string | test.py:41 | test.py:41:9:41:29 | bytes() | | --> | Taint externally controlled string | test.py:43 | test.py:43:13:43:13 | b | | -| Taint externally controlled string | test.py:41 | test.py:41:15:41:28 | tainted_string | | --> | Taint externally controlled string | test.py:41 | test.py:41:9:41:29 | bytes() | | -| Taint externally controlled string | test.py:42 | test.py:42:9:42:46 | bytes() | | --> | Taint externally controlled string | test.py:43 | test.py:43:16:43:16 | c | | -| Taint externally controlled string | test.py:42 | test.py:42:15:42:28 | tainted_string | | --> | Taint externally controlled string | test.py:42 | test.py:42:9:42:46 | bytes() | | -| Taint externally controlled string | test.py:45 | test.py:45:22:45:26 | taint | p1 = externally controlled string | --> | Taint externally controlled string | test.py:46 | test.py:46:17:46:21 | taint | p1 = externally controlled string | -| Taint externally controlled string | test.py:46 | test.py:46:17:46:21 | taint | p1 = externally controlled string | --> | Taint externally controlled string | test.py:46 | test.py:46:12:46:22 | func() | p1 = externally controlled string | -| Taint externally controlled string | test.py:46 | test.py:46:17:46:21 | taint | p1 = externally controlled string | --> | Taint externally controlled string | test.py:53 | test.py:53:19:53:21 | arg | p0 = externally controlled string | -| Taint externally controlled string | test.py:53 | test.py:53:19:53:21 | arg | p0 = externally controlled string | --> | Taint externally controlled string | test.py:54 | test.py:54:12:54:14 | arg | p0 = externally controlled string | -| Taint externally controlled string | test.py:57 | test.py:57:11:57:24 | TAINTED_STRING | | --> | Taint externally controlled string | test.py:58 | test.py:58:38:58:40 | ext | | -| Taint externally controlled string | test.py:58 | test.py:58:11:58:41 | cross_over() | | --> | Taint externally controlled string | test.py:59 | test.py:59:10:59:12 | res | | -| Taint externally controlled string | test.py:58 | test.py:58:38:58:40 | ext | | --> | Taint externally controlled string | test.py:45 | test.py:45:22:45:26 | taint | p1 = externally controlled string | -| Taint externally controlled string | test.py:58 | test.py:58:38:58:40 | ext | | --> | Taint externally controlled string | test.py:58 | test.py:58:11:58:41 | cross_over() | | -| Taint externally controlled string | test.py:70 | test.py:70:22:70:35 | TAINTED_STRING | | --> | Taint externally controlled string | test.py:71 | test.py:71:18:71:31 | tainted_string | | -| Taint externally controlled string | test.py:70 | test.py:70:22:70:35 | TAINTED_STRING | | --> | Taint externally controlled string | test.py:72 | test.py:72:18:72:31 | tainted_string | | -| Taint externally controlled string | test.py:70 | test.py:70:22:70:35 | TAINTED_STRING | | --> | Taint externally controlled string | test.py:73 | test.py:73:18:73:31 | tainted_string | | -| Taint externally controlled string | test.py:70 | test.py:70:22:70:35 | TAINTED_STRING | | --> | Taint externally controlled string | test.py:74 | test.py:74:19:74:32 | tainted_string | | -| Taint externally controlled string | test.py:71 | test.py:71:18:71:31 | tainted_string | | --> | Taint [externally controlled string] | test.py:71 | test.py:71:9:71:32 | urlsplit() | | -| Taint externally controlled string | test.py:72 | test.py:72:18:72:31 | tainted_string | | --> | Taint [externally controlled string] | test.py:72 | test.py:72:9:72:32 | urlparse() | | -| Taint externally controlled string | test.py:73 | test.py:73:18:73:31 | tainted_string | | --> | Taint {externally controlled string} | test.py:73 | test.py:73:9:73:32 | parse_qs() | | -| Taint externally controlled string | test.py:74 | test.py:74:19:74:32 | tainted_string | | --> | Taint [[externally controlled string]] | test.py:74 | test.py:74:9:74:33 | parse_qsl() | | -| Taint externally controlled string | test.py:78 | test.py:78:22:78:35 | TAINTED_STRING | | --> | Taint externally controlled string | test.py:80 | test.py:80:9:80:22 | tainted_string | | -| Taint externally controlled string | test.py:78 | test.py:78:22:78:35 | TAINTED_STRING | | --> | Taint externally controlled string | test.py:82 | test.py:82:12:82:25 | tainted_string | | -| Taint externally controlled string | test.py:80 | test.py:80:9:80:22 | tainted_string | | --> | Taint externally controlled string | test.py:80 | test.py:80:9:80:30 | Attribute() | | -| Taint externally controlled string | test.py:80 | test.py:80:9:80:30 | Attribute() | | --> | Taint externally controlled string | test.py:85 | test.py:85:10:85:10 | a | | -| Taint externally controlled string | test.py:88 | test.py:88:22:88:35 | TAINTED_STRING | | --> | Taint externally controlled string | test.py:91 | test.py:91:9:91:22 | tainted_string | | -| Taint externally controlled string | test.py:88 | test.py:88:22:88:35 | TAINTED_STRING | | --> | Taint externally controlled string | test.py:92 | test.py:92:9:92:22 | tainted_string | | -| Taint externally controlled string | test.py:88 | test.py:88:22:88:35 | TAINTED_STRING | | --> | Taint externally controlled string | test.py:93 | test.py:93:9:93:22 | tainted_string | | -| Taint externally controlled string | test.py:88 | test.py:88:22:88:35 | TAINTED_STRING | | --> | Taint externally controlled string | test.py:94 | test.py:94:9:94:22 | tainted_string | | -| Taint externally controlled string | test.py:88 | test.py:88:22:88:35 | TAINTED_STRING | | --> | Taint externally controlled string | test.py:95 | test.py:95:9:95:22 | tainted_string | | -| Taint externally controlled string | test.py:88 | test.py:88:22:88:35 | TAINTED_STRING | | --> | Taint externally controlled string | test.py:96 | test.py:96:9:96:22 | tainted_string | | -| Taint externally controlled string | test.py:88 | test.py:88:22:88:35 | TAINTED_STRING | | --> | Taint externally controlled string | test.py:97 | test.py:97:9:97:22 | tainted_string | | -| Taint externally controlled string | test.py:88 | test.py:88:22:88:35 | TAINTED_STRING | | --> | Taint externally controlled string | test.py:98 | test.py:98:9:98:22 | tainted_string | | -| Taint externally controlled string | test.py:88 | test.py:88:22:88:35 | TAINTED_STRING | | --> | Taint externally controlled string | test.py:99 | test.py:99:9:99:22 | tainted_string | | -| Taint externally controlled string | test.py:88 | test.py:88:22:88:35 | TAINTED_STRING | | --> | Taint externally controlled string | test.py:100 | test.py:100:9:100:22 | tainted_string | | -| Taint externally controlled string | test.py:88 | test.py:88:22:88:35 | TAINTED_STRING | | --> | Taint externally controlled string | test.py:101 | test.py:101:9:101:22 | tainted_string | | -| Taint externally controlled string | test.py:88 | test.py:88:22:88:35 | TAINTED_STRING | | --> | Taint externally controlled string | test.py:102 | test.py:102:9:102:22 | tainted_string | | -| Taint externally controlled string | test.py:88 | test.py:88:22:88:35 | TAINTED_STRING | | --> | Taint externally controlled string | test.py:103 | test.py:103:9:103:22 | tainted_string | | -| Taint externally controlled string | test.py:88 | test.py:88:22:88:35 | TAINTED_STRING | | --> | Taint externally controlled string | test.py:104 | test.py:104:9:104:22 | tainted_string | | -| Taint externally controlled string | test.py:88 | test.py:88:22:88:35 | TAINTED_STRING | | --> | Taint externally controlled string | test.py:105 | test.py:105:9:105:22 | tainted_string | | -| Taint externally controlled string | test.py:88 | test.py:88:22:88:35 | TAINTED_STRING | | --> | Taint externally controlled string | test.py:106 | test.py:106:9:106:22 | tainted_string | | -| Taint externally controlled string | test.py:88 | test.py:88:22:88:35 | TAINTED_STRING | | --> | Taint externally controlled string | test.py:107 | test.py:107:9:107:22 | tainted_string | | -| Taint externally controlled string | test.py:88 | test.py:88:22:88:35 | TAINTED_STRING | | --> | Taint externally controlled string | test.py:108 | test.py:108:9:108:22 | tainted_string | | -| Taint externally controlled string | test.py:88 | test.py:88:22:88:35 | TAINTED_STRING | | --> | Taint externally controlled string | test.py:109 | test.py:109:9:109:22 | tainted_string | | -| Taint externally controlled string | test.py:88 | test.py:88:22:88:35 | TAINTED_STRING | | --> | Taint externally controlled string | test.py:110 | test.py:110:9:110:22 | tainted_string | | -| Taint externally controlled string | test.py:88 | test.py:88:22:88:35 | TAINTED_STRING | | --> | Taint externally controlled string | test.py:111 | test.py:111:9:111:22 | tainted_string | | -| Taint externally controlled string | test.py:88 | test.py:88:22:88:35 | TAINTED_STRING | | --> | Taint externally controlled string | test.py:112 | test.py:112:9:112:22 | tainted_string | | -| Taint externally controlled string | test.py:88 | test.py:88:22:88:35 | TAINTED_STRING | | --> | Taint externally controlled string | test.py:113 | test.py:113:9:113:22 | tainted_string | | -| Taint externally controlled string | test.py:88 | test.py:88:22:88:35 | TAINTED_STRING | | --> | Taint externally controlled string | test.py:114 | test.py:114:9:114:22 | tainted_string | | -| Taint externally controlled string | test.py:88 | test.py:88:22:88:35 | TAINTED_STRING | | --> | Taint externally controlled string | test.py:115 | test.py:115:9:115:22 | tainted_string | | -| Taint externally controlled string | test.py:88 | test.py:88:22:88:35 | TAINTED_STRING | | --> | Taint externally controlled string | test.py:116 | test.py:116:9:116:22 | tainted_string | | -| Taint externally controlled string | test.py:88 | test.py:88:22:88:35 | TAINTED_STRING | | --> | Taint externally controlled string | test.py:117 | test.py:117:9:117:22 | tainted_string | | -| Taint externally controlled string | test.py:88 | test.py:88:22:88:35 | TAINTED_STRING | | --> | Taint externally controlled string | test.py:118 | test.py:118:9:118:22 | tainted_string | | -| Taint externally controlled string | test.py:88 | test.py:88:22:88:35 | TAINTED_STRING | | --> | Taint externally controlled string | test.py:121 | test.py:121:9:121:22 | tainted_string | | -| Taint externally controlled string | test.py:88 | test.py:88:22:88:35 | TAINTED_STRING | | --> | Taint externally controlled string | test.py:122 | test.py:122:9:122:22 | tainted_string | | -| Taint externally controlled string | test.py:91 | test.py:91:9:91:22 | tainted_string | | --> | Taint externally controlled string | test.py:91 | test.py:91:9:91:35 | Attribute() | | -| Taint externally controlled string | test.py:92 | test.py:92:9:92:22 | tainted_string | | --> | Taint externally controlled string | test.py:92 | test.py:92:9:92:33 | Attribute() | | -| Taint externally controlled string | test.py:93 | test.py:93:9:93:22 | tainted_string | | --> | Taint externally controlled string | test.py:93 | test.py:93:9:93:31 | Attribute() | | -| Taint externally controlled string | test.py:94 | test.py:94:9:94:22 | tainted_string | | --> | Taint externally controlled string | test.py:94 | test.py:94:9:94:38 | Attribute() | | -| Taint externally controlled string | test.py:95 | test.py:95:9:95:22 | tainted_string | | --> | Taint externally controlled string | test.py:95 | test.py:95:9:95:38 | Attribute() | | -| Taint externally controlled string | test.py:95 | test.py:95:9:95:38 | Attribute() | | --> | Taint externally controlled string | test.py:95 | test.py:95:9:95:54 | Attribute() | | -| Taint externally controlled string | test.py:96 | test.py:96:9:96:22 | tainted_string | | --> | Taint externally controlled string | test.py:96 | test.py:96:9:96:35 | Attribute() | | -| Taint externally controlled string | test.py:97 | test.py:97:9:97:22 | tainted_string | | --> | Taint externally controlled string | test.py:97 | test.py:97:9:97:37 | Attribute() | | -| Taint externally controlled string | test.py:98 | test.py:98:9:98:22 | tainted_string | | --> | Taint externally controlled string | test.py:98 | test.py:98:9:98:46 | Attribute() | | -| Taint externally controlled string | test.py:99 | test.py:99:9:99:22 | tainted_string | | --> | Taint externally controlled string | test.py:99 | test.py:99:9:99:33 | Attribute() | | -| Taint externally controlled string | test.py:100 | test.py:100:9:100:22 | tainted_string | | --> | Taint externally controlled string | test.py:100 | test.py:100:9:100:30 | Attribute() | | -| Taint externally controlled string | test.py:101 | test.py:101:9:101:22 | tainted_string | | --> | Taint externally controlled string | test.py:101 | test.py:101:9:101:31 | Attribute() | | -| Taint externally controlled string | test.py:102 | test.py:102:9:102:22 | tainted_string | | --> | Taint externally controlled string | test.py:102 | test.py:102:9:102:35 | Attribute() | | -| Taint externally controlled string | test.py:103 | test.py:103:9:103:22 | tainted_string | | --> | Taint [externally controlled string] | test.py:103 | test.py:103:9:103:37 | Attribute() | | -| Taint externally controlled string | test.py:104 | test.py:104:9:104:22 | tainted_string | | --> | Taint [externally controlled string] | test.py:104 | test.py:104:9:104:37 | Attribute() | | -| Taint externally controlled string | test.py:105 | test.py:105:9:105:22 | tainted_string | | --> | Taint externally controlled string | test.py:105 | test.py:105:9:105:42 | Attribute() | | -| Taint externally controlled string | test.py:106 | test.py:106:9:106:22 | tainted_string | | --> | Taint externally controlled string | test.py:106 | test.py:106:9:106:33 | Attribute() | | -| Taint externally controlled string | test.py:107 | test.py:107:9:107:22 | tainted_string | | --> | Taint [externally controlled string] | test.py:107 | test.py:107:9:107:38 | Attribute() | | -| Taint externally controlled string | test.py:108 | test.py:108:9:108:22 | tainted_string | | --> | Taint [externally controlled string] | test.py:108 | test.py:108:9:108:38 | Attribute() | | -| Taint externally controlled string | test.py:109 | test.py:109:9:109:22 | tainted_string | | --> | Taint [externally controlled string] | test.py:109 | test.py:109:9:109:37 | Attribute() | | -| Taint externally controlled string | test.py:110 | test.py:110:9:110:22 | tainted_string | | --> | Taint [externally controlled string] | test.py:110 | test.py:110:9:110:37 | Attribute() | | -| Taint externally controlled string | test.py:111 | test.py:111:9:111:22 | tainted_string | | --> | Taint externally controlled string | test.py:111 | test.py:111:9:111:31 | Attribute() | | -| Taint externally controlled string | test.py:112 | test.py:112:9:112:22 | tainted_string | | --> | Taint [externally controlled string] | test.py:112 | test.py:112:9:112:30 | Attribute() | | -| Taint externally controlled string | test.py:113 | test.py:113:9:113:22 | tainted_string | | --> | Taint [externally controlled string] | test.py:113 | test.py:113:9:113:30 | Attribute() | | -| Taint externally controlled string | test.py:114 | test.py:114:9:114:22 | tainted_string | | --> | Taint [externally controlled string] | test.py:114 | test.py:114:9:114:35 | Attribute() | | -| Taint externally controlled string | test.py:115 | test.py:115:9:115:22 | tainted_string | | --> | Taint [externally controlled string] | test.py:115 | test.py:115:9:115:35 | Attribute() | | -| Taint externally controlled string | test.py:116 | test.py:116:9:116:22 | tainted_string | | --> | Taint externally controlled string | test.py:116 | test.py:116:9:116:30 | Attribute() | | -| Taint externally controlled string | test.py:117 | test.py:117:9:117:22 | tainted_string | | --> | Taint externally controlled string | test.py:117 | test.py:117:9:117:33 | Attribute() | | -| Taint externally controlled string | test.py:118 | test.py:118:9:118:22 | tainted_string | | --> | Taint externally controlled string | test.py:118 | test.py:118:9:118:30 | Attribute() | | -| Taint externally controlled string | test.py:121 | test.py:121:9:121:22 | tainted_string | | --> | Taint externally controlled string | test.py:121 | test.py:121:9:121:30 | Attribute() | | -| Taint externally controlled string | test.py:122 | test.py:122:9:122:22 | tainted_string | | --> | Taint externally controlled string | test.py:122 | test.py:122:9:122:33 | Attribute() | | -| Taint externally controlled string | test.py:133 | test.py:133:5:133:29 | For | | --> | Taint externally controlled string | test.py:134 | test.py:134:14:134:17 | line | | -| Taint file[externally controlled string] | test.py:126 | test.py:126:20:126:31 | TAINTED_FILE | | --> | Taint file[externally controlled string] | test.py:128 | test.py:128:9:128:20 | tainted_file | | -| Taint file[externally controlled string] | test.py:126 | test.py:126:20:126:31 | TAINTED_FILE | | --> | Taint file[externally controlled string] | test.py:129 | test.py:129:9:129:20 | tainted_file | | -| Taint file[externally controlled string] | test.py:126 | test.py:126:20:126:31 | TAINTED_FILE | | --> | Taint file[externally controlled string] | test.py:130 | test.py:130:9:130:20 | tainted_file | | -| Taint file[externally controlled string] | test.py:126 | test.py:126:20:126:31 | TAINTED_FILE | | --> | Taint file[externally controlled string] | test.py:131 | test.py:131:9:131:20 | tainted_file | | -| Taint file[externally controlled string] | test.py:126 | test.py:126:20:126:31 | TAINTED_FILE | | --> | Taint file[externally controlled string] | test.py:133 | test.py:133:17:133:28 | tainted_file | | -| Taint file[externally controlled string] | test.py:129 | test.py:129:9:129:20 | tainted_file | | --> | Taint externally controlled string | test.py:129 | test.py:129:9:129:27 | Attribute() | | -| Taint file[externally controlled string] | test.py:130 | test.py:130:9:130:20 | tainted_file | | --> | Taint externally controlled string | test.py:130 | test.py:130:9:130:31 | Attribute() | | -| Taint file[externally controlled string] | test.py:131 | test.py:131:9:131:20 | tainted_file | | --> | Taint [externally controlled string] | test.py:131 | test.py:131:9:131:32 | Attribute() | | -| Taint file[externally controlled string] | test.py:133 | test.py:133:17:133:28 | tainted_file | | --> | Taint externally controlled string | test.py:133 | test.py:133:5:133:29 | For | | -| Taint json[externally controlled string] | test.py:7 | test.py:7:20:7:45 | Attribute() | | --> | Taint json[externally controlled string] | test.py:8 | test.py:8:9:8:20 | tainted_json | | -| Taint json[externally controlled string] | test.py:8 | test.py:8:9:8:20 | tainted_json | | --> | Taint externally controlled string | test.py:8 | test.py:8:9:8:25 | Subscript | | -| Taint json[externally controlled string] | test.py:8 | test.py:8:9:8:20 | tainted_json | | --> | Taint json[externally controlled string] | test.py:8 | test.py:8:9:8:25 | Subscript | | -| Taint json[externally controlled string] | test.py:8 | test.py:8:9:8:25 | Subscript | | --> | Taint json[externally controlled string] | test.py:9 | test.py:9:9:9:9 | a | | -| Taint json[externally controlled string] | test.py:8 | test.py:8:9:8:25 | Subscript | | --> | Taint json[externally controlled string] | test.py:11 | test.py:11:10:11:10 | a | | -| Taint json[externally controlled string] | test.py:9 | test.py:9:9:9:9 | a | | --> | Taint externally controlled string | test.py:9 | test.py:9:9:9:18 | Attribute() | | -| Taint json[externally controlled string] | test.py:9 | test.py:9:9:9:9 | a | | --> | Taint json[externally controlled string] | test.py:9 | test.py:9:9:9:18 | Attribute() | | -| Taint json[externally controlled string] | test.py:9 | test.py:9:9:9:18 | Attribute() | | --> | Taint json[externally controlled string] | test.py:10 | test.py:10:9:10:9 | b | | -| Taint json[externally controlled string] | test.py:9 | test.py:9:9:9:18 | Attribute() | | --> | Taint json[externally controlled string] | test.py:11 | test.py:11:13:11:13 | b | | -| Taint json[externally controlled string] | test.py:10 | test.py:10:9:10:9 | b | | --> | Taint externally controlled string | test.py:10 | test.py:10:9:10:14 | Subscript | | -| Taint json[externally controlled string] | test.py:10 | test.py:10:9:10:9 | b | | --> | Taint json[externally controlled string] | test.py:10 | test.py:10:9:10:14 | Subscript | | -| Taint json[externally controlled string] | test.py:10 | test.py:10:9:10:14 | Subscript | | --> | Taint json[externally controlled string] | test.py:11 | test.py:11:16:11:16 | c | | -| Taint {externally controlled string} | test.py:73 | test.py:73:9:73:32 | parse_qs() | | --> | Taint {externally controlled string} | test.py:75 | test.py:75:16:75:16 | c | | diff --git a/python/ql/test/library-tests/taint/strings/TestStep.ql b/python/ql/test/library-tests/taint/strings/TestStep.ql deleted file mode 100644 index 177edce3498..00000000000 --- a/python/ql/test/library-tests/taint/strings/TestStep.ql +++ /dev/null @@ -1,11 +0,0 @@ -import python -import semmle.python.dataflow.TaintTracking -import Taint - -from TaintedNode n, TaintedNode s -where - n.getLocation().getFile().getShortName() = "test.py" and - s.getLocation().getFile().getShortName() = "test.py" and - s = n.getASuccessor() -select "Taint " + n.getTaintKind(), n.getLocation().toString(), n.getAstNode(), n.getContext(), - " --> ", "Taint " + s.getTaintKind(), s.getLocation().toString(), s.getAstNode(), s.getContext() diff --git a/python/ql/test/library-tests/taint/strings/TestTaint.expected b/python/ql/test/library-tests/taint/strings/TestTaint.expected deleted file mode 100644 index afc30f98899..00000000000 --- a/python/ql/test/library-tests/taint/strings/TestTaint.expected +++ /dev/null @@ -1,63 +0,0 @@ -| test.py:11 | test_json | a | externally controlled string | -| test.py:11 | test_json | a | json[externally controlled string] | -| test.py:11 | test_json | b | externally controlled string | -| test.py:11 | test_json | b | json[externally controlled string] | -| test.py:11 | test_json | c | externally controlled string | -| test.py:11 | test_json | c | json[externally controlled string] | -| test.py:22 | test_str | a | externally controlled string | -| test.py:22 | test_str | b | externally controlled string | -| test.py:22 | test_str | c | externally controlled string | -| test.py:22 | test_str | d | externally controlled string | -| test.py:22 | test_str | e | externally controlled string | -| test.py:22 | test_str | f | externally controlled string | -| test.py:22 | test_str | g | externally controlled string | -| test.py:27 | test_const_sanitizer1 | tainted_string | NO TAINT | -| test.py:29 | test_const_sanitizer1 | tainted_string | externally controlled string | -| test.py:34 | test_const_sanitizer2 | tainted_string | NO TAINT | -| test.py:36 | test_const_sanitizer2 | tainted_string | externally controlled string | -| test.py:43 | test_str2 | a | externally controlled string | -| test.py:43 | test_str2 | b | externally controlled string | -| test.py:43 | test_str2 | c | externally controlled string | -| test.py:51 | test_exc_info | res | exception.info | -| test.py:59 | test_untrusted | res | externally controlled string | -| test.py:75 | test_urlsplit_urlparse | a | [externally controlled string] | -| test.py:75 | test_urlsplit_urlparse | b | [externally controlled string] | -| test.py:75 | test_urlsplit_urlparse | c | {externally controlled string} | -| test.py:75 | test_urlsplit_urlparse | d | [[externally controlled string]] | -| test.py:85 | test_method_reference | a | externally controlled string | -| test.py:85 | test_method_reference | b | NO TAINT | -| test.py:91 | test_str_methods | Attribute() | externally controlled string | -| test.py:92 | test_str_methods | Attribute() | externally controlled string | -| test.py:93 | test_str_methods | Attribute() | externally controlled string | -| test.py:94 | test_str_methods | Attribute() | externally controlled string | -| test.py:95 | test_str_methods | Attribute() | externally controlled string | -| test.py:96 | test_str_methods | Attribute() | externally controlled string | -| test.py:97 | test_str_methods | Attribute() | externally controlled string | -| test.py:98 | test_str_methods | Attribute() | externally controlled string | -| test.py:99 | test_str_methods | Attribute() | externally controlled string | -| test.py:100 | test_str_methods | Attribute() | externally controlled string | -| test.py:101 | test_str_methods | Attribute() | externally controlled string | -| test.py:102 | test_str_methods | Attribute() | externally controlled string | -| test.py:103 | test_str_methods | Attribute() | [externally controlled string] | -| test.py:104 | test_str_methods | Subscript | externally controlled string | -| test.py:105 | test_str_methods | Attribute() | externally controlled string | -| test.py:106 | test_str_methods | Attribute() | externally controlled string | -| test.py:107 | test_str_methods | Attribute() | [externally controlled string] | -| test.py:108 | test_str_methods | Subscript | externally controlled string | -| test.py:109 | test_str_methods | Attribute() | [externally controlled string] | -| test.py:110 | test_str_methods | Subscript | externally controlled string | -| test.py:111 | test_str_methods | Attribute() | externally controlled string | -| test.py:112 | test_str_methods | Attribute() | [externally controlled string] | -| test.py:113 | test_str_methods | Subscript | externally controlled string | -| test.py:114 | test_str_methods | Attribute() | [externally controlled string] | -| test.py:115 | test_str_methods | Subscript | externally controlled string | -| test.py:116 | test_str_methods | Attribute() | externally controlled string | -| test.py:117 | test_str_methods | Attribute() | externally controlled string | -| test.py:118 | test_str_methods | Attribute() | externally controlled string | -| test.py:121 | test_str_methods | Attribute() | externally controlled string | -| test.py:122 | test_str_methods | Attribute() | externally controlled string | -| test.py:128 | test_tainted_file | tainted_file | file[externally controlled string] | -| test.py:129 | test_tainted_file | Attribute() | externally controlled string | -| test.py:130 | test_tainted_file | Attribute() | externally controlled string | -| test.py:131 | test_tainted_file | Attribute() | [externally controlled string] | -| test.py:134 | test_tainted_file | line | externally controlled string | diff --git a/python/ql/test/library-tests/taint/strings/TestTaint.ql b/python/ql/test/library-tests/taint/strings/TestTaint.ql deleted file mode 100644 index 47883578516..00000000000 --- a/python/ql/test/library-tests/taint/strings/TestTaint.ql +++ /dev/null @@ -1,19 +0,0 @@ -import python -import semmle.python.dataflow.TaintTracking -import Taint - -from Call call, Expr arg, string taint_string -where - call.getLocation().getFile().getShortName() = "test.py" and - call.getFunc().(Name).getId() = "test" and - arg = call.getAnArg() and - ( - not exists(TaintedNode tainted | tainted.getAstNode() = arg) and - taint_string = "NO TAINT" - or - exists(TaintedNode tainted | tainted.getAstNode() = arg | - taint_string = tainted.getTaintKind().toString() - ) - ) -select arg.getLocation().toString(), call.getScope().(Function).getName(), arg.toString(), - taint_string diff --git a/python/ql/test/library-tests/taint/strings/test.py b/python/ql/test/library-tests/taint/strings/test.py deleted file mode 100644 index 78a0712ceca..00000000000 --- a/python/ql/test/library-tests/taint/strings/test.py +++ /dev/null @@ -1,134 +0,0 @@ -import json -from copy import copy -import sys - -def test_json(): - tainted_string = TAINTED_STRING - tainted_json = json.loads(tainted_string) - a = tainted_json["x"] - b = a.get("y") - c = b["z"] - test(a, b, c) - -def test_str(): - tainted_string = TAINTED_STRING - a = tainted_string.ljust(8) - b = tainted_string.copy() - c = tainted_string[:] - d = tainted_string[::2] - e = reversed(tainted_string) - f = copy(tainted_string) - g = tainted_string.strip() - test(a, b, c, d, e, f, g) - -def test_const_sanitizer1(): - tainted_string = TAINTED_STRING - if tainted_string == "OK": - test(tainted_string) # not tainted - else: - test(tainted_string) # still tainted - -def test_const_sanitizer2(): - tainted_string = TAINTED_STRING - if tainted_string == "OK" or tainted_string == "ALSO_OK": - test(tainted_string) # not tainted - else: - test(tainted_string) # still tainted - -def test_str2(): - tainted_string = TAINTED_STRING - a = str(tainted_string) - b = bytes(tainted_string) # This is an error in Python 3 - c = bytes(tainted_string, encoding="utf8") # This is an error in Python 2 - test(a, b, c) - -def cross_over(func, taint): - return func(taint) - -def test_exc_info(): - info = TAINTED_EXCEPTION_INFO - res = cross_over(exc_info_call, info) - test(res) - -def exc_info_call(arg): - return arg - -def test_untrusted(): - ext = TAINTED_STRING - res = cross_over(untrusted_call, ext) - test(res) - -def exc_untrusted_call(arg): - return arg - -if sys.version_info[0] == 2: - from urlparse import urlsplit, urlparse, parse_qs, parse_qsl -if sys.version_info[0] == 3: - from urllib.parse import urlsplit, urlparse, parse_qs, parse_qsl - -def test_urlsplit_urlparse(): - tainted_string = TAINTED_STRING - a = urlsplit(tainted_string) - b = urlparse(tainted_string) - c = parse_qs(tainted_string) - d = parse_qsl(tainted_string) - test(a, b, c, d) - -def test_method_reference(): - tainted_string = TAINTED_STRING - - a = tainted_string.title() - - func = tainted_string.title - b = func() - - test(a, b) # TODO: `b` not tainted - -def test_str_methods(): - tainted_string = TAINTED_STRING - - test( - tainted_string.capitalize(), - tainted_string.casefold(), - tainted_string.center(), - tainted_string.encode('utf-8'), - tainted_string.encode('utf-8').decode('utf-8'), - tainted_string.expandtabs(), - tainted_string.format(foo=42), - tainted_string.format_map({'foo': 42}), - tainted_string.ljust(100), - tainted_string.lower(), - tainted_string.lstrip(), - tainted_string.lstrip('w.'), - tainted_string.partition(';'), - tainted_string.partition(';')[0], - tainted_string.replace('/', '', 1), - tainted_string.rjust(100), - tainted_string.rpartition(';'), - tainted_string.rpartition(';')[2], - tainted_string.rsplit(';', 4), - tainted_string.rsplit(';', 4)[-1], - tainted_string.rstrip(), - tainted_string.split(), - tainted_string.split()[0], - tainted_string.splitlines(), - tainted_string.splitlines()[0], - tainted_string.strip(), - tainted_string.swapcase(), - tainted_string.title(), - # ignoring, as I have never seen this in practice - # tainted_string.translate(translation_table), - tainted_string.upper(), - tainted_string.zfill(100), - ) - -def test_tainted_file(): - tainted_file = TAINTED_FILE - test( - tainted_file, - tainted_file.read(), - tainted_file.readline(), - tainted_file.readlines(), - ) - for line in tainted_file: - test(line) diff --git a/python/ql/test/library-tests/taint/unpacking/Taint.qll b/python/ql/test/library-tests/taint/unpacking/Taint.qll deleted file mode 100644 index 010b9738c5c..00000000000 --- a/python/ql/test/library-tests/taint/unpacking/Taint.qll +++ /dev/null @@ -1,27 +0,0 @@ -import python -import semmle.python.dataflow.TaintTracking -import semmle.python.security.strings.Untrusted - -class SimpleSource extends TaintSource { - SimpleSource() { this.(NameNode).getId() = "TAINTED_STRING" } - - override predicate isSourceOf(TaintKind kind) { kind instanceof ExternalStringKind } - - override string toString() { result = "taint source" } -} - -class ListSource extends TaintSource { - ListSource() { this.(NameNode).getId() = "TAINTED_LIST" } - - override predicate isSourceOf(TaintKind kind) { kind instanceof ExternalStringSequenceKind } - - override string toString() { result = "list taint source" } -} - -class DictSource extends TaintSource { - DictSource() { this.(NameNode).getId() = "TAINTED_DICT" } - - override predicate isSourceOf(TaintKind kind) { kind instanceof ExternalStringDictKind } - - override string toString() { result = "dict taint source" } -} diff --git a/python/ql/test/library-tests/taint/unpacking/TestStep.expected b/python/ql/test/library-tests/taint/unpacking/TestStep.expected deleted file mode 100644 index 5d800e6b5b8..00000000000 --- a/python/ql/test/library-tests/taint/unpacking/TestStep.expected +++ /dev/null @@ -1,41 +0,0 @@ -| Taint [[externally controlled string]] | test.py:19 | test.py:19:10:19:18 | List | | --> | Taint [[externally controlled string]] | test.py:22 | test.py:22:28:22:29 | ll | | -| Taint [[externally controlled string]] | test.py:19 | test.py:19:10:19:18 | List | | --> | Taint [[externally controlled string]] | test.py:26 | test.py:26:28:26:29 | ll | | -| Taint [[externally controlled string]] | test.py:19 | test.py:19:10:19:18 | List | | --> | Taint [[externally controlled string]] | test.py:30 | test.py:30:28:30:29 | ll | | -| Taint [[externally controlled string]] | test.py:22 | test.py:22:28:22:29 | ll | | --> | Taint [externally controlled string] | test.py:23 | test.py:23:22:23:22 | b | | -| Taint [[externally controlled string]] | test.py:22 | test.py:22:28:22:29 | ll | | --> | Taint [externally controlled string] | test.py:23 | test.py:23:25:23:25 | c | | -| Taint [[externally controlled string]] | test.py:22 | test.py:22:28:22:29 | ll | | --> | Taint externally controlled string | test.py:23 | test.py:23:10:23:11 | a1 | | -| Taint [[externally controlled string]] | test.py:22 | test.py:22:28:22:29 | ll | | --> | Taint externally controlled string | test.py:23 | test.py:23:14:23:15 | a2 | | -| Taint [[externally controlled string]] | test.py:22 | test.py:22:28:22:29 | ll | | --> | Taint externally controlled string | test.py:23 | test.py:23:18:23:19 | a3 | | -| Taint [[externally controlled string]] | test.py:26 | test.py:26:28:26:29 | ll | | --> | Taint [externally controlled string] | test.py:27 | test.py:27:22:27:22 | b | | -| Taint [[externally controlled string]] | test.py:26 | test.py:26:28:26:29 | ll | | --> | Taint [externally controlled string] | test.py:27 | test.py:27:25:27:25 | c | | -| Taint [[externally controlled string]] | test.py:26 | test.py:26:28:26:29 | ll | | --> | Taint externally controlled string | test.py:27 | test.py:27:10:27:11 | a1 | | -| Taint [[externally controlled string]] | test.py:26 | test.py:26:28:26:29 | ll | | --> | Taint externally controlled string | test.py:27 | test.py:27:14:27:15 | a2 | | -| Taint [[externally controlled string]] | test.py:26 | test.py:26:28:26:29 | ll | | --> | Taint externally controlled string | test.py:27 | test.py:27:18:27:19 | a3 | | -| Taint [[externally controlled string]] | test.py:30 | test.py:30:28:30:29 | ll | | --> | Taint [externally controlled string] | test.py:31 | test.py:31:22:31:22 | b | | -| Taint [[externally controlled string]] | test.py:30 | test.py:30:28:30:29 | ll | | --> | Taint [externally controlled string] | test.py:31 | test.py:31:25:31:25 | c | | -| Taint [[externally controlled string]] | test.py:30 | test.py:30:28:30:29 | ll | | --> | Taint externally controlled string | test.py:31 | test.py:31:10:31:11 | a1 | | -| Taint [[externally controlled string]] | test.py:30 | test.py:30:28:30:29 | ll | | --> | Taint externally controlled string | test.py:31 | test.py:31:14:31:15 | a2 | | -| Taint [[externally controlled string]] | test.py:30 | test.py:30:28:30:29 | ll | | --> | Taint externally controlled string | test.py:31 | test.py:31:18:31:19 | a3 | | -| Taint [[externally controlled string]] | test.py:47 | test.py:47:28:47:54 | Tuple | | --> | Taint externally controlled string | test.py:48 | test.py:48:10:48:10 | a | | -| Taint [[externally controlled string]] | test.py:47 | test.py:47:28:47:54 | Tuple | | --> | Taint externally controlled string | test.py:48 | test.py:48:13:48:13 | b | | -| Taint [[externally controlled string]] | test.py:47 | test.py:47:28:47:54 | Tuple | | --> | Taint externally controlled string | test.py:48 | test.py:48:16:48:16 | c | | -| Taint [[externally controlled string]] | test.py:47 | test.py:47:28:47:54 | Tuple | | --> | Taint externally controlled string | test.py:48 | test.py:48:19:48:19 | d | | -| Taint [[externally controlled string]] | test.py:47 | test.py:47:28:47:54 | Tuple | | --> | Taint externally controlled string | test.py:48 | test.py:48:22:48:22 | e | | -| Taint [[externally controlled string]] | test.py:47 | test.py:47:28:47:54 | Tuple | | --> | Taint externally controlled string | test.py:48 | test.py:48:25:48:25 | f | | -| Taint [externally controlled string] | test.py:6 | test.py:6:9:6:20 | TAINTED_LIST | | --> | Taint [externally controlled string] | test.py:7 | test.py:7:15:7:15 | l | | -| Taint [externally controlled string] | test.py:7 | test.py:7:15:7:15 | l | | --> | Taint externally controlled string | test.py:8 | test.py:8:10:8:10 | a | | -| Taint [externally controlled string] | test.py:7 | test.py:7:15:7:15 | l | | --> | Taint externally controlled string | test.py:8 | test.py:8:13:8:13 | b | | -| Taint [externally controlled string] | test.py:7 | test.py:7:15:7:15 | l | | --> | Taint externally controlled string | test.py:8 | test.py:8:16:8:16 | c | | -| Taint [externally controlled string] | test.py:12 | test.py:12:9:12:20 | TAINTED_LIST | | --> | Taint [externally controlled string] | test.py:13 | test.py:13:17:13:17 | l | | -| Taint [externally controlled string] | test.py:13 | test.py:13:17:13:17 | l | | --> | Taint externally controlled string | test.py:14 | test.py:14:10:14:10 | a | | -| Taint [externally controlled string] | test.py:13 | test.py:13:17:13:17 | l | | --> | Taint externally controlled string | test.py:14 | test.py:14:13:14:13 | b | | -| Taint [externally controlled string] | test.py:13 | test.py:13:17:13:17 | l | | --> | Taint externally controlled string | test.py:14 | test.py:14:16:14:16 | c | | -| Taint [externally controlled string] | test.py:18 | test.py:18:9:18:20 | TAINTED_LIST | | --> | Taint [externally controlled string] | test.py:19 | test.py:19:11:19:11 | l | | -| Taint [externally controlled string] | test.py:18 | test.py:18:9:18:20 | TAINTED_LIST | | --> | Taint [externally controlled string] | test.py:19 | test.py:19:14:19:14 | l | | -| Taint [externally controlled string] | test.py:18 | test.py:18:9:18:20 | TAINTED_LIST | | --> | Taint [externally controlled string] | test.py:19 | test.py:19:17:19:17 | l | | -| Taint [externally controlled string] | test.py:19 | test.py:19:11:19:11 | l | | --> | Taint [[externally controlled string]] | test.py:19 | test.py:19:10:19:18 | List | | -| Taint [externally controlled string] | test.py:19 | test.py:19:14:19:14 | l | | --> | Taint [[externally controlled string]] | test.py:19 | test.py:19:10:19:18 | List | | -| Taint [externally controlled string] | test.py:19 | test.py:19:17:19:17 | l | | --> | Taint [[externally controlled string]] | test.py:19 | test.py:19:10:19:18 | List | | -| Taint [externally controlled string] | test.py:43 | test.py:43:20:43:31 | TAINTED_LIST | | --> | Taint [externally controlled string] | test.py:47 | test.py:47:28:47:39 | tainted_list | | -| Taint [externally controlled string] | test.py:47 | test.py:47:28:47:39 | tainted_list | | --> | Taint [[externally controlled string]] | test.py:47 | test.py:47:28:47:54 | Tuple | | -| Taint [externally controlled string] | test.py:55 | test.py:55:27:55:38 | TAINTED_LIST | | --> | Taint [[externally controlled string]] | test.py:55 | test.py:55:25:55:40 | List | | diff --git a/python/ql/test/library-tests/taint/unpacking/TestStep.ql b/python/ql/test/library-tests/taint/unpacking/TestStep.ql deleted file mode 100644 index 177edce3498..00000000000 --- a/python/ql/test/library-tests/taint/unpacking/TestStep.ql +++ /dev/null @@ -1,11 +0,0 @@ -import python -import semmle.python.dataflow.TaintTracking -import Taint - -from TaintedNode n, TaintedNode s -where - n.getLocation().getFile().getShortName() = "test.py" and - s.getLocation().getFile().getShortName() = "test.py" and - s = n.getASuccessor() -select "Taint " + n.getTaintKind(), n.getLocation().toString(), n.getAstNode(), n.getContext(), - " --> ", "Taint " + s.getTaintKind(), s.getLocation().toString(), s.getAstNode(), s.getContext() diff --git a/python/ql/test/library-tests/taint/unpacking/TestTaint.expected b/python/ql/test/library-tests/taint/unpacking/TestTaint.expected deleted file mode 100644 index d1bad70f811..00000000000 --- a/python/ql/test/library-tests/taint/unpacking/TestTaint.expected +++ /dev/null @@ -1,33 +0,0 @@ -| test.py:8 | unpacking | a | externally controlled string | -| test.py:8 | unpacking | b | externally controlled string | -| test.py:8 | unpacking | c | externally controlled string | -| test.py:14 | unpacking_to_list | a | externally controlled string | -| test.py:14 | unpacking_to_list | b | externally controlled string | -| test.py:14 | unpacking_to_list | c | externally controlled string | -| test.py:23 | nested | a1 | externally controlled string | -| test.py:23 | nested | a2 | externally controlled string | -| test.py:23 | nested | a3 | externally controlled string | -| test.py:23 | nested | b | [externally controlled string] | -| test.py:23 | nested | c | [externally controlled string] | -| test.py:27 | nested | a1 | externally controlled string | -| test.py:27 | nested | a2 | externally controlled string | -| test.py:27 | nested | a3 | externally controlled string | -| test.py:27 | nested | b | [externally controlled string] | -| test.py:27 | nested | c | [externally controlled string] | -| test.py:31 | nested | a1 | externally controlled string | -| test.py:31 | nested | a2 | externally controlled string | -| test.py:31 | nested | a3 | externally controlled string | -| test.py:31 | nested | b | [externally controlled string] | -| test.py:31 | nested | c | [externally controlled string] | -| test.py:38 | unpack_from_set | a | NO TAINT | -| test.py:38 | unpack_from_set | b | NO TAINT | -| test.py:38 | unpack_from_set | c | NO TAINT | -| test.py:48 | contrived_1 | a | externally controlled string | -| test.py:48 | contrived_1 | b | externally controlled string | -| test.py:48 | contrived_1 | c | externally controlled string | -| test.py:48 | contrived_1 | d | externally controlled string | -| test.py:48 | contrived_1 | e | externally controlled string | -| test.py:48 | contrived_1 | f | externally controlled string | -| test.py:56 | contrived_2 | a | NO TAINT | -| test.py:56 | contrived_2 | b | NO TAINT | -| test.py:56 | contrived_2 | c | NO TAINT | diff --git a/python/ql/test/library-tests/taint/unpacking/TestTaint.ql b/python/ql/test/library-tests/taint/unpacking/TestTaint.ql deleted file mode 100644 index 47883578516..00000000000 --- a/python/ql/test/library-tests/taint/unpacking/TestTaint.ql +++ /dev/null @@ -1,19 +0,0 @@ -import python -import semmle.python.dataflow.TaintTracking -import Taint - -from Call call, Expr arg, string taint_string -where - call.getLocation().getFile().getShortName() = "test.py" and - call.getFunc().(Name).getId() = "test" and - arg = call.getAnArg() and - ( - not exists(TaintedNode tainted | tainted.getAstNode() = arg) and - taint_string = "NO TAINT" - or - exists(TaintedNode tainted | tainted.getAstNode() = arg | - taint_string = tainted.getTaintKind().toString() - ) - ) -select arg.getLocation().toString(), call.getScope().(Function).getName(), arg.toString(), - taint_string diff --git a/python/ql/test/library-tests/taint/unpacking/test.py b/python/ql/test/library-tests/taint/unpacking/test.py deleted file mode 100644 index df3961ad6ab..00000000000 --- a/python/ql/test/library-tests/taint/unpacking/test.py +++ /dev/null @@ -1,58 +0,0 @@ -def test(*args): - pass - - -def unpacking(): - l = TAINTED_LIST - a, b, c = l - test(a, b, c) - - -def unpacking_to_list(): - l = TAINTED_LIST - [a, b, c] = l - test(a, b, c) - - -def nested(): - l = TAINTED_LIST - ll = [l, l, l] - - # list - [[a1, a2, a3], b, c] = ll - test(a1, a2, a3, b, c) - - # tuple - ((a1, a2, a3), b, c) = ll - test(a1, a2, a3, b, c) - - # mixed - [(a1, a2, a3), b, c] = ll - test(a1, a2, a3, b, c) - - -def unpack_from_set(): - # no guarantee on ordering ... don't know why you would ever do this - a, b, c = {"foo", "bar", TAINTED_STRING} - # either all should be tainted, or none of them - test(a, b, c) - - -def contrived_1(): - # A contrived example. Don't know why anyone would ever actually do this. - tainted_list = TAINTED_LIST - no_taint_list = [1,2,3] - - # We don't handle this case currently, since we mark `d`, `e` and `f` as tainted. - (a, b, c), (d, e, f) = tainted_list, no_taint_list - test(a, b, c, d, e, f) - - -def contrived_2(): - # A contrived example. Don't know why anyone would ever actually do this. - - # We currently only handle taint nested 2 levels. - [[[ (a,b,c) ]]] = [[[ TAINTED_LIST ]]] - test(a, b, c) - -# For Python 3, see https://www.python.org/dev/peps/pep-3132/ diff --git a/python/ql/test/query-tests/Security/CWE-022-PathInjection/DataflowQueryTest.expected b/python/ql/test/query-tests/Security/CWE-022-PathInjection/DataflowQueryTest.expected index 3875da4e143..04431311999 100644 --- a/python/ql/test/query-tests/Security/CWE-022-PathInjection/DataflowQueryTest.expected +++ b/python/ql/test/query-tests/Security/CWE-022-PathInjection/DataflowQueryTest.expected @@ -1,2 +1,3 @@ missingAnnotationOnSink failures +testFailures diff --git a/python/ql/test/query-tests/Security/CWE-078-CommandInjection/DataflowQueryTest.expected b/python/ql/test/query-tests/Security/CWE-078-CommandInjection/DataflowQueryTest.expected index 3875da4e143..04431311999 100644 --- a/python/ql/test/query-tests/Security/CWE-078-CommandInjection/DataflowQueryTest.expected +++ b/python/ql/test/query-tests/Security/CWE-078-CommandInjection/DataflowQueryTest.expected @@ -1,2 +1,3 @@ missingAnnotationOnSink failures +testFailures diff --git a/python/ql/test/query-tests/Security/CWE-078-UnsafeShellCommandConstruction/DataflowQueryTest.expected b/python/ql/test/query-tests/Security/CWE-078-UnsafeShellCommandConstruction/DataflowQueryTest.expected index 3875da4e143..04431311999 100644 --- a/python/ql/test/query-tests/Security/CWE-078-UnsafeShellCommandConstruction/DataflowQueryTest.expected +++ b/python/ql/test/query-tests/Security/CWE-078-UnsafeShellCommandConstruction/DataflowQueryTest.expected @@ -1,2 +1,3 @@ missingAnnotationOnSink failures +testFailures diff --git a/python/ql/test/query-tests/Security/CWE-798-HardcodedCredentials/HardcodedCredentials.expected b/python/ql/test/query-tests/Security/CWE-798-HardcodedCredentials/HardcodedCredentials.expected index efea6e2f054..61251a633e2 100644 --- a/python/ql/test/query-tests/Security/CWE-798-HardcodedCredentials/HardcodedCredentials.expected +++ b/python/ql/test/query-tests/Security/CWE-798-HardcodedCredentials/HardcodedCredentials.expected @@ -1,8 +1,16 @@ edges -| test.py:5:12:5:24 | hard coded value | test.py:14:18:14:25 | hard coded value | -| test.py:5:12:5:24 | hard coded value | test.py:14:18:14:25 | hard coded value | -| test.py:6:12:6:25 | hard coded value | test.py:15:18:15:25 | hard coded value | -| test.py:6:12:6:25 | hard coded value | test.py:15:18:15:25 | hard coded value | +| test.py:5:1:5:8 | GSSA Variable USERNAME | test.py:14:18:14:25 | ControlFlowNode for USERNAME | +| test.py:5:12:5:24 | ControlFlowNode for Str | test.py:5:1:5:8 | GSSA Variable USERNAME | +| test.py:6:1:6:8 | GSSA Variable PASSWORD | test.py:15:18:15:25 | ControlFlowNode for PASSWORD | +| test.py:6:12:6:25 | ControlFlowNode for Str | test.py:6:1:6:8 | GSSA Variable PASSWORD | +nodes +| test.py:5:1:5:8 | GSSA Variable USERNAME | semmle.label | GSSA Variable USERNAME | +| test.py:5:12:5:24 | ControlFlowNode for Str | semmle.label | ControlFlowNode for Str | +| test.py:6:1:6:8 | GSSA Variable PASSWORD | semmle.label | GSSA Variable PASSWORD | +| test.py:6:12:6:25 | ControlFlowNode for Str | semmle.label | ControlFlowNode for Str | +| test.py:14:18:14:25 | ControlFlowNode for USERNAME | semmle.label | ControlFlowNode for USERNAME | +| test.py:15:18:15:25 | ControlFlowNode for PASSWORD | semmle.label | ControlFlowNode for PASSWORD | +subpaths #select -| test.py:5:12:5:24 | Str | test.py:5:12:5:24 | hard coded value | test.py:14:18:14:25 | hard coded value | This hardcoded value is $@. | test.py:14:18:14:25 | USERNAME | used as credentials | -| test.py:6:12:6:25 | Str | test.py:6:12:6:25 | hard coded value | test.py:15:18:15:25 | hard coded value | This hardcoded value is $@. | test.py:15:18:15:25 | PASSWORD | used as credentials | +| test.py:5:12:5:24 | ControlFlowNode for Str | test.py:5:12:5:24 | ControlFlowNode for Str | test.py:14:18:14:25 | ControlFlowNode for USERNAME | This hardcoded value is $@. | test.py:14:18:14:25 | ControlFlowNode for USERNAME | used as credentials | +| test.py:6:12:6:25 | ControlFlowNode for Str | test.py:6:12:6:25 | ControlFlowNode for Str | test.py:15:18:15:25 | ControlFlowNode for PASSWORD | This hardcoded value is $@. | test.py:15:18:15:25 | ControlFlowNode for PASSWORD | used as credentials | diff --git a/python/tools/recorded-call-graph-metrics/ql/qlpack.yml b/python/tools/recorded-call-graph-metrics/ql/qlpack.yml index 3fee59d70bc..a08193635d5 100644 --- a/python/tools/recorded-call-graph-metrics/ql/qlpack.yml +++ b/python/tools/recorded-call-graph-metrics/ql/qlpack.yml @@ -3,3 +3,4 @@ version: 0.0.1 extractor: python dependencies: codeql/python-all: '*' +warnOnImplicitThis: true diff --git a/ql/ql/consistency-queries/qlpack.yml b/ql/ql/consistency-queries/qlpack.yml index ffc5cdb7a1a..499d2130ef1 100644 --- a/ql/ql/consistency-queries/qlpack.yml +++ b/ql/ql/consistency-queries/qlpack.yml @@ -3,3 +3,4 @@ groups: [ql, test, consistency-queries] dependencies: codeql/ql: ${workspace} extractor: ql +warnOnImplicitThis: true diff --git a/ql/ql/examples/qlpack.yml b/ql/ql/examples/qlpack.yml index 0652ac37bd5..88fb9314a20 100644 --- a/ql/ql/examples/qlpack.yml +++ b/ql/ql/examples/qlpack.yml @@ -2,3 +2,4 @@ name: codeql/ql-examples groups: [ql, examples] dependencies: codeql/ql: ${workspace} +warnOnImplicitThis: true diff --git a/ql/ql/src/codeql_ql/ast/Ast.qll b/ql/ql/src/codeql_ql/ast/Ast.qll index 616438da756..acc36be15ee 100644 --- a/ql/ql/src/codeql_ql/ast/Ast.qll +++ b/ql/ql/src/codeql_ql/ast/Ast.qll @@ -567,7 +567,7 @@ class ClasslessPredicate extends TClasslessPredicate, Predicate, ModuleDeclarati override predicate isPrivate() { Predicate.super.isPrivate() } /** Holds if this classless predicate is a signature predicate with no body. */ - predicate isSignature() { not exists(this.getBody()) } + override predicate isSignature() { not exists(this.getBody()) } override QLDoc getQLDoc() { result = any(TopLevel m).getQLDocFor(this) @@ -611,6 +611,8 @@ class ClassPredicate extends TClassPredicate, Predicate { predicate overrides(ClassPredicate other) { predOverrides(this, other) } + predicate shadows(ClassPredicate other) { predShadows(this, other) } + override TypeExpr getReturnTypeExpr() { toQL(result) = pred.getReturnType() } override AstNode getAChild(string pred_name) { @@ -834,6 +836,12 @@ class Module extends TModule, ModuleDeclaration { toMock(result) = mod.asRight().getMember(i) } + pragma[nomagic] + Declaration getDeclaration(string name) { + result = this.getAMember() and + name = result.getName() + } + QLDoc getQLDocFor(AstNode m) { exists(int i | result = this.getMember(i) and m = this.getMember(i + 1)) } @@ -878,6 +886,36 @@ class Module extends TModule, ModuleDeclaration { class ModuleMember extends TModuleMember, AstNode { /** Holds if this member is declared as `private`. */ predicate isPrivate() { this.hasAnnotation("private") } + + /** Holds if this member is declared as `final`. */ + predicate isFinal() { this.hasAnnotation("final") } +} + +private newtype TDeclarationKind = + TClassKind() or + TModuleKind() or + TPredicateKind(int arity) { arity = any(Predicate p).getArity() } + +private TDeclarationKind getDeclarationKind(Declaration d) { + d instanceof Class and result = TClassKind() + or + d instanceof Module and result = TModuleKind() + or + d = any(Predicate p | result = TPredicateKind(p.getArity())) +} + +/** Holds if module `m` must implement signature declaration `d` with name `name` and kind `kind`. */ +pragma[nomagic] +private predicate mustImplement(Module m, string name, TDeclarationKind kind, Declaration d) { + d = m.getImplements(_).getResolvedType().getDeclaration().(Module).getAMember() and + name = d.getName() and + kind = getDeclarationKind(d) +} + +pragma[nomagic] +private Declaration getDeclaration(Module m, string name, TDeclarationKind kind) { + result = m.getDeclaration(name) and + kind = getDeclarationKind(result) } /** A declaration. E.g. a class, type, predicate, newtype... */ @@ -894,6 +932,16 @@ class Declaration extends TDeclaration, AstNode { or result = any(Class c).getQLDocFor(this) } + + predicate isSignature() { this.hasAnnotation("signature") } + + /** Holds if this declaration implements `other`. */ + predicate implements(Declaration other) { + exists(Module m, string name, TDeclarationKind kind | + this = getDeclaration(m, name, kind) and + mustImplement(m, name, kind, other) + ) + } } /** An entity that can be declared in a module. */ diff --git a/ql/ql/src/codeql_ql/ast/internal/Predicate.qll b/ql/ql/src/codeql_ql/ast/internal/Predicate.qll index 46dc86113da..b45eb2166f1 100644 --- a/ql/ql/src/codeql_ql/ast/internal/Predicate.qll +++ b/ql/ql/src/codeql_ql/ast/internal/Predicate.qll @@ -90,16 +90,28 @@ private module Cached { ) } + pragma[nomagic] + private ClassPredicate getClassPredicate(Class c, string name, int arity) { + result = c.getClassPredicate(name) and + arity = result.getArity() + } + + pragma[nomagic] + private predicate resolveSelfClassCalls0(Class c, string name, int arity, MemberCall mc) { + mc.getBase() instanceof ThisAccess and + c = mc.getEnclosingPredicate().getParent() and + name = mc.getMemberName() and + arity = mc.getNumberOfArguments() + } + /** * Holds if `mc` is a `this.method()` call to a predicate defined in the same class. * helps avoid spuriously resolving to predicates in super-classes. */ private predicate resolveSelfClassCalls(MemberCall mc, PredicateOrBuiltin p) { - exists(Class c | - mc.getBase() instanceof ThisAccess and - c = mc.getEnclosingPredicate().getParent() and - p = c.getClassPredicate(mc.getMemberName()) and - p.getArity() = mc.getNumberOfArguments() + exists(Class c, string name, int arity | + resolveSelfClassCalls0(c, name, arity, mc) and + p = getClassPredicate(c, name, arity) ) } diff --git a/ql/ql/src/codeql_ql/ast/internal/Type.qll b/ql/ql/src/codeql_ql/ast/internal/Type.qll index c1aaf84d8c2..2053c657904 100644 --- a/ql/ql/src/codeql_ql/ast/internal/Type.qll +++ b/ql/ql/src/codeql_ql/ast/internal/Type.qll @@ -21,7 +21,7 @@ private newtype TType = private predicate primTypeName(string s) { s = ["int", "float", "string", "boolean", "date"] } private predicate isActualClass(Class c) { - not exists(c.getAliasType()) and + (not exists(c.getAliasType()) or c.isFinal()) and not exists(c.getUnionMember()) } @@ -36,6 +36,10 @@ class Type extends TType { /** * Gets a supertype of this type. This follows the user-visible type hierarchy, * and doesn't include internal types like the characteristic and domain types of classes. + * + * For supertypes that are `final` aliases, this returns the alias itself, and for + * types that are `final` aliases, this returns the supertypes of the type that is + * being aliased. */ Type getASuperType() { none() } @@ -94,9 +98,23 @@ class ClassType extends Type, TClass { override Class getDeclaration() { result = decl } - override Type getASuperType() { result = decl.getASuperType().getResolvedType() } + override Type getASuperType() { + result = decl.getASuperType().getResolvedType() + or + exists(ClassType alias | + this.isFinalAlias(alias) and + result = alias.getASuperType() + ) + } - Type getAnInstanceofType() { result = decl.getAnInstanceofType().getResolvedType() } + Type getAnInstanceofType() { + result = decl.getAnInstanceofType().getResolvedType() + or + exists(ClassType alias | + this.isFinalAlias(alias) and + result = alias.getAnInstanceofType() + ) + } override Type getAnInternalSuperType() { result.(ClassCharType).getClassType() = this @@ -110,6 +128,12 @@ class ClassType extends Type, TClass { other.getDeclaringType().getASuperType+() = result.getDeclaringType() ) } + + /** Holds if this class is a `final` alias of `c`. */ + predicate isFinalAlias(ClassType c) { + decl.isFinal() and + decl.getAliasType().getResolvedType() = c + } } class FileType extends Type, TFile { @@ -136,23 +160,37 @@ private PredicateOrBuiltin declaredPred(Type ty, string name, int arity) { result.getDeclaringType() = ty and result.getName() = name and result.getArity() = arity + or + exists(ClassType alias | + ty.(ClassType).isFinalAlias(alias) and + result = declaredPred(alias, name, arity) + ) } pragma[nomagic] -private PredicateOrBuiltin classPredCandidate(Type ty, string name, int arity) { - result = declaredPred(ty, name, arity) +private PredicateOrBuiltin classPredCandidate(Type ty, string name, int arity, boolean isFinal) { + result = declaredPred(ty, name, arity) and + if ty.(ClassType).getDeclaration().isFinal() then isFinal = true else isFinal = false or not exists(declaredPred(ty, name, arity)) and - result = inherClassPredCandidate(ty, name, arity) + result = inherClassPredCandidate(ty, name, arity, isFinal) } -private PredicateOrBuiltin inherClassPredCandidate(Type ty, string name, int arity) { - result = classPredCandidate(ty.getAnInternalSuperType(), name, arity) and +private PredicateOrBuiltin classPredCandidate(Type ty, string name, int arity) { + result = classPredCandidate(ty, name, arity, _) +} + +private PredicateOrBuiltin inherClassPredCandidate(Type ty, string name, int arity, boolean isFinal) { + result = classPredCandidate(ty.getAnInternalSuperType(), name, arity, isFinal) and not result.isPrivate() } predicate predOverrides(ClassPredicate sub, ClassPredicate sup) { - sup = inherClassPredCandidate(sub.getDeclaringType(), sub.getName(), sub.getArity()) + sup = inherClassPredCandidate(sub.getDeclaringType(), sub.getName(), sub.getArity(), false) +} + +predicate predShadows(ClassPredicate sub, ClassPredicate sup) { + sup = inherClassPredCandidate(sub.getDeclaringType(), sub.getName(), sub.getArity(), true) } private VarDecl declaredField(ClassType ty, string name) { @@ -376,7 +414,8 @@ private predicate defines(FileOrModule m, string name, Type t, boolean public) { exists(Class ty | t = ty.getAliasType().getResolvedType() | getEnclosingModule(ty) = m and ty.getName() = name and - public = getPublicBool(ty) + public = getPublicBool(ty) and + not ty.isFinal() ) or exists(Import im | diff --git a/ql/ql/src/codeql_ql/style/DeadCodeQuery.qll b/ql/ql/src/codeql_ql/style/DeadCodeQuery.qll index 5d6e1dc3ff7..211084f2915 100644 --- a/ql/ql/src/codeql_ql/style/DeadCodeQuery.qll +++ b/ql/ql/src/codeql_ql/style/DeadCodeQuery.qll @@ -186,6 +186,16 @@ private AstNode aliveStep(AstNode prev) { result = prev.(Module).getImplements(_) or result = prev.(PredicateExpr).getQualifier() + or + // a module argument is live if the constructed module is + result = prev.(ModuleExpr).getArgument(_) + or + // a type declaration is live if a reference to it is live + result = prev.(TypeExpr).getResolvedType().getDeclaration() + or + // a module member that implements a signature member is live if the module is + prev.(Module).getAMember() = result and + result.(Declaration).implements(_) } private AstNode deprecated() { diff --git a/ql/ql/src/queries/performance/AbstractClassImport.ql b/ql/ql/src/queries/performance/AbstractClassImport.ql index abd00689909..24e05860f0a 100644 --- a/ql/ql/src/queries/performance/AbstractClassImport.ql +++ b/ql/ql/src/queries/performance/AbstractClassImport.ql @@ -38,14 +38,15 @@ Class getASubclassOfAbstract(Class ab) { /** Gets a non-abstract subclass of `ab` that contributes to the extent of `ab`. */ Class concreteExternalSubclass(Class ab) { - ab.isAbstract() and not result.isAbstract() and result = getASubclassOfAbstract+(ab) and // Heuristic: An abstract class with subclasses in the same file and no other // imported subclasses is likely intentional. result.getLocation().getFile() != ab.getLocation().getFile() and // Exclude subclasses in tests and libraries that are only used in tests. - liveNonTestFile(result.getLocation().getFile()) + liveNonTestFile(result.getLocation().getFile()) and + // exclude `final` aliases + not result.getType().isFinalAlias(_) } /** Holds if there is a bidirectional import between the abstract class `ab` and its subclass `sub` */ diff --git a/ql/ql/src/queries/style/AndroidIdPrefix.ql b/ql/ql/src/queries/style/AndroidIdPrefix.ql new file mode 100644 index 00000000000..88b479cc7d4 --- /dev/null +++ b/ql/ql/src/queries/style/AndroidIdPrefix.ql @@ -0,0 +1,32 @@ +/** + * @name Android query without android @id prefix + * @description Android queries should include the `android/` prefix in their `@id`. + * @kind problem + * @problem.severity warning + * @id ql/android-id-prefix + * @precision high + */ + +import ql + +/** Holds if `t` transitively imports an Android module. */ +predicate importsAndroidModule(TopLevel t) { + t.getFile() = + any(YAML::QLPack pack | pack.getADependency*().getExtractor() = "java").getAFileInPack() and + exists(Import i | t.getAnImport() = i | + i.getImportString().toLowerCase().matches("%android%") + or + exists(TopLevel t2 | + t2.getAModule() = i.getResolvedModule().asModule() and + importsAndroidModule(t2) + ) + ) +} + +from QueryDoc d +where + d.getLocation().getFile().getRelativePath().matches("%src/Security/%") and + not d.getQueryId().matches("android/%") and + not d.getQueryId() = ["improper-intent-verification", "improper-webview-certificate-validation"] and // known badly identified queries that sadly we can't fix + importsAndroidModule(d.getParent()) +select d, "This Android query is missing the `android/` prefix in its `@id`." diff --git a/ql/ql/src/queries/style/FieldOnlyUsedInCharPred.ql b/ql/ql/src/queries/style/FieldOnlyUsedInCharPred.ql index e1f4810d391..3216cc9a6af 100644 --- a/ql/ql/src/queries/style/FieldOnlyUsedInCharPred.ql +++ b/ql/ql/src/queries/style/FieldOnlyUsedInCharPred.ql @@ -21,5 +21,6 @@ where any(PredicateCall call | call.getEnclosingPredicate() = c.getCharPred() and call.getTarget() instanceof NewTypeBranch ).getAnArgument() and - not f.getVarDecl().overrides(_) + not f.getVarDecl().overrides(_) and + not any(FieldDecl other).getVarDecl().overrides(f.getVarDecl()) select f, "Field is only used in CharPred." diff --git a/ql/ql/test/callgraph/packs/lib/qlpack.yml b/ql/ql/test/callgraph/packs/lib/qlpack.yml index 92e83d1e3d8..00c40b2431a 100644 --- a/ql/ql/test/callgraph/packs/lib/qlpack.yml +++ b/ql/ql/test/callgraph/packs/lib/qlpack.yml @@ -1,3 +1,4 @@ name: ql-testing-lib-pack version: 0.1.0 -extractor: ql-test-stuff \ No newline at end of file +extractor: ql-test-stuff +warnOnImplicitThis: true diff --git a/ql/ql/test/callgraph/packs/other/qlpack.yml b/ql/ql/test/callgraph/packs/other/qlpack.yml index d0b95cb68be..e0bc6a0a652 100644 --- a/ql/ql/test/callgraph/packs/other/qlpack.yml +++ b/ql/ql/test/callgraph/packs/other/qlpack.yml @@ -2,3 +2,4 @@ name: ql-other-pack-thing version: 0.1.0 dependencies: ql-testing-src-pack: '*' +warnOnImplicitThis: true diff --git a/ql/ql/test/callgraph/packs/src/qlpack.yml b/ql/ql/test/callgraph/packs/src/qlpack.yml index 2512d8f8483..c0d374d7cb4 100644 --- a/ql/ql/test/callgraph/packs/src/qlpack.yml +++ b/ql/ql/test/callgraph/packs/src/qlpack.yml @@ -1,4 +1,5 @@ name: ql-testing-src-pack version: 0.1.0 dependencies: - ql-testing-lib-pack: ${workspace} \ No newline at end of file + ql-testing-lib-pack: ${workspace} +warnOnImplicitThis: true diff --git a/ql/ql/test/qlpack.yml b/ql/ql/test/qlpack.yml index f31941cdf10..b16fd7e3e20 100644 --- a/ql/ql/test/qlpack.yml +++ b/ql/ql/test/qlpack.yml @@ -5,3 +5,4 @@ dependencies: codeql/ql-examples: ${workspace} extractor: ql tests: . +warnOnImplicitThis: true diff --git a/ql/ql/test/queries/performance/AbstractClassImport/AbstractClassImport.expected b/ql/ql/test/queries/performance/AbstractClassImport/AbstractClassImport.expected new file mode 100644 index 00000000000..952a49efc65 --- /dev/null +++ b/ql/ql/test/queries/performance/AbstractClassImport/AbstractClassImport.expected @@ -0,0 +1,4 @@ +| AbstractClassImportTest1.qll:4:16:4:19 | Class Base | This abstract class doesn't import its subclass $@ but imports 2 other subclasses, such as $@. | AbstractClassImportTest3.qll:4:7:4:11 | Class Sub31 | Sub31 | AbstractClassImportTest2.qll:4:7:4:11 | Class Sub21 | Sub21 | +| AbstractClassImportTest1.qll:4:16:4:19 | Class Base | This abstract class doesn't import its subclass $@ but imports 2 other subclasses, such as $@. | AbstractClassImportTest3.qll:8:7:8:11 | Class Sub32 | Sub32 | AbstractClassImportTest2.qll:4:7:4:11 | Class Sub21 | Sub21 | +| AbstractClassImportTest1.qll:4:16:4:19 | Class Base | This abstract class imports its subclass $@ but doesn't import 2 other subclasses, such as $@. | AbstractClassImportTest2.qll:4:7:4:11 | Class Sub21 | Sub21 | AbstractClassImportTest3.qll:4:7:4:11 | Class Sub31 | Sub31 | +| AbstractClassImportTest1.qll:4:16:4:19 | Class Base | This abstract class imports its subclass $@ but doesn't import 2 other subclasses, such as $@. | AbstractClassImportTest2.qll:8:7:8:11 | Class Sub22 | Sub22 | AbstractClassImportTest3.qll:4:7:4:11 | Class Sub31 | Sub31 | diff --git a/ql/ql/test/queries/performance/AbstractClassImport/AbstractClassImport.qlref b/ql/ql/test/queries/performance/AbstractClassImport/AbstractClassImport.qlref new file mode 100644 index 00000000000..4d7907c36ef --- /dev/null +++ b/ql/ql/test/queries/performance/AbstractClassImport/AbstractClassImport.qlref @@ -0,0 +1 @@ +queries/performance/AbstractClassImport.ql \ No newline at end of file diff --git a/ql/ql/test/queries/performance/AbstractClassImport/AbstractClassImportTest1.qll b/ql/ql/test/queries/performance/AbstractClassImport/AbstractClassImportTest1.qll new file mode 100644 index 00000000000..ce7f7c4ea68 --- /dev/null +++ b/ql/ql/test/queries/performance/AbstractClassImport/AbstractClassImportTest1.qll @@ -0,0 +1,4 @@ +import ql +import AbstractClassImportTest2 + +abstract class Base extends AstNode { } diff --git a/ql/ql/test/queries/performance/AbstractClassImport/AbstractClassImportTest2.qll b/ql/ql/test/queries/performance/AbstractClassImport/AbstractClassImportTest2.qll new file mode 100644 index 00000000000..df29a5c18de --- /dev/null +++ b/ql/ql/test/queries/performance/AbstractClassImport/AbstractClassImportTest2.qll @@ -0,0 +1,8 @@ +import ql +import AbstractClassImportTest1 + +class Sub21 extends Base { + Sub21() { this instanceof TopLevel } +} + +class Sub22 extends Base instanceof Comment { } diff --git a/ql/ql/test/queries/performance/AbstractClassImport/AbstractClassImportTest3.qll b/ql/ql/test/queries/performance/AbstractClassImport/AbstractClassImportTest3.qll new file mode 100644 index 00000000000..d2917d9aeb4 --- /dev/null +++ b/ql/ql/test/queries/performance/AbstractClassImport/AbstractClassImportTest3.qll @@ -0,0 +1,18 @@ +import ql +import AbstractClassImportTest1 + +class Sub31 extends Base { + Sub31() { this instanceof Comment } +} + +class Sub32 extends Base instanceof Comment { } + +final class BaseFinal = Base; + +class Sub33 extends BaseFinal instanceof Comment { } + +abstract class Sub34 extends Base { } + +final class Sub34Final = Sub34; + +class Sub35 extends Sub34Final instanceof Comment { } diff --git a/ql/ql/test/queries/performance/AbstractClassImport/AbstractClassImportTestQuery.expected b/ql/ql/test/queries/performance/AbstractClassImport/AbstractClassImportTestQuery.expected new file mode 100644 index 00000000000..e69de29bb2d diff --git a/ql/ql/test/queries/performance/AbstractClassImport/AbstractClassImportTestQuery.ql b/ql/ql/test/queries/performance/AbstractClassImport/AbstractClassImportTestQuery.ql new file mode 100644 index 00000000000..f15c7fafb43 --- /dev/null +++ b/ql/ql/test/queries/performance/AbstractClassImport/AbstractClassImportTestQuery.ql @@ -0,0 +1,6 @@ +import ql +import AbstractClassImportTest3 + +from AstNode n +where none() +select n diff --git a/ql/ql/test/queries/style/DeadCode/DeadCode.expected b/ql/ql/test/queries/style/DeadCode/DeadCode.expected index 5238a57c6ed..017cf546a92 100644 --- a/ql/ql/test/queries/style/DeadCode/DeadCode.expected +++ b/ql/ql/test/queries/style/DeadCode/DeadCode.expected @@ -1,2 +1,5 @@ -| Foo.qll:2:21:2:25 | ClasslessPredicate dead1 | This code is never used, and it's not publicly exported. | -| Foo.qll:6:13:6:17 | ClasslessPredicate dead2 | This code is never used, and it's not publicly exported. | +| Foo.qll:4:21:4:25 | ClasslessPredicate dead1 | This code is never used, and it's not publicly exported. | +| Foo.qll:8:13:8:17 | ClasslessPredicate dead2 | This code is never used, and it's not publicly exported. | +| Foo.qll:46:16:46:21 | Module Input2 | This code is never used, and it's not publicly exported. | +| Foo.qll:56:16:56:17 | Module M2 | This code is never used, and it's not publicly exported. | +| Foo.qll:68:15:68:20 | Class CImpl2 | This code is never used, and it's not publicly exported. | diff --git a/ql/ql/test/queries/style/DeadCode/Foo.qll b/ql/ql/test/queries/style/DeadCode/Foo.qll index f273e3882e3..5b13fd99ca9 100644 --- a/ql/ql/test/queries/style/DeadCode/Foo.qll +++ b/ql/ql/test/queries/style/DeadCode/Foo.qll @@ -1,3 +1,5 @@ +import ql + private module Mixed { private predicate dead1() { none() } @@ -30,3 +32,37 @@ private module Foo { module ValidationMethod { predicate impl() { sig() } } + +signature module InputSig { + predicate foo(); +} + +module ParameterizedModule { } + +private module Input1 implements InputSig { + predicate foo() { any() } +} + +private module Input2 implements InputSig { + predicate foo() { any() } +} + +private module Input3 implements InputSig { + predicate foo() { any() } +} + +module M1 = ParameterizedModule; + +private module M2 = ParameterizedModule; + +import ParameterizedModule + +private module MImpl { } + +module MPublic = MImpl; + +private class CImpl1 extends AstNode { } + +final class CPublic1 = CImpl1; + +private class CImpl2 extends AstNode { } diff --git a/ql/ql/test/queries/style/FieldOnlyUsedInCharPred/FieldOnlyUsedInCharPred.expected b/ql/ql/test/queries/style/FieldOnlyUsedInCharPred/FieldOnlyUsedInCharPred.expected new file mode 100644 index 00000000000..f41af0cebaf --- /dev/null +++ b/ql/ql/test/queries/style/FieldOnlyUsedInCharPred/FieldOnlyUsedInCharPred.expected @@ -0,0 +1 @@ +| FieldOnlyUsedInCharPred.qll:2:3:2:12 | FieldDecl | Field is only used in CharPred. | diff --git a/ql/ql/test/queries/style/FieldOnlyUsedInCharPred/FieldOnlyUsedInCharPred.qll b/ql/ql/test/queries/style/FieldOnlyUsedInCharPred/FieldOnlyUsedInCharPred.qll new file mode 100644 index 00000000000..edfc8b4576e --- /dev/null +++ b/ql/ql/test/queries/style/FieldOnlyUsedInCharPred/FieldOnlyUsedInCharPred.qll @@ -0,0 +1,29 @@ +class C1 extends int { + int field; // BAD + + C1() { + this = field and + this = 0 + } +} + +class C2 extends C1 { + int field2; // GOOD + + C2() { + this = field2 and + this = 0 + } + + int getField() { result = field2 } +} + +class C3 extends int { + C1 field; // GOOD (overridden) + + C3() { this = field } +} + +class C4 extends C3 { + override C2 field; // GOOD (overriding) +} diff --git a/ql/ql/test/queries/style/FieldOnlyUsedInCharPred/FieldOnlyUsedInCharPred.qlref b/ql/ql/test/queries/style/FieldOnlyUsedInCharPred/FieldOnlyUsedInCharPred.qlref new file mode 100644 index 00000000000..0e77c6ae6fe --- /dev/null +++ b/ql/ql/test/queries/style/FieldOnlyUsedInCharPred/FieldOnlyUsedInCharPred.qlref @@ -0,0 +1 @@ +queries/style/FieldOnlyUsedInCharPred.ql diff --git a/ql/ql/test/queries/style/MissingOverride/MissingOverride.expected b/ql/ql/test/queries/style/MissingOverride/MissingOverride.expected index 2ef0dd4b1ad..d64a6ed1544 100644 --- a/ql/ql/test/queries/style/MissingOverride/MissingOverride.expected +++ b/ql/ql/test/queries/style/MissingOverride/MissingOverride.expected @@ -1 +1,2 @@ | Test.qll:12:13:12:16 | ClassPredicate test | Wrong.test overrides $@ but does not have an override annotation. | Test.qll:4:13:4:16 | ClassPredicate test | Super.test | +| Test.qll:18:13:18:16 | ClassPredicate test | Wrong2.test overrides $@ but does not have an override annotation. | Test.qll:4:13:4:16 | ClassPredicate test | Super.test | diff --git a/ql/ql/test/queries/style/MissingOverride/Test.qll b/ql/ql/test/queries/style/MissingOverride/Test.qll index e7f6a2c6b87..82d5199bf9e 100644 --- a/ql/ql/test/queries/style/MissingOverride/Test.qll +++ b/ql/ql/test/queries/style/MissingOverride/Test.qll @@ -11,3 +11,27 @@ class Correct extends Super { class Wrong extends Super { predicate test(int i) { i = 2 } } + +class Mid extends Super { } + +class Wrong2 extends Mid { + predicate test(int i) { i = 2 } +} + +final class SuperFinal = Super; + +class Correct2 extends SuperFinal { + predicate test(int i) { i = 4 } +} + +class Correct3 extends AstNode instanceof SuperFinal { + predicate test(int i) { i = 4 } +} + +final class Super2 extends AstNode { + predicate test(int i) { i = [1 .. 5] } +} + +class Correct4 extends Super2 { + predicate test(int i) { i = 3 } +} diff --git a/ruby/downgrades/qlpack.yml b/ruby/downgrades/qlpack.yml index c23e8cc44d6..c777eec353c 100644 --- a/ruby/downgrades/qlpack.yml +++ b/ruby/downgrades/qlpack.yml @@ -2,3 +2,4 @@ name: codeql/ruby-downgrades groups: ruby downgrades: . library: true +warnOnImplicitThis: true diff --git a/ruby/ql/consistency-queries/AstConsistency.ql b/ruby/ql/consistency-queries/AstConsistency.ql index 8a5ebcdcda7..3a49d7cacc0 100644 --- a/ruby/ql/consistency-queries/AstConsistency.ql +++ b/ruby/ql/consistency-queries/AstConsistency.ql @@ -23,3 +23,8 @@ query predicate multipleParents(AstNode node, AstNode parent, string cls) { one != two ) } + +query predicate multipleToString(AstNode n, string s) { + s = strictconcat(n.toString(), ",") and + strictcount(n.toString()) > 1 +} diff --git a/ruby/ql/consistency-queries/CfgConsistency.ql b/ruby/ql/consistency-queries/CfgConsistency.ql index 8a5b311ca47..1961bbf7b3a 100644 --- a/ruby/ql/consistency-queries/CfgConsistency.ql +++ b/ruby/ql/consistency-queries/CfgConsistency.ql @@ -1,5 +1,6 @@ import codeql.ruby.controlflow.internal.ControlFlowGraphImplShared::Consistency import codeql.ruby.AST +import codeql.ruby.CFG import codeql.ruby.controlflow.internal.Completion import codeql.ruby.controlflow.internal.ControlFlowGraphImpl @@ -18,3 +19,8 @@ query predicate nonPostOrderExpr(Expr e, string cls) { c instanceof NormalCompletion ) } + +query predicate multipleToString(CfgNode n, string s) { + s = strictconcat(n.toString(), ",") and + strictcount(n.toString()) > 1 +} diff --git a/ruby/ql/consistency-queries/DataFlowConsistency.ql b/ruby/ql/consistency-queries/DataFlowConsistency.ql index fbfccce4bcb..46384305f7a 100644 --- a/ruby/ql/consistency-queries/DataFlowConsistency.ql +++ b/ruby/ql/consistency-queries/DataFlowConsistency.ql @@ -33,3 +33,8 @@ private class MyConsistencyConfiguration extends ConsistencyConfiguration { ) } } + +query predicate multipleToString(Node n, string s) { + s = strictconcat(n.toString(), ",") and + strictcount(n.toString()) > 1 +} diff --git a/ruby/ql/consistency-queries/qlpack.yml b/ruby/ql/consistency-queries/qlpack.yml index 707cada6cd2..9a1292eefa0 100644 --- a/ruby/ql/consistency-queries/qlpack.yml +++ b/ruby/ql/consistency-queries/qlpack.yml @@ -2,3 +2,4 @@ name: codeql/ruby-consistency-queries groups: [ruby, test, consistency-queries] dependencies: codeql/ruby-all: ${workspace} +warnOnImplicitThis: true diff --git a/ruby/ql/examples/qlpack.yml b/ruby/ql/examples/qlpack.yml index fc159c65692..5d2cd48bbb9 100644 --- a/ruby/ql/examples/qlpack.yml +++ b/ruby/ql/examples/qlpack.yml @@ -4,3 +4,4 @@ groups: - examples dependencies: codeql/ruby-all: ${workspace} +warnOnImplicitThis: true diff --git a/ruby/ql/integration-tests/all-platforms/qlpack.yml b/ruby/ql/integration-tests/all-platforms/qlpack.yml index a27def4e4d7..5a103c573d4 100644 --- a/ruby/ql/integration-tests/all-platforms/qlpack.yml +++ b/ruby/ql/integration-tests/all-platforms/qlpack.yml @@ -1,3 +1,4 @@ dependencies: codeql/ruby-all: '*' codeql/ruby-queries: '*' +warnOnImplicitThis: true diff --git a/ruby/ql/lib/CHANGELOG.md b/ruby/ql/lib/CHANGELOG.md index 65eba10cc10..5803375fd51 100644 --- a/ruby/ql/lib/CHANGELOG.md +++ b/ruby/ql/lib/CHANGELOG.md @@ -1,3 +1,15 @@ +## 0.6.3 + +### Minor Analysis Improvements + +* Deleted many deprecated predicates and classes with uppercase `URL`, `XSS`, etc. in their names. Use the PascalCased versions instead. +* Deleted the deprecated `getValueText` predicate from the `Expr`, `StringComponent`, and `ExprCfgNode` classes. Use `getConstantValue` instead. +* Deleted the deprecated `VariableReferencePattern` class, use `ReferencePattern` instead. +* Deleted all deprecated aliases in `StandardLibrary.qll`, use `codeql.ruby.frameworks.Core` and `codeql.ruby.frameworks.Stdlib` instead. +* Support for the `sequel` gem has been added. Method calls that execute queries against a database that may be vulnerable to injection attacks will now be recognized. +* Support for the `mysql2` gem has been added. Method calls that execute queries against an MySQL database that may be vulnerable to injection attacks will now be recognized. +* Support for the `pg` gem has been added. Method calls that execute queries against a PostgreSQL database that may be vulnerable to injection attacks will now be recognized. + ## 0.6.2 ### Minor Analysis Improvements diff --git a/ruby/ql/lib/change-notes/2023-05-06-mysql2.md b/ruby/ql/lib/change-notes/2023-05-06-mysql2.md deleted file mode 100644 index d8fa92dd394..00000000000 --- a/ruby/ql/lib/change-notes/2023-05-06-mysql2.md +++ /dev/null @@ -1,4 +0,0 @@ ---- -category: minorAnalysis ---- -* Support for the `mysql2` gem has been added. Method calls that execute queries against an MySQL database that may be vulnerable to injection attacks will now be recognized. \ No newline at end of file diff --git a/ruby/ql/lib/change-notes/2023-05-06-pg.md b/ruby/ql/lib/change-notes/2023-05-06-pg.md deleted file mode 100644 index 0e671ff9106..00000000000 --- a/ruby/ql/lib/change-notes/2023-05-06-pg.md +++ /dev/null @@ -1,4 +0,0 @@ ---- -category: minorAnalysis ---- -* Support for the `pg` gem has been added. Method calls that execute queries against a PostgreSQL database that may be vulnerable to injection attacks will now be recognized. \ No newline at end of file diff --git a/ruby/ql/lib/change-notes/2023-05-07-sequel.md b/ruby/ql/lib/change-notes/2023-05-07-sequel.md deleted file mode 100644 index 3688f28db56..00000000000 --- a/ruby/ql/lib/change-notes/2023-05-07-sequel.md +++ /dev/null @@ -1,4 +0,0 @@ ---- -category: minorAnalysis ---- -* Support for the `sequel` gem has been added. Method calls that execute queries against a database that may be vulnerable to injection attacks will now be recognized. diff --git a/ruby/ql/lib/change-notes/2023-06-02-delete-deps.md b/ruby/ql/lib/change-notes/2023-06-02-delete-deps.md deleted file mode 100644 index f4df20530dc..00000000000 --- a/ruby/ql/lib/change-notes/2023-06-02-delete-deps.md +++ /dev/null @@ -1,7 +0,0 @@ ---- -category: minorAnalysis ---- -* Deleted many deprecated predicates and classes with uppercase `URL`, `XSS`, etc. in their names. Use the PascalCased versions instead. -* Deleted the deprecated `getValueText` predicate from the `Expr`, `StringComponent`, and `ExprCfgNode` classes. Use `getConstantValue` instead. -* Deleted the deprecated `VariableReferencePattern` class, use `ReferencePattern` instead. -* Deleted all deprecated aliases in `StandardLibrary.qll`, use `codeql.ruby.frameworks.Core` and `codeql.ruby.frameworks.Stdlib` instead. \ No newline at end of file diff --git a/ruby/ql/lib/change-notes/2023-06-08-rack-redirect.md b/ruby/ql/lib/change-notes/2023-06-08-rack-redirect.md new file mode 100644 index 00000000000..09687fa95be --- /dev/null +++ b/ruby/ql/lib/change-notes/2023-06-08-rack-redirect.md @@ -0,0 +1,4 @@ +--- +category: minorAnalysis +--- +* HTTP redirect responses from Rack applications are now recognized as a potential sink for open redirect alerts. diff --git a/ruby/ql/lib/change-notes/2023-06-14-insecure-download-config.md b/ruby/ql/lib/change-notes/2023-06-14-insecure-download-config.md new file mode 100644 index 00000000000..6bf019cd051 --- /dev/null +++ b/ruby/ql/lib/change-notes/2023-06-14-insecure-download-config.md @@ -0,0 +1,4 @@ +--- +category: deprecated +--- +* The `Configuration` taint flow configuration class from `codeql.ruby.security.InsecureDownloadQuery` has been deprecated. Use the `Flow` module instead. diff --git a/ruby/ql/lib/change-notes/2023-06-28-tracking-on-demand.md b/ruby/ql/lib/change-notes/2023-06-28-tracking-on-demand.md new file mode 100644 index 00000000000..5aa79d5e2f3 --- /dev/null +++ b/ruby/ql/lib/change-notes/2023-06-28-tracking-on-demand.md @@ -0,0 +1,7 @@ +--- +category: majorAnalysis +--- +* The API graph library (`codeql.ruby.ApiGraphs`) has been significantly improved, with better support for inheritance, + and data-flow nodes can now be converted to API nodes by calling `.track()` or `.backtrack()` on the node. + API graphs allow for efficient modelling of how a given value is used by the code base, or how values produced by the code base + are consumed by a library. See the documentation for `API::Node` for details and examples. diff --git a/ruby/ql/lib/change-notes/released/0.6.3.md b/ruby/ql/lib/change-notes/released/0.6.3.md new file mode 100644 index 00000000000..35121021e9a --- /dev/null +++ b/ruby/ql/lib/change-notes/released/0.6.3.md @@ -0,0 +1,11 @@ +## 0.6.3 + +### Minor Analysis Improvements + +* Deleted many deprecated predicates and classes with uppercase `URL`, `XSS`, etc. in their names. Use the PascalCased versions instead. +* Deleted the deprecated `getValueText` predicate from the `Expr`, `StringComponent`, and `ExprCfgNode` classes. Use `getConstantValue` instead. +* Deleted the deprecated `VariableReferencePattern` class, use `ReferencePattern` instead. +* Deleted all deprecated aliases in `StandardLibrary.qll`, use `codeql.ruby.frameworks.Core` and `codeql.ruby.frameworks.Stdlib` instead. +* Support for the `sequel` gem has been added. Method calls that execute queries against a database that may be vulnerable to injection attacks will now be recognized. +* Support for the `mysql2` gem has been added. Method calls that execute queries against an MySQL database that may be vulnerable to injection attacks will now be recognized. +* Support for the `pg` gem has been added. Method calls that execute queries against a PostgreSQL database that may be vulnerable to injection attacks will now be recognized. diff --git a/ruby/ql/lib/codeql-pack.release.yml b/ruby/ql/lib/codeql-pack.release.yml index 5501a2a1cc5..b7dafe32c5d 100644 --- a/ruby/ql/lib/codeql-pack.release.yml +++ b/ruby/ql/lib/codeql-pack.release.yml @@ -1,2 +1,2 @@ --- -lastReleaseVersion: 0.6.2 +lastReleaseVersion: 0.6.3 diff --git a/ruby/ql/lib/codeql/Locations.qll b/ruby/ql/lib/codeql/Locations.qll index 3a16bdec40d..87198146d88 100644 --- a/ruby/ql/lib/codeql/Locations.qll +++ b/ruby/ql/lib/codeql/Locations.qll @@ -2,15 +2,6 @@ import files.FileSystem -bindingset[loc] -pragma[inline_late] -private string locationToString(Location loc) { - exists(string filepath, int startline, int startcolumn, int endline, int endcolumn | - loc.hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn) and - result = filepath + "@" + startline + ":" + startcolumn + ":" + endline + ":" + endcolumn - ) -} - /** * A location as given by a file, a start line, a start column, * an end line, and an end column. @@ -37,8 +28,14 @@ class Location extends @location_default { int getNumLines() { result = this.getEndLine() - this.getStartLine() + 1 } /** Gets a textual representation of this element. */ - pragma[inline] - string toString() { result = locationToString(this) } + bindingset[this] + pragma[inline_late] + string toString() { + exists(string filepath, int startline, int startcolumn, int endline, int endcolumn | + this.hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn) and + result = filepath + "@" + startline + ":" + startcolumn + ":" + endline + ":" + endcolumn + ) + } /** * Holds if this element is at the specified location. diff --git a/ruby/ql/lib/codeql/ruby/ApiGraphs.qll b/ruby/ql/lib/codeql/ruby/ApiGraphs.qll index 1cf4c445781..0d6e669b1de 100644 --- a/ruby/ql/lib/codeql/ruby/ApiGraphs.qll +++ b/ruby/ql/lib/codeql/ruby/ApiGraphs.qll @@ -1,13 +1,13 @@ /** - * Provides an implementation of _API graphs_, which are an abstract representation of the API - * surface used and/or defined by a code base. + * Provides an implementation of _API graphs_, which allow efficient modelling of how a given + * value is used by the code base or how values produced by the code base are consumed by a library. * - * The nodes of the API graph represent definitions and uses of API components. The edges are - * directed and labeled; they specify how the components represented by nodes relate to each other. + * See `API::Node` for more details. */ private import codeql.ruby.AST private import codeql.ruby.DataFlow +private import codeql.ruby.typetracking.ApiGraphShared private import codeql.ruby.typetracking.TypeTracker private import codeql.ruby.typetracking.TypeTrackerSpecific as TypeTrackerSpecific private import codeql.ruby.controlflow.CfgNodes @@ -19,85 +19,140 @@ private import codeql.ruby.dataflow.internal.DataFlowDispatch as DataFlowDispatc */ module API { /** - * A node in the API graph, representing a value that has crossed the boundary between this - * codebase and an external library (or in general, any external codebase). + * A node in the API graph, that is, a value that can be tracked interprocedurally. * - * ### Basic usage + * The API graph is a graph for tracking values of certain types in a way that accounts for inheritance + * and interprocedural data flow. * * API graphs are typically used to identify "API calls", that is, calls to an external function * whose implementation is not necessarily part of the current codebase. * + * ### Basic usage + * * The most basic use of API graphs is typically as follows: * 1. Start with `API::getTopLevelMember` for the relevant library. * 2. Follow up with a chain of accessors such as `getMethod` describing how to get to the relevant API function. - * 3. Map the resulting API graph nodes to data-flow nodes, using `asSource` or `asSink`. + * 3. Map the resulting API graph nodes to data-flow nodes, using `asSource`, `asSink`, or `asCall`. * - * For example, a simplified way to get arguments to `Foo.bar` would be - * ```ql - * API::getTopLevelMember("Foo").getMethod("bar").getParameter(0).asSink() + * The following examples demonstrate how to identify the expression `x` in various basic cases: + * ```rb + * # API::getTopLevelMember("Foo").getMethod("bar").getArgument(0).asSink() + * Foo.bar(x) + * + * # API::getTopLevelMember("Foo").getMethod("bar").getKeywordArgument("foo").asSink() + * Foo.bar(foo: x) + * + * # API::getTopLevelMember("Foo").getInstance().getMethod("bar").getArgument(0).asSink() + * Foo.new.bar(x) + * + * Foo.bar do |x| # API::getTopLevelMember("Foo").getMethod("bar").getBlock().getParameter(0).asSource() + * end * ``` * - * The most commonly used accessors are `getMember`, `getMethod`, `getParameter`, and `getReturn`. + * ### Data flow * - * ### API graph nodes + * The members predicates on this class generally take inheritance and data flow into account. * - * There are two kinds of nodes in the API graphs, distinguished by who is "holding" the value: - * - **Use-nodes** represent values held by the current codebase, which came from an external library. - * (The current codebase is "using" a value that came from the library). - * - **Def-nodes** represent values held by the external library, which came from this codebase. - * (The current codebase "defines" the value seen by the library). - * - * API graph nodes are associated with data-flow nodes in the current codebase. - * (Since external libraries are not part of the database, there is no way to associate with concrete - * data-flow nodes from the external library). - * - **Use-nodes** are associated with data-flow nodes where a value enters the current codebase, - * such as the return value of a call to an external function. - * - **Def-nodes** are associated with data-flow nodes where a value leaves the current codebase, - * such as an argument passed in a call to an external function. - * - * - * ### Access paths and edge labels - * - * Nodes in the API graph are associated with a set of access paths, describing a series of operations - * that may be performed to obtain that value. - * - * For example, the access path `API::getTopLevelMember("Foo").getMethod("bar")` represents the action of - * reading the top-level constant `Foo` and then accessing the method `bar` on the resulting object. - * It would be associated with a call such as `Foo.bar()`. - * - * Each edge in the graph is labelled by such an "operation". For an edge `A->B`, the type of the `A` node - * determines who is performing the operation, and the type of the `B` node determines who ends up holding - * the result: - * - An edge starting from a use-node describes what the current codebase is doing to a value that - * came from a library. - * - An edge starting from a def-node describes what the external library might do to a value that - * came from the current codebase. - * - An edge ending in a use-node means the result ends up in the current codebase (at its associated data-flow node). - * - An edge ending in a def-node means the result ends up in external code (its associated data-flow node is - * the place where it was "last seen" in the current codebase before flowing out) - * - * Because the implementation of the external library is not visible, it is not known exactly what operations - * it will perform on values that flow there. Instead, the edges starting from a def-node are operations that would - * lead to an observable effect within the current codebase; without knowing for certain if the library will actually perform - * those operations. (When constructing these edges, we assume the library is somewhat well-behaved). - * - * For example, given this snippet: + * The following example demonstrates a case where data flow was used to find the sink `x`: * ```ruby - * Foo.bar(->(x) { doSomething(x) }) + * def doSomething f + * f.bar(x) # API::getTopLevelMember("Foo").getInstance().getMethod("bar").getArgument(0).asSink() + * end + * doSomething Foo.new * ``` - * A callback is passed to the external function `Foo.bar`. We can't know if `Foo.bar` will actually invoke this callback. - * But _if_ the library should decide to invoke the callback, then a value will flow into the current codebase via the `x` parameter. - * For that reason, an edge is generated representing the argument-passing operation that might be performed by `Foo.bar`. - * This edge is going from the def-node associated with the callback to the use-node associated with the parameter `x` of the lambda. + * The call `API::getTopLevelMember("Foo").getInstance()` identifies the `Foo.new` call, and `getMethod("bar")` + * then follows data flow from there to find calls to `bar` where that object flows to the receiver. + * This results in the `f.bar` call. + * + * ### Backward data flow + * + * When inspecting the arguments of a call, the data flow direction is backwards. + * The following example illustrates this when we match the `x` parameter of a block: + * ```ruby + * def doSomething &blk + * Foo.bar &blk + * end + * doSomething do |x| # API::getTopLevelMember("Foo").getMethod("bar").getBlock().getParameter(0).asSource() + * end + * ``` + * When `getParameter(0)` is evaluated, the API graph backtracks the `&blk` argument to the block argument a few + * lines below. As a result, it eventually matches the `x` parameter of that block. + * + * ### Inheritance + * + * When a class or module object is tracked, inheritance is taken into account. + * + * In the following example, a call to `Foo.bar` was found via a subclass of `Foo`, + * because classes inherit singleton methods from their base class: + * ```ruby + * class Subclass < Foo + * def self.doSomething + * bar(x) # API::getTopLevelMember("Foo").getMethod("bar").getArgument(0).asSink() + * end + * end + * ``` + * + * Similarly, instance methods can be found in subclasses, or ancestors of subclases in cases of multiple inheritance: + * ```rb + * module Mixin + * def doSomething + * bar(x) # API::getTopLevelMember("Foo").getInstance().getMethod("bar").getArgument(0).asSink() + * end + * end + * class Subclass < Foo + * include Mixin + * end + * ``` + * The value of `self` in `Mixin#doSomething` is seen as a potential instance of `Foo`, and is thus found by `getTopLevelMember("Foo").getInstance()`. + * This eventually results in finding the call `bar`, due to its implicit `self` receiver, and finally its argument `x` is found as the sink. + * + * ### Backward data flow and classes + * + * When inspecting the arguments of a call, and the value flowing into that argument is a user-defined class (or an instance thereof), + * uses of `getMethod` will find method definitions in that class (including inherited ones) rather than finding method calls. + * + * This example illustrates how this can be used to model cases where the library calls a specific named method on a user-defined class: + * ```rb + * class MyClass + * def doSomething + * x # API::getTopLevelMember("Foo").getMethod("bar").getArgument(0).getMethod("doSomething").getReturn().asSink() + * end + * end + * Foo.bar MyClass.new + * ``` + * + * When modeling an external library that is known to call a specific method on a parameter (in this case `doSomething`), this makes + * it possible to find the corresponding method definition in user code. + * + * ### Strict left-to-right evaluation + * + * Most member predicates on this class are intended to be chained, and are always evaluated from left to right, which means + * the caller should restrict the initial set of values. + * + * For example, in the following snippet, we always find the uses of `Foo` before finding calls to `bar`: + * ```ql + * API::getTopLevelMember("Foo").getMethod("bar") + * ``` + * In particular, the implementation will never look for calls to `bar` and work backward from there. + * + * Beware of the footgun that is to use API graphs with an unrestricted receiver: + * ```ql + * API::Node barCall(API::Node base) { + * result = base.getMethod("bar") // Do not do this! + * } + * ``` + * The above predicate does not restrict the receiver, and will thus perform an interprocedural data flow + * search starting at every node in the graph, which is very expensive. */ class Node extends Impl::TApiNode { /** - * Gets a data-flow node where this value may flow after entering the current codebase. + * Gets a data-flow node where this value may flow interprocedurally. * * This is similar to `asSource()` but additionally includes nodes that are transitively reachable by data flow. * See `asSource()` for examples. */ - pragma[inline] + bindingset[this] + pragma[inline_late] DataFlow::Node getAValueReachableFromSource() { result = getAValueReachableFromSourceInline(this) } @@ -119,16 +174,14 @@ module API { * end * ``` */ - pragma[inline] - DataFlow::LocalSourceNode asSource() { - result = pragma[only_bind_out](this).(Node::Internal).asSourceInternal() - } + bindingset[this] + pragma[inline_late] + DataFlow::LocalSourceNode asSource() { result = asSourceInline(this) } /** - * Gets a data-flow node where this value leaves the current codebase and flows into an - * external library (or in general, any external codebase). + * Gets a data-flow node where this value potentially flows into an external library. * - * Concretely, this corresponds to an argument passed to a call to external code. + * This is usually the argument of a call, but can also be the return value of a callback. * * For example: * ```ruby @@ -143,15 +196,443 @@ module API { * }) * ``` */ - DataFlow::Node asSink() { Impl::def(this, result) } + bindingset[this] + pragma[inline_late] + DataFlow::Node asSink() { result = asSinkInline(this) } /** - * Get a data-flow node that transitively flows to an external library (or in general, any external codebase). + * Gets a callable that can reach this sink. + * + * For example: + * ```ruby + * Foo.bar do |x| # API::getTopLevelMember("Foo").getMethod("bar").getBlock().asCallable() + * end + * + * class Baz + * def m # API::getTopLevelMember("Foo").getMethod("bar").getArgument(0).getMethod("m").asCallable() + * end + * end + * Foo.bar(Baz.new) + * ``` + */ + bindingset[this] + pragma[inline_late] + DataFlow::CallableNode asCallable() { Impl::asCallable(this.getAnEpsilonSuccessor(), result) } + + /** + * Get a data-flow node that transitively flows to this value, provided that this value corresponds + * to a sink. * * This is similar to `asSink()` but additionally includes nodes that transitively reach a sink by data flow. * See `asSink()` for examples. */ - DataFlow::Node getAValueReachingSink() { result = Impl::trackDefNode(this.asSink()) } + bindingset[this] + pragma[inline_late] + DataFlow::Node getAValueReachingSink() { result = getAValueReachingSinkInline(this) } + + /** + * Gets a module or class referred to by this API node. + * + * For example: + * ```ruby + * module Foo + * class Bar # API::getTopLevelMember("Foo").getMember("Bar").asModule() + * end + * end + * ``` + */ + bindingset[this] + pragma[inline_late] + DataFlow::ModuleNode asModule() { this = Impl::MkModuleObjectDown(result) } + + /** + * Gets the call referred to by this API node. + * + * For example: + * ```ruby + * # API::getTopLevelMember("Foo").getMethod("bar").asCall() + * Foo.bar + * + * class Bar < Foo + * def doSomething + * # API::getTopLevelMember("Foo").getInstance().getMethod("baz").asCall() + * baz + * end + * end + * ``` + */ + bindingset[this] + pragma[inline_late] + DataFlow::CallNode asCall() { this = Impl::MkMethodAccessNode(result) } + + /** + * DEPRECATED. Use `asCall()` instead. + */ + pragma[inline] + deprecated DataFlow::CallNode getCallNode() { this = Impl::MkMethodAccessNode(result) } + + /** + * Gets a module or class that descends from the module or class referenced by this API node. + */ + bindingset[this] + pragma[inline_late] + DataFlow::ModuleNode getADescendentModule() { result = this.getAnEpsilonSuccessor().asModule() } + + /** + * Gets a call to a method on the receiver represented by this API node. + * + * This is a shorthand for `getMethod(method).asCall()`, and thus returns a data-flow node + * rather than an API node. + * + * For example: + * ```ruby + * # API::getTopLevelMember("Foo").getAMethodCall("bar") + * Foo.bar + * ``` + */ + pragma[inline] + DataFlow::CallNode getAMethodCall(string method) { + // This predicate is currently not 'inline_late' because 'method' can be an input or output + result = this.getMethod(method).asCall() + } + + /** + * Gets an access to the constant `m` with this value as the base of the access. + * + * For example: + * ```ruby + * A::B # API::getATopLevelMember("A").getMember("B") + * + * module A + * class B # API::getATopLevelMember("A").getMember("B") + * end + * end + * ``` + */ + pragma[inline] + Node getMember(string m) { + // This predicate is currently not 'inline_late' because 'm' can be an input or output + Impl::memberEdge(this.getAnEpsilonSuccessor(), m, result) + } + + /** + * Gets an access to a constant with this value as the base of the access. + * + * This is equivalent to `getMember(_)` but can be more efficient. + */ + bindingset[this] + pragma[inline_late] + Node getAMember() { Impl::anyMemberEdge(this.getAnEpsilonSuccessor(), result) } + + /** + * Gets a node that may refer to an instance of the module or class represented by this API node. + * + * This includes the following: + * - Calls to `new` on this module or class or a descendent thereof + * - References to `self` in instance methods declared in any ancestor of any descendent of this module or class + * + * For example: + * ```ruby + * A.new # API::getTopLevelMember("A").getInstance() + * + * class B < A + * def m + * self # API::getTopLevelMember("A").getInstance() + * end + * end + * + * B.new # API::getTopLevelMember("A").getInstance() + * + * class C < A + * include Mixin + * end + * module Mixin + * def m + * # Although 'Mixin' is not directly related to 'A', 'self' may refer to an instance of 'A' + * # due to its inclusion in a subclass of 'A'. + * self # API::getTopLevelMember("A").getInstance() + * end + * end + * ``` + */ + bindingset[this] + pragma[inline_late] + Node getInstance() { Impl::instanceEdge(this.getAnEpsilonSuccessor(), result) } + + /** + * Gets a call to `method` with this value as the receiver, or the definition of `method` on + * an object that can reach this sink. + * + * If the receiver represents a module or class object, this includes calls on descendents of that module or class. + * + * For example: + * ```ruby + * # API::getTopLevelMember("Foo").getMethod("bar") + * Foo.bar + * + * # API::getTopLevelMember("Foo").getInstance().getMethod("bar") + * Foo.new.bar + * + * class B < Foo + * end + * B.bar # API::getTopLevelMember("Foo").getMethod("bar") + * + * class C + * def m # API::getTopLevelMember("Foo").getMethod("bar").getArgument(0).getMethod("m") + * end + * end + * Foo.bar(C.new) + * ``` + */ + pragma[inline] + Node getMethod(string method) { + // TODO: Consider 'getMethodTarget(method)' for looking up method definitions? + // This predicate is currently not 'inline_late' because 'method' can be an input or output + Impl::methodEdge(this.getAnEpsilonSuccessor(), method, result) + } + + /** + * Gets the result of this call, or the return value of this callable. + */ + bindingset[this] + pragma[inline_late] + Node getReturn() { Impl::returnEdge(this.getAnEpsilonSuccessor(), result) } + + /** + * Gets the result of a call to `method` with this value as the receiver, or the return value of `method` defined on + * an object that can reach this sink. + * + * This is a shorthand for `getMethod(method).getReturn()`. + */ + pragma[inline] + Node getReturn(string method) { + // This predicate is currently not 'inline_late' because 'method' can be an input or output + result = this.getMethod(method).getReturn() + } + + /** + * Gets the `n`th positional argument to this call. + * + * For example, this would get `x` in the following snippet: + * ```ruby + * Foo.bar(x) # API::getTopLevelMember("Foo").getMethod("bar").getArgument(0) + * ``` + */ + pragma[inline] + Node getArgument(int n) { + // This predicate is currently not 'inline_late' because 'n' can be an input or output + Impl::positionalArgumentEdge(this, n, result) + } + + /** + * Gets the given keyword argument to this call. + * + * For example, this would get `x` in the following snippet: + * ```ruby + * Foo.bar(baz: x) # API::getTopLevelMember("Foo").getMethod("bar").getKeywordArgument("baz") + * ``` + */ + pragma[inline] + Node getKeywordArgument(string name) { + // This predicate is currently not 'inline_late' because 'name' can be an input or output + Impl::keywordArgumentEdge(this, name, result) + } + + /** + * Gets the block parameter of a callable that can reach this sink. + * + * For example, this would get the `&blk` in the following snippet: + * ```ruby + * # API::getTopLevelMember("Foo").getMethod("bar").getArgument(0).getBlockParameter() + * Foo.bar(->(&blk) {}) + * end + * ``` + */ + bindingset[this] + pragma[inline_late] + Node getBlockParameter() { Impl::blockParameterEdge(this.getAnEpsilonSuccessor(), result) } + + /** + * Gets the `n`th positional parameter of this callable, or the `n`th positional argument to this call. + * + * Note: for historical reasons, this predicate may refer to an argument of a call, but this may change in the future. + * When referring to an argument, it is recommended to use `getArgument(n)` instead. + */ + pragma[inline] + Node getParameter(int n) { + // This predicate is currently not 'inline_late' because 'n' can be an input or output + Impl::positionalParameterOrArgumentEdge(this.getAnEpsilonSuccessor(), n, result) + } + + /** + * Gets the given keyword parameter of this callable, or keyword argument to this call. + * + * Note: for historical reasons, this predicate may refer to an argument of a call, but this may change in the future. + * When referring to an argument, it is recommended to use `getKeywordArgument(n)` instead. + */ + pragma[inline] + Node getKeywordParameter(string name) { + // This predicate is currently not 'inline_late' because 'name' can be an input or output + Impl::keywordParameterOrArgumentEdge(this.getAnEpsilonSuccessor(), name, result) + } + + /** + * Gets the block argument to this call, or the block parameter of this callable. + * + * Note: this predicate may refer to either an argument or a parameter. When referring to a block parameter, + * it is recommended to use `getBlockParameter()` instead. + * + * For example: + * ```ruby + * Foo.bar do |x| # API::getTopLevelMember("Foo").getMethod("bar").getBlock().getParameter(0) + * end + * ``` + */ + bindingset[this] + pragma[inline_late] + Node getBlock() { Impl::blockParameterOrArgumentEdge(this.getAnEpsilonSuccessor(), result) } + + /** + * Gets the argument passed in argument position `pos` at this call. + */ + pragma[inline] + Node getArgumentAtPosition(DataFlowDispatch::ArgumentPosition pos) { + // This predicate is currently not 'inline_late' because 'pos' can be an input or output + Impl::argumentEdge(pragma[only_bind_out](this), pos, result) // note: no need for epsilon step since 'this' must be a call + } + + /** + * Gets the parameter at position `pos` of this callable. + */ + pragma[inline] + Node getParameterAtPosition(DataFlowDispatch::ParameterPosition pos) { + // This predicate is currently not 'inline_late' because 'pos' can be an input or output + Impl::parameterEdge(this.getAnEpsilonSuccessor(), pos, result) + } + + /** + * Gets a `new` call with this value as the receiver. + */ + bindingset[this] + pragma[inline_late] + DataFlow::ExprNode getAnInstantiation() { result = this.getReturn("new").asSource() } + + /** + * Gets a representative for the `content` of this value. + * + * When possible, it is preferrable to use one of the specialized variants of this predicate, such as `getAnElement`. + * + * Concretely, this gets sources where `content` is read from this value, and as well as sinks where + * `content` is stored onto this value or onto an object that can reach this sink. + */ + pragma[inline] + Node getContent(DataFlow::Content content) { + // This predicate is currently not 'inline_late' because 'content' can be an input or output + Impl::contentEdge(this.getAnEpsilonSuccessor(), content, result) + } + + /** + * Gets a representative for the `contents` of this value. + * + * See `getContent()` for more details. + */ + bindingset[this, contents] + pragma[inline_late] + Node getContents(DataFlow::ContentSet contents) { + // We always use getAStoreContent when generating content edges, and we always use getAReadContent when querying the graph. + result = this.getContent(contents.getAReadContent()) + } + + /** + * Gets a representative for the instance field of the given `name`, which must include the `@` character. + * + * This can be used to find cases where a class accesses the fields used by a base class. + * + * ```ruby + * class A < B + * def m + * @foo # API::getTopLevelMember("B").getInstance().getField("@foo") + * end + * end + * ``` + */ + pragma[inline] + Node getField(string name) { + // This predicate is currently not 'inline_late' because 'name' can be an input or output + Impl::fieldEdge(this.getAnEpsilonSuccessor(), name, result) + } + + /** + * Gets a representative for an arbitrary element of this collection. + * + * For example: + * ```ruby + * Foo.bar.each do |x| # API::getTopLevelMember("Foo").getMethod("bar").getReturn().getAnElement() + * end + * + * Foo.bar[0] # API::getTopLevelMember("Foo").getMethod("bar").getReturn().getAnElement() + * ``` + */ + bindingset[this] + pragma[inline_late] + Node getAnElement() { Impl::elementEdge(this.getAnEpsilonSuccessor(), result) } + + /** + * Gets the data-flow node that gives rise to this node, if any. + */ + DataFlow::Node getInducingNode() { + this = Impl::MkMethodAccessNode(result) or + this = Impl::MkBackwardNode(result, _) or + this = Impl::MkForwardNode(result, _) or + this = Impl::MkSinkNode(result) + } + + /** Gets the location of this node. */ + Location getLocation() { + result = this.getInducingNode().getLocation() + or + exists(DataFlow::ModuleNode mod | + this = Impl::MkModuleObjectDown(mod) + or + this = Impl::MkModuleInstanceUp(mod) + | + result = mod.getLocation() + ) + or + this instanceof RootNode and + result instanceof EmptyLocation + } + + /** + * Gets a textual representation of this element. + */ + string toString() { none() } + + /** + * Gets a node representing a (direct or indirect) subclass of the class represented by this node. + * ```rb + * class A; end + * class B < A; end + * class C < B; end + * ``` + * In the example above, `getMember("A").getASubclass()` will return uses of `A`, `B` and `C`. + */ + pragma[inline] + deprecated Node getASubclass() { result = this } + + /** + * Gets a node representing a direct subclass of the class represented by this node. + * ```rb + * class A; end + * class B < A; end + * class C < B; end + * ``` + * In the example above, `getMember("A").getAnImmediateSubclass()` will return uses of `B` only. + */ + pragma[inline] + deprecated Node getAnImmediateSubclass() { + result = this.asModule().getAnImmediateDescendent().trackModule() + } /** DEPRECATED. This predicate has been renamed to `getAValueReachableFromSource()`. */ deprecated DataFlow::Node getAUse() { result = this.getAValueReachableFromSource() } @@ -166,326 +647,143 @@ module API { deprecated DataFlow::Node getAValueReachingRhs() { result = this.getAValueReachingSink() } /** - * Gets a call to a method on the receiver represented by this API component. - */ - pragma[inline] - DataFlow::CallNode getAMethodCall(string method) { result = this.getReturn(method).asSource() } - - /** - * Gets a node representing member `m` of this API component. + * DEPRECATED. API graph nodes are no longer associated with specific paths. * - * For example, a member can be: - * - * - A submodule of a module - * - An attribute of an object - */ - pragma[inline] - Node getMember(string m) { - result = pragma[only_bind_out](this).(Node::Internal).getMemberInternal(m) - } - - /** - * Gets a node representing a member of this API component where the name of the member may - * or may not be known statically. - */ - cached - Node getAMember() { - Impl::forceCachingInSameStage() and - result = this.getASuccessor(Label::member(_)) - } - - /** - * Gets a node representing an instance of this API component, that is, an object whose - * constructor is the function represented by this node. - * - * For example, if this node represents a use of some class `A`, then there might be a node - * representing instances of `A`, typically corresponding to expressions `A.new` at the - * source level. - * - * This predicate may have multiple results when there are multiple constructor calls invoking this API component. - * Consider using `getAnInstantiation()` if there is a need to distinguish between individual constructor calls. - */ - pragma[inline] - Node getInstance() { result = this.getASubclass().getReturn("new") } - - /** - * Gets a node representing a call to `method` on the receiver represented by this node. - */ - pragma[inline] - MethodAccessNode getMethod(string method) { - result = pragma[only_bind_out](this).(Node::Internal).getMethodInternal(method) - } - - /** - * Gets a node representing the result of this call. - */ - pragma[inline] - Node getReturn() { result = pragma[only_bind_out](this).(Node::Internal).getReturnInternal() } - - /** - * Gets a node representing the result of calling a method on the receiver represented by this node. - */ - pragma[inline] - Node getReturn(string method) { result = this.getMethod(method).getReturn() } - - /** Gets an API node representing the `n`th positional parameter. */ - cached - Node getParameter(int n) { - Impl::forceCachingInSameStage() and - result = this.getASuccessor(Label::parameter(n)) - } - - /** Gets an API node representing the given keyword parameter. */ - cached - Node getKeywordParameter(string name) { - Impl::forceCachingInSameStage() and - result = this.getASuccessor(Label::keywordParameter(name)) - } - - /** Gets an API node representing the block parameter. */ - cached - Node getBlock() { - Impl::forceCachingInSameStage() and - result = this.getASuccessor(Label::blockParameter()) - } - - /** - * Gets a `new` call to the function represented by this API component. - */ - pragma[inline] - DataFlow::ExprNode getAnInstantiation() { result = this.getInstance().asSource() } - - /** - * Gets a node representing a (direct or indirect) subclass of the class represented by this node. - * ```rb - * class A; end - * class B < A; end - * class C < B; end - * ``` - * In the example above, `getMember("A").getASubclass()` will return uses of `A`, `B` and `C`. - */ - Node getASubclass() { result = this.getAnImmediateSubclass*() } - - /** - * Gets a node representing a direct subclass of the class represented by this node. - * ```rb - * class A; end - * class B < A; end - * class C < B; end - * ``` - * In the example above, `getMember("A").getAnImmediateSubclass()` will return uses of `B` only. - */ - cached - Node getAnImmediateSubclass() { - Impl::forceCachingInSameStage() and result = this.getASuccessor(Label::subclass()) - } - - /** - * Gets a node representing the `content` stored on the base object. - */ - cached - Node getContent(DataFlow::Content content) { - Impl::forceCachingInSameStage() and - result = this.getASuccessor(Label::content(content)) - } - - /** - * Gets a node representing the `contents` stored on the base object. - */ - pragma[inline] - Node getContents(DataFlow::ContentSet contents) { - // We always use getAStoreContent when generating the graph, and we always use getAReadContent when querying the graph. - result = this.getContent(contents.getAReadContent()) - } - - /** Gets a node representing the instance field of the given `name`, which must include the `@` character. */ - cached - Node getField(string name) { - Impl::forceCachingInSameStage() and - result = this.getContent(DataFlowPrivate::TFieldContent(name)) - } - - /** Gets a node representing an element of this collection (known or unknown). */ - cached - Node getAnElement() { - Impl::forceCachingInSameStage() and - result = this.getContents(any(DataFlow::ContentSet set | set.isAnyElement())) - } - - /** * Gets a string representation of the lexicographically least among all shortest access paths * from the root to this node. */ - string getPath() { - result = min(string p | p = this.getAPath(Impl::distanceFromRoot(this)) | p) - } + deprecated string getPath() { none() } /** + * DEPRECATED. Use label-specific predicates in this class, such as `getMember`, instead of using `getASuccessor`. + * * Gets a node such that there is an edge in the API graph between this node and the other * one, and that edge is labeled with `lbl`. */ - Node getASuccessor(Label::ApiLabel lbl) { Impl::edge(this, lbl, result) } + pragma[inline] + deprecated Node getASuccessor(Label::ApiLabel lbl) { + labelledEdge(this.getAnEpsilonSuccessor(), lbl, result) + } /** + * DEPRECATED. API graphs no longer support backward traversal of edges. If possible use `.backtrack()` to get + * a node intended for backtracking. + * * Gets a node such that there is an edge in the API graph between that other node and * this one, and that edge is labeled with `lbl` */ - Node getAPredecessor(Label::ApiLabel lbl) { this = result.getASuccessor(lbl) } + deprecated Node getAPredecessor(Label::ApiLabel lbl) { this = result.getASuccessor(lbl) } /** + * DEPRECATED. API graphs no longer support backward traversal of edges. If possible use `.backtrack()` to get + * a node intended for backtracking. + * * Gets a node such that there is an edge in the API graph between this node and the other * one. */ - Node getAPredecessor() { result = this.getAPredecessor(_) } + deprecated Node getAPredecessor() { result = this.getAPredecessor(_) } /** * Gets a node such that there is an edge in the API graph between that other node and * this one. */ - Node getASuccessor() { result = this.getASuccessor(_) } + pragma[inline] + deprecated Node getASuccessor() { result = this.getASuccessor(_) } - /** - * Gets the data-flow node that gives rise to this node, if any. - */ - DataFlow::Node getInducingNode() { - this = Impl::MkUse(result) - or - this = Impl::MkDef(result) - or - this = Impl::MkMethodAccessNode(result) - } + /** DEPRECATED. API graphs are no longer associated with a depth. */ + deprecated int getDepth() { none() } - /** Gets the location of this node. */ - Location getLocation() { - result = this.getInducingNode().getLocation() - or - exists(DataFlow::ModuleNode mod | - this = Impl::MkModuleObject(mod) and - result = mod.getLocation() - ) - or - // For nodes that do not have a meaningful location, `path` is the empty string and all other - // parameters are zero. - not exists(this.getInducingNode()) and - result instanceof EmptyLocation - } - - /** - * Gets a textual representation of this element. - */ - string toString() { none() } - - /** - * Gets a path of the given `length` from the root to this node. - */ - private string getAPath(int length) { - this instanceof Impl::MkRoot and - length = 0 and - result = "" - or - exists(Node pred, Label::ApiLabel lbl, string predpath | - Impl::edge(pred, lbl, this) and - predpath = pred.getAPath(length - 1) and - exists(string dot | if length = 1 then dot = "" else dot = "." | - result = predpath + dot + lbl and - // avoid producing strings longer than 1MB - result.length() < 1000 * 1000 - ) - ) and - length in [1 .. Impl::distanceFromRoot(this)] - } - - /** Gets the shortest distance from the root to this node in the API graph. */ - int getDepth() { result = Impl::distanceFromRoot(this) } + pragma[inline] + private Node getAnEpsilonSuccessor() { result = getAnEpsilonSuccessorInline(this) } } - /** Companion module to the `Node` class. */ - module Node { - /** - * INTERNAL USE ONLY. - * - * An API node, with some internal predicates exposed. - */ - class Internal extends Node { - /** - * INTERNAL USE ONLY. - * - * Same as `asSource()` but without join-order hints. - */ - cached - DataFlow::LocalSourceNode asSourceInternal() { - Impl::forceCachingInSameStage() and - Impl::use(this, result) - } + /** DEPRECATED. Use `API::root()` to access the root node. */ + deprecated class Root = RootNode; - /** - * Same as `getMember` but without join-order hints. - */ - cached - Node getMemberInternal(string m) { - Impl::forceCachingInSameStage() and - result = this.getASuccessor(Label::member(m)) - } + /** DEPRECATED. A node corresponding to the use of an API component. */ + deprecated class Use = ForwardNode; - /** - * Same as `getMethod` but without join-order hints. - */ - cached - MethodAccessNode getMethodInternal(string method) { - Impl::forceCachingInSameStage() and - result = this.getASubclass().getASuccessor(Label::method(method)) - } - - /** - * INTERNAL USE ONLY. - * - * Same as `getReturn()` but without join-order hints. - */ - cached - Node getReturnInternal() { - Impl::forceCachingInSameStage() and result = this.getASuccessor(Label::return()) - } - } - } - - bindingset[node] - pragma[inline_late] - private DataFlow::Node getAValueReachableFromSourceInline(Node node) { - exists(DataFlow::LocalSourceNode src, DataFlow::LocalSourceNode dst | - Impl::use(node, pragma[only_bind_into](src)) and - pragma[only_bind_into](dst) = Impl::trackUseNode(src) and - dst.flowsTo(result) - ) - } + /** DEPRECATED. A node corresponding to a value escaping into an API component. */ + deprecated class Def = SinkNode; /** The root node of an API graph. */ - class Root extends Node, Impl::MkRoot { - override string toString() { result = "root" } + private class RootNode extends Node, Impl::MkRoot { + override string toString() { result = "Root()" } } - private string tryGetPath(Node node) { - result = node.getPath() - or - not exists(node.getPath()) and - result = "with no path" + /** A node representing a given type-tracking state when tracking forwards. */ + private class ForwardNode extends Node, Impl::MkForwardNode { + private DataFlow::LocalSourceNode node; + private TypeTracker tracker; + + ForwardNode() { this = Impl::MkForwardNode(node, tracker) } + + override string toString() { + if tracker.start() + then result = "ForwardNode(" + node + ")" + else result = "ForwardNode(" + node + ", " + tracker + ")" + } } - /** A node corresponding to the use of an API component. */ - class Use extends Node, Impl::MkUse { - override string toString() { result = "Use " + tryGetPath(this) } + /** A node representing a given type-tracking state when tracking backwards. */ + private class BackwardNode extends Node, Impl::MkBackwardNode { + private DataFlow::LocalSourceNode node; + private TypeTracker tracker; + + BackwardNode() { this = Impl::MkBackwardNode(node, tracker) } + + override string toString() { + if tracker.start() + then result = "BackwardNode(" + node + ")" + else result = "BackwardNode(" + node + ", " + tracker + ")" + } } - /** A node corresponding to a value escaping into an API component. */ - class Def extends Node, Impl::MkDef { - override string toString() { result = "Def " + tryGetPath(this) } + /** A node representing a module/class object with epsilon edges to its descendents. */ + private class ModuleObjectDownNode extends Node, Impl::MkModuleObjectDown { + /** Gets the module represented by this API node. */ + DataFlow::ModuleNode getModule() { this = Impl::MkModuleObjectDown(result) } + + override string toString() { result = "ModuleObjectDown(" + this.getModule() + ")" } + } + + /** A node representing a module/class object with epsilon edges to its ancestors. */ + private class ModuleObjectUpNode extends Node, Impl::MkModuleObjectUp { + /** Gets the module represented by this API node. */ + DataFlow::ModuleNode getModule() { this = Impl::MkModuleObjectUp(result) } + + override string toString() { result = "ModuleObjectUp(" + this.getModule() + ")" } + } + + /** A node representing instances of a module/class with epsilon edges to its ancestors. */ + private class ModuleInstanceUpNode extends Node, Impl::MkModuleInstanceUp { + /** Gets the module whose instances are represented by this API node. */ + DataFlow::ModuleNode getModule() { this = Impl::MkModuleInstanceUp(result) } + + override string toString() { result = "ModuleInstanceUp(" + this.getModule() + ")" } + } + + /** A node representing instances of a module/class with epsilon edges to its descendents. */ + private class ModuleInstanceDownNode extends Node, Impl::MkModuleInstanceDown { + /** Gets the module whose instances are represented by this API node. */ + DataFlow::ModuleNode getModule() { this = Impl::MkModuleInstanceDown(result) } + + override string toString() { result = "ModuleInstanceDown(" + this.getModule() + ")" } } /** A node corresponding to the method being invoked at a method call. */ class MethodAccessNode extends Node, Impl::MkMethodAccessNode { - override string toString() { result = "MethodAccessNode " + tryGetPath(this) } + override string toString() { result = "MethodAccessNode(" + this.asCall() + ")" } + } - /** Gets the call node corresponding to this method access. */ - DataFlow::CallNode getCallNode() { this = Impl::MkMethodAccessNode(result) } + /** + * A node corresponding to an argument, right-hand side of a store, or return value from a callable. + * + * Such a node may serve as the starting-point of backtracking, and has epsilon edges going to + * the backward nodes corresponding to `getALocalSource`. + */ + private class SinkNode extends Node, Impl::MkSinkNode { + override string toString() { result = "SinkNode(" + this.getInducingNode() + ")" } } /** @@ -499,6 +797,8 @@ module API { * additional entry points may be added by extending this class. */ abstract class EntryPoint extends string { + // Note: this class can be deprecated in Ruby, but is still referenced by shared code in ApiGraphModels.qll, + // where it can't be removed since other languages are still dependent on the EntryPoint class. bindingset[this] EntryPoint() { any() } @@ -518,7 +818,7 @@ module API { DataFlow::CallNode getACall() { none() } /** Gets an API-node for this entry point. */ - API::Node getANode() { result = root().getASuccessor(Label::entryPoint(this)) } + API::Node getANode() { Impl::entryPointEdge(this, result) } } // Ensure all entry points are imported from ApiGraphs.qll @@ -527,88 +827,301 @@ module API { } /** Gets the root node. */ - Root root() { any() } + Node root() { result instanceof RootNode } /** - * Gets a node corresponding to a top-level member `m` (typically a module). + * Gets an access to the top-level constant `name`. * - * This is equivalent to `root().getAMember("m")`. - * - * Note: You should only use this predicate for top level modules or classes. If you want nodes corresponding to a nested module or class, - * you should use `.getMember` on the parent module/class. For example, for nodes corresponding to the class `Gem::Version`, + * To access nested constants, use `getMember()` on the resulting node. For example, for nodes corresponding to the class `Gem::Version`, * use `getTopLevelMember("Gem").getMember("Version")`. */ - cached - Node getTopLevelMember(string m) { - Impl::forceCachingInSameStage() and result = root().(Node::Internal).getMemberInternal(m) - } + pragma[inline] + Node getTopLevelMember(string name) { Impl::topLevelMember(name, result) } /** - * Provides the actual implementation of API graphs, cached for performance. - * - * Ideally, we'd like nodes to correspond to (global) access paths, with edge labels - * corresponding to extending the access path by one element. We also want to be able to map - * nodes to their definitions and uses in the data-flow graph, and this should happen modulo - * (inter-procedural) data flow. - * - * This, however, is not easy to implement, since access paths can have unbounded length - * and we need some way of recognizing cycles to avoid non-termination. Unfortunately, expressing - * a condition like "this node hasn't been involved in constructing any predecessor of - * this node in the API graph" without negative recursion is tricky. - * - * So instead most nodes are directly associated with a data-flow node, representing - * either a use or a definition of an API component. This ensures that we only have a finite - * number of nodes. However, we can now have multiple nodes with the same access - * path, which are essentially indistinguishable for a client of the API. - * - * On the other hand, a single node can have multiple access paths (which is, of - * course, unavoidable). We pick as canonical the alphabetically least access path with - * shortest length. + * Gets an unqualified call at the top-level with the given method name. */ - cached - private module Impl { - cached - predicate forceCachingInSameStage() { any() } + pragma[inline] + MethodAccessNode getTopLevelCall(string name) { Impl::toplevelCall(name, result) } - cached - predicate forceCachingBackref() { - 1 = 1 + pragma[nomagic] + private predicate isReachable(DataFlow::LocalSourceNode node, TypeTracker t) { + t.start() and exists(node) + or + exists(DataFlow::LocalSourceNode prev, TypeTracker t2 | + isReachable(prev, t2) and + node = prev.track(t2, t) and + notSelfParameter(node) + ) + } + + bindingset[node] + pragma[inline_late] + private predicate notSelfParameter(DataFlow::Node node) { + not node instanceof DataFlow::SelfParameterNode + } + + private module SharedArg implements ApiGraphSharedSig { + class ApiNode = Node; + + ApiNode getForwardNode(DataFlow::LocalSourceNode node, TypeTracker t) { + result = Impl::MkForwardNode(node, t) + } + + ApiNode getBackwardNode(DataFlow::LocalSourceNode node, TypeTracker t) { + result = Impl::MkBackwardNode(node, t) + } + + ApiNode getSinkNode(DataFlow::Node node) { result = Impl::MkSinkNode(node) } + + pragma[nomagic] + predicate specificEpsilonEdge(ApiNode pred, ApiNode succ) { + exists(DataFlow::ModuleNode mod | + moduleReferenceEdge(mod, pred, succ) + or + moduleInheritanceEdge(mod, pred, succ) + or + pred = getForwardEndNode(getSuperClassNode(mod)) and + succ = Impl::MkModuleObjectDown(mod) + ) or - exists(getTopLevelMember(_)) + implicitCallEdge(pred, succ) or - exists( - any(Node n) - .(Node::Internal) - .getMemberInternal("foo") - .getAMember() - .(Node::Internal) - .getMethodInternal("foo") - .(Node::Internal) - .getReturnInternal() - .getParameter(0) - .getKeywordParameter("foo") - .getBlock() - .getAnImmediateSubclass() - .getContent(_) - .getField(_) - .getAnElement() - .(Node::Internal) - .asSourceInternal() + exists(DataFlow::HashLiteralNode splat | hashSplatEdge(splat, pred, succ)) + } + + /** + * Holds if the epsilon edge `pred -> succ` should be generated, to handle inheritance relations of `mod`. + */ + pragma[inline] + private predicate moduleInheritanceEdge(DataFlow::ModuleNode mod, ApiNode pred, ApiNode succ) { + pred = Impl::MkModuleObjectDown(mod) and + succ = Impl::MkModuleObjectDown(mod.getAnImmediateDescendent()) + or + pred = Impl::MkModuleInstanceDown(mod) and + succ = Impl::MkModuleInstanceDown(mod.getAnImmediateDescendent()) + or + exists(DataFlow::ModuleNode ancestor | + ancestor = mod.getAnImmediateAncestor() and + // Restrict flow back to Object to avoid spurious flow for methods that happen + // to exist on Object, such as top-level methods. + not ancestor.getQualifiedName() = "Object" + | + pred = Impl::MkModuleInstanceUp(mod) and + succ = Impl::MkModuleInstanceUp(ancestor) + or + pred = Impl::MkModuleObjectUp(mod) and + succ = Impl::MkModuleObjectUp(ancestor) + ) + or + // Due to multiple inheritance, allow upwards traversal after downward traversal, + // so we can detect calls sideways in the hierarchy. + // Note that a similar case does not exist for ModuleObject since singleton methods are only inherited + // from the superclass, and there can only be one superclass. + pred = Impl::MkModuleInstanceDown(mod) and + succ = Impl::MkModuleInstanceUp(mod) + } + + /** + * Holds if the epsilon `pred -> succ` should be generated, to associate `mod` with its references in the codebase. + */ + bindingset[mod] + pragma[inline_late] + private predicate moduleReferenceEdge(DataFlow::ModuleNode mod, ApiNode pred, ApiNode succ) { + pred = Impl::MkModuleObjectDown(mod) and + succ = getForwardStartNode(getAModuleReference(mod)) + or + pred = getBackwardEndNode(getAModuleReference(mod)) and + ( + succ = Impl::MkModuleObjectUp(mod) + or + succ = Impl::MkModuleObjectDown(mod) + ) + or + pred = Impl::MkModuleInstanceUp(mod) and + succ = getAModuleInstanceUseNode(mod) + or + pred = getAModuleInstanceDefNode(mod) and + succ = Impl::MkModuleInstanceUp(mod) + or + pred = getAModuleDescendentInstanceDefNode(mod) and + succ = Impl::MkModuleInstanceDown(mod) + } + + /** + * Holds if the epsilon step `pred -> succ` should be generated to account for the fact that `getMethod("call")` + * may be omitted when dealing with blocks, lambda, or procs. + * + * For example, a block may be invoked by a `yield`, or can be converted to a proc and then invoked via `.call`. + * To simplify this, the implicit proc conversion is seen as a no-op and the `.call` is omitted. + */ + pragma[nomagic] + private predicate implicitCallEdge(ApiNode pred, ApiNode succ) { + // Step from &block parameter to yield call without needing `getMethod("call")`. + exists(DataFlow::MethodNode method | + pred = getForwardEndNode(method.getBlockParameter()) and + succ = Impl::MkMethodAccessNode(method.getABlockCall()) + ) + or + // Step from x -> x.call (the call itself, not its return value), without needing `getMethod("call")`. + exists(DataFlow::CallNode call | + call.getMethodName() = "call" and + pred = getForwardEndNode(getALocalSourceStrict(call.getReceiver())) and + succ = Impl::MkMethodAccessNode(call) + ) + or + exists(DataFlow::ModuleNode mod | + // Step from module/class to its own `call` method without needing `getMethod("call")`. + (pred = Impl::MkModuleObjectDown(mod) or pred = Impl::MkModuleObjectUp(mod)) and + succ = getBackwardEndNode(mod.getOwnSingletonMethod("call")) + or + pred = Impl::MkModuleInstanceUp(mod) and + succ = getBackwardEndNode(mod.getOwnInstanceMethod("call")) ) } + pragma[nomagic] + private DataFlow::Node getHashSplatArgument(DataFlow::HashLiteralNode literal) { + result = DataFlowPrivate::TSynthHashSplatArgumentNode(literal.asExpr()) + } + + /** + * Holds if the epsilon edge `pred -> succ` should be generated to account for the members of a hash literal. + * + * This currently exists because hash literals are desugared to `Hash.[]` calls, whose summary relies on `WithContent`. + * However, `contentEdge` does not currently generate edges for `WithContent` steps. + */ + bindingset[literal] + pragma[inline_late] + private predicate hashSplatEdge(DataFlow::HashLiteralNode literal, ApiNode pred, ApiNode succ) { + exists(TypeTracker t | + pred = Impl::MkForwardNode(getALocalSourceStrict(getHashSplatArgument(literal)), t) and + succ = Impl::MkForwardNode(pragma[only_bind_out](literal), pragma[only_bind_out](t)) + or + succ = Impl::MkBackwardNode(getALocalSourceStrict(getHashSplatArgument(literal)), t) and + pred = Impl::MkBackwardNode(pragma[only_bind_out](literal), pragma[only_bind_out](t)) + ) + } + + pragma[nomagic] + private DataFlow::LocalSourceNode getAModuleReference(DataFlow::ModuleNode mod) { + result = mod.getAnImmediateReference() + or + mod.getAnAncestor().getAnOwnInstanceSelf() = getANodeReachingClassCall(result) + } + + /** + * Gets an API node that may refer to an instance of `mod`. + */ + bindingset[mod] + pragma[inline_late] + private ApiNode getAModuleInstanceUseNode(DataFlow::ModuleNode mod) { + result = getForwardStartNode(mod.getAnOwnInstanceSelf()) + } + + /** + * Gets a node that can be backtracked to an instance of `mod`. + */ + bindingset[mod] + pragma[inline_late] + private ApiNode getAModuleInstanceDefNode(DataFlow::ModuleNode mod) { + result = getBackwardEndNode(mod.getAnImmediateReference().getAMethodCall("new")) + } + + /** + * Gets a node that can be backtracked to an instance of `mod` or any of its descendents. + */ + bindingset[mod] + pragma[inline_late] + private ApiNode getAModuleDescendentInstanceDefNode(DataFlow::ModuleNode mod) { + result = getBackwardEndNode(mod.getAnOwnInstanceSelf()) + } + + /** + * Holds if `superclass` is the superclass of `mod`. + */ + pragma[nomagic] + private DataFlow::LocalSourceNode getSuperClassNode(DataFlow::ModuleNode mod) { + result.getALocalUse().asExpr().getExpr() = + mod.getADeclaration().(ClassDeclaration).getSuperclassExpr() + } + + /** Gets a node that can reach the receiver of the given `.class` call. */ + private DataFlow::LocalSourceNode getANodeReachingClassCall( + DataFlow::CallNode call, TypeBackTracker t + ) { + t.start() and + call.getMethodName() = "class" and + result = getALocalSourceStrict(call.getReceiver()) + or + exists(DataFlow::LocalSourceNode prev, TypeBackTracker t2 | + prev = getANodeReachingClassCall(call, t2) and + result = prev.backtrack(t2, t) and + notSelfParameter(prev) + ) + } + + /** Gets a node that can reach the receiver of the given `.class` call. */ + private DataFlow::LocalSourceNode getANodeReachingClassCall(DataFlow::CallNode call) { + result = getANodeReachingClassCall(call, TypeBackTracker::end()) + } + } + + /** INTERNAL USE ONLY. */ + module Internal { + private module Shared = ApiGraphShared; + + import Shared + + /** Gets the API node corresponding to the module/class object for `mod`. */ + bindingset[mod] + pragma[inline_late] + Node getModuleNode(DataFlow::ModuleNode mod) { result = Impl::MkModuleObjectDown(mod) } + + /** Gets the API node corresponding to instances of `mod`. */ + bindingset[mod] + pragma[inline_late] + Node getModuleInstance(DataFlow::ModuleNode mod) { result = getModuleNode(mod).getInstance() } + } + + private import Internal + import Internal::Public + + cached + private module Impl { cached newtype TApiNode = /** The root of the API graph. */ MkRoot() or /** The method accessed at `call`, synthetically treated as a separate object. */ - MkMethodAccessNode(DataFlow::CallNode call) { isUse(call) } or - /** A use of an API member at the node `nd`. */ - MkUse(DataFlow::Node nd) { isUse(nd) } or - /** A value that escapes into an external library at the node `nd` */ - MkDef(DataFlow::Node nd) { isDef(nd) } or - /** A module object seen as a use node. */ - MkModuleObject(DataFlow::ModuleNode mod) + MkMethodAccessNode(DataFlow::CallNode call) or + /** The module object `mod` with epsilon edges to its ancestors. */ + MkModuleObjectUp(DataFlow::ModuleNode mod) or + /** The module object `mod` with epsilon edges to its descendents. */ + MkModuleObjectDown(DataFlow::ModuleNode mod) or + /** Instances of `mod` with epsilon edges to its ancestors. */ + MkModuleInstanceUp(DataFlow::ModuleNode mod) or + /** Instances of `mod` with epsilon edges to its descendents, and to its upward node. */ + MkModuleInstanceDown(DataFlow::ModuleNode mod) or + /** Intermediate node for following forward data flow. */ + MkForwardNode(DataFlow::LocalSourceNode node, TypeTracker t) { isReachable(node, t) } or + /** Intermediate node for following backward data flow. */ + MkBackwardNode(DataFlow::LocalSourceNode node, TypeTracker t) { isReachable(node, t) } or + MkSinkNode(DataFlow::Node node) { needsSinkNode(node) } + + private predicate needsSinkNode(DataFlow::Node node) { + node instanceof DataFlowPrivate::ArgumentNode + or + TypeTrackerSpecific::basicStoreStep(node, _, _) + or + node = any(DataFlow::CallableNode callable).getAReturnNode() + or + node = any(EntryPoint e).getASink() + } + + bindingset[e] + pragma[inline_late] + private DataFlow::Node getNodeFromExpr(Expr e) { result.asExpr().getExpr() = e } private string resolveTopLevel(ConstantReadAccess read) { result = read.getModule().getQualifiedName() and @@ -616,300 +1129,288 @@ module API { } /** - * Holds if `ref` is a use of a node that should have an incoming edge from the root - * node labeled `lbl` in the API graph (not including those from API::EntryPoint). + * Holds `pred` should have a member edge to `mod`. */ pragma[nomagic] - private predicate useRoot(Label::ApiLabel lbl, DataFlow::Node ref) { - exists(string name, ConstantReadAccess read | - read = ref.asExpr().getExpr() and - lbl = Label::member(read.getName()) + private predicate moduleScope(DataFlow::ModuleNode mod, Node pred, string name) { + exists(Namespace namespace | + name = namespace.getName() and + namespace = mod.getADeclaration() | - name = resolveTopLevel(read) + exists(DataFlow::Node scopeNode | + scopeNode.asExpr().getExpr() = namespace.getScopeExpr() and + pred = getForwardEndNode(getALocalSourceStrict(scopeNode)) + ) or - name = read.getName() and - not exists(resolveTopLevel(read)) and - not exists(read.getScopeExpr()) + not exists(namespace.getScopeExpr()) and + if namespace.hasGlobalScope() or namespace.getEnclosingModule() instanceof Toplevel + then pred = MkRoot() + else pred = MkModuleObjectDown(namespace.getEnclosingModule().getModule()) ) } - /** - * Holds if `ref` is a use of a node that should have an incoming edge labeled `lbl`, - * from a use node that flows to `node`. - */ - private predicate useStep(Label::ApiLabel lbl, DataFlow::Node node, DataFlow::Node ref) { - // // Referring to an attribute on a node that is a use of `base`: - // pred = `Rails` part of `Rails::Whatever` - // lbl = `Whatever` - // ref = `Rails::Whatever` - exists(ExprNodes::ConstantAccessCfgNode c, ConstantReadAccess read | - not exists(resolveTopLevel(read)) and - node.asExpr() = c.getScopeExpr() and - lbl = Label::member(read.getName()) and - ref.asExpr() = c and - read = c.getExpr() - ) - or - exists(TypeTrackerSpecific::TypeTrackerContent c | - TypeTrackerSpecific::basicLoadStep(node, ref, c) and - lbl = Label::content(c.getAStoreContent()) and - not c.isSingleton(any(DataFlow::Content::AttributeNameContent k)) - ) - // note: method calls are not handled here as there is no DataFlow::Node for the intermediate MkMethodAccessNode API node - } - - /** - * Holds if `rhs` is a definition of a node that should have an incoming edge labeled `lbl`, - * from a def node that is reachable from `node`. - */ - private predicate defStep(Label::ApiLabel lbl, DataFlow::Node node, DataFlow::Node rhs) { - exists(TypeTrackerSpecific::TypeTrackerContent c | - TypeTrackerSpecific::basicStoreStep(rhs, node, c) and - lbl = Label::content(c.getAStoreContent()) - ) - } - - pragma[nomagic] - private predicate isUse(DataFlow::Node nd) { - useRoot(_, nd) - or - exists(DataFlow::Node node | - useCandFwd().flowsTo(node) and - useStep(_, node, nd) - ) - or - useCandFwd().flowsTo(nd.(DataFlow::CallNode).getReceiver()) - or - parameterStep(_, defCand(), nd) - or - nd = any(EntryPoint entry).getASource() - or - nd = any(EntryPoint entry).getACall() - } - - /** - * Holds if `ref` is a use of node `nd`. - */ cached - predicate use(TApiNode nd, DataFlow::Node ref) { - nd = MkUse(ref) + predicate memberEdge(Node pred, string name, Node succ) { + exists(ConstantReadAccess read | succ = getForwardStartNode(getNodeFromExpr(read)) | + name = resolveTopLevel(read) and + pred = MkRoot() + or + not exists(resolveTopLevel(read)) and + not exists(read.getScopeExpr()) and + name = read.getName() and + pred = MkRoot() + or + pred = getForwardEndNode(getALocalSourceStrict(getNodeFromExpr(read.getScopeExpr()))) and + name = read.getName() + ) or exists(DataFlow::ModuleNode mod | - nd = MkModuleObject(mod) and - ref = mod.getAnImmediateReference() + moduleScope(mod, pred, name) and + (succ = MkModuleObjectDown(mod) or succ = MkModuleObjectUp(mod)) ) } - /** - * Holds if `rhs` is a RHS of node `nd`. - */ cached - predicate def(TApiNode nd, DataFlow::Node rhs) { nd = MkDef(rhs) } + predicate topLevelMember(string name, Node node) { memberEdge(root(), name, node) } - /** Gets a node reachable from a use-node. */ - private DataFlow::LocalSourceNode useCandFwd(TypeTracker t) { - t.start() and - isUse(result) - or - exists(TypeTracker t2 | result = useCandFwd(t2).track(t2, t)) - } - - /** Gets a node reachable from a use-node. */ - private DataFlow::LocalSourceNode useCandFwd() { result = useCandFwd(TypeTracker::end()) } - - private predicate isDef(DataFlow::Node rhs) { - // If a call node is relevant as a use-node, treat its arguments as def-nodes - argumentStep(_, useCandFwd(), rhs) - or - defStep(_, defCand(), rhs) - or - rhs = any(EntryPoint entry).getASink() - } - - /** Gets a data flow node that flows to the RHS of a def-node. */ - private DataFlow::LocalSourceNode defCand(TypeBackTracker t) { - t.start() and - exists(DataFlow::Node rhs | - isDef(rhs) and - result = rhs.getALocalSource() - ) - or - exists(TypeBackTracker t2 | result = defCand(t2).backtrack(t2, t)) - } - - /** Gets a data flow node that flows to the RHS of a def-node. */ - private DataFlow::LocalSourceNode defCand() { result = defCand(TypeBackTracker::end()) } - - /** - * Holds if there should be a `lbl`-edge from the given call to an argument. - */ - pragma[nomagic] - private predicate argumentStep( - Label::ApiLabel lbl, DataFlow::CallNode call, DataFlowPrivate::ArgumentNode argument - ) { - exists(DataFlowDispatch::ArgumentPosition argPos | - argument.sourceArgumentOf(call.asExpr(), argPos) and - lbl = Label::getLabelFromArgumentPosition(argPos) - ) - } - - /** - * Holds if there should be a `lbl`-edge from the given callable to a parameter. - */ - pragma[nomagic] - private predicate parameterStep( - Label::ApiLabel lbl, DataFlow::Node callable, DataFlowPrivate::ParameterNodeImpl paramNode - ) { - exists(DataFlowDispatch::ParameterPosition paramPos | - paramNode.isSourceParameterOf(callable.asExpr().getExpr(), paramPos) and - lbl = Label::getLabelFromParameterPosition(paramPos) - ) - } - - /** - * Gets a data-flow node to which `src`, which is a use of an API-graph node, flows. - * - * The flow from `src` to the returned node may be inter-procedural. - */ - private DataFlow::LocalSourceNode trackUseNode(DataFlow::LocalSourceNode src, TypeTracker t) { - result = src and - isUse(src) and - t.start() - or - exists(TypeTracker t2 | - result = trackUseNode(src, t2).track(t2, t) and - not result instanceof DataFlow::SelfParameterNode - ) - } - - /** - * Gets a data-flow node to which `src`, which is a use of an API-graph node, flows. - * - * The flow from `src` to the returned node may be inter-procedural. - */ cached - DataFlow::LocalSourceNode trackUseNode(DataFlow::LocalSourceNode src) { - result = trackUseNode(src, TypeTracker::end()) - } - - /** Gets a data flow node reaching the RHS of the given def node. */ - private DataFlow::LocalSourceNode trackDefNode(DataFlow::Node rhs, TypeBackTracker t) { - t.start() and - isDef(rhs) and - result = rhs.getALocalSource() - or - exists(TypeBackTracker t2, DataFlow::LocalSourceNode mid | - mid = trackDefNode(rhs, t2) and - not mid instanceof DataFlow::SelfParameterNode and - result = mid.backtrack(t2, t) + predicate toplevelCall(string name, Node node) { + exists(DataFlow::CallNode call | + call.asExpr().getExpr().getCfgScope() instanceof Toplevel and + call.getMethodName() = name and + node = MkMethodAccessNode(call) ) } - /** Gets a data flow node reaching the RHS of the given def node. */ cached - DataFlow::LocalSourceNode trackDefNode(DataFlow::Node rhs) { - result = trackDefNode(rhs, TypeBackTracker::end()) - } + predicate anyMemberEdge(Node pred, Node succ) { memberEdge(pred, _, succ) } - pragma[nomagic] - private predicate useNodeReachesReceiver(DataFlow::Node use, DataFlow::CallNode call) { - trackUseNode(use).flowsTo(call.getReceiver()) - } - - /** - * Holds if `superclass` is the superclass of `mod`. - */ - pragma[nomagic] - private predicate superclassNode(DataFlow::ModuleNode mod, DataFlow::Node superclass) { - superclass.asExpr().getExpr() = mod.getADeclaration().(ClassDeclaration).getSuperclassExpr() - } - - /** - * Holds if there is an edge from `pred` to `succ` in the API graph that is labeled with `lbl`. - */ cached - predicate edge(TApiNode pred, Label::ApiLabel lbl, TApiNode succ) { - /* Every node that is a use of an API component is itself added to the API graph. */ - exists(DataFlow::LocalSourceNode ref | succ = MkUse(ref) | - pred = MkRoot() and - useRoot(lbl, ref) + predicate methodEdge(Node pred, string name, Node succ) { + exists(DataFlow::ModuleNode mod, DataFlow::CallNode call | + // Treat super calls as if they were calls to the module object/instance. + succ = MkMethodAccessNode(call) and + name = call.getMethodName() + | + pred = MkModuleObjectDown(mod) and + call = mod.getAnOwnSingletonMethod().getASuperCall() or - exists(DataFlow::Node node, DataFlow::Node src | - use(pred, src) and - trackUseNode(src).flowsTo(node) and - useStep(lbl, node, ref) - ) - or - exists(DataFlow::Node callback | - def(pred, callback) and - parameterStep(lbl, trackDefNode(callback), ref) - ) - ) - or - exists(DataFlow::Node predNode, DataFlow::Node succNode | - def(pred, predNode) and - succ = MkDef(succNode) and - defStep(lbl, trackDefNode(predNode), succNode) - ) - or - exists(DataFlow::Node predNode, DataFlow::Node superclassNode, DataFlow::ModuleNode mod | - use(pred, predNode) and - trackUseNode(predNode).flowsTo(superclassNode) and - superclassNode(mod, superclassNode) and - succ = MkModuleObject(mod) and - lbl = Label::subclass() + pred = MkModuleInstanceUp(mod) and + call = mod.getAnOwnInstanceMethod().getASuperCall() ) or exists(DataFlow::CallNode call | // from receiver to method call node - exists(DataFlow::Node receiver | - use(pred, receiver) and - useNodeReachesReceiver(receiver, call) and - lbl = Label::method(call.getMethodName()) and - succ = MkMethodAccessNode(call) - ) - or - // from method call node to return and arguments - pred = MkMethodAccessNode(call) and - ( - lbl = Label::return() and - succ = MkUse(call) - or - exists(DataFlow::Node rhs | - argumentStep(lbl, call, rhs) and - succ = MkDef(rhs) - ) - ) + pred = getForwardEndNode(getALocalSourceStrict(call.getReceiver())) and + succ = MkMethodAccessNode(call) and + name = call.getMethodName() ) or - exists(EntryPoint entry | - pred = root() and - lbl = Label::entryPoint(entry) - | - succ = MkDef(entry.getASink()) + exists(DataFlow::ModuleNode mod | + (pred = MkModuleObjectDown(mod) or pred = MkModuleObjectUp(mod)) and + succ = getBackwardStartNode(mod.getOwnSingletonMethod(name)) or - succ = MkUse(entry.getASource()) - or - succ = MkMethodAccessNode(entry.getACall()) + pred = MkModuleInstanceUp(mod) and + succ = getBackwardStartNode(mod.getOwnInstanceMethod(name)) ) } - /** - * Holds if there is an edge from `pred` to `succ` in the API graph. - */ - private predicate edge(TApiNode pred, TApiNode succ) { edge(pred, _, succ) } - - /** Gets the shortest distance from the root to `nd` in the API graph. */ cached - int distanceFromRoot(TApiNode nd) = shortestDistances(MkRoot/0, edge/2)(_, nd, result) + predicate asCallable(Node apiNode, DataFlow::CallableNode callable) { + apiNode = getBackwardStartNode(callable) + } + cached + predicate contentEdge(Node pred, DataFlow::Content content, Node succ) { + exists( + DataFlow::Node object, DataFlow::Node value, TypeTrackerSpecific::TypeTrackerContent c + | + TypeTrackerSpecific::basicLoadStep(object, value, c) and + content = c.getAStoreContent() and + not c.isSingleton(any(DataFlow::Content::AttributeNameContent k)) and + // `x -> x.foo` with content "foo" + pred = getForwardOrBackwardEndNode(getALocalSourceStrict(object)) and + succ = getForwardStartNode(value) + or + // Based on `object.c = value` generate `object -> value` with content `c` + TypeTrackerSpecific::basicStoreStep(value, object, c) and + content = c.getAStoreContent() and + pred = getForwardOrBackwardEndNode(getALocalSourceStrict(object)) and + succ = MkSinkNode(value) + ) + } + + cached + predicate fieldEdge(Node pred, string name, Node succ) { + Impl::contentEdge(pred, DataFlowPrivate::TFieldContent(name), succ) + } + + cached + predicate elementEdge(Node pred, Node succ) { + contentEdge(pred, any(DataFlow::ContentSet set | set.isAnyElement()).getAReadContent(), succ) + } + + cached + predicate parameterEdge(Node pred, DataFlowDispatch::ParameterPosition paramPos, Node succ) { + exists(DataFlowPrivate::ParameterNodeImpl parameter, DataFlow::CallableNode callable | + parameter.isSourceParameterOf(callable.asExpr().getExpr(), paramPos) and + pred = getBackwardEndNode(callable) and + succ = getForwardStartNode(parameter) + ) + } + + cached + predicate argumentEdge(Node pred, DataFlowDispatch::ArgumentPosition argPos, Node succ) { + exists(DataFlow::CallNode call, DataFlowPrivate::ArgumentNode argument | + argument.sourceArgumentOf(call.asExpr(), argPos) and + pred = MkMethodAccessNode(call) and + succ = MkSinkNode(argument) + ) + } + + cached + predicate positionalArgumentEdge(Node pred, int n, Node succ) { + argumentEdge(pred, any(DataFlowDispatch::ArgumentPosition pos | pos.isPositional(n)), succ) + } + + cached + predicate keywordArgumentEdge(Node pred, string name, Node succ) { + argumentEdge(pred, any(DataFlowDispatch::ArgumentPosition pos | pos.isKeyword(name)), succ) + } + + private predicate blockArgumentEdge(Node pred, Node succ) { + argumentEdge(pred, any(DataFlowDispatch::ArgumentPosition pos | pos.isBlock()), succ) + } + + private predicate positionalParameterEdge(Node pred, int n, Node succ) { + parameterEdge(pred, any(DataFlowDispatch::ParameterPosition pos | pos.isPositional(n)), succ) + } + + private predicate keywordParameterEdge(Node pred, string name, Node succ) { + parameterEdge(pred, any(DataFlowDispatch::ParameterPosition pos | pos.isKeyword(name)), succ) + } + + cached + predicate blockParameterEdge(Node pred, Node succ) { + parameterEdge(pred, any(DataFlowDispatch::ParameterPosition pos | pos.isBlock()), succ) + } + + cached + predicate positionalParameterOrArgumentEdge(Node pred, int n, Node succ) { + positionalArgumentEdge(pred, n, succ) + or + positionalParameterEdge(pred, n, succ) + } + + cached + predicate keywordParameterOrArgumentEdge(Node pred, string name, Node succ) { + keywordArgumentEdge(pred, name, succ) + or + keywordParameterEdge(pred, name, succ) + } + + cached + predicate blockParameterOrArgumentEdge(Node pred, Node succ) { + blockArgumentEdge(pred, succ) + or + blockParameterEdge(pred, succ) + } + + pragma[nomagic] + private predicate newCall(DataFlow::LocalSourceNode receiver, DataFlow::CallNode call) { + call = receiver.getAMethodCall("new") + } + + cached + predicate instanceEdge(Node pred, Node succ) { + exists(DataFlow::ModuleNode mod | + pred = MkModuleObjectDown(mod) and + succ = MkModuleInstanceUp(mod) + ) + or + exists(DataFlow::LocalSourceNode receiver, DataFlow::CallNode call | + newCall(receiver, call) and + pred = getForwardEndNode(receiver) and + succ = getForwardStartNode(call) + ) + } + + cached + predicate returnEdge(Node pred, Node succ) { + exists(DataFlow::CallNode call | + pred = MkMethodAccessNode(call) and + succ = getForwardStartNode(call) + ) + or + exists(DataFlow::CallableNode callable | + pred = getBackwardEndNode(callable) and + succ = MkSinkNode(callable.getAReturnNode()) + ) + } + + cached + predicate entryPointEdge(EntryPoint entry, Node node) { + node = MkSinkNode(entry.getASink()) or + node = getForwardStartNode(entry.getASource()) or + node = MkMethodAccessNode(entry.getACall()) + } + } + + /** + * Holds if there is an edge from `pred` to `succ` in the API graph that is labeled with `lbl`. + */ + pragma[nomagic] + deprecated private predicate labelledEdge(Node pred, Label::ApiLabel lbl, Node succ) { + exists(string name | + Impl::memberEdge(pred, name, succ) and + lbl = Label::member(name) + ) + or + exists(string name | + Impl::methodEdge(pred, name, succ) and + lbl = Label::method(name) + ) + or + exists(DataFlow::Content content | + Impl::contentEdge(pred, content, succ) and + lbl = Label::content(content) + ) + or + exists(DataFlowDispatch::ParameterPosition pos | + Impl::parameterEdge(pred, pos, succ) and + lbl = Label::getLabelFromParameterPosition(pos) + ) + or + exists(DataFlowDispatch::ArgumentPosition pos | + Impl::argumentEdge(pred, pos, succ) and + lbl = Label::getLabelFromArgumentPosition(pos) + ) + or + Impl::instanceEdge(pred, succ) and + lbl = Label::instance() + or + Impl::returnEdge(pred, succ) and + lbl = Label::return() + or + exists(EntryPoint entry | + Impl::entryPointEdge(entry, succ) and + pred = root() and + lbl = Label::entryPoint(entry) + ) + } + + /** + * DEPRECATED. Treating the API graph as an explicit labelled graph is deprecated - instead use the methods on `API:Node` directly. + * + * Provides classes modeling the various edges (labels) in the API graph. + */ + deprecated module Label { /** All the possible labels in the API graph. */ - cached - newtype TLabel = + private newtype TLabel = MkLabelMember(string member) { member = any(ConstantReadAccess a).getName() } or MkLabelMethod(string m) { m = any(DataFlow::CallNode c).getMethodName() } or MkLabelReturn() or - MkLabelSubclass() or + MkLabelInstance() or MkLabelKeywordParameter(string name) { any(DataFlowDispatch::ArgumentPosition arg).isKeyword(name) or @@ -923,12 +1424,9 @@ module API { MkLabelBlockParameter() or MkLabelEntryPoint(EntryPoint name) or MkLabelContent(DataFlow::Content content) - } - /** Provides classes modeling the various edges (labels) in the API graph. */ - module Label { /** A label in the API-graph */ - class ApiLabel extends Impl::TLabel { + class ApiLabel extends TLabel { /** Gets a string representation of this label. */ string toString() { result = "???" } } @@ -967,9 +1465,9 @@ module API { override string toString() { result = "getReturn()" } } - /** A label for the subclass relationship. */ - class LabelSubclass extends ApiLabel, MkLabelSubclass { - override string toString() { result = "getASubclass()" } + /** A label for getting instances of a module/class. */ + class LabelInstance extends ApiLabel, MkLabelInstance { + override string toString() { result = "getInstance()" } } /** A label for a keyword parameter. */ @@ -1037,8 +1535,8 @@ module API { /** Gets the `return` edge label. */ LabelReturn return() { any() } - /** Gets the `subclass` edge label. */ - LabelSubclass subclass() { any() } + /** Gets the `instance` edge label. */ + LabelInstance instance() { any() } /** Gets the label representing the given keyword argument/parameter. */ LabelKeywordParameter keywordParameter(string name) { result.getName() = name } diff --git a/ruby/ql/lib/codeql/ruby/dataflow/internal/DataFlowImpl.qll b/ruby/ql/lib/codeql/ruby/dataflow/internal/DataFlowImpl.qll index 984c5ae2018..284fff191ae 100644 --- a/ruby/ql/lib/codeql/ruby/dataflow/internal/DataFlowImpl.qll +++ b/ruby/ql/lib/codeql/ruby/dataflow/internal/DataFlowImpl.qll @@ -2021,7 +2021,8 @@ module Impl { FlowCheckNode() { castNode(this.asNode()) or clearsContentCached(this.asNode(), _) or - expectsContentCached(this.asNode(), _) + expectsContentCached(this.asNode(), _) or + neverSkipInPathGraph(this.asNode()) } } diff --git a/ruby/ql/lib/codeql/ruby/dataflow/internal/DataFlowPrivate.qll b/ruby/ql/lib/codeql/ruby/dataflow/internal/DataFlowPrivate.qll index 0fb0bac0462..f8469e99a23 100644 --- a/ruby/ql/lib/codeql/ruby/dataflow/internal/DataFlowPrivate.qll +++ b/ruby/ql/lib/codeql/ruby/dataflow/internal/DataFlowPrivate.qll @@ -1290,10 +1290,16 @@ private import PostUpdateNodes /** A node that performs a type cast. */ class CastNode extends Node { - CastNode() { - // ensure that all variable assignments are included in the path graph - this.(SsaDefinitionExtNode).getDefinitionExt() instanceof Ssa::WriteDefinition - } + CastNode() { none() } +} + +/** + * Holds if `n` should never be skipped over in the `PathGraph` and in path + * explanations. + */ +predicate neverSkipInPathGraph(Node n) { + // ensure that all variable assignments are included in the path graph + n.(SsaDefinitionExtNode).getDefinitionExt() instanceof Ssa::WriteDefinition } class DataFlowExpr = CfgNodes::ExprCfgNode; diff --git a/ruby/ql/lib/codeql/ruby/dataflow/internal/DataFlowPublic.qll b/ruby/ql/lib/codeql/ruby/dataflow/internal/DataFlowPublic.qll index c6adcbcbe3a..7772e5235d7 100644 --- a/ruby/ql/lib/codeql/ruby/dataflow/internal/DataFlowPublic.qll +++ b/ruby/ql/lib/codeql/ruby/dataflow/internal/DataFlowPublic.qll @@ -6,12 +6,17 @@ private import codeql.ruby.typetracking.TypeTracker private import codeql.ruby.dataflow.SSA private import FlowSummaryImpl as FlowSummaryImpl private import SsaImpl as SsaImpl +private import codeql.ruby.ApiGraphs /** * An element, viewed as a node in a data flow graph. Either an expression * (`ExprNode`) or a parameter (`ParameterNode`). */ class Node extends TNode { + /** Starts backtracking from this node using API graphs. */ + pragma[inline] + API::Node backtrack() { result = API::Internal::getNodeForBacktracking(this) } + /** Gets the expression corresponding to this node, if any. */ CfgNodes::ExprCfgNode asExpr() { result = this.(ExprNode).getExprNode() } @@ -76,6 +81,11 @@ class Node extends TNode { result.asCallableAstNode() = c.asCallable() ) } + + /** Gets the enclosing method, if any. */ + MethodNode getEnclosingMethod() { + result.asCallableAstNode() = this.asExpr().getExpr().getEnclosingMethod() + } } /** A data-flow node corresponding to a call in the control-flow graph. */ @@ -144,6 +154,18 @@ class CallNode extends LocalSourceNode, ExprNode { result.asExpr() = pair.getValue() ) } + + /** + * Gets a potential target of this call, if any. + */ + final CallableNode getATarget() { + result.asCallableAstNode() = this.asExpr().getExpr().(Call).getATarget() + } + + /** + * Holds if this is a `super` call. + */ + final predicate isSuperCall() { this.asExpr().getExpr() instanceof SuperCall } } /** @@ -217,6 +239,10 @@ class SelfParameterNode extends ParameterNode instanceof SelfParameterNodeImpl { class LocalSourceNode extends Node { LocalSourceNode() { isLocalSourceNode(this) } + /** Starts tracking this node forward using API graphs. */ + pragma[inline] + API::Node track() { result = API::Internal::getNodeForForwardTracking(this) } + /** Holds if this `LocalSourceNode` can flow to `nodeTo` in one or more local flow steps. */ pragma[inline] predicate flowsTo(Node nodeTo) { hasLocalSource(nodeTo, this) } @@ -359,6 +385,11 @@ private module Cached { ) } + cached + predicate methodHasSuperCall(MethodNode method, CallNode call) { + call.isSuperCall() and method = call.getEnclosingMethod() + } + /** * A place in which a named constant can be looked up during constant lookup. */ @@ -387,6 +418,39 @@ private module Cached { result.asExpr().getExpr() = access } + /** + * Gets a module for which `constRef` is the reference to an ancestor module. + * + * For example, `M` is the ancestry target of `C` in the following examples: + * ```rb + * class M < C {} + * + * module M + * include C + * end + * + * module M + * prepend C + * end + * ``` + */ + private ModuleNode getAncestryTarget(ConstRef constRef) { result.getAnAncestorExpr() = constRef } + + /** + * Gets a scope in which a constant lookup may access the contents of the module referenced by `constRef`. + */ + cached + TConstLookupScope getATargetScope(ConstRef constRef) { + result = MkAncestorLookup(getAncestryTarget(constRef).getAnImmediateDescendent*()) + or + constRef.asConstantAccess() = any(ConstantAccess ac).getScopeExpr() and + result = MkQualifiedLookup(constRef.asConstantAccess()) + or + result = MkNestedLookup(getAncestryTarget(constRef)) + or + result = MkExactLookup(constRef.asConstantAccess().(Namespace).getModule()) + } + cached predicate forceCachingInSameStage() { any() } @@ -1028,6 +1092,33 @@ class ModuleNode instanceof Module { * this predicate. */ ModuleNode getNestedModule(string name) { result = super.getNestedModule(name) } + + /** + * Starts tracking the module object using API graphs. + * + * Concretely, this tracks forward from the following starting points: + * - A constant access that resolves to this module. + * - `self` in the module scope or in a singleton method of the module. + * - A call to `self.class` in an instance method of this module or an ancestor module. + */ + bindingset[this] + pragma[inline] + API::Node trackModule() { result = API::Internal::getModuleNode(this) } + + /** + * Starts tracking instances of this module forward using API graphs. + * + * Concretely, this tracks forward from the following starting points: + * - `self` in instance methods of this module and ancestor modules + * - Calls to `new` on the module object + * + * Note that this includes references to `self` in ancestor modules, but not in descendent modules. + * This is usually the desired behavior, particularly if this module was itself found using + * a call to `getADescendentModule()`. + */ + bindingset[this] + pragma[inline] + API::Node trackInstance() { result = API::Internal::getModuleInstance(this) } } /** @@ -1216,6 +1307,9 @@ class MethodNode extends CallableNode { /** Holds if this method is protected. */ predicate isProtected() { this.asCallableAstNode().isProtected() } + + /** Gets a `super` call in this method. */ + CallNode getASuperCall() { methodHasSuperCall(this, result) } } /** @@ -1284,13 +1378,16 @@ class HashLiteralNode extends LocalSourceNode, ExprNode { * into calls to `Array.[]`, so this includes both desugared calls as well as * explicit calls. */ -class ArrayLiteralNode extends LocalSourceNode, ExprNode { +class ArrayLiteralNode extends LocalSourceNode, CallNode { ArrayLiteralNode() { super.getExprNode() instanceof CfgNodes::ExprNodes::ArrayLiteralCfgNode } /** * Gets an element of the array. */ - Node getAnElement() { result = this.(CallNode).getPositionalArgument(_) } + Node getAnElement() { result = this.getElement(_) } + + /** Gets the `i`th element of the array. */ + Node getElement(int i) { result = this.getPositionalArgument(i) } } /** @@ -1331,24 +1428,6 @@ class ConstRef extends LocalSourceNode { not exists(access.getScopeExpr()) } - /** - * Gets a module for which this constant is the reference to an ancestor module. - * - * For example, `M` is the ancestry target of `C` in the following examples: - * ```rb - * class M < C {} - * - * module M - * include C - * end - * - * module M - * prepend C - * end - * ``` - */ - private ModuleNode getAncestryTarget() { result.getAnAncestorExpr() = this } - /** * Gets the known target module. * @@ -1356,22 +1435,6 @@ class ConstRef extends LocalSourceNode { */ private Module getExactTarget() { result.getAnImmediateReference() = access } - /** - * Gets a scope in which a constant lookup may access the contents of the module referenced by this constant. - */ - cached - private TConstLookupScope getATargetScope() { - forceCachingInSameStage() and - result = MkAncestorLookup(this.getAncestryTarget().getAnImmediateDescendent*()) - or - access = any(ConstantAccess ac).getScopeExpr() and - result = MkQualifiedLookup(access) - or - result = MkNestedLookup(this.getAncestryTarget()) - or - result = MkExactLookup(access.(Namespace).getModule()) - } - /** * Gets the scope expression, or the immediately enclosing `Namespace` (skipping over singleton classes). * @@ -1433,7 +1496,7 @@ class ConstRef extends LocalSourceNode { pragma[inline] ConstRef getConstant(string name) { exists(TConstLookupScope scope | - pragma[only_bind_into](scope) = pragma[only_bind_out](this).getATargetScope() and + pragma[only_bind_into](scope) = getATargetScope(pragma[only_bind_out](this)) and result.accesses(pragma[only_bind_out](scope), name) ) } @@ -1455,7 +1518,9 @@ class ConstRef extends LocalSourceNode { * end * ``` */ - ModuleNode getADescendentModule() { MkAncestorLookup(result) = this.getATargetScope() } + bindingset[this] + pragma[inline_late] + ModuleNode getADescendentModule() { MkAncestorLookup(result) = getATargetScope(this) } } /** diff --git a/ruby/ql/lib/codeql/ruby/experimental/UnicodeBypassValidationQuery.qll b/ruby/ql/lib/codeql/ruby/experimental/UnicodeBypassValidationQuery.qll index 5c24978c4c3..449e05ad9e8 100644 --- a/ruby/ql/lib/codeql/ruby/experimental/UnicodeBypassValidationQuery.qll +++ b/ruby/ql/lib/codeql/ruby/experimental/UnicodeBypassValidationQuery.qll @@ -91,19 +91,19 @@ class Configuration extends TaintTracking::Configuration { // unicode_utils exists(API::MethodAccessNode mac | mac = API::getTopLevelMember("UnicodeUtils").getMethod(["nfkd", "nfc", "nfd", "nfkc"]) and - sink = mac.getParameter(0).asSink() + sink = mac.getArgument(0).asSink() ) or // eprun exists(API::MethodAccessNode mac | mac = API::getTopLevelMember("Eprun").getMethod("normalize") and - sink = mac.getParameter(0).asSink() + sink = mac.getArgument(0).asSink() ) or // unf exists(API::MethodAccessNode mac | mac = API::getTopLevelMember("UNF").getMember("Normalizer").getMethod("normalize") and - sink = mac.getParameter(0).asSink() + sink = mac.getArgument(0).asSink() ) or // ActiveSupport::Multibyte::Chars @@ -113,7 +113,7 @@ class Configuration extends TaintTracking::Configuration { .getMember("Multibyte") .getMember("Chars") .getMethod("new") - .getCallNode() and + .asCall() and n = cn.getAMethodCall("normalize") and sink = cn.getArgument(0) ) diff --git a/ruby/ql/lib/codeql/ruby/experimental/ZipSlipCustomizations.qll b/ruby/ql/lib/codeql/ruby/experimental/ZipSlipCustomizations.qll index e94cabb414c..656ceedbe2b 100644 --- a/ruby/ql/lib/codeql/ruby/experimental/ZipSlipCustomizations.qll +++ b/ruby/ql/lib/codeql/ruby/experimental/ZipSlipCustomizations.qll @@ -89,7 +89,7 @@ module ZipSlip { // If argument refers to a string object, then it's a hardcoded path and // this file is safe. not zipOpen - .getCallNode() + .asCall() .getArgument(0) .getALocalSource() .getConstantValue() diff --git a/ruby/ql/lib/codeql/ruby/frameworks/ActionController.qll b/ruby/ql/lib/codeql/ruby/frameworks/ActionController.qll index 31bdc42e350..a687837f8fd 100644 --- a/ruby/ql/lib/codeql/ruby/frameworks/ActionController.qll +++ b/ruby/ql/lib/codeql/ruby/frameworks/ActionController.qll @@ -83,8 +83,8 @@ class ActionControllerClass extends DataFlow::ClassNode { } } -private DataFlow::LocalSourceNode actionControllerInstance() { - result = any(ActionControllerClass cls).getSelf() +private API::Node actionControllerInstance() { + result = any(ActionControllerClass cls).getSelf().track() } /** @@ -222,19 +222,19 @@ private class ActionControllerRenderToCall extends RenderToCallImpl { } } +pragma[nomagic] +private DataFlow::CallNode renderCall() { + // ActionController#render is an alias for ActionController::Renderer#render + result = + [ + any(ActionControllerClass c).trackModule().getAMethodCall("render"), + any(ActionControllerClass c).trackModule().getReturn("renderer").getAMethodCall("render") + ] +} + /** A call to `ActionController::Renderer#render`. */ private class RendererRenderCall extends RenderCallImpl { - RendererRenderCall() { - this = - [ - // ActionController#render is an alias for ActionController::Renderer#render - any(ActionControllerClass c).getAnImmediateReference().getAMethodCall("render"), - any(ActionControllerClass c) - .getAnImmediateReference() - .getAMethodCall("renderer") - .getAMethodCall("render") - ].asExpr().getExpr() - } + RendererRenderCall() { this = renderCall().asExpr().getExpr() } } /** A call to `html_escape` from within a controller. */ @@ -260,6 +260,7 @@ class RedirectToCall extends MethodCall { this = controller .getSelf() + .track() .getAMethodCall(["redirect_to", "redirect_back", "redirect_back_or_to"]) .asExpr() .getExpr() @@ -600,9 +601,7 @@ private module ParamsSummaries { * response. */ private module Response { - DataFlow::LocalSourceNode response() { - result = actionControllerInstance().getAMethodCall("response") - } + API::Node response() { result = actionControllerInstance().getReturn("response") } class BodyWrite extends DataFlow::CallNode, Http::Server::HttpResponse::Range { BodyWrite() { this = response().getAMethodCall("body=") } @@ -628,7 +627,7 @@ private module Response { HeaderWrite() { // response.header[key] = val // response.headers[key] = val - this = response().getAMethodCall(["header", "headers"]).getAMethodCall("[]=") + this = response().getReturn(["header", "headers"]).getAMethodCall("[]=") or // response.set_header(key) = val // response[header] = val @@ -673,18 +672,12 @@ private module Response { } } -private class ActionControllerLoggerInstance extends DataFlow::Node { - ActionControllerLoggerInstance() { - this = actionControllerInstance().getAMethodCall("logger") - or - any(ActionControllerLoggerInstance i).(DataFlow::LocalSourceNode).flowsTo(this) - } -} - private class ActionControllerLoggingCall extends DataFlow::CallNode, Logging::Range { ActionControllerLoggingCall() { - this.getReceiver() instanceof ActionControllerLoggerInstance and - this.getMethodName() = ["debug", "error", "fatal", "info", "unknown", "warn"] + this = + actionControllerInstance() + .getReturn("logger") + .getAMethodCall(["debug", "error", "fatal", "info", "unknown", "warn"]) } // Note: this is identical to the definition `stdlib.Logger.LoggerInfoStyleCall`. diff --git a/ruby/ql/lib/codeql/ruby/frameworks/ActionMailbox.qll b/ruby/ql/lib/codeql/ruby/frameworks/ActionMailbox.qll index 13607f67926..f237e42a9a9 100644 --- a/ruby/ql/lib/codeql/ruby/frameworks/ActionMailbox.qll +++ b/ruby/ql/lib/codeql/ruby/frameworks/ActionMailbox.qll @@ -27,8 +27,8 @@ module ActionMailbox { Mail() { this = [ - controller().getAnInstanceSelf().getAMethodCall("inbound_email").getAMethodCall("mail"), - controller().getAnInstanceSelf().getAMethodCall("mail") + controller().trackInstance().getReturn("inbound_email").getAMethodCall("mail"), + controller().trackInstance().getAMethodCall("mail") ] } } @@ -40,7 +40,7 @@ module ActionMailbox { RemoteContent() { this = any(Mail m) - .(DataFlow::LocalSourceNode) + .track() .getAMethodCall([ "body", "to", "from", "raw_source", "subject", "from_address", "recipients_addresses", "cc_addresses", "bcc_addresses", "in_reply_to", diff --git a/ruby/ql/lib/codeql/ruby/frameworks/ActionMailer.qll b/ruby/ql/lib/codeql/ruby/frameworks/ActionMailer.qll index af183333d3d..4884f46aac3 100644 --- a/ruby/ql/lib/codeql/ruby/frameworks/ActionMailer.qll +++ b/ruby/ql/lib/codeql/ruby/frameworks/ActionMailer.qll @@ -4,12 +4,26 @@ private import codeql.ruby.AST private import codeql.ruby.ApiGraphs +private import codeql.ruby.DataFlow private import codeql.ruby.frameworks.internal.Rails /** * Provides modeling for the `ActionMailer` library. */ module ActionMailer { + private DataFlow::ClassNode actionMailerClass() { + result = + [ + DataFlow::getConstant("ActionMailer").getConstant("Base"), + // In Rails applications `ApplicationMailer` typically extends + // `ActionMailer::Base`, but we treat it separately in case the + // `ApplicationMailer` definition is not in the database. + DataFlow::getConstant("ApplicationMailer") + ].getADescendentModule() + } + + private API::Node actionMailerInstance() { result = actionMailerClass().trackInstance() } + /** * A `ClassDeclaration` for a class that extends `ActionMailer::Base`. * For example, @@ -21,33 +35,11 @@ module ActionMailer { * ``` */ class MailerClass extends ClassDeclaration { - MailerClass() { - this.getSuperclassExpr() = - [ - API::getTopLevelMember("ActionMailer").getMember("Base"), - // In Rails applications `ApplicationMailer` typically extends - // `ActionMailer::Base`, but we treat it separately in case the - // `ApplicationMailer` definition is not in the database. - API::getTopLevelMember("ApplicationMailer") - ].getASubclass().getAValueReachableFromSource().asExpr().getExpr() - } - } - - /** A method call with a `self` receiver from within a mailer class */ - private class ContextCall extends MethodCall { - private MailerClass mailerClass; - - ContextCall() { - this.getReceiver() instanceof SelfVariableAccess and - this.getEnclosingModule() = mailerClass - } - - /** Gets the mailer class containing this method. */ - MailerClass getMailerClass() { result = mailerClass } + MailerClass() { this = actionMailerClass().getADeclaration() } } /** A call to `params` from within a mailer. */ - class ParamsCall extends ContextCall, ParamsCallImpl { - ParamsCall() { this.getMethodName() = "params" } + class ParamsCall extends ParamsCallImpl { + ParamsCall() { this = actionMailerInstance().getAMethodCall("params").asExpr().getExpr() } } } diff --git a/ruby/ql/lib/codeql/ruby/frameworks/ActiveRecord.qll b/ruby/ql/lib/codeql/ruby/frameworks/ActiveRecord.qll index fcca078f933..4a5d342eeba 100644 --- a/ruby/ql/lib/codeql/ruby/frameworks/ActiveRecord.qll +++ b/ruby/ql/lib/codeql/ruby/frameworks/ActiveRecord.qll @@ -30,10 +30,8 @@ private predicate isBuiltInMethodForActiveRecordModelInstance(string methodName) methodName = objectInstanceMethodName() } -private API::Node activeRecordClassApiNode() { +private API::Node activeRecordBaseClass() { result = - // class Foo < ActiveRecord::Base - // class Bar < Foo [ API::getTopLevelMember("ActiveRecord").getMember("Base"), // In Rails applications `ApplicationRecord` typically extends `ActiveRecord::Base`, but we @@ -42,6 +40,46 @@ private API::Node activeRecordClassApiNode() { ] } +/** + * Gets an object with methods from the ActiveRecord query interface. + */ +private API::Node activeRecordQueryBuilder() { + result = activeRecordBaseClass() + or + result = activeRecordBaseClass().getInstance() + or + // Assume any method call might return an ActiveRecord::Relation + // These are dynamically generated + result = activeRecordQueryBuilderMethodAccess(_).getReturn() +} + +/** Gets a call targeting the ActiveRecord query interface. */ +private API::MethodAccessNode activeRecordQueryBuilderMethodAccess(string name) { + result = activeRecordQueryBuilder().getMethod(name) and + // Due to the heuristic tracking of query builder objects, add a restriction for methods with a known call target + not isUnlikelyExternalCall(result) +} + +/** Gets a call targeting the ActiveRecord query interface. */ +private DataFlow::CallNode activeRecordQueryBuilderCall(string name) { + result = activeRecordQueryBuilderMethodAccess(name).asCall() +} + +/** + * Holds if `call` is unlikely to call into an external library, since it has a possible + * call target in its enclosing module. + */ +private predicate isUnlikelyExternalCall(API::MethodAccessNode node) { + exists(DataFlow::ModuleNode mod, DataFlow::CallNode call | call = node.asCall() | + call.getATarget() = [mod.getAnOwnSingletonMethod(), mod.getAnOwnInstanceMethod()] and + call.getEnclosingMethod() = [mod.getAnOwnSingletonMethod(), mod.getAnOwnInstanceMethod()] + ) +} + +private API::Node activeRecordConnectionInstance() { + result = activeRecordBaseClass().getReturn("connection") +} + /** * A `ClassDeclaration` for a class that inherits from `ActiveRecord::Base`. For example, * @@ -55,20 +93,19 @@ private API::Node activeRecordClassApiNode() { * ``` */ class ActiveRecordModelClass extends ClassDeclaration { + private DataFlow::ClassNode cls; + ActiveRecordModelClass() { - this.getSuperclassExpr() = - activeRecordClassApiNode().getASubclass().getAValueReachableFromSource().asExpr().getExpr() + cls = activeRecordBaseClass().getADescendentModule() and this = cls.getADeclaration() } // Gets the class declaration for this class and all of its super classes - private ModuleBase getAllClassDeclarations() { - result = this.getModule().getSuperClass*().getADeclaration() - } + private ModuleBase getAllClassDeclarations() { result = cls.getAnAncestor().getADeclaration() } /** * Gets methods defined in this class that may access a field from the database. */ - Method getAPotentialFieldAccessMethod() { + deprecated Method getAPotentialFieldAccessMethod() { // It's a method on this class or one of its super classes result = this.getAllClassDeclarations().getAMethod() and // There is a value that can be returned by this method which may include field data @@ -90,58 +127,84 @@ class ActiveRecordModelClass extends ClassDeclaration { ) ) } + + /** Gets the class as a `DataFlow::ClassNode`. */ + DataFlow::ClassNode getClassNode() { result = cls } } -/** A class method call whose receiver is an `ActiveRecordModelClass`. */ -class ActiveRecordModelClassMethodCall extends MethodCall { - private ActiveRecordModelClass recvCls; +/** + * Gets a potential reference to an ActiveRecord class object. + */ +deprecated private API::Node getAnActiveRecordModelClassRef() { + result = any(ActiveRecordModelClass cls).getClassNode().trackModule() + or + // For methods with an unknown call target, assume this might be a database field, thus returning another ActiveRecord object. + // In this case we do not know which class it belongs to, which is why this predicate can't associate the reference with a specific class. + result = getAnUnknownActiveRecordModelClassCall().getReturn() +} +/** + * Gets a call performed on an ActiveRecord class object, without a known call target in the codebase. + */ +deprecated private API::MethodAccessNode getAnUnknownActiveRecordModelClassCall() { + result = getAnActiveRecordModelClassRef().getMethod(_) and + result.asCall().asExpr().getExpr() instanceof UnknownMethodCall +} + +/** + * DEPRECATED. Use `ActiveRecordModelClass.getClassNode().trackModule().getMethod()` instead. + * + * A class method call whose receiver is an `ActiveRecordModelClass`. + */ +deprecated class ActiveRecordModelClassMethodCall extends MethodCall { ActiveRecordModelClassMethodCall() { - // e.g. Foo.where(...) - recvCls.getModule() = this.getReceiver().(ConstantReadAccess).getModule() - or - // e.g. Foo.joins(:bars).where(...) - recvCls = this.getReceiver().(ActiveRecordModelClassMethodCall).getReceiverClass() - or - // e.g. self.where(...) within an ActiveRecordModelClass - this.getReceiver() instanceof SelfVariableAccess and - this.getEnclosingModule() = recvCls + this = getAnUnknownActiveRecordModelClassCall().asCall().asExpr().getExpr() } - /** The `ActiveRecordModelClass` of the receiver of this method. */ - ActiveRecordModelClass getReceiverClass() { result = recvCls } + /** Gets the `ActiveRecordModelClass` of the receiver of this method, if it can be determined. */ + ActiveRecordModelClass getReceiverClass() { + this = result.getClassNode().trackModule().getMethod(_).asCall().asExpr().getExpr() + } } -private Expr sqlFragmentArgument(MethodCall call) { - exists(string methodName | - methodName = call.getMethodName() and - ( - methodName = - [ - "delete_all", "delete_by", "destroy_all", "destroy_by", "exists?", "find_by", "find_by!", - "find_or_create_by", "find_or_create_by!", "find_or_initialize_by", "find_by_sql", "from", - "group", "having", "joins", "lock", "not", "order", "reorder", "pluck", "where", - "rewhere", "select", "reselect", "update_all" - ] and - result = call.getArgument(0) - or - methodName = "calculate" and result = call.getArgument(1) - or - methodName in ["average", "count", "maximum", "minimum", "sum", "count_by_sql"] and - result = call.getArgument(0) - or - // This format was supported until Rails 2.3.8 - methodName = ["all", "find", "first", "last"] and - result = call.getKeywordArgument("conditions") - or - methodName = "reload" and - result = call.getKeywordArgument("lock") - or - // Calls to `annotate` can be used to add block comments to SQL queries. These are potentially vulnerable to - // SQLi if user supplied input is passed in as an argument. - methodName = "annotate" and - result = call.getArgument(_) - ) +private predicate sqlFragmentArgumentInner(DataFlow::CallNode call, DataFlow::Node sink) { + call = + activeRecordQueryBuilderCall([ + "delete_all", "delete_by", "destroy_all", "destroy_by", "exists?", "find_by", "find_by!", + "find_or_create_by", "find_or_create_by!", "find_or_initialize_by", "find_by_sql", "from", + "group", "having", "joins", "lock", "not", "order", "reorder", "pluck", "where", "rewhere", + "select", "reselect", "update_all" + ]) and + sink = call.getArgument(0) + or + call = activeRecordQueryBuilderCall("calculate") and + sink = call.getArgument(1) + or + call = + activeRecordQueryBuilderCall(["average", "count", "maximum", "minimum", "sum", "count_by_sql"]) and + sink = call.getArgument(0) + or + // This format was supported until Rails 2.3.8 + call = activeRecordQueryBuilderCall(["all", "find", "first", "last"]) and + sink = call.getKeywordArgument("conditions") + or + call = activeRecordQueryBuilderCall("reload") and + sink = call.getKeywordArgument("lock") + or + // Calls to `annotate` can be used to add block comments to SQL queries. These are potentially vulnerable to + // SQLi if user supplied input is passed in as an argument. + call = activeRecordQueryBuilderCall("annotate") and + sink = call.getArgument(_) + or + call = activeRecordConnectionInstance().getAMethodCall("execute") and + sink = call.getArgument(0) +} + +private predicate sqlFragmentArgument(DataFlow::CallNode call, DataFlow::Node sink) { + exists(DataFlow::Node arg | + sqlFragmentArgumentInner(call, arg) and + sink = [arg, arg.(DataFlow::ArrayLiteralNode).getElement(0)] and + unsafeSqlExpr(sink.asExpr().getExpr()) ) } @@ -162,6 +225,8 @@ private predicate unsafeSqlExpr(Expr sqlFragmentExpr) { } /** + * DEPRECATED. Use the `SqlExecution` concept or `ActiveRecordSqlExecutionRange`. + * * A method call that may result in executing unintended user-controlled SQL * queries if the `getSqlFragmentSinkArgument()` expression is tainted by * unsanitized user-controlled input. For example, supposing that `User` is an @@ -175,55 +240,32 @@ private predicate unsafeSqlExpr(Expr sqlFragmentExpr) { * as `"') OR 1=1 --"` could result in the application looking up all users * rather than just one with a matching name. */ -class PotentiallyUnsafeSqlExecutingMethodCall extends ActiveRecordModelClassMethodCall { - // The SQL fragment argument itself - private Expr sqlFragmentExpr; +deprecated class PotentiallyUnsafeSqlExecutingMethodCall extends ActiveRecordModelClassMethodCall { + private DataFlow::CallNode call; PotentiallyUnsafeSqlExecutingMethodCall() { - exists(Expr arg | - arg = sqlFragmentArgument(this) and - unsafeSqlExpr(sqlFragmentExpr) and - ( - sqlFragmentExpr = arg - or - sqlFragmentExpr = arg.(ArrayLiteral).getElement(0) - ) and - // Check that method has not been overridden - not exists(SingletonMethod m | - m.getName() = this.getMethodName() and - m.getOuterScope() = this.getReceiverClass() - ) - ) + call.asExpr().getExpr() = this and sqlFragmentArgument(call, _) } /** * Gets the SQL fragment argument of this method call. */ - Expr getSqlFragmentSinkArgument() { result = sqlFragmentExpr } + Expr getSqlFragmentSinkArgument() { + exists(DataFlow::Node sink | + sqlFragmentArgument(call, sink) and result = sink.asExpr().getExpr() + ) + } } /** - * An `SqlExecution::Range` for an argument to a - * `PotentiallyUnsafeSqlExecutingMethodCall` that may be vulnerable to being - * controlled by user input. + * A SQL execution arising from a call to the ActiveRecord library. */ class ActiveRecordSqlExecutionRange extends SqlExecution::Range { - ActiveRecordSqlExecutionRange() { - exists(PotentiallyUnsafeSqlExecutingMethodCall mc | - this.asExpr().getNode() = mc.getSqlFragmentSinkArgument() - ) - or - this = activeRecordConnectionInstance().getAMethodCall("execute").getArgument(0) and - unsafeSqlExpr(this.asExpr().getExpr()) - } + ActiveRecordSqlExecutionRange() { sqlFragmentArgument(_, this) } override DataFlow::Node getSql() { result = this } } -private API::Node activeRecordConnectionInstance() { - result = activeRecordClassApiNode().getReturn("connection") -} - // TODO: model `ActiveRecord` sanitizers // https://api.rubyonrails.org/classes/ActiveRecord/Sanitization/ClassMethods.html /** @@ -241,15 +283,8 @@ abstract class ActiveRecordModelInstantiation extends OrmInstantiation::Range, override predicate methodCallMayAccessField(string methodName) { // The method is not a built-in, and... not isBuiltInMethodForActiveRecordModelInstance(methodName) and - ( - // ...There is no matching method definition in the class, or... - not exists(this.getClass().getMethod(methodName)) - or - // ...the called method can access a field. - exists(Method m | m = this.getClass().getAPotentialFieldAccessMethod() | - m.getName() = methodName - ) - ) + // ...There is no matching method definition in the class + not exists(this.getClass().getMethod(methodName)) } } @@ -317,21 +352,10 @@ private class ActiveRecordModelFinderCall extends ActiveRecordModelInstantiation } // A `self` reference that may resolve to an active record model object -private class ActiveRecordModelClassSelfReference extends ActiveRecordModelInstantiation, - DataFlow::SelfParameterNode -{ +private class ActiveRecordModelClassSelfReference extends ActiveRecordModelInstantiation { private ActiveRecordModelClass cls; - ActiveRecordModelClassSelfReference() { - exists(MethodBase m | - m = this.getCallable() and - m.getEnclosingModule() = cls and - m = cls.getAMethod() - ) and - // In a singleton method, `self` refers to the class itself rather than an - // instance of that class - not this.getSelfVariable().getDeclaringScope() instanceof SingletonMethod - } + ActiveRecordModelClassSelfReference() { this = cls.getClassNode().getAnOwnInstanceSelf() } final override ActiveRecordModelClass getClass() { result = cls } } @@ -342,7 +366,7 @@ private class ActiveRecordModelClassSelfReference extends ActiveRecordModelInsta class ActiveRecordInstance extends DataFlow::Node { private ActiveRecordModelInstantiation instantiation; - ActiveRecordInstance() { this = instantiation or instantiation.flowsTo(this) } + ActiveRecordInstance() { this = instantiation.track().getAValueReachableFromSource() } /** Gets the `ActiveRecordModelClass` that this is an instance of. */ ActiveRecordModelClass getClass() { result = instantiation.getClass() } @@ -380,12 +404,12 @@ private module Persistence { /** A call to e.g. `User.create(name: "foo")` */ private class CreateLikeCall extends DataFlow::CallNode, PersistentWriteAccess::Range { CreateLikeCall() { - exists(this.asExpr().getExpr().(ActiveRecordModelClassMethodCall).getReceiverClass()) and - this.getMethodName() = - [ - "create", "create!", "create_or_find_by", "create_or_find_by!", "find_or_create_by", - "find_or_create_by!", "insert", "insert!" - ] + this = + activeRecordBaseClass() + .getAMethodCall([ + "create", "create!", "create_or_find_by", "create_or_find_by!", "find_or_create_by", + "find_or_create_by!", "insert", "insert!" + ]) } override DataFlow::Node getValue() { @@ -402,8 +426,7 @@ private module Persistence { /** A call to e.g. `User.update(1, name: "foo")` */ private class UpdateLikeClassMethodCall extends DataFlow::CallNode, PersistentWriteAccess::Range { UpdateLikeClassMethodCall() { - exists(this.asExpr().getExpr().(ActiveRecordModelClassMethodCall).getReceiverClass()) and - this.getMethodName() = ["update", "update!", "upsert"] + this = activeRecordBaseClass().getAMethodCall(["update", "update!", "upsert"]) } override DataFlow::Node getValue() { @@ -448,10 +471,7 @@ private module Persistence { * ``` */ private class TouchAllCall extends DataFlow::CallNode, PersistentWriteAccess::Range { - TouchAllCall() { - exists(this.asExpr().getExpr().(ActiveRecordModelClassMethodCall).getReceiverClass()) and - this.getMethodName() = "touch_all" - } + TouchAllCall() { this = activeRecordQueryBuilderCall("touch_all") } override DataFlow::Node getValue() { result = this.getKeywordArgument("time") } } @@ -461,8 +481,7 @@ private module Persistence { private ExprNodes::ArrayLiteralCfgNode arr; InsertAllLikeCall() { - exists(this.asExpr().getExpr().(ActiveRecordModelClassMethodCall).getReceiverClass()) and - this.getMethodName() = ["insert_all", "insert_all!", "upsert_all"] and + this = activeRecordBaseClass().getAMethodCall(["insert_all", "insert_all!", "upsert_all"]) and arr = this.getArgument(0).asExpr() } diff --git a/ruby/ql/lib/codeql/ruby/frameworks/ActiveResource.qll b/ruby/ql/lib/codeql/ruby/frameworks/ActiveResource.qll index 96219915770..9f0e0f4b859 100644 --- a/ruby/ql/lib/codeql/ruby/frameworks/ActiveResource.qll +++ b/ruby/ql/lib/codeql/ruby/frameworks/ActiveResource.qll @@ -18,8 +18,12 @@ module ActiveResource { * An ActiveResource model class. This is any (transitive) subclass of ActiveResource. */ pragma[nomagic] - private API::Node modelApiNode() { - result = API::getTopLevelMember("ActiveResource").getMember("Base").getASubclass() + private API::Node activeResourceBaseClass() { + result = API::getTopLevelMember("ActiveResource").getMember("Base") + } + + private DataFlow::ClassNode activeResourceClass() { + result = activeResourceBaseClass().getADescendentModule() } /** @@ -30,16 +34,8 @@ module ActiveResource { * end * ``` */ - class ModelClass extends ClassDeclaration { - API::Node model; - - ModelClass() { - model = modelApiNode() and - this.getSuperclassExpr() = model.getAValueReachableFromSource().asExpr().getExpr() - } - - /** Gets the API node for this model */ - API::Node getModelApiNode() { result = model } + class ModelClassNode extends DataFlow::ClassNode { + ModelClassNode() { this = activeResourceClass() } /** Gets a call to `site=`, which sets the base URL for this model. */ SiteAssignCall getASiteAssignment() { result.getModelClass() = this } @@ -49,6 +45,46 @@ module ActiveResource { c = this.getASiteAssignment() and c.disablesCertificateValidation() } + + /** Gets a method call on this class that returns an instance of the class. */ + private DataFlow::CallNode getAChainedCall() { + result.(FindCall).getModelClass() = this + or + result.(CreateCall).getModelClass() = this + or + result.(CustomHttpCall).getModelClass() = this + or + result.(CollectionCall).getCollection().getModelClass() = this and + result.getMethodName() = ["first", "last"] + } + + /** Gets an API node referring to an instance of this class. */ + API::Node getAnInstanceReference() { + result = this.trackInstance() + or + result = this.getAChainedCall().track() + } + } + + /** DEPRECATED. Use `ModelClassNode` instead. */ + deprecated class ModelClass extends ClassDeclaration { + private ModelClassNode cls; + + ModelClass() { this = cls.getADeclaration() } + + /** Gets the class for which this is a declaration. */ + ModelClassNode getClassNode() { result = cls } + + /** Gets the API node for this class object. */ + deprecated API::Node getModelApiNode() { result = cls.trackModule() } + + /** Gets a call to `site=`, which sets the base URL for this model. */ + SiteAssignCall getASiteAssignment() { result = cls.getASiteAssignment() } + + /** Holds if `c` sets a base URL which does not use HTTPS. */ + predicate disablesCertificateValidation(SiteAssignCall c) { + cls.disablesCertificateValidation(c) + } } /** @@ -62,25 +98,20 @@ module ActiveResource { * ``` */ class ModelClassMethodCall extends DataFlow::CallNode { - API::Node model; + private ModelClassNode cls; - ModelClassMethodCall() { - model = modelApiNode() and - this = classMethodCall(model, _) - } + ModelClassMethodCall() { this = cls.trackModule().getAMethodCall(_) } /** Gets the model class for this call. */ - ModelClass getModelClass() { result.getModelApiNode() = model } + ModelClassNode getModelClass() { result = cls } } /** * A call to `site=` on an ActiveResource model class. * This sets the base URL for all HTTP requests made by this class. */ - private class SiteAssignCall extends DataFlow::CallNode { - API::Node model; - - SiteAssignCall() { model = modelApiNode() and this = classMethodCall(model, "site=") } + private class SiteAssignCall extends ModelClassMethodCall { + SiteAssignCall() { this.getMethodName() = "site=" } /** * Gets a node that contributes to the URLs used for HTTP requests by the parent @@ -88,12 +119,10 @@ module ActiveResource { */ DataFlow::Node getAUrlPart() { result = this.getArgument(0) } - /** Gets the model class for this call. */ - ModelClass getModelClass() { result.getModelApiNode() = model } - /** Holds if this site value specifies HTTP rather than HTTPS. */ predicate disablesCertificateValidation() { this.getAUrlPart() + // TODO: We should not need all this just to get the string value .asExpr() .(ExprNodes::AssignExprCfgNode) .getRhs() @@ -141,87 +170,70 @@ module ActiveResource { } /** + * DEPRECATED. Use `ModelClassNode.getAnInstanceReference()` instead. + * * An ActiveResource model object. */ - class ModelInstance extends DataFlow::Node { - ModelClass cls; + deprecated class ModelInstance extends DataFlow::Node { + private ModelClassNode cls; - ModelInstance() { - exists(API::Node model | model = modelApiNode() | - this = model.getInstance().getAValueReachableFromSource() and - cls.getModelApiNode() = model - ) - or - exists(FindCall call | call.flowsTo(this) | cls = call.getModelClass()) - or - exists(CreateCall call | call.flowsTo(this) | cls = call.getModelClass()) - or - exists(CustomHttpCall call | call.flowsTo(this) | cls = call.getModelClass()) - or - exists(CollectionCall call | - call.getMethodName() = ["first", "last"] and - call.flowsTo(this) - | - cls = call.getCollection().getModelClass() - ) - } + ModelInstance() { this = cls.getAnInstanceReference().getAValueReachableFromSource() } /** Gets the model class for this instance. */ - ModelClass getModelClass() { result = cls } + ModelClassNode getModelClass() { result = cls } } /** * A call to a method on an ActiveResource model object. */ class ModelInstanceMethodCall extends DataFlow::CallNode { - ModelInstance i; + private ModelClassNode cls; - ModelInstanceMethodCall() { this.getReceiver() = i } + ModelInstanceMethodCall() { this = cls.getAnInstanceReference().getAMethodCall(_) } /** Gets the model instance for this call. */ - ModelInstance getInstance() { result = i } + deprecated ModelInstance getInstance() { result = this.getReceiver() } /** Gets the model class for this call. */ - ModelClass getModelClass() { result = i.getModelClass() } + ModelClassNode getModelClass() { result = cls } } /** - * A collection of ActiveResource model objects. + * DEPRECATED. Use `CollectionSource` instead. + * + * A data flow node that may refer to a collection of ActiveResource model objects. */ - class Collection extends DataFlow::Node { - ModelClassMethodCall classMethodCall; + deprecated class Collection extends DataFlow::Node { + Collection() { this = any(CollectionSource src).track().getAValueReachableFromSource() } + } - Collection() { - classMethodCall.flowsTo(this) and - ( - classMethodCall.getMethodName() = "all" - or - classMethodCall.getMethodName() = "find" and - classMethodCall.getArgument(0).asExpr().getConstantValue().isStringlikeValue("all") - ) + /** + * A call that returns a collection of ActiveResource model objects. + */ + class CollectionSource extends ModelClassMethodCall { + CollectionSource() { + this.getMethodName() = "all" + or + this.getArgument(0).asExpr().getConstantValue().isStringlikeValue("all") } - - /** Gets the model class for this collection. */ - ModelClass getModelClass() { result = classMethodCall.getModelClass() } } /** * A method call on a collection. */ class CollectionCall extends DataFlow::CallNode { - CollectionCall() { this.getReceiver() instanceof Collection } + private CollectionSource collection; + + CollectionCall() { this = collection.track().getAMethodCall(_) } /** Gets the collection for this call. */ - Collection getCollection() { result = this.getReceiver() } + CollectionSource getCollection() { result = collection } } private class ModelClassMethodCallAsHttpRequest extends Http::Client::Request::Range, ModelClassMethodCall { - ModelClass cls; - ModelClassMethodCallAsHttpRequest() { - this.getModelClass() = cls and this.getMethodName() = ["all", "build", "create", "create!", "find", "first", "last"] } @@ -230,12 +242,14 @@ module ActiveResource { override predicate disablesCertificateValidation( DataFlow::Node disablingNode, DataFlow::Node argumentOrigin ) { - cls.disablesCertificateValidation(disablingNode) and + this.getModelClass().disablesCertificateValidation(disablingNode) and // TODO: highlight real argument origin argumentOrigin = disablingNode } - override DataFlow::Node getAUrlPart() { result = cls.getASiteAssignment().getAUrlPart() } + override DataFlow::Node getAUrlPart() { + result = this.getModelClass().getASiteAssignment().getAUrlPart() + } override DataFlow::Node getResponseBody() { result = this } } @@ -243,10 +257,7 @@ module ActiveResource { private class ModelInstanceMethodCallAsHttpRequest extends Http::Client::Request::Range, ModelInstanceMethodCall { - ModelClass cls; - ModelInstanceMethodCallAsHttpRequest() { - this.getModelClass() = cls and this.getMethodName() = [ "exists?", "reload", "save", "save!", "destroy", "delete", "get", "patch", "post", "put", @@ -259,42 +270,15 @@ module ActiveResource { override predicate disablesCertificateValidation( DataFlow::Node disablingNode, DataFlow::Node argumentOrigin ) { - cls.disablesCertificateValidation(disablingNode) and + this.getModelClass().disablesCertificateValidation(disablingNode) and // TODO: highlight real argument origin argumentOrigin = disablingNode } - override DataFlow::Node getAUrlPart() { result = cls.getASiteAssignment().getAUrlPart() } + override DataFlow::Node getAUrlPart() { + result = this.getModelClass().getASiteAssignment().getAUrlPart() + } override DataFlow::Node getResponseBody() { result = this } } - - /** - * A call to a class method. - * - * TODO: is this general enough to be useful elsewhere? - * - * Examples: - * ```rb - * class A - * def self.m; end - * - * m # call - * end - * - * A.m # call - * ``` - */ - private DataFlow::CallNode classMethodCall(API::Node classNode, string methodName) { - // A.m - result = classNode.getAMethodCall(methodName) - or - // class A - // A.m - // end - result.getReceiver().asExpr() instanceof ExprNodes::SelfVariableAccessCfgNode and - result.asExpr().getExpr().getEnclosingModule().(ClassDeclaration).getSuperclassExpr() = - classNode.getAValueReachableFromSource().asExpr().getExpr() and - result.getMethodName() = methodName - } } diff --git a/ruby/ql/lib/codeql/ruby/frameworks/GraphQL.qll b/ruby/ql/lib/codeql/ruby/frameworks/GraphQL.qll index 8e673c4255d..98fbe241404 100644 --- a/ruby/ql/lib/codeql/ruby/frameworks/GraphQL.qll +++ b/ruby/ql/lib/codeql/ruby/frameworks/GraphQL.qll @@ -39,13 +39,8 @@ private API::Node graphQlSchema() { result = API::getTopLevelMember("GraphQL").g */ private class GraphqlRelayClassicMutationClass extends ClassDeclaration { GraphqlRelayClassicMutationClass() { - this.getSuperclassExpr() = - graphQlSchema() - .getMember("RelayClassicMutation") - .getASubclass() - .getAValueReachableFromSource() - .asExpr() - .getExpr() + this = + graphQlSchema().getMember("RelayClassicMutation").getADescendentModule().getADeclaration() } } @@ -74,13 +69,7 @@ private class GraphqlRelayClassicMutationClass extends ClassDeclaration { */ private class GraphqlSchemaResolverClass extends ClassDeclaration { GraphqlSchemaResolverClass() { - this.getSuperclassExpr() = - graphQlSchema() - .getMember("Resolver") - .getASubclass() - .getAValueReachableFromSource() - .asExpr() - .getExpr() + this = graphQlSchema().getMember("Resolver").getADescendentModule().getADeclaration() } } @@ -103,13 +92,7 @@ private string getASupportedHttpMethod() { result = ["get", "post"] } */ class GraphqlSchemaObjectClass extends ClassDeclaration { GraphqlSchemaObjectClass() { - this.getSuperclassExpr() = - graphQlSchema() - .getMember("Object") - .getASubclass() - .getAValueReachableFromSource() - .asExpr() - .getExpr() + this = graphQlSchema().getMember("Object").getADescendentModule().getADeclaration() } /** Gets a `GraphqlFieldDefinitionMethodCall` called in this class. */ diff --git a/ruby/ql/lib/codeql/ruby/frameworks/Rack.qll b/ruby/ql/lib/codeql/ruby/frameworks/Rack.qll index 49281c609bd..6963f37a81c 100644 --- a/ruby/ql/lib/codeql/ruby/frameworks/Rack.qll +++ b/ruby/ql/lib/codeql/ruby/frameworks/Rack.qll @@ -2,47 +2,13 @@ * Provides modeling for the Rack library. */ -private import codeql.ruby.controlflow.CfgNodes::ExprNodes -private import codeql.ruby.DataFlow -private import codeql.ruby.typetracking.TypeTracker - /** * Provides modeling for the Rack library. */ module Rack { - /** - * A class that may be a rack application. - * This is a class that has a `call` method that takes a single argument - * (traditionally called `env`) and returns a rack-compatible response. - */ - class AppCandidate extends DataFlow::ClassNode { - private DataFlow::MethodNode call; + import rack.internal.App + import rack.internal.Response::Public as Response - AppCandidate() { - call = this.getInstanceMethod("call") and - call.getNumberOfParameters() = 1 and - call.getAReturnNode() = trackRackResponse() - } - - /** - * Gets the environment of the request, which is the lone parameter to the `call` method. - */ - DataFlow::ParameterNode getEnv() { result = call.getParameter(0) } - } - - private predicate isRackResponse(DataFlow::Node r) { - // [status, headers, body] - r.asExpr().(ArrayLiteralCfgNode).getNumberOfArguments() = 3 - } - - private DataFlow::LocalSourceNode trackRackResponse(TypeTracker t) { - t.start() and - isRackResponse(result) - or - exists(TypeTracker t2 | result = trackRackResponse(t2).track(t2, t)) - } - - private DataFlow::Node trackRackResponse() { - trackRackResponse(TypeTracker::end()).flowsTo(result) - } + /** DEPRECATED: Alias for App::AppCandidate */ + deprecated class AppCandidate = App::AppCandidate; } diff --git a/ruby/ql/lib/codeql/ruby/frameworks/Sinatra.qll b/ruby/ql/lib/codeql/ruby/frameworks/Sinatra.qll index 4b4f1bbd404..ef9a9392527 100644 --- a/ruby/ql/lib/codeql/ruby/frameworks/Sinatra.qll +++ b/ruby/ql/lib/codeql/ruby/frameworks/Sinatra.qll @@ -105,15 +105,25 @@ module Sinatra { * Gets the template file referred to by `erbCall`. * This works on the AST level to avoid non-monotonic reecursion in `ErbLocalsHashSyntheticGlobal`. */ + pragma[nomagic] private ErbFile getTemplateFile(MethodCall erbCall) { erbCall.getMethodName() = "erb" and result.getTemplateName() = erbCall.getArgument(0).getConstantValue().getStringlikeValue() and result.getRelativePath().matches("%views/%") } + pragma[nomagic] + private predicate erbCallAtLocation(MethodCall erbCall, ErbFile erbFile, Location l) { + erbCall.getMethodName() = "erb" and + erbFile = getTemplateFile(erbCall) and + l = erbCall.getLocation() + } + /** * Like `Location.toString`, but displays the relative path rather than the full path. */ + bindingset[loc] + pragma[inline_late] private string locationRelativePathToString(Location loc) { result = loc.getFile().getRelativePath() + "@" + loc.getStartLine() + ":" + loc.getStartColumn() + ":" + @@ -121,7 +131,7 @@ module Sinatra { } /** - * A synthetic global representing the hash of local variables passed to an ERB template. + * A synthetic global representing the hash of local variables passed to an ERB template. */ class ErbLocalsHashSyntheticGlobal extends SummaryComponent::SyntheticGlobal { private string id; @@ -129,10 +139,11 @@ module Sinatra { private ErbFile erbFile; ErbLocalsHashSyntheticGlobal() { - this = "SinatraErbLocalsHash(" + id + ")" and - id = erbFile.getRelativePath() + "," + locationRelativePathToString(erbCall.getLocation()) and - erbCall.getMethodName() = "erb" and - erbFile = getTemplateFile(erbCall) + exists(Location l | + erbCallAtLocation(erbCall, erbFile, l) and + id = erbFile.getRelativePath() + "," + locationRelativePathToString(l) and + this = "SinatraErbLocalsHash(" + id + ")" + ) } /** diff --git a/ruby/ql/lib/codeql/ruby/frameworks/Sqlite3.qll b/ruby/ql/lib/codeql/ruby/frameworks/Sqlite3.qll index 70744d6fcc8..dfb93a0e6e5 100644 --- a/ruby/ql/lib/codeql/ruby/frameworks/Sqlite3.qll +++ b/ruby/ql/lib/codeql/ruby/frameworks/Sqlite3.qll @@ -15,21 +15,20 @@ private import codeql.ruby.Concepts * https://github.com/sparklemotion/sqlite3-ruby */ module Sqlite3 { + private API::Node databaseConst() { + result = API::getTopLevelMember("SQLite3").getMember("Database") + } + + private API::Node dbInstance() { + result = databaseConst().getInstance() + or + // e.g. SQLite3::Database.new("foo.db") |db| { db.some_method } + result = databaseConst().getMethod("new").getBlock().getParameter(0) + } + /** Gets a method call with a receiver that is a database instance. */ private DataFlow::CallNode getADatabaseMethodCall(string methodName) { - exists(API::Node dbInstance | - dbInstance = API::getTopLevelMember("SQLite3").getMember("Database").getInstance() and - ( - result = dbInstance.getAMethodCall(methodName) - or - // e.g. SQLite3::Database.new("foo.db") |db| { db.some_method } - exists(DataFlow::BlockNode block | - result.getMethodName() = methodName and - block = dbInstance.getAValueReachableFromSource().(DataFlow::CallNode).getBlock() and - block.getParameter(0).flowsTo(result.getReceiver()) - ) - ) - ) + result = dbInstance().getAMethodCall(methodName) } /** A prepared but unexecuted SQL statement. */ diff --git a/ruby/ql/lib/codeql/ruby/frameworks/Twirp.qll b/ruby/ql/lib/codeql/ruby/frameworks/Twirp.qll index 73ef87f5fd5..7b8648bd2b1 100644 --- a/ruby/ql/lib/codeql/ruby/frameworks/Twirp.qll +++ b/ruby/ql/lib/codeql/ruby/frameworks/Twirp.qll @@ -16,50 +16,28 @@ module Twirp { /** * A Twirp service instantiation */ - class ServiceInstantiation extends DataFlow::CallNode { + deprecated class ServiceInstantiation extends DataFlow::CallNode { ServiceInstantiation() { this = API::getTopLevelMember("Twirp").getMember("Service").getAnInstantiation() } - /** - * Gets a local source node for the Service instantiation argument (the service handler). - */ - private DataFlow::LocalSourceNode getHandlerSource() { - result = this.getArgument(0).getALocalSource() - } - - /** - * Gets the API::Node for the service handler's class. - */ - private API::Node getAHandlerClassApiNode() { - result.getAnInstantiation() = this.getHandlerSource() - } - - /** - * Gets the AST module for the service handler's class. - */ - private Ast::Module getAHandlerClassAstNode() { - result = - this.getAHandlerClassApiNode() - .asSource() - .asExpr() - .(CfgNodes::ExprNodes::ConstantReadAccessCfgNode) - .getExpr() - .getModule() - } - /** * Gets a handler's method. */ - Ast::Method getAHandlerMethod() { - result = this.getAHandlerClassAstNode().getAnInstanceMethod() + DataFlow::MethodNode getAHandlerMethodNode() { + result = this.getArgument(0).backtrack().getMethod(_).asCallable() } + + /** + * Gets a handler's method as an AST node. + */ + Ast::Method getAHandlerMethod() { result = this.getAHandlerMethodNode().asCallableAstNode() } } /** * A Twirp client */ - class ClientInstantiation extends DataFlow::CallNode { + deprecated class ClientInstantiation extends DataFlow::CallNode { ClientInstantiation() { this = API::getTopLevelMember("Twirp").getMember("Client").getAnInstantiation() } @@ -67,7 +45,10 @@ module Twirp { /** The URL of a Twirp service, considered as a sink. */ class ServiceUrlAsSsrfSink extends ServerSideRequestForgery::Sink { - ServiceUrlAsSsrfSink() { exists(ClientInstantiation c | c.getArgument(0) = this) } + ServiceUrlAsSsrfSink() { + this = + API::getTopLevelMember("Twirp").getMember("Client").getMethod("new").getArgument(0).asSink() + } } /** A parameter that will receive parts of the url when handling an incoming request. */ @@ -75,7 +56,14 @@ module Twirp { DataFlow::ParameterNode { UnmarshaledParameter() { - exists(ServiceInstantiation i | i.getAHandlerMethod().getParameter(0) = this.asParameter()) + this = + API::getTopLevelMember("Twirp") + .getMember("Service") + .getMethod("new") + .getArgument(0) + .getMethod(_) + .getParameter(0) + .asSource() } override string getSourceType() { result = "Twirp Unmarhaled Parameter" } diff --git a/ruby/ql/lib/codeql/ruby/frameworks/actiondispatch/internal/Request.qll b/ruby/ql/lib/codeql/ruby/frameworks/actiondispatch/internal/Request.qll index d749b87f273..8c6a11b455b 100644 --- a/ruby/ql/lib/codeql/ruby/frameworks/actiondispatch/internal/Request.qll +++ b/ruby/ql/lib/codeql/ruby/frameworks/actiondispatch/internal/Request.qll @@ -128,7 +128,7 @@ module Request { private import codeql.ruby.frameworks.Rack private class RackEnv extends Env { - RackEnv() { this = any(Rack::AppCandidate app).getEnv().getALocalUse() } + RackEnv() { this = any(Rack::App::AppCandidate app).getEnv().getALocalUse() } } /** diff --git a/ruby/ql/lib/codeql/ruby/frameworks/core/Gem.qll b/ruby/ql/lib/codeql/ruby/frameworks/core/Gem.qll index 4095beb10af..f7345b22ed1 100644 --- a/ruby/ql/lib/codeql/ruby/frameworks/core/Gem.qll +++ b/ruby/ql/lib/codeql/ruby/frameworks/core/Gem.qll @@ -24,7 +24,7 @@ module Gem { GemSpec() { this.getExtension() = "gemspec" and - specCall = API::root().getMember("Gem").getMember("Specification").getMethod("new") and + specCall = API::getTopLevelMember("Gem").getMember("Specification").getMethod("new") and specCall.getLocation().getFile() = this } @@ -42,7 +42,7 @@ module Gem { .getBlock() .getParameter(0) .getMethod(name + "=") - .getParameter(0) + .getArgument(0) .asSink() .asExpr() .getExpr() diff --git a/ruby/ql/lib/codeql/ruby/frameworks/core/Kernel.qll b/ruby/ql/lib/codeql/ruby/frameworks/core/Kernel.qll index b445521adb8..ad87ee37ecd 100644 --- a/ruby/ql/lib/codeql/ruby/frameworks/core/Kernel.qll +++ b/ruby/ql/lib/codeql/ruby/frameworks/core/Kernel.qll @@ -19,7 +19,8 @@ module Kernel { */ class KernelMethodCall extends DataFlow::CallNode { KernelMethodCall() { - this = API::getTopLevelMember("Kernel").getAMethodCall(_) + // Match Kernel calls using local flow, to avoid finding singleton calls on subclasses + this = DataFlow::getConstant("Kernel").getAMethodCall(_) or this.asExpr().getExpr() instanceof UnknownMethodCall and ( diff --git a/ruby/ql/lib/codeql/ruby/frameworks/data/ModelsAsData.qll b/ruby/ql/lib/codeql/ruby/frameworks/data/ModelsAsData.qll index 2e3fa21a45b..21bc5f69dcb 100644 --- a/ruby/ql/lib/codeql/ruby/frameworks/data/ModelsAsData.qll +++ b/ruby/ql/lib/codeql/ruby/frameworks/data/ModelsAsData.qll @@ -44,7 +44,7 @@ private class SummarizedCallableFromModel extends SummarizedCallable { override Call getACall() { exists(API::MethodAccessNode base | ModelOutput::resolvedSummaryBase(type, path, base) and - result = base.getCallNode().asExpr().getExpr() + result = base.asCall().asExpr().getExpr() ) } diff --git a/ruby/ql/lib/codeql/ruby/frameworks/data/internal/ApiGraphModels.qll b/ruby/ql/lib/codeql/ruby/frameworks/data/internal/ApiGraphModels.qll index 227f4ea22fb..2e598711fcc 100644 --- a/ruby/ql/lib/codeql/ruby/frameworks/data/internal/ApiGraphModels.qll +++ b/ruby/ql/lib/codeql/ruby/frameworks/data/internal/ApiGraphModels.qll @@ -643,6 +643,15 @@ module ModelOutput { baseNode = getInvocationFromPath(type, path) } + /** + * Holds if a `baseNode` is a callable identified by the `type,path` part of a summary row. + */ + cached + predicate resolvedSummaryRefBase(string type, string path, API::Node baseNode) { + summaryModel(type, path, _, _, _) and + baseNode = getNodeFromPath(type, path) + } + /** * Holds if `node` is seen as an instance of `type` due to a type definition * contributed by a CSV model. @@ -653,6 +662,17 @@ module ModelOutput { import Cached import Specific::ModelOutputSpecific + private import codeql.mad.ModelValidation as SharedModelVal + + private module KindValConfig implements SharedModelVal::KindValidationConfigSig { + predicate summaryKind(string kind) { summaryModel(_, _, _, _, kind) } + + predicate sinkKind(string kind) { sinkModel(_, _, kind) } + + predicate sourceKind(string kind) { sourceModel(_, _, kind) } + } + + private module KindVal = SharedModelVal::KindValidation; /** * Gets an error message relating to an invalid CSV row in a model. @@ -698,5 +718,8 @@ module ModelOutput { not isValidNoArgumentTokenInIdentifyingAccessPath(token.getName()) and result = "Invalid token '" + token + "' is missing its arguments, in access path: " + path ) + or + // Check for invalid model kinds + result = KindVal::getInvalidModelKind() } } diff --git a/ruby/ql/lib/codeql/ruby/frameworks/data/internal/ApiGraphModelsSpecific.qll b/ruby/ql/lib/codeql/ruby/frameworks/data/internal/ApiGraphModelsSpecific.qll index 4c03522a9c5..ed7a331c452 100644 --- a/ruby/ql/lib/codeql/ruby/frameworks/data/internal/ApiGraphModelsSpecific.qll +++ b/ruby/ql/lib/codeql/ruby/frameworks/data/internal/ApiGraphModelsSpecific.qll @@ -99,9 +99,10 @@ API::Node getExtraNodeFromPath(string type, AccessPath path, int n) { // A row of form `any;Method[foo]` should match any method named `foo`. type = "any" and n = 1 and - exists(EntryPointFromAnyType entry | - methodMatchedByName(path, entry.getName()) and - result = entry.getANode() + exists(string methodName, DataFlow::CallNode call | + methodMatchedByName(path, methodName) and + call.getMethodName() = methodName and + result.(API::MethodAccessNode).asCall() = call ) } @@ -112,20 +113,10 @@ API::Node getExtraNodeFromType(string type) { constRef = getConstantFromConstPath(consts) | suffix = "!" and - ( - result.(API::Node::Internal).asSourceInternal() = constRef - or - result.(API::Node::Internal).asSourceInternal() = - constRef.getADescendentModule().getAnOwnModuleSelf() - ) + result = constRef.track() or suffix = "" and - ( - result.(API::Node::Internal).asSourceInternal() = constRef.getAMethodCall("new") - or - result.(API::Node::Internal).asSourceInternal() = - constRef.getADescendentModule().getAnInstanceSelf() - ) + result = constRef.track().getInstance() ) or type = "" and @@ -145,21 +136,6 @@ private predicate methodMatchedByName(AccessPath path, string methodName) { ) } -/** - * An API graph entry point corresponding to a method name such as `foo` in `;any;Method[foo]`. - * - * This ensures that the API graph rooted in that method call is materialized. - */ -private class EntryPointFromAnyType extends API::EntryPoint { - string name; - - EntryPointFromAnyType() { this = "AnyMethod[" + name + "]" and methodMatchedByName(_, name) } - - override DataFlow::CallNode getACall() { result.getMethodName() = name } - - string getName() { result = name } -} - /** * Gets a Ruby-specific API graph successor of `node` reachable by resolving `token`. */ @@ -175,9 +151,11 @@ API::Node getExtraSuccessorFromNode(API::Node node, AccessPathToken token) { result = node.getInstance() or token.getName() = "Parameter" and - result = - node.getASuccessor(API::Label::getLabelFromParameterPosition(FlowSummaryImplSpecific::parseArgBody(token - .getAnArgument()))) + exists(DataFlowDispatch::ArgumentPosition argPos, DataFlowDispatch::ParameterPosition paramPos | + argPos = FlowSummaryImplSpecific::parseParamBody(token.getAnArgument()) and + DataFlowDispatch::parameterMatch(paramPos, argPos) and + result = node.getParameterAtPosition(paramPos) + ) or exists(DataFlow::ContentSet contents | SummaryComponent::content(contents) = FlowSummaryImplSpecific::interpretComponentSpecific(token) and @@ -191,9 +169,11 @@ API::Node getExtraSuccessorFromNode(API::Node node, AccessPathToken token) { bindingset[token] API::Node getExtraSuccessorFromInvoke(InvokeNode node, AccessPathToken token) { token.getName() = "Argument" and - result = - node.getASuccessor(API::Label::getLabelFromArgumentPosition(FlowSummaryImplSpecific::parseParamBody(token - .getAnArgument()))) + exists(DataFlowDispatch::ArgumentPosition argPos, DataFlowDispatch::ParameterPosition paramPos | + paramPos = FlowSummaryImplSpecific::parseArgBody(token.getAnArgument()) and + DataFlowDispatch::parameterMatch(paramPos, argPos) and + result = node.getArgumentAtPosition(argPos) + ) } /** @@ -211,7 +191,7 @@ predicate invocationMatchesExtraCallSiteFilter(InvokeNode invoke, AccessPathToke /** An API graph node representing a method call. */ class InvokeNode extends API::MethodAccessNode { /** Gets the number of arguments to the call. */ - int getNumArgument() { result = this.getCallNode().getNumberOfArguments() } + int getNumArgument() { result = this.asCall().getNumberOfArguments() } } /** Gets the `InvokeNode` corresponding to a specific invocation of `node`. */ diff --git a/ruby/ql/lib/codeql/ruby/frameworks/rack/internal/App.qll b/ruby/ql/lib/codeql/ruby/frameworks/rack/internal/App.qll new file mode 100644 index 00000000000..bfdd988ac19 --- /dev/null +++ b/ruby/ql/lib/codeql/ruby/frameworks/rack/internal/App.qll @@ -0,0 +1,53 @@ +/** + * Provides modeling for Rack applications. + */ + +private import codeql.ruby.ApiGraphs +private import codeql.ruby.DataFlow +private import codeql.ruby.typetracking.TypeTracker +private import Response::Private as RP + +/** A method node for a method named `call`. */ +private class CallMethodNode extends DataFlow::MethodNode { + CallMethodNode() { this.getMethodName() = "call" } +} + +private DataFlow::LocalSourceNode trackRackResponse(TypeBackTracker t, CallMethodNode call) { + t.start() and + result = call.getAReturnNode().getALocalSource() + or + exists(TypeBackTracker t2 | result = trackRackResponse(t2, call).backtrack(t2, t)) +} + +private RP::PotentialResponseNode trackRackResponse(CallMethodNode call) { + result = trackRackResponse(TypeBackTracker::end(), call) +} + +/** + * Provides modeling for Rack applications. + */ +module App { + /** + * A class that may be a rack application. + * This is a class that has a `call` method that takes a single argument + * (traditionally called `env`) and returns a rack-compatible response. + */ + class AppCandidate extends DataFlow::ClassNode { + private CallMethodNode call; + private RP::PotentialResponseNode resp; + + AppCandidate() { + call = this.getInstanceMethod("call") and + call.getNumberOfParameters() = 1 and + resp = trackRackResponse(call) + } + + /** + * Gets the environment of the request, which is the lone parameter to the `call` method. + */ + DataFlow::ParameterNode getEnv() { result = call.getParameter(0) } + + /** Gets the response returned from a request to this application. */ + RP::PotentialResponseNode getResponse() { result = resp } + } +} diff --git a/ruby/ql/lib/codeql/ruby/frameworks/rack/internal/Response.qll b/ruby/ql/lib/codeql/ruby/frameworks/rack/internal/Response.qll new file mode 100644 index 00000000000..9d998c780ae --- /dev/null +++ b/ruby/ql/lib/codeql/ruby/frameworks/rack/internal/Response.qll @@ -0,0 +1,82 @@ +/** + * Provides modeling for the `Response` component of the `Rack` library. + */ + +private import codeql.ruby.AST +private import codeql.ruby.ApiGraphs +private import codeql.ruby.Concepts +private import codeql.ruby.controlflow.CfgNodes::ExprNodes +private import codeql.ruby.DataFlow +private import codeql.ruby.typetracking.TypeTracker +private import App as A + +/** Contains implementation details for modeling `Rack::Response`. */ +module Private { + /** A `DataFlow::Node` that may be a rack response. This is detected heuristically, if something "looks like" a rack response syntactically then we consider it to be a potential response node. */ + class PotentialResponseNode extends DataFlow::ArrayLiteralNode { + // [status, headers, body] + PotentialResponseNode() { this.getNumberOfArguments() = 3 } + + /** Gets the headers returned with this response. */ + DataFlow::Node getHeaders() { result = this.getElement(1) } + + /** Gets the body of this response. */ + DataFlow::Node getBody() { result = this.getElement(2) } + } +} + +/** + * Provides modeling for the `Response` component of the `Rack` library. + */ +module Public { + bindingset[headerName] + private DataFlow::Node getHeaderValue(ResponseNode resp, string headerName) { + exists(DataFlow::Node headers | headers = resp.getHeaders() | + // set via `headers.=` + exists( + DataFlow::CallNode contentTypeAssignment, Assignment assignment, + DataFlow::PostUpdateNode postUpdateHeaders + | + contentTypeAssignment.getMethodName() = headerName.replaceAll("-", "_").toLowerCase() + "=" and + assignment = + contentTypeAssignment.getArgument(0).(DataFlow::OperationNode).asOperationAstNode() and + postUpdateHeaders.(DataFlow::LocalSourceNode).flowsTo(headers) and + postUpdateHeaders.getPreUpdateNode() = contentTypeAssignment.getReceiver() + | + result.asExpr().getExpr() = assignment.getRightOperand() + ) + or + // set within a hash + exists(DataFlow::HashLiteralNode headersHash | headersHash.flowsTo(headers) | + result = + headersHash + .getElementFromKey(any(ConstantValue v | + v.getStringlikeValue().toLowerCase() = headerName.toLowerCase() + )) + ) + ) + } + + /** A `DataFlow::Node` returned from a rack request. */ + class ResponseNode extends Private::PotentialResponseNode, Http::Server::HttpResponse::Range { + ResponseNode() { this = any(A::App::AppCandidate app).getResponse() } + + override DataFlow::Node getBody() { result = this.getElement(2) } + + override DataFlow::Node getMimetypeOrContentTypeArg() { + result = getHeaderValue(this, "content-type") + } + + // TODO: is there a sensible value for this? + override string getMimetypeDefault() { none() } + } + + /** A `DataFlow::Node` returned from a rack request that has a redirect HTTP status code. */ + class RedirectResponse extends ResponseNode, Http::Server::HttpRedirectResponse::Range { + private DataFlow::Node redirectLocation; + + RedirectResponse() { redirectLocation = getHeaderValue(this, "location") } + + override DataFlow::Node getRedirectLocation() { result = redirectLocation } + } +} diff --git a/ruby/ql/lib/codeql/ruby/regexp/internal/ParseRegExp.qll b/ruby/ql/lib/codeql/ruby/regexp/internal/ParseRegExp.qll index 9160bf60506..d1f96ec407e 100644 --- a/ruby/ql/lib/codeql/ruby/regexp/internal/ParseRegExp.qll +++ b/ruby/ql/lib/codeql/ruby/regexp/internal/ParseRegExp.qll @@ -195,8 +195,8 @@ abstract class RegExp extends Ast::StringlikeLiteral { /** * Holds if the character set starting at `charset_start` contains a character range - * with lower bound found between `start` and `lower_end` - * and upper bound found between `upper_start` and `end`. + * with lower bound found between `start` and `lowerEnd` + * and upper bound found between `upperStart` and `end`. */ predicate charRange(int charsetStart, int start, int lowerEnd, int upperStart, int end) { exists(int index | @@ -844,11 +844,11 @@ abstract class RegExp extends Ast::StringlikeLiteral { } /** - * Holds if a qualified part is found between `start` and `part_end` and the qualifier is - * found between `part_end` and `end`. + * Holds if a qualified part is found between `start` and `partEnd` and the qualifier is + * found between `partEnd` and `end`. * - * `maybe_empty` is true if the part is optional. - * `may_repeat_forever` is true if the part may be repeated unboundedly. + * `maybeEmpty` is true if the part is optional. + * `mayRepeatForever` is true if the part may be repeated unboundedly. */ predicate qualifiedPart( int start, int partEnd, int end, boolean maybeEmpty, boolean mayRepeatForever diff --git a/ruby/ql/lib/codeql/ruby/security/InsecureDownloadQuery.qll b/ruby/ql/lib/codeql/ruby/security/InsecureDownloadQuery.qll index b701a3d1dd4..0b4c63f4392 100644 --- a/ruby/ql/lib/codeql/ruby/security/InsecureDownloadQuery.qll +++ b/ruby/ql/lib/codeql/ruby/security/InsecureDownloadQuery.qll @@ -13,7 +13,7 @@ import InsecureDownloadCustomizations::InsecureDownload /** * A taint tracking configuration for download of sensitive file through insecure connection. */ -class Configuration extends DataFlow::Configuration { +deprecated class Configuration extends DataFlow::Configuration { Configuration() { this = "InsecureDownload" } override predicate isSource(DataFlow::Node source, DataFlow::FlowState label) { @@ -29,3 +29,30 @@ class Configuration extends DataFlow::Configuration { node instanceof Sanitizer } } + +/** + * A taint tracking configuration for download of sensitive file through insecure connection. + */ +module Config implements DataFlow::StateConfigSig { + class FlowState = string; + + predicate isSource(DataFlow::Node source, DataFlow::FlowState label) { + source.(Source).getALabel() = label + } + + predicate isSink(DataFlow::Node sink, DataFlow::FlowState label) { + sink.(Sink).getALabel() = label + } + + predicate isBarrier(DataFlow::Node node) { node instanceof Sanitizer } + + predicate isBarrier(DataFlow::Node node, FlowState state) { none() } + + predicate isAdditionalFlowStep( + DataFlow::Node node1, FlowState state1, DataFlow::Node node2, FlowState state2 + ) { + none() + } +} + +module Flow = DataFlow::GlobalWithState; diff --git a/ruby/ql/lib/codeql/ruby/security/KernelOpenQuery.qll b/ruby/ql/lib/codeql/ruby/security/KernelOpenQuery.qll index 31c9055d7cc..3653fb23cee 100644 --- a/ruby/ql/lib/codeql/ruby/security/KernelOpenQuery.qll +++ b/ruby/ql/lib/codeql/ruby/security/KernelOpenQuery.qll @@ -55,7 +55,8 @@ class AmbiguousPathCall extends DataFlow::CallNode { } private predicate methodCallOnlyOnIO(DataFlow::CallNode node, string methodName) { - node = API::getTopLevelMember("IO").getAMethodCall(methodName) and + // Use local flow to find calls to 'IO' without subclasses + node = DataFlow::getConstant("IO").getAMethodCall(methodName) and not node = API::getTopLevelMember("File").getAMethodCall(methodName) // needed in e.g. opal/opal, where some calls have both paths (opal implements an own corelib) } diff --git a/ruby/ql/lib/codeql/ruby/security/OpenSSL.qll b/ruby/ql/lib/codeql/ruby/security/OpenSSL.qll index 261a7af462f..637f7dab04a 100644 --- a/ruby/ql/lib/codeql/ruby/security/OpenSSL.qll +++ b/ruby/ql/lib/codeql/ruby/security/OpenSSL.qll @@ -597,7 +597,7 @@ private module Digest { call = API::getTopLevelMember("OpenSSL").getMember("Digest").getMethod("new") | this = call.getReturn().getAMethodCall(["digest", "update", "<<"]) and - algo.matchesName(call.getCallNode() + algo.matchesName(call.asCall() .getArgument(0) .asExpr() .getExpr() @@ -619,7 +619,7 @@ private module Digest { Cryptography::HashingAlgorithm algo; DigestCallDirect() { - this = API::getTopLevelMember("OpenSSL").getMember("Digest").getMethod("digest").getCallNode() and + this = API::getTopLevelMember("OpenSSL").getMember("Digest").getMethod("digest").asCall() and algo.matchesName(this.getArgument(0).asExpr().getExpr().getConstantValue().getString()) } diff --git a/ruby/ql/lib/codeql/ruby/typetracking/ApiGraphShared.qll b/ruby/ql/lib/codeql/ruby/typetracking/ApiGraphShared.qll new file mode 100644 index 00000000000..07b10cb1303 --- /dev/null +++ b/ruby/ql/lib/codeql/ruby/typetracking/ApiGraphShared.qll @@ -0,0 +1,328 @@ +/** + * Parts of API graphs that can be shared with other dynamic languages. + * + * Depends on TypeTrackerSpecific for the corresponding language. + */ + +private import codeql.Locations +private import codeql.ruby.typetracking.TypeTracker +private import TypeTrackerSpecific + +/** + * The signature to use when instantiating `ApiGraphShared`. + * + * The implementor should define a newtype with at least three branches as follows: + * ```ql + * newtype TApiNode = + * MkForwardNode(LocalSourceNode node, TypeTracker t) { isReachable(node, t) } or + * MkBackwardNode(LocalSourceNode node, TypeTracker t) { isReachable(node, t) } or + * MkSinkNode(Node node) { ... } or + * ... + * ``` + * + * The three branches should be exposed through `getForwardNode`, `getBackwardNode`, and `getSinkNode`, respectively. + */ +signature module ApiGraphSharedSig { + /** A node in the API graph. */ + class ApiNode { + /** Gets a string representation of this API node. */ + string toString(); + + /** Gets the location associated with this API node, if any. */ + Location getLocation(); + } + + /** + * Gets the forward node with the given type-tracking state. + * + * This node will have outgoing epsilon edges to its type-tracking successors. + */ + ApiNode getForwardNode(TypeTrackingNode node, TypeTracker t); + + /** + * Gets the backward node with the given type-tracking state. + * + * This node will have outgoing epsilon edges to its type-tracking predecessors. + */ + ApiNode getBackwardNode(TypeTrackingNode node, TypeTracker t); + + /** + * Gets the sink node corresponding to `node`. + * + * Since sinks are not generally `LocalSourceNode`s, such nodes are materialised separately in order for + * the API graph to include representatives for sinks. Note that there is no corresponding case for "source" + * nodes as these are represented as forward nodes with initial-state type-trackers. + * + * Sink nodes have outgoing epsilon edges to the backward nodes corresponding to their local sources. + */ + ApiNode getSinkNode(Node node); + + /** + * Holds if a language-specific epsilon edge `pred -> succ` should be generated. + */ + predicate specificEpsilonEdge(ApiNode pred, ApiNode succ); +} + +/** + * Parts of API graphs that can be shared between language implementations. + */ +module ApiGraphShared { + private import S + + /** Gets a local source of `node`. */ + bindingset[node] + pragma[inline_late] + TypeTrackingNode getALocalSourceStrict(Node node) { result = node.getALocalSource() } + + cached + private module Cached { + /** + * Holds if there is an epsilon edge `pred -> succ`. + * + * That relation is reflexive, so `fastTC` produces the equivalent of a reflexive, transitive closure. + */ + pragma[noopt] + cached + predicate epsilonEdge(ApiNode pred, ApiNode succ) { + exists( + StepSummary summary, TypeTrackingNode predNode, TypeTracker predState, + TypeTrackingNode succNode, TypeTracker succState + | + StepSummary::stepCall(predNode, succNode, summary) + or + StepSummary::stepNoCall(predNode, succNode, summary) + | + pred = getForwardNode(predNode, predState) and + succState = StepSummary::append(predState, summary) and + succ = getForwardNode(succNode, succState) + or + succ = getBackwardNode(predNode, predState) and // swap order for backward flow + succState = StepSummary::append(predState, summary) and + pred = getBackwardNode(succNode, succState) // swap order for backward flow + ) + or + exists(Node sink, TypeTrackingNode localSource | + pred = getSinkNode(sink) and + localSource = getALocalSourceStrict(sink) and + succ = getBackwardStartNode(localSource) + ) + or + specificEpsilonEdge(pred, succ) + or + succ instanceof ApiNode and + succ = pred + } + + /** + * Holds if `pred` can reach `succ` by zero or more epsilon edges. + */ + cached + predicate epsilonStar(ApiNode pred, ApiNode succ) = fastTC(epsilonEdge/2)(pred, succ) + + /** Gets the API node to use when starting forward flow from `source` */ + cached + ApiNode forwardStartNode(TypeTrackingNode source) { + result = getForwardNode(source, TypeTracker::end(false)) + } + + /** Gets the API node to use when starting backward flow from `sink` */ + cached + ApiNode backwardStartNode(TypeTrackingNode sink) { + // There is backward flow A->B iff there is forward flow B->A. + // The starting point of backward flow corresponds to the end of a forward flow, and vice versa. + result = getBackwardNode(sink, TypeTracker::end(_)) + } + + /** Gets `node` as a data flow source. */ + cached + TypeTrackingNode asSourceCached(ApiNode node) { node = forwardEndNode(result) } + + /** Gets `node` as a data flow sink. */ + cached + Node asSinkCached(ApiNode node) { node = getSinkNode(result) } + } + + private import Cached + + /** Gets an API node corresponding to the end of forward-tracking to `localSource`. */ + pragma[nomagic] + private ApiNode forwardEndNode(TypeTrackingNode localSource) { + result = getForwardNode(localSource, TypeTracker::end(_)) + } + + /** Gets an API node corresponding to the end of backtracking to `localSource`. */ + pragma[nomagic] + private ApiNode backwardEndNode(TypeTrackingNode localSource) { + result = getBackwardNode(localSource, TypeTracker::end(false)) + } + + /** Gets a node reachable from `node` by zero or more epsilon edges, including `node` itself. */ + bindingset[node] + pragma[inline_late] + ApiNode getAnEpsilonSuccessorInline(ApiNode node) { epsilonStar(node, result) } + + /** Gets `node` as a data flow sink. */ + bindingset[node] + pragma[inline_late] + Node asSinkInline(ApiNode node) { result = asSinkCached(node) } + + /** Gets `node` as a data flow source. */ + bindingset[node] + pragma[inline_late] + TypeTrackingNode asSourceInline(ApiNode node) { result = asSourceCached(node) } + + /** Gets a value reachable from `source`. */ + bindingset[source] + pragma[inline_late] + Node getAValueReachableFromSourceInline(ApiNode source) { + exists(TypeTrackingNode src | + src = asSourceInline(getAnEpsilonSuccessorInline(source)) and + src.flowsTo(pragma[only_bind_into](result)) + ) + } + + /** Gets a value that can reach `sink`. */ + bindingset[sink] + pragma[inline_late] + Node getAValueReachingSinkInline(ApiNode sink) { + result = asSinkInline(getAnEpsilonSuccessorInline(sink)) + } + + /** + * Gets the starting point for forward-tracking at `node`. + * + * Should be used to obtain the successor of an edge when constructing labelled edges. + */ + bindingset[node] + pragma[inline_late] + ApiNode getForwardStartNode(Node node) { result = forwardStartNode(node) } + + /** + * Gets the starting point of backtracking from `node`. + * + * Should be used to obtain the successor of an edge when constructing labelled edges. + */ + bindingset[node] + pragma[inline_late] + ApiNode getBackwardStartNode(Node node) { result = backwardStartNode(node) } + + /** + * Gets a possible ending point of forward-tracking at `node`. + * + * Should be used to obtain the predecessor of an edge when constructing labelled edges. + * + * This is not backed by a `cached` predicate, and should only be used for materialising `cached` + * predicates in the API graph implementation - it should not be called in later stages. + */ + bindingset[node] + pragma[inline_late] + ApiNode getForwardEndNode(Node node) { result = forwardEndNode(node) } + + /** + * Gets a possible ending point backtracking to `node`. + * + * Should be used to obtain the predecessor of an edge when constructing labelled edges. + * + * This is not backed by a `cached` predicate, and should only be used for materialising `cached` + * predicates in the API graph implementation - it should not be called in later stages. + */ + bindingset[node] + pragma[inline_late] + ApiNode getBackwardEndNode(Node node) { result = backwardEndNode(node) } + + /** + * Gets a possible eding point of forward or backward tracking at `node`. + * + * Should be used to obtain the predecessor of an edge generated from store or load edges. + */ + bindingset[node] + pragma[inline_late] + ApiNode getForwardOrBackwardEndNode(Node node) { + result = getForwardEndNode(node) or result = getBackwardEndNode(node) + } + + /** Gets an API node for tracking forward starting at `node`. This is the implementation of `DataFlow::LocalSourceNode.track()` */ + bindingset[node] + pragma[inline_late] + ApiNode getNodeForForwardTracking(Node node) { result = forwardStartNode(node) } + + /** Gets an API node for backtracking starting at `node`. The implementation of `DataFlow::Node.backtrack()`. */ + bindingset[node] + pragma[inline_late] + ApiNode getNodeForBacktracking(Node node) { + result = getBackwardStartNode(getALocalSourceStrict(node)) + } + + /** Parts of the shared module to be re-exported by the user-facing `API` module. */ + module Public { + /** + * The signature to use when instantiating the `ExplainFlow` module. + */ + signature module ExplainFlowSig { + /** Holds if `node` should be a source. */ + predicate isSource(ApiNode node); + + /** Holds if `node` should be a sink. */ + default predicate isSink(ApiNode node) { any() } + + /** Holds if `node` should be skipped in the generated paths. */ + default predicate isHidden(ApiNode node) { none() } + } + + /** + * Module to help debug and visualize the data flows underlying API graphs. + * + * This module exports the query predicates for a path-problem query, and should be imported + * into the top-level of such a query. + * + * The module argument should specify source and sink API nodes, and the resulting query + * will show paths of epsilon edges that go from a source to a sink. Only epsilon edges are visualized. + * + * To condense the output a bit, paths in which the source and sink are the same node are omitted. + */ + module ExplainFlow { + private import T + + private ApiNode relevantNode() { + isSink(result) and + result = getAnEpsilonSuccessorInline(any(ApiNode node | isSource(node))) + or + epsilonEdge(result, relevantNode()) + } + + /** Holds if `node` is part of the graph to visualize. */ + query predicate nodes(ApiNode node) { node = relevantNode() and not isHidden(node) } + + private predicate edgeToHiddenNode(ApiNode pred, ApiNode succ) { + epsilonEdge(pred, succ) and + isHidden(succ) and + pred = relevantNode() and + succ = relevantNode() + } + + /** Holds if `pred -> succ` is an edge in the graph to visualize. */ + query predicate edges(ApiNode pred, ApiNode succ) { + nodes(pred) and + nodes(succ) and + exists(ApiNode mid | + edgeToHiddenNode*(pred, mid) and + epsilonEdge(mid, succ) + ) + } + + /** Holds for each source/sink pair to visualize in the graph. */ + query predicate problems( + ApiNode location, ApiNode sourceNode, ApiNode sinkNode, string message + ) { + nodes(sourceNode) and + nodes(sinkNode) and + isSource(sourceNode) and + isSink(sinkNode) and + sinkNode = getAnEpsilonSuccessorInline(sourceNode) and + sourceNode != sinkNode and + location = sinkNode and + message = "Node flows here" + } + } + } +} diff --git a/ruby/ql/lib/codeql/ruby/typetracking/TypeTracker.qll b/ruby/ql/lib/codeql/ruby/typetracking/TypeTracker.qll index 25521f5f1a5..001375b4dc5 100644 --- a/ruby/ql/lib/codeql/ruby/typetracking/TypeTracker.qll +++ b/ruby/ql/lib/codeql/ruby/typetracking/TypeTracker.qll @@ -55,10 +55,9 @@ private module Cached { ) } - pragma[nomagic] - private TypeTracker noContentTypeTracker(boolean hasCall) { - result = MkTypeTracker(hasCall, noContent()) - } + /** Gets a type tracker with no content and the call bit set to the given value. */ + cached + TypeTracker noContentTypeTracker(boolean hasCall) { result = MkTypeTracker(hasCall, noContent()) } /** Gets the summary resulting from appending `step` to type-tracking summary `tt`. */ cached @@ -235,17 +234,6 @@ private predicate stepProj(TypeTrackingNode nodeFrom, StepSummary summary) { step(nodeFrom, _, summary) } -bindingset[nodeFrom, t] -pragma[inline_late] -pragma[noopt] -private TypeTracker stepInlineLate(TypeTracker t, TypeTrackingNode nodeFrom, TypeTrackingNode nodeTo) { - exists(StepSummary summary | - stepProj(nodeFrom, summary) and - result = t.append(summary) and - step(nodeFrom, nodeTo, summary) - ) -} - private predicate smallstep(Node nodeFrom, TypeTrackingNode nodeTo, StepSummary summary) { smallstepNoCall(nodeFrom, nodeTo, summary) or @@ -257,17 +245,6 @@ private predicate smallstepProj(Node nodeFrom, StepSummary summary) { smallstep(nodeFrom, _, summary) } -bindingset[nodeFrom, t] -pragma[inline_late] -pragma[noopt] -private TypeTracker smallstepInlineLate(TypeTracker t, Node nodeFrom, Node nodeTo) { - exists(StepSummary summary | - smallstepProj(nodeFrom, summary) and - result = t.append(summary) and - smallstep(nodeFrom, nodeTo, summary) - ) -} - /** * Holds if `nodeFrom` is being written to the `content` of the object in `nodeTo`. * @@ -340,6 +317,8 @@ class StepSummary extends TStepSummary { /** Provides predicates for updating step summaries (`StepSummary`s). */ module StepSummary { + predicate append = Cached::append/2; + /** * Gets the summary that corresponds to having taken a forwards * inter-procedural step from `nodeFrom` to `nodeTo`. @@ -400,6 +379,35 @@ module StepSummary { } deprecated predicate localSourceStoreStep = flowsToStoreStep/3; + + /** Gets the step summary for a level step. */ + StepSummary levelStep() { result = LevelStep() } + + /** Gets the step summary for a call step. */ + StepSummary callStep() { result = CallStep() } + + /** Gets the step summary for a return step. */ + StepSummary returnStep() { result = ReturnStep() } + + /** Gets the step summary for storing into `content`. */ + StepSummary storeStep(TypeTrackerContent content) { result = StoreStep(content) } + + /** Gets the step summary for loading from `content`. */ + StepSummary loadStep(TypeTrackerContent content) { result = LoadStep(content) } + + /** Gets the step summary for loading from `load` and then storing into `store`. */ + StepSummary loadStoreStep(TypeTrackerContent load, TypeTrackerContent store) { + result = LoadStoreStep(load, store) + } + + /** Gets the step summary for a step that only permits contents matched by `filter`. */ + StepSummary withContent(ContentFilter filter) { result = WithContent(filter) } + + /** Gets the step summary for a step that blocks contents matched by `filter`. */ + StepSummary withoutContent(ContentFilter filter) { result = WithoutContent(filter) } + + /** Gets the step summary for a jump step. */ + StepSummary jumpStep() { result = JumpStep() } } /** @@ -501,9 +509,26 @@ class TypeTracker extends TTypeTracker { * Gets the summary that corresponds to having taken a forwards * heap and/or inter-procedural step from `nodeFrom` to `nodeTo`. */ - pragma[inline] + bindingset[nodeFrom, this] + pragma[inline_late] + pragma[noopt] TypeTracker step(TypeTrackingNode nodeFrom, TypeTrackingNode nodeTo) { - result = stepInlineLate(this, nodeFrom, nodeTo) + exists(StepSummary summary | + stepProj(nodeFrom, summary) and + result = this.append(summary) and + step(nodeFrom, nodeTo, summary) + ) + } + + bindingset[nodeFrom, this] + pragma[inline_late] + pragma[noopt] + private TypeTracker smallstepNoSimpleLocalFlowStep(Node nodeFrom, Node nodeTo) { + exists(StepSummary summary | + smallstepProj(nodeFrom, summary) and + result = this.append(summary) and + smallstep(nodeFrom, nodeTo, summary) + ) } /** @@ -532,7 +557,7 @@ class TypeTracker extends TTypeTracker { */ pragma[inline] TypeTracker smallstep(Node nodeFrom, Node nodeTo) { - result = smallstepInlineLate(this, nodeFrom, nodeTo) + result = this.smallstepNoSimpleLocalFlowStep(nodeFrom, nodeTo) or simpleLocalFlowStep(nodeFrom, nodeTo) and result = this @@ -545,6 +570,13 @@ module TypeTracker { * Gets a valid end point of type tracking. */ TypeTracker end() { result.end() } + + /** + * INTERNAL USE ONLY. + * + * Gets a valid end point of type tracking with the call bit set to the given value. + */ + predicate end = Cached::noContentTypeTracker/1; } pragma[nomagic] @@ -552,34 +584,10 @@ private predicate backStepProj(TypeTrackingNode nodeTo, StepSummary summary) { step(_, nodeTo, summary) } -bindingset[nodeTo, t] -pragma[inline_late] -pragma[noopt] -private TypeBackTracker backStepInlineLate( - TypeBackTracker t, TypeTrackingNode nodeFrom, TypeTrackingNode nodeTo -) { - exists(StepSummary summary | - backStepProj(nodeTo, summary) and - result = t.prepend(summary) and - step(nodeFrom, nodeTo, summary) - ) -} - private predicate backSmallstepProj(TypeTrackingNode nodeTo, StepSummary summary) { smallstep(_, nodeTo, summary) } -bindingset[nodeTo, t] -pragma[inline_late] -pragma[noopt] -private TypeBackTracker backSmallstepInlineLate(TypeBackTracker t, Node nodeFrom, Node nodeTo) { - exists(StepSummary summary | - backSmallstepProj(nodeTo, summary) and - result = t.prepend(summary) and - smallstep(nodeFrom, nodeTo, summary) - ) -} - /** * A summary of the steps needed to back-track a use of a value to a given dataflow node. * @@ -661,9 +669,26 @@ class TypeBackTracker extends TTypeBackTracker { * Gets the summary that corresponds to having taken a backwards * heap and/or inter-procedural step from `nodeTo` to `nodeFrom`. */ - pragma[inline] + bindingset[nodeTo, result] + pragma[inline_late] + pragma[noopt] TypeBackTracker step(TypeTrackingNode nodeFrom, TypeTrackingNode nodeTo) { - this = backStepInlineLate(result, nodeFrom, nodeTo) + exists(StepSummary summary | + backStepProj(nodeTo, summary) and + this = result.prepend(summary) and + step(nodeFrom, nodeTo, summary) + ) + } + + bindingset[nodeTo, result] + pragma[inline_late] + pragma[noopt] + private TypeBackTracker smallstepNoSimpleLocalFlowStep(Node nodeFrom, Node nodeTo) { + exists(StepSummary summary | + backSmallstepProj(nodeTo, summary) and + this = result.prepend(summary) and + smallstep(nodeFrom, nodeTo, summary) + ) } /** @@ -692,7 +717,7 @@ class TypeBackTracker extends TTypeBackTracker { */ pragma[inline] TypeBackTracker smallstep(Node nodeFrom, Node nodeTo) { - this = backSmallstepInlineLate(result, nodeFrom, nodeTo) + this = this.smallstepNoSimpleLocalFlowStep(nodeFrom, nodeTo) or simpleLocalFlowStep(nodeFrom, nodeTo) and this = result diff --git a/ruby/ql/lib/codeql/ruby/typetracking/TypeTrackerSpecific.qll b/ruby/ql/lib/codeql/ruby/typetracking/TypeTrackerSpecific.qll index 7725d201fba..d14c182d127 100644 --- a/ruby/ql/lib/codeql/ruby/typetracking/TypeTrackerSpecific.qll +++ b/ruby/ql/lib/codeql/ruby/typetracking/TypeTrackerSpecific.qll @@ -112,15 +112,7 @@ predicate levelStepCall(Node nodeFrom, Node nodeTo) { /** Holds if there is a level step from `nodeFrom` to `nodeTo`, which does not depend on the call graph. */ pragma[nomagic] predicate levelStepNoCall(Node nodeFrom, Node nodeTo) { - exists( - SummarizedCallable callable, DataFlowPublic::CallNode call, SummaryComponentStack input, - SummaryComponentStack output - | - callable.propagatesFlow(input, output, true) and - call.asExpr().getExpr() = callable.getACallSimple() and - nodeFrom = evaluateSummaryComponentStackLocal(callable, call, input) and - nodeTo = evaluateSummaryComponentStackLocal(callable, call, output) - ) + TypeTrackerSummaryFlow::levelStepNoCall(nodeFrom, nodeTo) or localFieldStep(nodeFrom, nodeTo) } @@ -276,16 +268,7 @@ predicate returnStep(Node nodeFrom, Node nodeTo) { predicate basicStoreStep(Node nodeFrom, Node nodeTo, DataFlow::ContentSet contents) { storeStepIntoSourceNode(nodeFrom, nodeTo, contents) or - exists( - SummarizedCallable callable, DataFlowPublic::CallNode call, SummaryComponentStack input, - SummaryComponentStack output - | - hasStoreSummary(callable, contents, pragma[only_bind_into](input), - pragma[only_bind_into](output)) and - call.asExpr().getExpr() = callable.getACallSimple() and - nodeFrom = evaluateSummaryComponentStackLocal(callable, call, input) and - nodeTo = evaluateSummaryComponentStackLocal(callable, call, output) - ) + TypeTrackerSummaryFlow::basicStoreStep(nodeFrom, nodeTo, contents) } /** @@ -319,15 +302,7 @@ predicate basicLoadStep(Node nodeFrom, Node nodeTo, DataFlow::ContentSet content nodeTo.asExpr() = call ) or - exists( - SummarizedCallable callable, DataFlowPublic::CallNode call, SummaryComponentStack input, - SummaryComponentStack output - | - hasLoadSummary(callable, contents, pragma[only_bind_into](input), pragma[only_bind_into](output)) and - call.asExpr().getExpr() = callable.getACallSimple() and - nodeFrom = evaluateSummaryComponentStackLocal(callable, call, input) and - nodeTo = evaluateSummaryComponentStackLocal(callable, call, output) - ) + TypeTrackerSummaryFlow::basicLoadStep(nodeFrom, nodeTo, contents) } /** @@ -336,48 +311,21 @@ predicate basicLoadStep(Node nodeFrom, Node nodeTo, DataFlow::ContentSet content predicate basicLoadStoreStep( Node nodeFrom, Node nodeTo, DataFlow::ContentSet loadContent, DataFlow::ContentSet storeContent ) { - exists( - SummarizedCallable callable, DataFlowPublic::CallNode call, SummaryComponentStack input, - SummaryComponentStack output - | - hasLoadStoreSummary(callable, loadContent, storeContent, pragma[only_bind_into](input), - pragma[only_bind_into](output)) and - call.asExpr().getExpr() = callable.getACallSimple() and - nodeFrom = evaluateSummaryComponentStackLocal(callable, call, input) and - nodeTo = evaluateSummaryComponentStackLocal(callable, call, output) - ) + TypeTrackerSummaryFlow::basicLoadStoreStep(nodeFrom, nodeTo, loadContent, storeContent) } /** * Holds if type-tracking should step from `nodeFrom` to `nodeTo` but block flow of contents matched by `filter` through here. */ predicate basicWithoutContentStep(Node nodeFrom, Node nodeTo, ContentFilter filter) { - exists( - SummarizedCallable callable, DataFlowPublic::CallNode call, SummaryComponentStack input, - SummaryComponentStack output - | - hasWithoutContentSummary(callable, filter, pragma[only_bind_into](input), - pragma[only_bind_into](output)) and - call.asExpr().getExpr() = callable.getACallSimple() and - nodeFrom = evaluateSummaryComponentStackLocal(callable, call, input) and - nodeTo = evaluateSummaryComponentStackLocal(callable, call, output) - ) + TypeTrackerSummaryFlow::basicWithoutContentStep(nodeFrom, nodeTo, filter) } /** * Holds if type-tracking should step from `nodeFrom` to `nodeTo` if inside a content matched by `filter`. */ predicate basicWithContentStep(Node nodeFrom, Node nodeTo, ContentFilter filter) { - exists( - SummarizedCallable callable, DataFlowPublic::CallNode call, SummaryComponentStack input, - SummaryComponentStack output - | - hasWithContentSummary(callable, filter, pragma[only_bind_into](input), - pragma[only_bind_into](output)) and - call.asExpr().getExpr() = callable.getACallSimple() and - nodeFrom = evaluateSummaryComponentStackLocal(callable, call, input) and - nodeTo = evaluateSummaryComponentStackLocal(callable, call, output) - ) + TypeTrackerSummaryFlow::basicWithContentStep(nodeFrom, nodeTo, filter) } /** @@ -389,121 +337,6 @@ class Boolean extends boolean { private import SummaryComponentStack -pragma[nomagic] -private predicate hasStoreSummary( - SummarizedCallable callable, DataFlow::ContentSet contents, SummaryComponentStack input, - SummaryComponentStack output -) { - not isNonLocal(input.head()) and - not isNonLocal(output.head()) and - ( - callable.propagatesFlow(input, push(SummaryComponent::content(contents), output), true) - or - // Allow the input to start with an arbitrary WithoutContent[X]. - // Since type-tracking only tracks one content deep, and we're about to store into another content, - // we're already preventing the input from being in a content. - callable - .propagatesFlow(push(SummaryComponent::withoutContent(_), input), - push(SummaryComponent::content(contents), output), true) - ) -} - -pragma[nomagic] -private predicate hasLoadSummary( - SummarizedCallable callable, DataFlow::ContentSet contents, SummaryComponentStack input, - SummaryComponentStack output -) { - callable.propagatesFlow(push(SummaryComponent::content(contents), input), output, true) and - not isNonLocal(input.head()) and - not isNonLocal(output.head()) -} - -pragma[nomagic] -private predicate hasLoadStoreSummary( - SummarizedCallable callable, DataFlow::ContentSet loadContents, - DataFlow::ContentSet storeContents, SummaryComponentStack input, SummaryComponentStack output -) { - callable - .propagatesFlow(push(SummaryComponent::content(loadContents), input), - push(SummaryComponent::content(storeContents), output), true) and - not isNonLocal(input.head()) and - not isNonLocal(output.head()) -} - -/** - * Gets a content filter to use for a `WithoutContent[content]` step, or has no result if - * the step should be treated as ordinary flow. - * - * `WithoutContent` is often used to perform strong updates on individual collection elements, but for - * type-tracking this is rarely beneficial and quite expensive. However, `WithoutContent` can be quite useful - * for restricting the type of an object, and in these cases we translate it to a filter. - */ -private ContentFilter getFilterFromWithoutContentStep(DataFlow::ContentSet content) { - ( - content.isAnyElement() - or - content.isElementLowerBoundOrUnknown(_) - or - content.isElementOfTypeOrUnknown(_) - or - content.isSingleton(any(DataFlow::Content::UnknownElementContent c)) - ) and - result = MkElementFilter() -} - -pragma[nomagic] -private predicate hasWithoutContentSummary( - SummarizedCallable callable, ContentFilter filter, SummaryComponentStack input, - SummaryComponentStack output -) { - exists(DataFlow::ContentSet content | - callable.propagatesFlow(push(SummaryComponent::withoutContent(content), input), output, true) and - filter = getFilterFromWithoutContentStep(content) and - not isNonLocal(input.head()) and - not isNonLocal(output.head()) and - input != output - ) -} - -/** - * Gets a content filter to use for a `WithContent[content]` step, or has no result if - * the step cannot be handled by type-tracking. - * - * `WithContent` is often used to perform strong updates on individual collection elements (or rather - * to preserve those that didn't get updated). But for type-tracking this is rarely beneficial and quite expensive. - * However, `WithContent` can be quite useful for restricting the type of an object, and in these cases we translate it to a filter. - */ -private ContentFilter getFilterFromWithContentStep(DataFlow::ContentSet content) { - ( - content.isAnyElement() - or - content.isElementLowerBound(_) - or - content.isElementLowerBoundOrUnknown(_) - or - content.isElementOfType(_) - or - content.isElementOfTypeOrUnknown(_) - or - content.isSingleton(any(DataFlow::Content::ElementContent c)) - ) and - result = MkElementFilter() -} - -pragma[nomagic] -private predicate hasWithContentSummary( - SummarizedCallable callable, ContentFilter filter, SummaryComponentStack input, - SummaryComponentStack output -) { - exists(DataFlow::ContentSet content | - callable.propagatesFlow(push(SummaryComponent::withContent(content), input), output, true) and - filter = getFilterFromWithContentStep(content) and - not isNonLocal(input.head()) and - not isNonLocal(output.head()) and - input != output - ) -} - /** * Holds if the given component can't be evaluated by `evaluateSummaryComponentStackLocal`. */ @@ -514,101 +347,95 @@ predicate isNonLocal(SummaryComponent component) { component = SC::withContent(_) } -/** - * Gets a data flow node corresponding an argument or return value of `call`, - * as specified by `component`. - */ -bindingset[call, component] -private DataFlow::Node evaluateSummaryComponentLocal( - DataFlow::CallNode call, SummaryComponent component -) { - exists(DataFlowDispatch::ParameterPosition pos | - component = SummaryComponent::argument(pos) and - argumentPositionMatch(call.asExpr(), result, pos) - ) - or - component = SummaryComponent::return() and - result = call -} +private import internal.SummaryTypeTracker as SummaryTypeTracker +private import codeql.ruby.dataflow.FlowSummary as FlowSummary -/** - * Holds if `callable` is relevant for type-tracking and we therefore want `stack` to - * be evaluated locally at its call sites. - */ -pragma[nomagic] -private predicate dependsOnSummaryComponentStack( - SummarizedCallable callable, SummaryComponentStack stack -) { - exists(callable.getACallSimple()) and - ( - callable.propagatesFlow(stack, _, true) - or - callable.propagatesFlow(_, stack, true) - or - // include store summaries as they may skip an initial step at the input - hasStoreSummary(callable, _, stack, _) - ) - or - dependsOnSummaryComponentStackCons(callable, _, stack) -} +private module SummaryTypeTrackerInput implements SummaryTypeTracker::Input { + // Dataflow nodes + class Node = DataFlow::Node; -pragma[nomagic] -private predicate dependsOnSummaryComponentStackCons( - SummarizedCallable callable, SummaryComponent head, SummaryComponentStack tail -) { - dependsOnSummaryComponentStack(callable, SCS::push(head, tail)) -} + // Content + class TypeTrackerContent = DataFlowPublic::ContentSet; -pragma[nomagic] -private predicate dependsOnSummaryComponentStackConsLocal( - SummarizedCallable callable, SummaryComponent head, SummaryComponentStack tail -) { - dependsOnSummaryComponentStackCons(callable, head, tail) and - not isNonLocal(head) -} + class TypeTrackerContentFilter = ContentFilter; -pragma[nomagic] -private predicate dependsOnSummaryComponentStackLeaf( - SummarizedCallable callable, SummaryComponent leaf -) { - dependsOnSummaryComponentStack(callable, SCS::singleton(leaf)) -} + TypeTrackerContentFilter getFilterFromWithoutContentStep(TypeTrackerContent content) { + ( + content.isAnyElement() + or + content.isElementLowerBoundOrUnknown(_) + or + content.isElementOfTypeOrUnknown(_) + or + content.isSingleton(any(DataFlow::Content::UnknownElementContent c)) + ) and + result = MkElementFilter() + } -/** - * Gets a data flow node corresponding to the local input or output of `call` - * identified by `stack`, if possible. - */ -pragma[nomagic] -private DataFlow::Node evaluateSummaryComponentStackLocal( - SummarizedCallable callable, DataFlow::CallNode call, SummaryComponentStack stack -) { - exists(SummaryComponent component | - dependsOnSummaryComponentStackLeaf(callable, component) and - stack = SCS::singleton(component) and - call.asExpr().getExpr() = callable.getACallSimple() and - result = evaluateSummaryComponentLocal(call, component) - ) - or - exists(DataFlow::Node prev, SummaryComponent head, SummaryComponentStack tail | - prev = evaluateSummaryComponentStackLocal(callable, call, tail) and - dependsOnSummaryComponentStackConsLocal(callable, pragma[only_bind_into](head), - pragma[only_bind_out](tail)) and - stack = SCS::push(pragma[only_bind_out](head), pragma[only_bind_out](tail)) - | + TypeTrackerContentFilter getFilterFromWithContentStep(TypeTrackerContent content) { + ( + content.isAnyElement() + or + content.isElementLowerBound(_) + or + content.isElementLowerBoundOrUnknown(_) + or + content.isElementOfType(_) + or + content.isElementOfTypeOrUnknown(_) + or + content.isSingleton(any(DataFlow::Content::ElementContent c)) + ) and + result = MkElementFilter() + } + + // Summaries and their stacks + class SummaryComponent = FlowSummary::SummaryComponent; + + class SummaryComponentStack = FlowSummary::SummaryComponentStack; + + predicate singleton = FlowSummary::SummaryComponentStack::singleton/1; + + predicate push = FlowSummary::SummaryComponentStack::push/2; + + // Relating content to summaries + predicate content = FlowSummary::SummaryComponent::content/1; + + predicate withoutContent = FlowSummary::SummaryComponent::withoutContent/1; + + predicate withContent = FlowSummary::SummaryComponent::withContent/1; + + predicate return = FlowSummary::SummaryComponent::return/0; + + // Callables + class SummarizedCallable = FlowSummary::SummarizedCallable; + + // Relating nodes to summaries + Node argumentOf(Node call, SummaryComponent arg) { + exists(DataFlowDispatch::ParameterPosition pos | + arg = SummaryComponent::argument(pos) and + argumentPositionMatch(call.asExpr(), result, pos) + ) + } + + Node parameterOf(Node callable, SummaryComponent param) { exists(DataFlowDispatch::ArgumentPosition apos, DataFlowDispatch::ParameterPosition ppos | - head = SummaryComponent::parameter(apos) and + param = SummaryComponent::parameter(apos) and DataFlowDispatch::parameterMatch(ppos, apos) and - result.(DataFlowPrivate::ParameterNodeImpl).isSourceParameterOf(prev.asExpr().getExpr(), ppos) + result + .(DataFlowPrivate::ParameterNodeImpl) + .isSourceParameterOf(callable.asExpr().getExpr(), ppos) ) - or - head = SummaryComponent::return() and + } + + Node returnOf(Node callable, SummaryComponent return) { + return = SummaryComponent::return() and result.(DataFlowPrivate::ReturnNode).(DataFlowPrivate::NodeImpl).getCfgScope() = - prev.asExpr().getExpr() - or - exists(DataFlow::ContentSet content | - head = SummaryComponent::withoutContent(content) and - not exists(getFilterFromWithoutContentStep(content)) and - result = prev - ) - ) + callable.asExpr().getExpr() + } + + // Relating callables to nodes + Node callTo(SummarizedCallable callable) { result.asExpr().getExpr() = callable.getACallSimple() } } + +private module TypeTrackerSummaryFlow = SummaryTypeTracker::SummaryFlow; diff --git a/ruby/ql/lib/codeql/ruby/typetracking/internal/SummaryTypeTracker.qll b/ruby/ql/lib/codeql/ruby/typetracking/internal/SummaryTypeTracker.qll new file mode 100644 index 00000000000..9c6f841651d --- /dev/null +++ b/ruby/ql/lib/codeql/ruby/typetracking/internal/SummaryTypeTracker.qll @@ -0,0 +1,391 @@ +/** + * Provides the implementation of type tracking steps through flow summaries. + * To use this, you must implement the `Input` signature. You can then use the predicates in the `Output` + * signature to implement the predicates of the same names inside `TypeTrackerSpecific.qll`. + */ + +/** The classes and predicates needed to generate type-tracking steps from summaries. */ +signature module Input { + // Dataflow nodes + class Node; + + // Content + class TypeTrackerContent; + + class TypeTrackerContentFilter; + + // Relating content and filters + /** + * Gets a content filter to use for a `WithoutContent[content]` step, (data is not allowed to be stored in `content`) + * or has no result if + * the step should be treated as ordinary flow. + * + * `WithoutContent` is often used to perform strong updates on individual collection elements, but for + * type-tracking this is rarely beneficial and quite expensive. However, `WithoutContent` can be quite useful + * for restricting the type of an object, and in these cases we translate it to a filter. + */ + TypeTrackerContentFilter getFilterFromWithoutContentStep(TypeTrackerContent content); + + /** + * Gets a content filter to use for a `WithContent[content]` step, (data must be stored in `content`) + * or has no result if + * the step cannot be handled by type-tracking. + * + * `WithContent` is often used to perform strong updates on individual collection elements (or rather + * to preserve those that didn't get updated). But for type-tracking this is rarely beneficial and quite expensive. + * However, `WithContent` can be quite useful for restricting the type of an object, and in these cases we translate it to a filter. + */ + TypeTrackerContentFilter getFilterFromWithContentStep(TypeTrackerContent content); + + // Summaries and their stacks + class SummaryComponent; + + class SummaryComponentStack { + SummaryComponent head(); + } + + /** Gets a singleton stack containing `component`. */ + SummaryComponentStack singleton(SummaryComponent component); + + /** + * Gets the stack obtained by pushing `head` onto `tail`. + */ + SummaryComponentStack push(SummaryComponent head, SummaryComponentStack tail); + + /** Gets a singleton stack representing a return. */ + SummaryComponent return(); + + // Relating content to summaries + /** Gets a summary component for content `c`. */ + SummaryComponent content(TypeTrackerContent contents); + + /** Gets a summary component where data is not allowed to be stored in `contents`. */ + SummaryComponent withoutContent(TypeTrackerContent contents); + + /** Gets a summary component where data must be stored in `contents`. */ + SummaryComponent withContent(TypeTrackerContent contents); + + // Callables + class SummarizedCallable { + predicate propagatesFlow( + SummaryComponentStack input, SummaryComponentStack output, boolean preservesValue + ); + } + + // Relating nodes to summaries + /** Gets a dataflow node respresenting the argument of `call` indicated by `arg`. */ + Node argumentOf(Node call, SummaryComponent arg); + + /** Gets a dataflow node respresenting the parameter of `callable` indicated by `param`. */ + Node parameterOf(Node callable, SummaryComponent param); + + /** Gets a dataflow node respresenting the return of `callable` indicated by `return`. */ + Node returnOf(Node callable, SummaryComponent return); + + // Relating callables to nodes + /** Gets a dataflow node respresenting a call to `callable`. */ + Node callTo(SummarizedCallable callable); +} + +/** + * The predicates provided by a summary type tracker. + * These are meant to be used in `TypeTrackerSpecific.qll` + * inside the predicates of the same names. + */ +signature module Output { + /** + * Holds if there is a level step from `nodeFrom` to `nodeTo`, which does not depend on the call graph. + */ + predicate levelStepNoCall(I::Node nodeFrom, I::Node nodeTo); + + /** + * Holds if `nodeTo` is the result of accessing the `content` content of `nodeFrom`. + */ + predicate basicLoadStep(I::Node nodeFrom, I::Node nodeTo, I::TypeTrackerContent content); + + /** + * Holds if `nodeFrom` is being written to the `content` content of the object in `nodeTo`. + */ + predicate basicStoreStep(I::Node nodeFrom, I::Node nodeTo, I::TypeTrackerContent content); + + /** + * Holds if the `loadContent` of `nodeFrom` is stored in the `storeContent` of `nodeTo`. + */ + predicate basicLoadStoreStep( + I::Node nodeFrom, I::Node nodeTo, I::TypeTrackerContent loadContent, + I::TypeTrackerContent storeContent + ); + + /** + * Holds if type-tracking should step from `nodeFrom` to `nodeTo` but block flow of contents matched by `filter` through here. + */ + predicate basicWithoutContentStep( + I::Node nodeFrom, I::Node nodeTo, I::TypeTrackerContentFilter filter + ); + + /** + * Holds if type-tracking should step from `nodeFrom` to `nodeTo` if inside a content matched by `filter`. + */ + predicate basicWithContentStep( + I::Node nodeFrom, I::Node nodeTo, I::TypeTrackerContentFilter filter + ); +} + +/** + * Implementation of the summary type tracker, that is type tracking through flow summaries. + */ +module SummaryFlow implements Output { + pragma[nomagic] + private predicate isNonLocal(I::SummaryComponent component) { + component = I::content(_) + or + component = I::withContent(_) + } + + pragma[nomagic] + private predicate hasLoadSummary( + I::SummarizedCallable callable, I::TypeTrackerContent contents, I::SummaryComponentStack input, + I::SummaryComponentStack output + ) { + callable.propagatesFlow(I::push(I::content(contents), input), output, true) and + not isNonLocal(input.head()) and + not isNonLocal(output.head()) + } + + pragma[nomagic] + private predicate hasStoreSummary( + I::SummarizedCallable callable, I::TypeTrackerContent contents, I::SummaryComponentStack input, + I::SummaryComponentStack output + ) { + not isNonLocal(input.head()) and + not isNonLocal(output.head()) and + ( + callable.propagatesFlow(input, I::push(I::content(contents), output), true) + or + // Allow the input to start with an arbitrary WithoutContent[X]. + // Since type-tracking only tracks one content deep, and we're about to store into another content, + // we're already preventing the input from being in a content. + callable + .propagatesFlow(I::push(I::withoutContent(_), input), + I::push(I::content(contents), output), true) + ) + } + + pragma[nomagic] + private predicate hasLoadStoreSummary( + I::SummarizedCallable callable, I::TypeTrackerContent loadContents, + I::TypeTrackerContent storeContents, I::SummaryComponentStack input, + I::SummaryComponentStack output + ) { + callable + .propagatesFlow(I::push(I::content(loadContents), input), + I::push(I::content(storeContents), output), true) and + not isNonLocal(input.head()) and + not isNonLocal(output.head()) + } + + pragma[nomagic] + private predicate hasWithoutContentSummary( + I::SummarizedCallable callable, I::TypeTrackerContentFilter filter, + I::SummaryComponentStack input, I::SummaryComponentStack output + ) { + exists(I::TypeTrackerContent content | + callable.propagatesFlow(I::push(I::withoutContent(content), input), output, true) and + filter = I::getFilterFromWithoutContentStep(content) and + not isNonLocal(input.head()) and + not isNonLocal(output.head()) and + input != output + ) + } + + pragma[nomagic] + private predicate hasWithContentSummary( + I::SummarizedCallable callable, I::TypeTrackerContentFilter filter, + I::SummaryComponentStack input, I::SummaryComponentStack output + ) { + exists(I::TypeTrackerContent content | + callable.propagatesFlow(I::push(I::withContent(content), input), output, true) and + filter = I::getFilterFromWithContentStep(content) and + not isNonLocal(input.head()) and + not isNonLocal(output.head()) and + input != output + ) + } + + private predicate componentLevelStep(I::SummaryComponent component) { + exists(I::TypeTrackerContent content | + component = I::withoutContent(content) and + not exists(I::getFilterFromWithoutContentStep(content)) + ) + } + + /** + * Gets a data flow `I::Node` corresponding an argument or return value of `call`, + * as specified by `component`. + */ + bindingset[call, component] + private I::Node evaluateSummaryComponentLocal(I::Node call, I::SummaryComponent component) { + result = I::argumentOf(call, component) + or + component = I::return() and + result = call + } + + /** + * Holds if `callable` is relevant for type-tracking and we therefore want `stack` to + * be evaluated locally at its call sites. + */ + pragma[nomagic] + private predicate dependsOnSummaryComponentStack( + I::SummarizedCallable callable, I::SummaryComponentStack stack + ) { + exists(I::callTo(callable)) and + ( + callable.propagatesFlow(stack, _, true) + or + callable.propagatesFlow(_, stack, true) + or + // include store summaries as they may skip an initial step at the input + hasStoreSummary(callable, _, stack, _) + ) + or + dependsOnSummaryComponentStackCons(callable, _, stack) + } + + pragma[nomagic] + private predicate dependsOnSummaryComponentStackCons( + I::SummarizedCallable callable, I::SummaryComponent head, I::SummaryComponentStack tail + ) { + dependsOnSummaryComponentStack(callable, I::push(head, tail)) + } + + pragma[nomagic] + private predicate dependsOnSummaryComponentStackConsLocal( + I::SummarizedCallable callable, I::SummaryComponent head, I::SummaryComponentStack tail + ) { + dependsOnSummaryComponentStackCons(callable, head, tail) and + not isNonLocal(head) + } + + pragma[nomagic] + private predicate dependsOnSummaryComponentStackLeaf( + I::SummarizedCallable callable, I::SummaryComponent leaf + ) { + dependsOnSummaryComponentStack(callable, I::singleton(leaf)) + } + + /** + * Gets a data flow I::Node corresponding to the local input or output of `call` + * identified by `stack`, if possible. + */ + pragma[nomagic] + private I::Node evaluateSummaryComponentStackLocal( + I::SummarizedCallable callable, I::Node call, I::SummaryComponentStack stack + ) { + exists(I::SummaryComponent component | + dependsOnSummaryComponentStackLeaf(callable, component) and + stack = I::singleton(component) and + call = I::callTo(callable) and + result = evaluateSummaryComponentLocal(call, component) + ) + or + exists(I::Node prev, I::SummaryComponent head, I::SummaryComponentStack tail | + prev = evaluateSummaryComponentStackLocal(callable, call, tail) and + dependsOnSummaryComponentStackConsLocal(callable, pragma[only_bind_into](head), + pragma[only_bind_out](tail)) and + stack = I::push(pragma[only_bind_out](head), pragma[only_bind_out](tail)) + | + result = I::parameterOf(prev, head) + or + result = I::returnOf(prev, head) + or + componentLevelStep(head) and + result = prev + ) + } + + // Implement Output + predicate levelStepNoCall(I::Node nodeFrom, I::Node nodeTo) { + exists( + I::SummarizedCallable callable, I::Node call, I::SummaryComponentStack input, + I::SummaryComponentStack output + | + callable.propagatesFlow(input, output, true) and + call = I::callTo(callable) and + nodeFrom = evaluateSummaryComponentStackLocal(callable, call, input) and + nodeTo = evaluateSummaryComponentStackLocal(callable, call, output) + ) + } + + predicate basicLoadStep(I::Node nodeFrom, I::Node nodeTo, I::TypeTrackerContent content) { + exists( + I::SummarizedCallable callable, I::Node call, I::SummaryComponentStack input, + I::SummaryComponentStack output + | + hasLoadSummary(callable, content, pragma[only_bind_into](input), + pragma[only_bind_into](output)) and + call = I::callTo(callable) and + nodeFrom = evaluateSummaryComponentStackLocal(callable, call, input) and + nodeTo = evaluateSummaryComponentStackLocal(callable, call, output) + ) + } + + predicate basicStoreStep(I::Node nodeFrom, I::Node nodeTo, I::TypeTrackerContent content) { + exists( + I::SummarizedCallable callable, I::Node call, I::SummaryComponentStack input, + I::SummaryComponentStack output + | + hasStoreSummary(callable, content, pragma[only_bind_into](input), + pragma[only_bind_into](output)) and + call = I::callTo(callable) and + nodeFrom = evaluateSummaryComponentStackLocal(callable, call, input) and + nodeTo = evaluateSummaryComponentStackLocal(callable, call, output) + ) + } + + predicate basicLoadStoreStep( + I::Node nodeFrom, I::Node nodeTo, I::TypeTrackerContent loadContent, + I::TypeTrackerContent storeContent + ) { + exists( + I::SummarizedCallable callable, I::Node call, I::SummaryComponentStack input, + I::SummaryComponentStack output + | + hasLoadStoreSummary(callable, loadContent, storeContent, pragma[only_bind_into](input), + pragma[only_bind_into](output)) and + call = I::callTo(callable) and + nodeFrom = evaluateSummaryComponentStackLocal(callable, call, input) and + nodeTo = evaluateSummaryComponentStackLocal(callable, call, output) + ) + } + + predicate basicWithoutContentStep( + I::Node nodeFrom, I::Node nodeTo, I::TypeTrackerContentFilter filter + ) { + exists( + I::SummarizedCallable callable, I::Node call, I::SummaryComponentStack input, + I::SummaryComponentStack output + | + hasWithoutContentSummary(callable, filter, pragma[only_bind_into](input), + pragma[only_bind_into](output)) and + call = I::callTo(callable) and + nodeFrom = evaluateSummaryComponentStackLocal(callable, call, input) and + nodeTo = evaluateSummaryComponentStackLocal(callable, call, output) + ) + } + + predicate basicWithContentStep( + I::Node nodeFrom, I::Node nodeTo, I::TypeTrackerContentFilter filter + ) { + exists( + I::SummarizedCallable callable, I::Node call, I::SummaryComponentStack input, + I::SummaryComponentStack output + | + hasWithContentSummary(callable, filter, pragma[only_bind_into](input), + pragma[only_bind_into](output)) and + call = I::callTo(callable) and + nodeFrom = evaluateSummaryComponentStackLocal(callable, call, input) and + nodeTo = evaluateSummaryComponentStackLocal(callable, call, output) + ) + } +} diff --git a/ruby/ql/lib/qlpack.yml b/ruby/ql/lib/qlpack.yml index bb01a5ff87d..f3a57fb8a1c 100644 --- a/ruby/ql/lib/qlpack.yml +++ b/ruby/ql/lib/qlpack.yml @@ -1,11 +1,12 @@ name: codeql/ruby-all -version: 0.6.3-dev +version: 0.6.4-dev groups: ruby extractor: ruby dbscheme: ruby.dbscheme upgrades: upgrades library: true dependencies: + codeql/mad: ${workspace} codeql/regex: ${workspace} codeql/ssa: ${workspace} codeql/tutorial: ${workspace} diff --git a/ruby/ql/src/CHANGELOG.md b/ruby/ql/src/CHANGELOG.md index 7e2e0df8b38..8bc499539cb 100644 --- a/ruby/ql/src/CHANGELOG.md +++ b/ruby/ql/src/CHANGELOG.md @@ -1,3 +1,13 @@ +## 0.6.3 + +### Minor Analysis Improvements + +* Fixed a bug that would occur when an `initialize` method returns `self` or one of its parameters. + In such cases, the corresponding calls to `new` would be associated with an incorrect return type. + This could result in inaccurate call target resolution and cause false positive alerts. +* Fixed an issue where calls to `delete` or `assoc` with a constant-valued argument would be analyzed imprecisely, + as if the argument value was not a known constant. + ## 0.6.2 No user-facing changes. diff --git a/ruby/ql/src/change-notes/2023-05-24-delete-name-clash.md b/ruby/ql/src/change-notes/2023-05-24-delete-name-clash.md deleted file mode 100644 index 347a7b118db..00000000000 --- a/ruby/ql/src/change-notes/2023-05-24-delete-name-clash.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -category: minorAnalysis ---- -* Fixed an issue where calls to `delete` or `assoc` with a constant-valued argument would be analyzed imprecisely, - as if the argument value was not a known constant. diff --git a/ruby/ql/src/change-notes/2023-06-16-zipslip-rename.md b/ruby/ql/src/change-notes/2023-06-16-zipslip-rename.md new file mode 100644 index 00000000000..eeb9c5254bb --- /dev/null +++ b/ruby/ql/src/change-notes/2023-06-16-zipslip-rename.md @@ -0,0 +1,4 @@ +--- +category: fix +--- +* The experimental query "Arbitrary file write during zipfile/tarfile extraction" (`ruby/zipslip`) has been renamed to "Arbitrary file access during archive extraction ("Zip Slip")." diff --git a/ruby/ql/src/change-notes/2023-05-26-super-and-flow-through.md b/ruby/ql/src/change-notes/released/0.6.3.md similarity index 57% rename from ruby/ql/src/change-notes/2023-05-26-super-and-flow-through.md rename to ruby/ql/src/change-notes/released/0.6.3.md index 7059c51f24e..53544eca039 100644 --- a/ruby/ql/src/change-notes/2023-05-26-super-and-flow-through.md +++ b/ruby/ql/src/change-notes/released/0.6.3.md @@ -1,6 +1,9 @@ ---- -category: minorAnalysis ---- +## 0.6.3 + +### Minor Analysis Improvements + * Fixed a bug that would occur when an `initialize` method returns `self` or one of its parameters. In such cases, the corresponding calls to `new` would be associated with an incorrect return type. This could result in inaccurate call target resolution and cause false positive alerts. +* Fixed an issue where calls to `delete` or `assoc` with a constant-valued argument would be analyzed imprecisely, + as if the argument value was not a known constant. diff --git a/ruby/ql/src/codeql-pack.release.yml b/ruby/ql/src/codeql-pack.release.yml index 5501a2a1cc5..b7dafe32c5d 100644 --- a/ruby/ql/src/codeql-pack.release.yml +++ b/ruby/ql/src/codeql-pack.release.yml @@ -1,2 +1,2 @@ --- -lastReleaseVersion: 0.6.2 +lastReleaseVersion: 0.6.3 diff --git a/ruby/ql/src/experimental/cwe-022-zipslip/ZipSlip.qhelp b/ruby/ql/src/experimental/cwe-022-zipslip/ZipSlip.qhelp index 759be8d95d0..7d7435ee907 100644 --- a/ruby/ql/src/experimental/cwe-022-zipslip/ZipSlip.qhelp +++ b/ruby/ql/src/experimental/cwe-022-zipslip/ZipSlip.qhelp @@ -4,16 +4,15 @@ -

Extracting files from a malicious tar archive without validating that the destination file path -is within the destination directory can cause files outside the destination directory to be -overwritten, due to the possible presence of directory traversal elements (..) in -archive paths.

+

Extracting files from a malicious zip file, or similar type of archive, +is at risk of directory traversal attacks if filenames from the archive are +not properly validated.

Tar archives contain archive entries representing each file in the archive. These entries include a file path for the entry, but these file paths are not restricted and may contain unexpected special elements such as the directory traversal element (..). If these -file paths are used to determine an output file to write the contents of the archive item to, then -the file may be written to an unexpected location. This can result in sensitive information being +file paths are used to create a filesystem path, then a file operation may happen in an +unexpected location. This can result in sensitive information being revealed or deleted, or an attacker being able to influence behavior by modifying unexpected files.

diff --git a/ruby/ql/src/experimental/cwe-022-zipslip/ZipSlip.ql b/ruby/ql/src/experimental/cwe-022-zipslip/ZipSlip.ql index e8be92fff75..329f4b89977 100644 --- a/ruby/ql/src/experimental/cwe-022-zipslip/ZipSlip.ql +++ b/ruby/ql/src/experimental/cwe-022-zipslip/ZipSlip.ql @@ -1,8 +1,8 @@ /** - * @name Arbitrary file write during zipfile/tarfile extraction - * @description Extracting files from a malicious tar archive without validating that the - * destination file path is within the destination directory can cause files outside - * the destination directory to be overwritten. + * @name Arbitrary file access during archive extraction ("Zip Slip") + * @description Extracting files from a malicious ZIP file, or similar type of archive, without + * validating that the destination file path is within the destination directory + * can allow an attacker to unexpectedly gain access to resources. * @kind path-problem * @id rb/zip-slip * @problem.severity error diff --git a/ruby/ql/src/qlpack.yml b/ruby/ql/src/qlpack.yml index 3bc462dc7ee..6e1eb058cd4 100644 --- a/ruby/ql/src/qlpack.yml +++ b/ruby/ql/src/qlpack.yml @@ -1,5 +1,5 @@ name: codeql/ruby-queries -version: 0.6.3-dev +version: 0.6.4-dev groups: - ruby - queries diff --git a/ruby/ql/src/queries/security/cwe-829/InsecureDownload.ql b/ruby/ql/src/queries/security/cwe-829/InsecureDownload.ql index b5f2610ae2e..cdd494134a5 100644 --- a/ruby/ql/src/queries/security/cwe-829/InsecureDownload.ql +++ b/ruby/ql/src/queries/security/cwe-829/InsecureDownload.ql @@ -14,9 +14,9 @@ import codeql.ruby.AST import codeql.ruby.DataFlow import codeql.ruby.security.InsecureDownloadQuery -import DataFlow::PathGraph +import Flow::PathGraph -from Configuration cfg, DataFlow::PathNode source, DataFlow::PathNode sink -where cfg.hasFlowPath(source, sink) +from Flow::PathNode source, Flow::PathNode sink +where Flow::flowPath(source, sink) select sink.getNode(), source, sink, "$@ of sensitive file from $@.", sink.getNode().(Sink).getDownloadCall(), "Download", source.getNode(), "HTTP source" diff --git a/ruby/ql/test/TestUtilities/InlineFlowTest.qll b/ruby/ql/test/TestUtilities/InlineFlowTest.qll index d653a3e414e..cefd109f0a9 100644 --- a/ruby/ql/test/TestUtilities/InlineFlowTest.qll +++ b/ruby/ql/test/TestUtilities/InlineFlowTest.qll @@ -4,10 +4,11 @@ * Example for a test.ql: * ```ql * import TestUtilities.InlineFlowTest + * import DefaultFlowTest * import PathGraph * - * from DataFlow::PathNode source, DataFlow::PathNode sink, DefaultValueFlowConf conf - * where conf.hasFlowPath(source, sink) + * from PathNode source, PathNode sink + * where flowPath(source, sink) * select sink, source, sink, "$@", source, source.toString() * ``` * @@ -20,14 +21,10 @@ * sink(t); // $ hasTaintFlow=2 * ``` * - * If you're not interested in a specific flow type, you can disable either value or taint flow expectations as follows: - * ```ql - * class HasFlowTest extends InlineFlowTest { - * override DataFlow::Configuration getTaintFlowConfig() { none() } - * - * override DataFlow::Configuration getValueFlowConfig() { none() } - * } - * ``` + * If you are only interested in value flow, then instead of importing `DefaultFlowTest`, you can import + * `ValueFlowTest`. Similarly, if you are only interested in taint flow, then instead of + * importing `DefaultFlowTest`, you can import `TaintFlowTest`. In both cases + * `DefaultFlowConfig` can be replaced by another implementation of `DataFlow::ConfigSig`. * * If you need more fine-grained tuning, consider implementing a test using `InlineExpectationsTest`. */ @@ -38,72 +35,62 @@ import codeql.ruby.TaintTracking import TestUtilities.InlineExpectationsTest import TestUtilities.InlineFlowTestUtil -class DefaultValueFlowConf extends DataFlow::Configuration { - DefaultValueFlowConf() { this = "qltest:defaultValueFlowConf" } +module DefaultFlowConfig implements DataFlow::ConfigSig { + predicate isSource(DataFlow::Node source) { defaultSource(source) } - override predicate isSource(DataFlow::Node n) { defaultSource(n) } + predicate isSink(DataFlow::Node sink) { defaultSink(sink) } - override predicate isSink(DataFlow::Node n) { defaultSink(n) } - - override int fieldFlowBranchLimit() { result = 1000 } + int fieldFlowBranchLimit() { result = 1000 } } -class DefaultTaintFlowConf extends TaintTracking::Configuration { - DefaultTaintFlowConf() { this = "qltest:defaultTaintFlowConf" } +private module NoFlowConfig implements DataFlow::ConfigSig { + predicate isSource(DataFlow::Node source) { none() } - override predicate isSource(DataFlow::Node n) { defaultSource(n) } - - override predicate isSink(DataFlow::Node n) { defaultSink(n) } - - override int fieldFlowBranchLimit() { result = 1000 } + predicate isSink(DataFlow::Node sink) { none() } } -class InlineFlowTest extends InlineExpectationsTest { - InlineFlowTest() { this = "HasFlowTest" } +module FlowTest { + module ValueFlow = DataFlow::Global; - override string getARelevantTag() { result = ["hasValueFlow", "hasTaintFlow"] } + module TaintFlow = TaintTracking::Global; - override predicate hasActualResult(Location location, string element, string tag, string value) { - tag = "hasValueFlow" and - exists(DataFlow::Node src, DataFlow::Node sink | this.getValueFlowConfig().hasFlow(src, sink) | - sink.getLocation() = location and - element = sink.toString() and - if exists(getSourceArgString(src)) then value = getSourceArgString(src) else value = "" - ) - or - tag = "hasTaintFlow" and - exists(DataFlow::Node src, DataFlow::Node sink | - this.getTaintFlowConfig().hasFlow(src, sink) and - not this.getValueFlowConfig().hasFlow(src, sink) - | - sink.getLocation() = location and - element = sink.toString() and - if exists(getSourceArgString(src)) then value = getSourceArgString(src) else value = "" - ) - } + private module InlineTest implements TestSig { + string getARelevantTag() { result = ["hasValueFlow", "hasTaintFlow"] } - DataFlow::Configuration getValueFlowConfig() { result = any(DefaultValueFlowConf config) } - - DataFlow::Configuration getTaintFlowConfig() { result = any(DefaultTaintFlowConf config) } -} - -module PathGraph { - private import DataFlow::PathGraph as PG - - private class PathNode extends DataFlow::PathNode { - PathNode() { - this.getConfiguration() = - [any(InlineFlowTest t).getValueFlowConfig(), any(InlineFlowTest t).getTaintFlowConfig()] + predicate hasActualResult(Location location, string element, string tag, string value) { + tag = "hasValueFlow" and + exists(DataFlow::Node src, DataFlow::Node sink | ValueFlow::flow(src, sink) | + sink.getLocation() = location and + element = sink.toString() and + if exists(getSourceArgString(src)) then value = getSourceArgString(src) else value = "" + ) + or + tag = "hasTaintFlow" and + exists(DataFlow::Node src, DataFlow::Node sink | + TaintFlow::flow(src, sink) and not ValueFlow::flow(src, sink) + | + sink.getLocation() = location and + element = sink.toString() and + if exists(getSourceArgString(src)) then value = getSourceArgString(src) else value = "" + ) } } - /** Holds if `(a,b)` is an edge in the graph of data flow path explanations. */ - query predicate edges(PathNode a, PathNode b) { PG::edges(a, b) } + import MakeTest + import DataFlow::MergePathGraph - /** Holds if `n` is a node in the graph of data flow path explanations. */ - query predicate nodes(PathNode n, string key, string val) { PG::nodes(n, key, val) } - - query predicate subpaths(PathNode arg, PathNode par, PathNode ret, PathNode out) { - PG::subpaths(arg, par, ret, out) + predicate flowPath(PathNode source, PathNode sink) { + ValueFlow::flowPath(source.asPathNode1(), sink.asPathNode1()) or + TaintFlow::flowPath(source.asPathNode2(), sink.asPathNode2()) } } + +module DefaultFlowTest = FlowTest; + +module ValueFlowTest { + import FlowTest +} + +module TaintFlowTest { + import FlowTest +} diff --git a/ruby/ql/test/TestUtilities/InlineTypeTrackingFlowTest.qll b/ruby/ql/test/TestUtilities/InlineTypeTrackingFlowTest.qll index 2e60d1ce0e8..6d584ef31dc 100644 --- a/ruby/ql/test/TestUtilities/InlineTypeTrackingFlowTest.qll +++ b/ruby/ql/test/TestUtilities/InlineTypeTrackingFlowTest.qll @@ -15,12 +15,10 @@ DataFlow::LocalSourceNode track(DataFlow::CallNode source) { result = track(TypeTracker::end(), source) } -class TypeTrackingFlowTest extends InlineExpectationsTest { - TypeTrackingFlowTest() { this = "TypeTrackingFlowTest" } +module TypeTrackingFlowTest implements TestSig { + string getARelevantTag() { result = "hasValueFlow" } - override string getARelevantTag() { result = "hasValueFlow" } - - override predicate hasActualResult(Location location, string element, string tag, string value) { + predicate hasActualResult(Location location, string element, string tag, string value) { exists(DataFlow::Node sink, DataFlow::Node source | defaultSink(sink) and track(source).flowsTo(sink) and @@ -31,3 +29,5 @@ class TypeTrackingFlowTest extends InlineExpectationsTest { ) } } + +import MakeTest diff --git a/ruby/ql/test/library-tests/dataflow/api-graphs/ApiGraphs.expected b/ruby/ql/test/library-tests/dataflow/api-graphs/ApiGraphs.expected index 43b6490b052..5677b16fedb 100644 --- a/ruby/ql/test/library-tests/dataflow/api-graphs/ApiGraphs.expected +++ b/ruby/ql/test/library-tests/dataflow/api-graphs/ApiGraphs.expected @@ -1,8 +1,8 @@ classMethodCalls -| test1.rb:58:1:58:8 | Use getMember("M1").getMember("C1").getMethod("m").getReturn() | -| test1.rb:59:1:59:8 | Use getMember("M2").getMember("C3").getMethod("m").getReturn() | +| test1.rb:58:1:58:8 | ForwardNode(call to m) | +| test1.rb:59:1:59:8 | ForwardNode(call to m) | instanceMethodCalls -| test1.rb:61:1:61:12 | Use getMember("M1").getMember("C1").getMethod("new").getReturn().getMethod("m").getReturn() | -| test1.rb:62:1:62:12 | Use getMember("M2").getMember("C3").getMethod("new").getReturn().getMethod("m").getReturn() | +| test1.rb:61:1:61:12 | ForwardNode(call to m) | +| test1.rb:62:1:62:12 | ForwardNode(call to m) | flowThroughArray | test1.rb:73:1:73:10 | call to m | diff --git a/ruby/ql/test/library-tests/dataflow/api-graphs/VerifyApiGraphExpectations.expected b/ruby/ql/test/library-tests/dataflow/api-graphs/VerifyApiGraphExpectations.expected new file mode 100644 index 00000000000..48de9172b36 --- /dev/null +++ b/ruby/ql/test/library-tests/dataflow/api-graphs/VerifyApiGraphExpectations.expected @@ -0,0 +1,2 @@ +failures +testFailures diff --git a/ruby/ql/test/library-tests/dataflow/api-graphs/VerifyApiGraphExpectations.ql b/ruby/ql/test/library-tests/dataflow/api-graphs/VerifyApiGraphExpectations.ql new file mode 100644 index 00000000000..93b5aaf745e --- /dev/null +++ b/ruby/ql/test/library-tests/dataflow/api-graphs/VerifyApiGraphExpectations.ql @@ -0,0 +1,77 @@ +import ruby +import codeql.ruby.ast.internal.TreeSitter +import codeql.ruby.dataflow.internal.AccessPathSyntax +import codeql.ruby.frameworks.data.internal.ApiGraphModels +import codeql.ruby.ApiGraphs +import TestUtilities.InlineExpectationsTest + +class AccessPathFromExpectation extends AccessPath::Range { + AccessPathFromExpectation() { hasExpectationWithValue(_, this) } +} + +API::Node evaluatePath(AccessPath path, int n) { + path instanceof AccessPathFromExpectation and + n = 1 and + exists(AccessPathToken token | token = path.getToken(0) | + token.getName() = "Member" and + result = API::getTopLevelMember(token.getAnArgument()) + or + token.getName() = "Method" and + result = API::getTopLevelCall(token.getAnArgument()) + or + token.getName() = "EntryPoint" and + result = token.getAnArgument().(API::EntryPoint).getANode() + ) + or + result = getSuccessorFromNode(evaluatePath(path, n - 1), path.getToken(n - 1)) + or + result = getSuccessorFromInvoke(evaluatePath(path, n - 1), path.getToken(n - 1)) + or + // TODO this is a workaround, support parsing of Method['[]'] instead + path.getToken(n - 1).getName() = "MethodBracket" and + result = evaluatePath(path, n - 1).getMethod("[]") +} + +API::Node evaluatePath(AccessPath path) { result = evaluatePath(path, path.getNumToken()) } + +module ApiUseTest implements TestSig { + string getARelevantTag() { result = ["source", "sink", "call", "reachableFromSource"] } + + predicate hasActualResult(Location location, string element, string tag, string value) { + // All results are considered optional + none() + } + + predicate hasOptionalResult(Location location, string element, string tag, string value) { + exists(API::Node apiNode, DataFlow::Node dataflowNode | + apiNode = evaluatePath(value) and + ( + tag = "source" and dataflowNode = apiNode.asSource() + or + tag = "reachableFromSource" and dataflowNode = apiNode.getAValueReachableFromSource() + or + tag = "sink" and dataflowNode = apiNode.asSink() + or + tag = "call" and dataflowNode = apiNode.asCall() + ) and + location = dataflowNode.getLocation() and + element = dataflowNode.toString() + ) + } +} + +import MakeTest + +class CustomEntryPointCall extends API::EntryPoint { + CustomEntryPointCall() { this = "CustomEntryPointCall" } + + override DataFlow::CallNode getACall() { result.getMethodName() = "customEntryPointCall" } +} + +class CustomEntryPointUse extends API::EntryPoint { + CustomEntryPointUse() { this = "CustomEntryPointUse" } + + override DataFlow::LocalSourceNode getASource() { + result.(DataFlow::CallNode).getMethodName() = "customEntryPointUse" + } +} diff --git a/ruby/ql/test/library-tests/dataflow/api-graphs/callbacks.rb b/ruby/ql/test/library-tests/dataflow/api-graphs/callbacks.rb index 34c4d17d212..35cf4471b48 100644 --- a/ruby/ql/test/library-tests/dataflow/api-graphs/callbacks.rb +++ b/ruby/ql/test/library-tests/dataflow/api-graphs/callbacks.rb @@ -1,39 +1,39 @@ -Something.foo.withCallback do |a, b| #$ use=getMember("Something").getMethod("foo").getReturn() - a.something #$ use=getMember("Something").getMethod("foo").getReturn().getMethod("withCallback").getBlock().getParameter(0).getMethod("something").getReturn() - b.somethingElse #$ use=getMember("Something").getMethod("foo").getReturn().getMethod("withCallback").getBlock().getParameter(1).getMethod("somethingElse").getReturn() -end #$ use=getMember("Something").getMethod("foo").getReturn().getMethod("withCallback").getReturn() +Something.foo.withCallback do |a, b| #$ source=Member[Something].Method[foo].ReturnValue + a.something #$ source=Member[Something].Method[foo].ReturnValue.Method[withCallback].Argument[block].Argument[0].Method[something].ReturnValue + b.somethingElse #$ source=Member[Something].Method[foo].ReturnValue.Method[withCallback].Argument[block].Argument[1].Method[somethingElse].ReturnValue +end #$ source=Member[Something].Method[foo].ReturnValue.Method[withCallback].ReturnValue -Something.withNamedArg do |a:, b: nil| #$ use=getMember("Something") - a.something #$ use=getMember("Something").getMethod("withNamedArg").getBlock().getKeywordParameter("a").getMethod("something").getReturn() - b.somethingElse #$ use=getMember("Something").getMethod("withNamedArg").getBlock().getKeywordParameter("b").getMethod("somethingElse").getReturn() -end #$ use=getMember("Something").getMethod("withNamedArg").getReturn() +Something.withNamedArg do |a:, b: nil| #$ source=Member[Something] + a.something #$ source=Member[Something].Method[withNamedArg].Argument[block].Parameter[a:].Method[something].ReturnValue + b.somethingElse #$ source=Member[Something].Method[withNamedArg].Argument[block].Parameter[b:].Method[somethingElse].ReturnValue +end #$ source=Member[Something].Method[withNamedArg].ReturnValue -Something.withLambda ->(a, b) { #$ use=getMember("Something") - a.something #$ use=getMember("Something").getMethod("withLambda").getParameter(0).getParameter(0).getMethod("something").getReturn() - b.something #$ use=getMember("Something").getMethod("withLambda").getParameter(0).getParameter(1).getMethod("something").getReturn() -} #$ use=getMember("Something").getMethod("withLambda").getReturn() +Something.withLambda ->(a, b) { #$ source=Member[Something] + a.something #$ source=Member[Something].Method[withLambda].Argument[0].Parameter[0].Method[something].ReturnValue + b.something #$ source=Member[Something].Method[withLambda].Argument[0].Parameter[1].Method[something].ReturnValue +} #$ source=Member[Something].Method[withLambda].ReturnValue -Something.namedCallback( #$ use=getMember("Something") +Something.namedCallback( #$ source=Member[Something] onEvent: ->(a, b) { - a.something #$ use=getMember("Something").getMethod("namedCallback").getKeywordParameter("onEvent").getParameter(0).getMethod("something").getReturn() - b.something #$ use=getMember("Something").getMethod("namedCallback").getKeywordParameter("onEvent").getParameter(1).getMethod("something").getReturn() + a.something #$ source=Member[Something].Method[namedCallback].Argument[onEvent:].Parameter[0].Method[something].ReturnValue + b.something #$ source=Member[Something].Method[namedCallback].Argument[onEvent:].Parameter[1].Method[something].ReturnValue } -) #$ use=getMember("Something").getMethod("namedCallback").getReturn() +) #$ source=Member[Something].Method[namedCallback].ReturnValue -Something.nestedCall1 do |a| #$ use=getMember("Something") - a.nestedCall2 do |b:| #$ use=getMember("Something").getMethod("nestedCall1").getBlock().getParameter(0) - b.something #$ use=getMember("Something").getMethod("nestedCall1").getBlock().getParameter(0).getMethod("nestedCall2").getBlock().getKeywordParameter("b").getMethod("something").getReturn() - end #$ use=getMember("Something").getMethod("nestedCall1").getBlock().getParameter(0).getMethod("nestedCall2").getReturn() -end #$ use=getMember("Something").getMethod("nestedCall1").getReturn() +Something.nestedCall1 do |a| #$ source=Member[Something] + a.nestedCall2 do |b:| #$ reachableFromSource=Member[Something].Method[nestedCall1].Argument[block].Parameter[0] + b.something #$ source=Member[Something].Method[nestedCall1].Argument[block].Parameter[0].Method[nestedCall2].Argument[block].Parameter[b:].Method[something].ReturnValue + end #$ source=Member[Something].Method[nestedCall1].Argument[block].Parameter[0].Method[nestedCall2].ReturnValue +end #$ source=Member[Something].Method[nestedCall1].ReturnValue def getCallback() ->(x) { - x.something #$ use=getMember("Something").getMethod("indirectCallback").getParameter(0).getParameter(0).getMethod("something").getReturn() + x.something #$ source=Member[Something].Method[indirectCallback].Argument[0].Parameter[0].Method[something].ReturnValue } end -Something.indirectCallback(getCallback()) #$ use=getMember("Something").getMethod("indirectCallback").getReturn() +Something.indirectCallback(getCallback()) #$ source=Member[Something].Method[indirectCallback].ReturnValue -Something.withMixed do |a, *args, b| #$ use=getMember("Something") - a.something #$ use=getMember("Something").getMethod("withMixed").getBlock().getParameter(0).getMethod("something").getReturn() +Something.withMixed do |a, *args, b| #$ source=Member[Something] + a.something #$ source=Member[Something].Method[withMixed].Argument[block].Parameter[0].Method[something].ReturnValue # b.something # not currently handled correctly -end #$ use=getMember("Something").getMethod("withMixed").getReturn() +end #$ source=Member[Something].Method[withMixed].ReturnValue diff --git a/ruby/ql/test/library-tests/dataflow/api-graphs/chained-access.rb b/ruby/ql/test/library-tests/dataflow/api-graphs/chained-access.rb new file mode 100644 index 00000000000..b0e4f07e701 --- /dev/null +++ b/ruby/ql/test/library-tests/dataflow/api-graphs/chained-access.rb @@ -0,0 +1,31 @@ +def chained_access1 + Something.foo [[[ + 'sink' # $ sink=Member[Something].Method[foo].Argument[0].Element[0].Element[0].Element[0] + ]]] +end + +def chained_access2 + array = [] + array[0] = [[ + 'sink' # $ sink=Member[Something].Method[foo].Argument[0].Element[0].Element[0].Element[0] + ]] + Something.foo array +end + +def chained_access3 + array = [[]] + array[0][0] = [ + 'sink' # $ sink=Member[Something].Method[foo].Argument[0].Element[0].Element[0].Element[0] + ] + Something.foo array +end + +def chained_access4 + Something.foo { + :one => { + :two => { + :three => 'sink' # $ sink=Member[Something].Method[foo].Argument[0].Element[:one].Element[:two].Element[:three] + } + } + } +end diff --git a/ruby/ql/test/library-tests/dataflow/api-graphs/method-callbacks.rb b/ruby/ql/test/library-tests/dataflow/api-graphs/method-callbacks.rb new file mode 100644 index 00000000000..63a4b4c3dce --- /dev/null +++ b/ruby/ql/test/library-tests/dataflow/api-graphs/method-callbacks.rb @@ -0,0 +1,64 @@ +class BaseClass + def inheritedInstanceMethod + yield "taint" # $ sink=Member[Something].Method[foo].Argument[block].ReturnValue.Method[inheritedInstanceMethod].Parameter[block].Argument[0] + end + + def self.inheritedSingletonMethod + yield "taint" # $ sink=Member[Something].Method[bar].Argument[block].ReturnValue.Method[inheritedSingletonMethod].Parameter[block].Argument[0] + end +end + +class ClassWithCallbacks < BaseClass + def instanceMethod + yield "taint" # $ sink=Member[Something].Method[foo].Argument[block].ReturnValue.Method[instanceMethod].Parameter[block].Argument[0] + end + + def self.singletonMethod + yield "bar" # $ sink=Member[Something].Method[bar].Argument[block].ReturnValue.Method[singletonMethod].Parameter[block].Argument[0] + end + + def escapeSelf + Something.baz { self } + end + + def self.escapeSingletonSelf + Something.baz { self } + end + + def self.foo x + x # $ reachableFromSource=Member[BaseClass].Method[foo].Parameter[0] + x # $ reachableFromSource=Member[ClassWithCallbacks].Method[foo].Parameter[0] + x # $ reachableFromSource=Member[Subclass].Method[foo].Parameter[0] + end + + def bar x + x # $ reachableFromSource=Member[BaseClass].Instance.Method[bar].Parameter[0] + x # $ reachableFromSource=Member[ClassWithCallbacks].Instance.Method[bar].Parameter[0] + x # $ reachableFromSource=Member[Subclass].Instance.Method[bar].Parameter[0] + end +end + +class Subclass < ClassWithCallbacks + def instanceMethodInSubclass + yield "bar" # $ sink=Member[Something].Method[baz].Argument[block].ReturnValue.Method[instanceMethodInSubclass].Parameter[block].Argument[0] + end + + def self.singletonMethodInSubclass + yield "bar" # $ sink=Member[Something].Method[baz].Argument[block].ReturnValue.Method[singletonMethodInSubclass].Parameter[block].Argument[0] + end +end + +Something.foo { ClassWithCallbacks.new } +Something.bar { ClassWithCallbacks } + +class ClassWithCallMethod + def call x + x # $ reachableFromSource=Method[topLevelMethod].Argument[0].Parameter[0] + "bar" # $ sink=Method[topLevelMethod].Argument[0].ReturnValue + end +end + +topLevelMethod ClassWithCallMethod.new + +blah = topLevelMethod +blah # $ reachableFromSource=Method[topLevelMethod].ReturnValue diff --git a/ruby/ql/test/library-tests/dataflow/api-graphs/self-dot-class.rb b/ruby/ql/test/library-tests/dataflow/api-graphs/self-dot-class.rb new file mode 100644 index 00000000000..178cacbe2c0 --- /dev/null +++ b/ruby/ql/test/library-tests/dataflow/api-graphs/self-dot-class.rb @@ -0,0 +1,10 @@ +module SelfDotClass + module Mixin + def foo + self.class.bar # $ call=Member[Foo].Method[bar] + end + end + class Subclass < Foo + include Mixin + end +end diff --git a/ruby/ql/test/library-tests/dataflow/api-graphs/test1.rb b/ruby/ql/test/library-tests/dataflow/api-graphs/test1.rb index 86b8bce9587..3af793dd4f7 100644 --- a/ruby/ql/test/library-tests/dataflow/api-graphs/test1.rb +++ b/ruby/ql/test/library-tests/dataflow/api-graphs/test1.rb @@ -1,34 +1,34 @@ -MyModule #$ use=getMember("MyModule") -print MyModule.foo #$ use=getMember("MyModule").getMethod("foo").getReturn() -Kernel.print(e) #$ use=getMember("Kernel").getMethod("print").getReturn() def=getMember("Kernel").getMethod("print").getParameter(0) -Object::Kernel #$ use=getMember("Kernel") -Object::Kernel.print(e) #$ use=getMember("Kernel").getMethod("print").getReturn() +MyModule #$ source=Member[MyModule] +print MyModule.foo #$ source=Member[MyModule].Method[foo].ReturnValue +Kernel.print(e) #$ source=Member[Kernel].Method[print].ReturnValue sink=Member[Kernel].Method[print].Argument[0] +Object::Kernel #$ source=Member[Kernel] +Object::Kernel.print(e) #$ source=Member[Kernel].Method[print].ReturnValue begin - print MyModule.bar #$ use=getMember("MyModule").getMethod("bar").getReturn() - raise AttributeError #$ use=getMember("AttributeError") -rescue AttributeError => e #$ use=getMember("AttributeError") - Kernel.print(e) #$ use=getMember("Kernel").getMethod("print").getReturn() + print MyModule.bar #$ source=Member[MyModule].Method[bar].ReturnValue + raise AttributeError #$ source=Member[AttributeError] +rescue AttributeError => e #$ source=Member[AttributeError] + Kernel.print(e) #$ source=Member[Kernel].Method[print].ReturnValue end -Unknown.new.run #$ use=getMember("Unknown").getMethod("new").getReturn().getMethod("run").getReturn() -Foo::Bar::Baz #$ use=getMember("Foo").getMember("Bar").getMember("Baz") +Unknown.new.run #$ source=Member[Unknown].Method[new].ReturnValue.Method[run].ReturnValue +Foo::Bar::Baz #$ source=Member[Foo].Member[Bar].Member[Baz] -Const = [1, 2, 3] #$ use=getMember("Array").getMethod("[]").getReturn() -Const.each do |c| #$ use=getMember("Const") - puts c #$ use=getMember("Const").getMethod("each").getBlock().getParameter(0) use=getMember("Const").getContent(element) -end #$ use=getMember("Const").getMethod("each").getReturn() def=getMember("Const").getMethod("each").getBlock() +Const = [1, 2, 3] #$ source=Member[Array].MethodBracket.ReturnValue +Const.each do |c| #$ source=Member[Const] + puts c #$ reachableFromSource=Member[Const].Method[each].Argument[block].Parameter[0] reachableFromSource=Member[Const].Element[any] +end #$ source=Member[Const].Method[each].ReturnValue sink=Member[Const].Method[each].Argument[block] -foo = Foo #$ use=getMember("Foo") -foo::Bar::Baz #$ use=getMember("Foo").getMember("Bar").getMember("Baz") +foo = Foo #$ source=Member[Foo] +foo::Bar::Baz #$ source=Member[Foo].Member[Bar].Member[Baz] -FooAlias = Foo #$ use=getMember("Foo") -FooAlias::Bar::Baz #$ use=getMember("Foo").getMember("Bar").getMember("Baz") +FooAlias = Foo #$ source=Member[Foo] +FooAlias::Bar::Baz #$ source=Member[Foo].Member[Bar].Member[Baz] source=Member[FooAlias].Member[Bar].Member[Baz] module Outer module Inner end end -Outer::Inner.foo #$ use=getMember("Outer").getMember("Inner").getMethod("foo").getReturn() +Outer::Inner.foo #$ source=Member[Outer].Member[Inner].Method[foo].ReturnValue module M1 class C1 @@ -40,36 +40,36 @@ module M1 end end -class C2 < M1::C1 #$ use=getMember("M1").getMember("C1") +class C2 < M1::C1 #$ source=Member[M1].Member[C1] end module M2 - class C3 < M1::C1 #$ use=getMember("M1").getMember("C1") + class C3 < M1::C1 #$ source=Member[M1].Member[C1] end - class C4 < C2 #$ use=getMember("C2") + class C4 < C2 #$ source=Member[C2] end end -C2 #$ use=getMember("C2") use=getMember("M1").getMember("C1").getASubclass() -M2::C3 #$ use=getMember("M2").getMember("C3") use=getMember("M1").getMember("C1").getASubclass() -M2::C4 #$ use=getMember("M2").getMember("C4") use=getMember("C2").getASubclass() use=getMember("M1").getMember("C1").getASubclass().getASubclass() +C2 #$ source=Member[C2] reachableFromSource=Member[M1].Member[C1] +M2::C3 #$ source=Member[M2].Member[C3] reachableFromSource=Member[M1].Member[C1] +M2::C4 #$ source=Member[M2].Member[C4] reachableFromSource=Member[C2] reachableFromSource=Member[M1].Member[C1] -M1::C1.m #$ use=getMember("M1").getMember("C1").getMethod("m").getReturn() -M2::C3.m #$ use=getMember("M2").getMember("C3").getMethod("m").getReturn() use=getMember("M1").getMember("C1").getASubclass().getMethod("m").getReturn() +M1::C1.m #$ source=Member[M1].Member[C1].Method[m].ReturnValue +M2::C3.m #$ source=Member[M2].Member[C3].Method[m].ReturnValue source=Member[M1].Member[C1].Method[m].ReturnValue -M1::C1.new.m #$ use=getMember("M1").getMember("C1").getMethod("new").getReturn().getMethod("m").getReturn() -M2::C3.new.m #$ use=getMember("M2").getMember("C3").getMethod("new").getReturn().getMethod("m").getReturn() +M1::C1.new.m #$ source=Member[M1].Member[C1].Method[new].ReturnValue.Method[m].ReturnValue +M2::C3.new.m #$ source=Member[M2].Member[C3].Method[new].ReturnValue.Method[m].ReturnValue -Foo.foo(a,b:c) #$ use=getMember("Foo").getMethod("foo").getReturn() def=getMember("Foo").getMethod("foo").getParameter(0) def=getMember("Foo").getMethod("foo").getKeywordParameter("b") +Foo.foo(a,b:c) #$ source=Member[Foo].Method[foo].ReturnValue sink=Member[Foo].Method[foo].Argument[0] sink=Member[Foo].Method[foo].Argument[b:] def userDefinedFunction(x, y) x.noApiGraph(y) - x.customEntryPointCall(y) #$ call=entryPoint("CustomEntryPointCall") use=entryPoint("CustomEntryPointCall").getReturn() rhs=entryPoint("CustomEntryPointCall").getParameter(0) - x.customEntryPointUse(y) #$ use=entryPoint("CustomEntryPointUse") + x.customEntryPointCall(y) #$ call=EntryPoint[CustomEntryPointCall] source=EntryPoint[CustomEntryPointCall].ReturnValue sink=EntryPoint[CustomEntryPointCall].Parameter[0] + x.customEntryPointUse(y) #$ source=EntryPoint[CustomEntryPointUse] end -array = [A::B::C] #$ use=getMember("Array").getMethod("[]").getReturn() -array[0].m #$ use=getMember("A").getMember("B").getMember("C").getMethod("m").getReturn() +array = [A::B::C] #$ source=Member[Array].MethodBracket.ReturnValue +array[0].m #$ source=Member[A].Member[B].Member[C].Method[m].ReturnValue source=Member[Array].MethodBracket.ReturnValue.Element[0].Method[m].ReturnValue -A::B::C[0] #$ use=getMember("A").getMember("B").getMember("C").getContent(element_0) +A::B::C[0] #$ source=Member[A].Member[B].Member[C].Element[0] diff --git a/ruby/ql/test/library-tests/dataflow/api-graphs/use.ql b/ruby/ql/test/library-tests/dataflow/api-graphs/use.ql deleted file mode 100644 index a0c11640ce0..00000000000 --- a/ruby/ql/test/library-tests/dataflow/api-graphs/use.ql +++ /dev/null @@ -1,88 +0,0 @@ -import codeql.ruby.AST -import codeql.ruby.DataFlow -import TestUtilities.InlineExpectationsTest -import codeql.ruby.ApiGraphs - -class CustomEntryPointCall extends API::EntryPoint { - CustomEntryPointCall() { this = "CustomEntryPointCall" } - - override DataFlow::CallNode getACall() { result.getMethodName() = "customEntryPointCall" } -} - -class CustomEntryPointUse extends API::EntryPoint { - CustomEntryPointUse() { this = "CustomEntryPointUse" } - - override DataFlow::LocalSourceNode getASource() { - result.(DataFlow::CallNode).getMethodName() = "customEntryPointUse" - } -} - -module ApiUseTest implements TestSig { - string getARelevantTag() { result = ["use", "def", "call"] } - - private predicate relevantNode(API::Node a, DataFlow::Node n, Location l, string tag) { - l = n.getLocation() and - ( - tag = "use" and - n = a.getAValueReachableFromSource() - or - tag = "def" and - n = a.asSink() - or - tag = "call" and - n = a.(API::MethodAccessNode).getCallNode() - ) - } - - predicate hasActualResult(Location location, string element, string tag, string value) { - tag = "use" and // def tags are always optional - exists(DataFlow::Node n | relevantNode(_, n, location, tag) | - // Only report the longest path on this line: - value = - max(API::Node a2, Location l2, DataFlow::Node n2 | - relevantNode(a2, n2, l2, tag) and - l2.getFile() = location.getFile() and - l2.getEndLine() = location.getEndLine() - | - a2.getPath() - order by - size(n2.asExpr().getExpr()), a2.getPath().length() desc, a2.getPath() desc - ) and - element = n.toString() - ) - } - - // We also permit optional annotations for any other path on the line. - // This is used to test subclass paths, which typically have a shorter canonical path. - predicate hasOptionalResult(Location location, string element, string tag, string value) { - exists(API::Node a, DataFlow::Node n | relevantNode(a, n, location, tag) | - element = n.toString() and - value = getAPath(a, _) - ) - } -} - -import MakeTest - -private int size(AstNode n) { not n instanceof StmtSequence and result = count(n.getAChild*()) } - -/** - * Gets a path of the given `length` from the root to the given node. - * This is a copy of `API::getAPath()` without the restriction on path length, - * which would otherwise rule out paths involving `getASubclass()`. - */ -string getAPath(API::Node node, int length) { - node instanceof API::Root and - length = 0 and - result = "" - or - exists(API::Node pred, API::Label::ApiLabel lbl, string predpath | - pred.getASuccessor(lbl) = node and - predpath = getAPath(pred, length - 1) and - exists(string dot | if length = 1 then dot = "" else dot = "." | - result = predpath + dot + lbl and - // avoid producing strings longer than 1MB - result.length() < 1000 * 1000 - ) - ) -} diff --git a/ruby/ql/test/library-tests/dataflow/array-flow/array-flow.expected b/ruby/ql/test/library-tests/dataflow/array-flow/array-flow.expected index d258253537a..b406f9feecb 100644 --- a/ruby/ql/test/library-tests/dataflow/array-flow/array-flow.expected +++ b/ruby/ql/test/library-tests/dataflow/array-flow/array-flow.expected @@ -1,4 +1,5 @@ failures +testFailures edges | array_flow.rb:2:5:2:5 | a [element 0] | array_flow.rb:3:10:3:10 | a [element 0] | | array_flow.rb:2:5:2:5 | a [element 0] | array_flow.rb:3:10:3:10 | a [element 0] | diff --git a/ruby/ql/test/library-tests/dataflow/array-flow/array-flow.ql b/ruby/ql/test/library-tests/dataflow/array-flow/array-flow.ql index 0c4597b8f07..dfd6242a414 100644 --- a/ruby/ql/test/library-tests/dataflow/array-flow/array-flow.ql +++ b/ruby/ql/test/library-tests/dataflow/array-flow/array-flow.ql @@ -4,8 +4,9 @@ import codeql.ruby.AST import TestUtilities.InlineFlowTest +import DefaultFlowTest import PathGraph -from DataFlow::PathNode source, DataFlow::PathNode sink, DefaultValueFlowConf conf -where conf.hasFlowPath(source, sink) +from ValueFlow::PathNode source, ValueFlow::PathNode sink +where ValueFlow::flowPath(source, sink) select sink, source, sink, "$@", source, source.toString() diff --git a/ruby/ql/test/library-tests/dataflow/array-flow/type-tracking-array-flow.expected b/ruby/ql/test/library-tests/dataflow/array-flow/type-tracking-array-flow.expected index 83e81729d98..5374f468c3e 100644 --- a/ruby/ql/test/library-tests/dataflow/array-flow/type-tracking-array-flow.expected +++ b/ruby/ql/test/library-tests/dataflow/array-flow/type-tracking-array-flow.expected @@ -1,3 +1,5 @@ +failures +testFailures | array_flow.rb:107:10:107:13 | ...[...] | Unexpected result: hasValueFlow=11.2 | | array_flow.rb:179:28:179:46 | # $ hasValueFlow=19 | Missing result:hasValueFlow=19 | | array_flow.rb:180:28:180:46 | # $ hasValueFlow=19 | Missing result:hasValueFlow=19 | diff --git a/ruby/ql/test/library-tests/dataflow/call-sensitivity/call-sensitivity.expected b/ruby/ql/test/library-tests/dataflow/call-sensitivity/call-sensitivity.expected index 58d5861bae9..a3ea8f01feb 100644 --- a/ruby/ql/test/library-tests/dataflow/call-sensitivity/call-sensitivity.expected +++ b/ruby/ql/test/library-tests/dataflow/call-sensitivity/call-sensitivity.expected @@ -1,4 +1,5 @@ failures +testFailures edges | call_sensitivity.rb:9:7:9:13 | call to taint | call_sensitivity.rb:9:6:9:14 | ( ... ) | | call_sensitivity.rb:9:7:9:13 | call to taint | call_sensitivity.rb:9:6:9:14 | ( ... ) | diff --git a/ruby/ql/test/library-tests/dataflow/call-sensitivity/call-sensitivity.ql b/ruby/ql/test/library-tests/dataflow/call-sensitivity/call-sensitivity.ql index 6403c4b04c2..08c0fa8fc45 100644 --- a/ruby/ql/test/library-tests/dataflow/call-sensitivity/call-sensitivity.ql +++ b/ruby/ql/test/library-tests/dataflow/call-sensitivity/call-sensitivity.ql @@ -5,13 +5,14 @@ import codeql.ruby.AST import codeql.ruby.DataFlow import TestUtilities.InlineFlowTest -import DataFlow::PathGraph +import DefaultFlowTest +import PathGraph import codeql.ruby.dataflow.internal.DataFlowDispatch as DataFlowDispatch query predicate mayBenefitFromCallContext = DataFlowDispatch::mayBenefitFromCallContext/2; query predicate viableImplInCallContext = DataFlowDispatch::viableImplInCallContext/2; -from DataFlow::PathNode source, DataFlow::PathNode sink, DefaultTaintFlowConf conf -where conf.hasFlowPath(source, sink) +from TaintFlow::PathNode source, TaintFlow::PathNode sink +where TaintFlow::flowPath(source, sink) select sink, source, sink, "$@", source, source.toString() diff --git a/ruby/ql/test/library-tests/dataflow/flow-summaries/semantics.expected b/ruby/ql/test/library-tests/dataflow/flow-summaries/semantics.expected index fc781f05d3c..b3e010e86b6 100644 --- a/ruby/ql/test/library-tests/dataflow/flow-summaries/semantics.expected +++ b/ruby/ql/test/library-tests/dataflow/flow-summaries/semantics.expected @@ -1,4 +1,5 @@ failures +testFailures edges | semantics.rb:2:5:2:5 | a | semantics.rb:3:9:3:9 | a | | semantics.rb:2:5:2:5 | a | semantics.rb:3:9:3:9 | a | diff --git a/ruby/ql/test/library-tests/dataflow/flow-summaries/semantics.ql b/ruby/ql/test/library-tests/dataflow/flow-summaries/semantics.ql index 9733e0c5393..c3c2e45322a 100644 --- a/ruby/ql/test/library-tests/dataflow/flow-summaries/semantics.ql +++ b/ruby/ql/test/library-tests/dataflow/flow-summaries/semantics.ql @@ -5,6 +5,7 @@ import codeql.ruby.AST import TestUtilities.InlineFlowTest +import DefaultFlowTest import PathGraph private import codeql.ruby.dataflow.FlowSummary diff --git a/ruby/ql/test/library-tests/dataflow/global/Flow.expected b/ruby/ql/test/library-tests/dataflow/global/Flow.expected index 63359aa9a36..5ddbe3ce98c 100644 --- a/ruby/ql/test/library-tests/dataflow/global/Flow.expected +++ b/ruby/ql/test/library-tests/dataflow/global/Flow.expected @@ -1,4 +1,5 @@ failures +testFailures edges | captured_variables.rb:1:24:1:24 | x | captured_variables.rb:2:20:2:20 | x | | captured_variables.rb:1:24:1:24 | x | captured_variables.rb:2:20:2:20 | x | diff --git a/ruby/ql/test/library-tests/dataflow/global/Flow.ql b/ruby/ql/test/library-tests/dataflow/global/Flow.ql index 33825f765c2..64ce30508fb 100644 --- a/ruby/ql/test/library-tests/dataflow/global/Flow.ql +++ b/ruby/ql/test/library-tests/dataflow/global/Flow.ql @@ -5,8 +5,9 @@ import codeql.ruby.AST import codeql.ruby.DataFlow private import TestUtilities.InlineFlowTest -import DataFlow::PathGraph +import DefaultFlowTest +import PathGraph -from DataFlow::PathNode source, DataFlow::PathNode sink, DefaultTaintFlowConf conf -where conf.hasFlowPath(source, sink) +from TaintFlow::PathNode source, TaintFlow::PathNode sink +where TaintFlow::flowPath(source, sink) select sink, source, sink, "$@", source, source.toString() diff --git a/ruby/ql/test/library-tests/dataflow/global/TypeTrackingInlineTest.expected b/ruby/ql/test/library-tests/dataflow/global/TypeTrackingInlineTest.expected index 88d69578cf7..8fbd6f51044 100644 --- a/ruby/ql/test/library-tests/dataflow/global/TypeTrackingInlineTest.expected +++ b/ruby/ql/test/library-tests/dataflow/global/TypeTrackingInlineTest.expected @@ -1,3 +1,5 @@ +failures +testFailures | captured_variables.rb:9:14:9:14 | x | Fixed missing result:hasValueFlow=1.2 | | captured_variables.rb:16:14:16:14 | x | Fixed missing result:hasValueFlow=1.3 | | instance_variables.rb:20:16:20:33 | # $ hasValueFlow=7 | Missing result:hasValueFlow=7 | diff --git a/ruby/ql/test/library-tests/dataflow/hash-flow/hash-flow.expected b/ruby/ql/test/library-tests/dataflow/hash-flow/hash-flow.expected index fff890f80b9..7be100aa1fe 100644 --- a/ruby/ql/test/library-tests/dataflow/hash-flow/hash-flow.expected +++ b/ruby/ql/test/library-tests/dataflow/hash-flow/hash-flow.expected @@ -1,4 +1,5 @@ failures +testFailures edges | hash_flow.rb:10:5:10:8 | hash [element 0] | hash_flow.rb:30:10:30:13 | hash [element 0] | | hash_flow.rb:10:5:10:8 | hash [element :a] | hash_flow.rb:22:10:22:13 | hash [element :a] | diff --git a/ruby/ql/test/library-tests/dataflow/hash-flow/hash-flow.ql b/ruby/ql/test/library-tests/dataflow/hash-flow/hash-flow.ql index bb1b611d7fc..31941897936 100644 --- a/ruby/ql/test/library-tests/dataflow/hash-flow/hash-flow.ql +++ b/ruby/ql/test/library-tests/dataflow/hash-flow/hash-flow.ql @@ -4,12 +4,9 @@ import codeql.ruby.AST import TestUtilities.InlineFlowTest +import ValueFlowTest import PathGraph -class HasFlowTest extends InlineFlowTest { - override DataFlow::Configuration getTaintFlowConfig() { none() } -} - -from DataFlow::PathNode source, DataFlow::PathNode sink, DefaultValueFlowConf conf -where conf.hasFlowPath(source, sink) +from ValueFlow::PathNode source, ValueFlow::PathNode sink +where ValueFlow::flowPath(source, sink) select sink, source, sink, "$@", source, source.toString() diff --git a/ruby/ql/test/library-tests/dataflow/hash-flow/type-tracking-hash-flow.expected b/ruby/ql/test/library-tests/dataflow/hash-flow/type-tracking-hash-flow.expected index ff6899d41c2..af062eec4fd 100644 --- a/ruby/ql/test/library-tests/dataflow/hash-flow/type-tracking-hash-flow.expected +++ b/ruby/ql/test/library-tests/dataflow/hash-flow/type-tracking-hash-flow.expected @@ -1,3 +1,5 @@ +failures +testFailures | hash_flow.rb:65:21:65:40 | # $ hasValueFlow=3.3 | Missing result:hasValueFlow=3.3 | | hash_flow.rb:66:21:66:49 | # $ SPURIOUS hasValueFlow=3.3 | Missing result:hasValueFlow=3.3 | | hash_flow.rb:114:10:114:17 | ...[...] | Unexpected result: hasValueFlow=7.2 | diff --git a/ruby/ql/test/library-tests/dataflow/local/InlineFlowTest.expected b/ruby/ql/test/library-tests/dataflow/local/InlineFlowTest.expected index b5ad38dfcbb..d979e6f052a 100644 --- a/ruby/ql/test/library-tests/dataflow/local/InlineFlowTest.expected +++ b/ruby/ql/test/library-tests/dataflow/local/InlineFlowTest.expected @@ -1,4 +1,5 @@ failures +testFailures edges | local_dataflow.rb:78:3:78:3 | z | local_dataflow.rb:89:8:89:8 | z | | local_dataflow.rb:78:12:78:20 | call to source | local_dataflow.rb:79:13:79:13 | b | diff --git a/ruby/ql/test/library-tests/dataflow/local/InlineFlowTest.ql b/ruby/ql/test/library-tests/dataflow/local/InlineFlowTest.ql index 4f6740c7e17..9a5ca73aa12 100644 --- a/ruby/ql/test/library-tests/dataflow/local/InlineFlowTest.ql +++ b/ruby/ql/test/library-tests/dataflow/local/InlineFlowTest.ql @@ -4,8 +4,9 @@ import codeql.ruby.AST import TestUtilities.InlineFlowTest +import DefaultFlowTest import PathGraph -from DataFlow::PathNode source, DataFlow::PathNode sink, DefaultTaintFlowConf conf -where conf.hasFlowPath(source, sink) +from TaintFlow::PathNode source, TaintFlow::PathNode sink +where TaintFlow::flowPath(source, sink) select sink, source, sink, "$@", source, source.toString() diff --git a/ruby/ql/test/library-tests/dataflow/params/params-flow.expected b/ruby/ql/test/library-tests/dataflow/params/params-flow.expected index cd2d1c87b28..b54948d17a4 100644 --- a/ruby/ql/test/library-tests/dataflow/params/params-flow.expected +++ b/ruby/ql/test/library-tests/dataflow/params/params-flow.expected @@ -1,4 +1,5 @@ failures +testFailures edges | params_flow.rb:9:16:9:17 | p1 | params_flow.rb:10:10:10:11 | p1 | | params_flow.rb:9:20:9:21 | p2 | params_flow.rb:11:10:11:11 | p2 | diff --git a/ruby/ql/test/library-tests/dataflow/params/params-flow.ql b/ruby/ql/test/library-tests/dataflow/params/params-flow.ql index bb1b611d7fc..31941897936 100644 --- a/ruby/ql/test/library-tests/dataflow/params/params-flow.ql +++ b/ruby/ql/test/library-tests/dataflow/params/params-flow.ql @@ -4,12 +4,9 @@ import codeql.ruby.AST import TestUtilities.InlineFlowTest +import ValueFlowTest import PathGraph -class HasFlowTest extends InlineFlowTest { - override DataFlow::Configuration getTaintFlowConfig() { none() } -} - -from DataFlow::PathNode source, DataFlow::PathNode sink, DefaultValueFlowConf conf -where conf.hasFlowPath(source, sink) +from ValueFlow::PathNode source, ValueFlow::PathNode sink +where ValueFlow::flowPath(source, sink) select sink, source, sink, "$@", source, source.toString() diff --git a/ruby/ql/test/library-tests/dataflow/pathname-flow/pathame-flow.expected b/ruby/ql/test/library-tests/dataflow/pathname-flow/pathame-flow.expected index 37a7d126193..44fc7a3d3d9 100644 --- a/ruby/ql/test/library-tests/dataflow/pathname-flow/pathame-flow.expected +++ b/ruby/ql/test/library-tests/dataflow/pathname-flow/pathame-flow.expected @@ -1,4 +1,5 @@ failures +testFailures edges | pathname_flow.rb:4:5:4:6 | pn | pathname_flow.rb:5:10:5:11 | pn | | pathname_flow.rb:4:10:4:33 | call to new | pathname_flow.rb:4:5:4:6 | pn | diff --git a/ruby/ql/test/library-tests/dataflow/pathname-flow/pathame-flow.ql b/ruby/ql/test/library-tests/dataflow/pathname-flow/pathame-flow.ql index 0c4597b8f07..dfd6242a414 100644 --- a/ruby/ql/test/library-tests/dataflow/pathname-flow/pathame-flow.ql +++ b/ruby/ql/test/library-tests/dataflow/pathname-flow/pathame-flow.ql @@ -4,8 +4,9 @@ import codeql.ruby.AST import TestUtilities.InlineFlowTest +import DefaultFlowTest import PathGraph -from DataFlow::PathNode source, DataFlow::PathNode sink, DefaultValueFlowConf conf -where conf.hasFlowPath(source, sink) +from ValueFlow::PathNode source, ValueFlow::PathNode sink +where ValueFlow::flowPath(source, sink) select sink, source, sink, "$@", source, source.toString() diff --git a/ruby/ql/test/library-tests/dataflow/ssa-flow/ssa-flow.expected b/ruby/ql/test/library-tests/dataflow/ssa-flow/ssa-flow.expected index e1e2893fad6..744c508f33f 100644 --- a/ruby/ql/test/library-tests/dataflow/ssa-flow/ssa-flow.expected +++ b/ruby/ql/test/library-tests/dataflow/ssa-flow/ssa-flow.expected @@ -1,4 +1,5 @@ failures +testFailures edges | ssa_flow.rb:12:9:12:9 | [post] a [element 0] | ssa_flow.rb:16:10:16:10 | a [element 0] | | ssa_flow.rb:12:9:12:9 | [post] a [element 0] | ssa_flow.rb:16:10:16:10 | a [element 0] | diff --git a/ruby/ql/test/library-tests/dataflow/ssa-flow/ssa-flow.ql b/ruby/ql/test/library-tests/dataflow/ssa-flow/ssa-flow.ql index 0c4597b8f07..dfd6242a414 100644 --- a/ruby/ql/test/library-tests/dataflow/ssa-flow/ssa-flow.ql +++ b/ruby/ql/test/library-tests/dataflow/ssa-flow/ssa-flow.ql @@ -4,8 +4,9 @@ import codeql.ruby.AST import TestUtilities.InlineFlowTest +import DefaultFlowTest import PathGraph -from DataFlow::PathNode source, DataFlow::PathNode sink, DefaultValueFlowConf conf -where conf.hasFlowPath(source, sink) +from ValueFlow::PathNode source, ValueFlow::PathNode sink +where ValueFlow::flowPath(source, sink) select sink, source, sink, "$@", source, source.toString() diff --git a/ruby/ql/test/library-tests/dataflow/string-flow/string-flow.expected b/ruby/ql/test/library-tests/dataflow/string-flow/string-flow.expected index 4cab79b9e42..57eb688e6af 100644 --- a/ruby/ql/test/library-tests/dataflow/string-flow/string-flow.expected +++ b/ruby/ql/test/library-tests/dataflow/string-flow/string-flow.expected @@ -1,4 +1,5 @@ failures +testFailures | string_flow.rb:85:10:85:10 | a | Unexpected result: hasValueFlow=a | | string_flow.rb:227:10:227:10 | a | Unexpected result: hasValueFlow=a | edges diff --git a/ruby/ql/test/library-tests/dataflow/string-flow/string-flow.ql b/ruby/ql/test/library-tests/dataflow/string-flow/string-flow.ql index 0c4597b8f07..dfd6242a414 100644 --- a/ruby/ql/test/library-tests/dataflow/string-flow/string-flow.ql +++ b/ruby/ql/test/library-tests/dataflow/string-flow/string-flow.ql @@ -4,8 +4,9 @@ import codeql.ruby.AST import TestUtilities.InlineFlowTest +import DefaultFlowTest import PathGraph -from DataFlow::PathNode source, DataFlow::PathNode sink, DefaultValueFlowConf conf -where conf.hasFlowPath(source, sink) +from ValueFlow::PathNode source, ValueFlow::PathNode sink +where ValueFlow::flowPath(source, sink) select sink, source, sink, "$@", source, source.toString() diff --git a/ruby/ql/test/library-tests/dataflow/summaries/Summaries.expected b/ruby/ql/test/library-tests/dataflow/summaries/Summaries.expected index eb990f9ab27..0597947595a 100644 --- a/ruby/ql/test/library-tests/dataflow/summaries/Summaries.expected +++ b/ruby/ql/test/library-tests/dataflow/summaries/Summaries.expected @@ -1,4 +1,5 @@ failures +testFailures edges | summaries.rb:1:1:1:7 | tainted | summaries.rb:2:6:2:12 | tainted | | summaries.rb:1:1:1:7 | tainted | summaries.rb:2:6:2:12 | tainted | diff --git a/ruby/ql/test/library-tests/dataflow/summaries/Summaries.ql b/ruby/ql/test/library-tests/dataflow/summaries/Summaries.ql index b59862d0e5a..11145ed991c 100644 --- a/ruby/ql/test/library-tests/dataflow/summaries/Summaries.ql +++ b/ruby/ql/test/library-tests/dataflow/summaries/Summaries.ql @@ -10,7 +10,7 @@ import codeql.ruby.dataflow.internal.FlowSummaryImpl import codeql.ruby.dataflow.internal.AccessPathSyntax import codeql.ruby.frameworks.data.ModelsAsData import TestUtilities.InlineFlowTest -import DataFlow::PathGraph +import PathGraph query predicate invalidSpecComponent(SummarizedCallable sc, string s, string c) { (sc.propagatesFlowExt(s, _, _) or sc.propagatesFlowExt(_, s, _)) and @@ -149,22 +149,18 @@ private class SinkFromModel extends ModelInput::SinkModelCsv { } } -class CustomValueSink extends DefaultValueFlowConf { - override predicate isSink(DataFlow::Node sink) { - super.isSink(sink) +module CustomConfig implements DataFlow::ConfigSig { + predicate isSource(DataFlow::Node source) { DefaultFlowConfig::isSource(source) } + + predicate isSink(DataFlow::Node sink) { + DefaultFlowConfig::isSink(sink) or sink = ModelOutput::getASinkNode("test-sink").asSink() } } -class CustomTaintSink extends DefaultTaintFlowConf { - override predicate isSink(DataFlow::Node sink) { - super.isSink(sink) - or - sink = ModelOutput::getASinkNode("test-sink").asSink() - } -} +import FlowTest -from DataFlow::PathNode source, DataFlow::PathNode sink, DataFlow::Configuration conf -where conf.hasFlowPath(source, sink) +from PathNode source, PathNode sink +where flowPath(source, sink) select sink, source, sink, "$@", source, source.toString() diff --git a/ruby/ql/test/library-tests/frameworks/Twirp/Twirp.expected b/ruby/ql/test/library-tests/frameworks/Twirp/Twirp.expected index 66da43ab78b..4f1b0c30920 100644 --- a/ruby/ql/test/library-tests/frameworks/Twirp/Twirp.expected +++ b/ruby/ql/test/library-tests/frameworks/Twirp/Twirp.expected @@ -1,6 +1,8 @@ sourceTest | hello_world_server.rb:8:13:8:15 | req | +| hello_world_server.rb:32:18:32:20 | req | ssrfSinkTest | hello_world_client.rb:6:47:6:75 | "http://localhost:8080/twirp" | serviceInstantiationTest | hello_world_server.rb:24:11:24:61 | call to new | +| hello_world_server.rb:38:1:38:57 | call to new | diff --git a/ruby/ql/test/library-tests/frameworks/Twirp/Twirp.ql b/ruby/ql/test/library-tests/frameworks/Twirp/Twirp.ql index 4c0494f9100..fee49cbb48c 100644 --- a/ruby/ql/test/library-tests/frameworks/Twirp/Twirp.ql +++ b/ruby/ql/test/library-tests/frameworks/Twirp/Twirp.ql @@ -5,4 +5,4 @@ query predicate sourceTest(Twirp::UnmarshaledParameter source) { any() } query predicate ssrfSinkTest(Twirp::ServiceUrlAsSsrfSink sink) { any() } -query predicate serviceInstantiationTest(Twirp::ServiceInstantiation si) { any() } +deprecated query predicate serviceInstantiationTest(Twirp::ServiceInstantiation si) { any() } diff --git a/ruby/ql/test/library-tests/frameworks/Twirp/hello_world_server.rb b/ruby/ql/test/library-tests/frameworks/Twirp/hello_world_server.rb index 1aa0b9aa8de..7cd117a5843 100644 --- a/ruby/ql/test/library-tests/frameworks/Twirp/hello_world_server.rb +++ b/ruby/ql/test/library-tests/frameworks/Twirp/hello_world_server.rb @@ -5,7 +5,7 @@ require_relative 'hello_world/service_twirp.rb' class HelloWorldHandler # test: request - def hello(req, env) + def hello(req, env) puts ">> Hello #{req.name}" {message: "Hello #{req.name}"} end @@ -13,7 +13,7 @@ end class FakeHelloWorldHandler # test: !request - def hello(req, env) + def hello(req, env) puts ">> Hello #{req.name}" {message: "Hello #{req.name}"} end @@ -21,9 +21,18 @@ end handler = HelloWorldHandler.new() # test: serviceInstantiation -service = Example::HelloWorld::HelloWorldService.new(handler) +service = Example::HelloWorld::HelloWorldService.new(handler) path_prefix = "/twirp/" + service.full_name server = WEBrick::HTTPServer.new(Port: 8080) server.mount path_prefix, Rack::Handler::WEBrick, service server.start + +class StaticHandler + def self.hello(req, env) + puts ">> Hello #{req.name}" + {message: "Hello #{req.name}"} + end +end + +Example::HelloWorld::HelloWorldService.new(StaticHandler) diff --git a/ruby/ql/test/library-tests/frameworks/action_controller/params-flow.expected b/ruby/ql/test/library-tests/frameworks/action_controller/params-flow.expected index 2fb3829dd40..d9b6f59f4af 100644 --- a/ruby/ql/test/library-tests/frameworks/action_controller/params-flow.expected +++ b/ruby/ql/test/library-tests/frameworks/action_controller/params-flow.expected @@ -1,4 +1,5 @@ failures +testFailures | filter_flow.rb:21:10:21:13 | @foo | Unexpected result: hasTaintFlow= | | filter_flow.rb:38:10:38:13 | @foo | Unexpected result: hasTaintFlow= | | filter_flow.rb:55:10:55:13 | @foo | Unexpected result: hasTaintFlow= | diff --git a/ruby/ql/test/library-tests/frameworks/action_controller/params-flow.ql b/ruby/ql/test/library-tests/frameworks/action_controller/params-flow.ql index 412ba5534b8..1e4e66c5584 100644 --- a/ruby/ql/test/library-tests/frameworks/action_controller/params-flow.ql +++ b/ruby/ql/test/library-tests/frameworks/action_controller/params-flow.ql @@ -7,12 +7,14 @@ import TestUtilities.InlineFlowTest import PathGraph import codeql.ruby.frameworks.Rails -class ParamsTaintFlowConf extends DefaultTaintFlowConf { - override predicate isSource(DataFlow::Node n) { - n.asExpr().getExpr() instanceof Rails::ParamsCall - } +module ParamsTaintFlowConfig implements DataFlow::ConfigSig { + predicate isSource(DataFlow::Node n) { n.asExpr().getExpr() instanceof Rails::ParamsCall } + + predicate isSink(DataFlow::Node n) { DefaultFlowConfig::isSink(n) } } -from DataFlow::PathNode source, DataFlow::PathNode sink, ParamsTaintFlowConf conf -where conf.hasFlowPath(source, sink) +import FlowTest + +from TaintFlow::PathNode source, TaintFlow::PathNode sink +where TaintFlow::flowPath(source, sink) select sink, source, sink, "$@", source, source.toString() diff --git a/ruby/ql/test/library-tests/frameworks/action_dispatch/ActionDispatch.expected b/ruby/ql/test/library-tests/frameworks/action_dispatch/ActionDispatch.expected index 71327350941..4eacd48bd60 100644 --- a/ruby/ql/test/library-tests/frameworks/action_dispatch/ActionDispatch.expected +++ b/ruby/ql/test/library-tests/frameworks/action_dispatch/ActionDispatch.expected @@ -55,12 +55,12 @@ underscore | LotsOfCapitalLetters | lots_of_capital_letters | | invalid | invalid | mimeTypeInstances -| mime_type.rb:2:6:2:28 | Use getMember("Mime").getContent(element_text/html) | -| mime_type.rb:3:6:3:32 | Use getMember("Mime").getMember("Type").getMethod("new").getReturn() | -| mime_type.rb:4:6:4:35 | Use getMember("Mime").getMember("Type").getMethod("lookup").getReturn() | -| mime_type.rb:5:6:5:43 | Use getMember("Mime").getMember("Type").getMethod("lookup_by_extension").getReturn() | -| mime_type.rb:6:6:6:47 | Use getMember("Mime").getMember("Type").getMethod("register").getReturn() | -| mime_type.rb:7:6:7:64 | Use getMember("Mime").getMember("Type").getMethod("register_alias").getReturn() | +| mime_type.rb:2:6:2:28 | ForwardNode(call to fetch) | +| mime_type.rb:3:6:3:32 | ForwardNode(call to new) | +| mime_type.rb:4:6:4:35 | ForwardNode(call to lookup) | +| mime_type.rb:5:6:5:43 | ForwardNode(call to lookup_by_extension) | +| mime_type.rb:6:6:6:47 | ForwardNode(call to register) | +| mime_type.rb:7:6:7:64 | ForwardNode(call to register_alias) | mimeTypeMatchRegExpInterpretations | mime_type.rb:11:11:11:19 | "foo/bar" | | mime_type.rb:12:7:12:15 | "foo/bar" | diff --git a/ruby/ql/test/library-tests/frameworks/action_mailer/params-flow.expected b/ruby/ql/test/library-tests/frameworks/action_mailer/params-flow.expected index 39a6a7b8d8e..11ff425927b 100644 --- a/ruby/ql/test/library-tests/frameworks/action_mailer/params-flow.expected +++ b/ruby/ql/test/library-tests/frameworks/action_mailer/params-flow.expected @@ -1,4 +1,5 @@ failures +testFailures edges | mailer.rb:3:10:3:15 | call to params | mailer.rb:3:10:3:21 | ...[...] | nodes diff --git a/ruby/ql/test/library-tests/frameworks/action_mailer/params-flow.ql b/ruby/ql/test/library-tests/frameworks/action_mailer/params-flow.ql index 412ba5534b8..1e4e66c5584 100644 --- a/ruby/ql/test/library-tests/frameworks/action_mailer/params-flow.ql +++ b/ruby/ql/test/library-tests/frameworks/action_mailer/params-flow.ql @@ -7,12 +7,14 @@ import TestUtilities.InlineFlowTest import PathGraph import codeql.ruby.frameworks.Rails -class ParamsTaintFlowConf extends DefaultTaintFlowConf { - override predicate isSource(DataFlow::Node n) { - n.asExpr().getExpr() instanceof Rails::ParamsCall - } +module ParamsTaintFlowConfig implements DataFlow::ConfigSig { + predicate isSource(DataFlow::Node n) { n.asExpr().getExpr() instanceof Rails::ParamsCall } + + predicate isSink(DataFlow::Node n) { DefaultFlowConfig::isSink(n) } } -from DataFlow::PathNode source, DataFlow::PathNode sink, ParamsTaintFlowConf conf -where conf.hasFlowPath(source, sink) +import FlowTest + +from TaintFlow::PathNode source, TaintFlow::PathNode sink +where TaintFlow::flowPath(source, sink) select sink, source, sink, "$@", source, source.toString() diff --git a/ruby/ql/test/library-tests/frameworks/active_record/ActiveRecord.expected b/ruby/ql/test/library-tests/frameworks/active_record/ActiveRecord.expected index 0374a54c0c1..a3ee4ebebf5 100644 --- a/ruby/ql/test/library-tests/frameworks/active_record/ActiveRecord.expected +++ b/ruby/ql/test/library-tests/frameworks/active_record/ActiveRecord.expected @@ -10,6 +10,7 @@ activeRecordInstances | ActiveRecord.rb:9:5:9:68 | call to find | | ActiveRecord.rb:13:5:13:40 | call to find_by | | ActiveRecord.rb:13:5:13:46 | call to users | +| ActiveRecord.rb:35:5:35:51 | call to authenticate | | ActiveRecord.rb:36:5:36:30 | call to find_by_name | | ActiveRecord.rb:55:5:57:7 | if ... | | ActiveRecord.rb:55:43:56:40 | then ... | @@ -107,12 +108,14 @@ activeRecordSqlExecutionRanges | ActiveRecord.rb:19:16:19:24 | condition | | ActiveRecord.rb:28:30:28:44 | ...[...] | | ActiveRecord.rb:29:20:29:42 | "id = '#{...}'" | +| ActiveRecord.rb:30:21:30:45 | call to [] | | ActiveRecord.rb:30:22:30:44 | "id = '#{...}'" | | ActiveRecord.rb:31:16:31:21 | <<-SQL | | ActiveRecord.rb:34:20:34:47 | "user.id = '#{...}'" | | ActiveRecord.rb:46:20:46:32 | ... + ... | | ActiveRecord.rb:52:16:52:28 | "name #{...}" | | ActiveRecord.rb:56:20:56:39 | "username = #{...}" | +| ActiveRecord.rb:68:21:68:44 | ...[...] | | ActiveRecord.rb:106:27:106:76 | "this is an unsafe annotation:..." | activeRecordModelClassMethodCalls | ActiveRecord.rb:2:3:2:17 | call to has_many | @@ -127,7 +130,6 @@ activeRecordModelClassMethodCalls | ActiveRecord.rb:31:5:31:35 | call to where | | ActiveRecord.rb:34:5:34:14 | call to where | | ActiveRecord.rb:34:5:34:48 | call to not | -| ActiveRecord.rb:35:5:35:51 | call to authenticate | | ActiveRecord.rb:36:5:36:30 | call to find_by_name | | ActiveRecord.rb:37:5:37:36 | call to not_a_find_by_method | | ActiveRecord.rb:46:5:46:33 | call to delete_by | @@ -135,7 +137,6 @@ activeRecordModelClassMethodCalls | ActiveRecord.rb:56:7:56:40 | call to find_by | | ActiveRecord.rb:60:5:60:33 | call to find_by | | ActiveRecord.rb:62:5:62:34 | call to find | -| ActiveRecord.rb:68:5:68:45 | call to delete_by | | ActiveRecord.rb:72:5:72:24 | call to create | | ActiveRecord.rb:76:5:76:66 | call to create | | ActiveRecord.rb:80:5:80:68 | call to create | @@ -152,6 +153,96 @@ activeRecordModelClassMethodCalls | associations.rb:12:3:12:32 | call to has_and_belongs_to_many | | associations.rb:16:3:16:18 | call to belongs_to | | associations.rb:19:11:19:20 | call to new | +| associations.rb:21:9:21:21 | call to posts | +| associations.rb:21:9:21:28 | call to create | +| associations.rb:23:12:23:25 | call to comments | +| associations.rb:23:12:23:32 | call to create | +| associations.rb:25:11:25:22 | call to author | +| associations.rb:27:9:27:21 | call to posts | +| associations.rb:27:9:27:28 | call to create | +| associations.rb:29:1:29:13 | call to posts | +| associations.rb:29:1:29:22 | ... << ... | +| associations.rb:31:1:31:12 | call to author= | +| associations.rb:35:1:35:14 | call to comments | +| associations.rb:35:1:35:21 | call to create | +| associations.rb:35:1:35:28 | call to create | +| associations.rb:37:1:37:13 | call to posts | +| associations.rb:37:1:37:20 | call to reload | +| associations.rb:37:1:37:27 | call to create | +| associations.rb:39:1:39:15 | call to build_tag | +| associations.rb:40:1:40:15 | call to build_tag | +| associations.rb:42:1:42:13 | call to posts | +| associations.rb:42:1:42:25 | call to push | +| associations.rb:43:1:43:13 | call to posts | +| associations.rb:43:1:43:27 | call to concat | +| associations.rb:44:1:44:13 | call to posts | +| associations.rb:44:1:44:19 | call to build | +| associations.rb:45:1:45:13 | call to posts | +| associations.rb:45:1:45:20 | call to create | +| associations.rb:46:1:46:13 | call to posts | +| associations.rb:46:1:46:21 | call to create! | +| associations.rb:47:1:47:13 | call to posts | +| associations.rb:47:1:47:20 | call to delete | +| associations.rb:48:1:48:13 | call to posts | +| associations.rb:48:1:48:24 | call to delete_all | +| associations.rb:49:1:49:13 | call to posts | +| associations.rb:49:1:49:21 | call to destroy | +| associations.rb:50:1:50:13 | call to posts | +| associations.rb:50:1:50:25 | call to destroy_all | +| associations.rb:51:1:51:13 | call to posts | +| associations.rb:51:1:51:22 | call to distinct | +| associations.rb:51:1:51:36 | call to find | +| associations.rb:52:1:52:13 | call to posts | +| associations.rb:52:1:52:19 | call to reset | +| associations.rb:52:1:52:33 | call to find | +| associations.rb:53:1:53:13 | call to posts | +| associations.rb:53:1:53:20 | call to reload | +| associations.rb:53:1:53:34 | call to find | +activeRecordModelClassMethodCallsReplacement +| ActiveRecord.rb:1:1:3:3 | UserGroup | ActiveRecord.rb:2:3:2:17 | call to has_many | +| ActiveRecord.rb:1:1:3:3 | UserGroup | ActiveRecord.rb:13:5:13:40 | call to find_by | +| ActiveRecord.rb:5:1:15:3 | User | ActiveRecord.rb:6:3:6:24 | call to belongs_to | +| ActiveRecord.rb:5:1:15:3 | User | ActiveRecord.rb:9:5:9:68 | call to find | +| ActiveRecord.rb:5:1:15:3 | User | ActiveRecord.rb:19:5:19:25 | call to destroy_by | +| ActiveRecord.rb:5:1:15:3 | User | ActiveRecord.rb:28:5:28:45 | call to calculate | +| ActiveRecord.rb:5:1:15:3 | User | ActiveRecord.rb:29:5:29:43 | call to delete_by | +| ActiveRecord.rb:5:1:15:3 | User | ActiveRecord.rb:30:5:30:46 | call to destroy_by | +| ActiveRecord.rb:5:1:15:3 | User | ActiveRecord.rb:31:5:31:35 | call to where | +| ActiveRecord.rb:5:1:15:3 | User | ActiveRecord.rb:34:5:34:14 | call to where | +| ActiveRecord.rb:5:1:15:3 | User | ActiveRecord.rb:35:5:35:51 | call to authenticate | +| ActiveRecord.rb:5:1:15:3 | User | ActiveRecord.rb:36:5:36:30 | call to find_by_name | +| ActiveRecord.rb:5:1:15:3 | User | ActiveRecord.rb:37:5:37:36 | call to not_a_find_by_method | +| ActiveRecord.rb:5:1:15:3 | User | ActiveRecord.rb:46:5:46:33 | call to delete_by | +| ActiveRecord.rb:5:1:15:3 | User | ActiveRecord.rb:52:5:52:29 | call to order | +| ActiveRecord.rb:5:1:15:3 | User | ActiveRecord.rb:56:7:56:40 | call to find_by | +| ActiveRecord.rb:5:1:15:3 | User | ActiveRecord.rb:60:5:60:33 | call to find_by | +| ActiveRecord.rb:5:1:15:3 | User | ActiveRecord.rb:62:5:62:34 | call to find | +| ActiveRecord.rb:5:1:15:3 | User | ActiveRecord.rb:68:5:68:45 | call to delete_by | +| ActiveRecord.rb:5:1:15:3 | User | ActiveRecord.rb:72:5:72:24 | call to create | +| ActiveRecord.rb:5:1:15:3 | User | ActiveRecord.rb:76:5:76:66 | call to create | +| ActiveRecord.rb:5:1:15:3 | User | ActiveRecord.rb:80:5:80:68 | call to create | +| ActiveRecord.rb:5:1:15:3 | User | ActiveRecord.rb:84:5:84:16 | call to create | +| ActiveRecord.rb:5:1:15:3 | User | ActiveRecord.rb:88:5:88:27 | call to update | +| ActiveRecord.rb:5:1:15:3 | User | ActiveRecord.rb:92:5:92:69 | call to update | +| ActiveRecord.rb:5:1:15:3 | User | ActiveRecord.rb:96:5:96:71 | call to update | +| ActiveRecord.rb:5:1:15:3 | User | ActiveRecord.rb:102:13:102:54 | call to annotate | +| ActiveRecord.rb:5:1:15:3 | User | ActiveRecord.rb:106:13:106:77 | call to annotate | +| ActiveRecord.rb:17:1:21:3 | Admin | ActiveRecord.rb:19:5:19:25 | call to destroy_by | +| ActiveRecord.rb:17:1:21:3 | Admin | ActiveRecord.rb:68:5:68:45 | call to delete_by | +| ActiveRecord.rb:17:1:21:3 | Admin | ActiveRecord.rb:72:5:72:24 | call to create | +| ActiveRecord.rb:17:1:21:3 | Admin | ActiveRecord.rb:76:5:76:66 | call to create | +| ActiveRecord.rb:17:1:21:3 | Admin | ActiveRecord.rb:80:5:80:68 | call to create | +| ActiveRecord.rb:17:1:21:3 | Admin | ActiveRecord.rb:84:5:84:16 | call to create | +| ActiveRecord.rb:17:1:21:3 | Admin | ActiveRecord.rb:88:5:88:27 | call to update | +| ActiveRecord.rb:17:1:21:3 | Admin | ActiveRecord.rb:92:5:92:69 | call to update | +| ActiveRecord.rb:17:1:21:3 | Admin | ActiveRecord.rb:96:5:96:71 | call to update | +| associations.rb:1:1:3:3 | Author | associations.rb:2:3:2:17 | call to has_many | +| associations.rb:1:1:3:3 | Author | associations.rb:19:11:19:20 | call to new | +| associations.rb:5:1:9:3 | Post | associations.rb:6:3:6:20 | call to belongs_to | +| associations.rb:5:1:9:3 | Post | associations.rb:7:3:7:20 | call to has_many | +| associations.rb:5:1:9:3 | Post | associations.rb:8:3:8:31 | call to has_and_belongs_to_many | +| associations.rb:11:1:13:3 | Tag | associations.rb:12:3:12:32 | call to has_and_belongs_to_many | +| associations.rb:15:1:17:3 | Comment | associations.rb:16:3:16:18 | call to belongs_to | potentiallyUnsafeSqlExecutingMethodCall | ActiveRecord.rb:9:5:9:68 | call to find | | ActiveRecord.rb:19:5:19:25 | call to destroy_by | diff --git a/ruby/ql/test/library-tests/frameworks/active_record/ActiveRecord.ql b/ruby/ql/test/library-tests/frameworks/active_record/ActiveRecord.ql index 731679e437b..348ca1456e2 100644 --- a/ruby/ql/test/library-tests/frameworks/active_record/ActiveRecord.ql +++ b/ruby/ql/test/library-tests/frameworks/active_record/ActiveRecord.ql @@ -9,9 +9,19 @@ query predicate activeRecordInstances(ActiveRecordInstance i) { any() } query predicate activeRecordSqlExecutionRanges(ActiveRecordSqlExecutionRange range) { any() } -query predicate activeRecordModelClassMethodCalls(ActiveRecordModelClassMethodCall call) { any() } +deprecated query predicate activeRecordModelClassMethodCalls(ActiveRecordModelClassMethodCall call) { + any() +} -query predicate potentiallyUnsafeSqlExecutingMethodCall(PotentiallyUnsafeSqlExecutingMethodCall call) { +query predicate activeRecordModelClassMethodCallsReplacement( + ActiveRecordModelClass cls, DataFlow::CallNode call +) { + call = cls.getClassNode().trackModule().getAMethodCall(_) +} + +deprecated query predicate potentiallyUnsafeSqlExecutingMethodCall( + PotentiallyUnsafeSqlExecutingMethodCall call +) { any() } diff --git a/ruby/ql/test/library-tests/frameworks/active_resource/ActiveResource.expected b/ruby/ql/test/library-tests/frameworks/active_resource/ActiveResource.expected index a55946c1852..e6d3b056971 100644 --- a/ruby/ql/test/library-tests/frameworks/active_resource/ActiveResource.expected +++ b/ruby/ql/test/library-tests/frameworks/active_resource/ActiveResource.expected @@ -33,6 +33,13 @@ modelInstances | active_resource.rb:26:9:26:14 | people | | active_resource.rb:26:9:26:20 | call to first | | active_resource.rb:27:1:27:5 | alice | +modelInstancesAsSource +| active_resource.rb:1:1:3:3 | Person | active_resource.rb:5:9:5:33 | call to new | +| active_resource.rb:1:1:3:3 | Person | active_resource.rb:8:9:8:22 | call to find | +| active_resource.rb:1:1:3:3 | Person | active_resource.rb:16:1:16:23 | call to new | +| active_resource.rb:1:1:3:3 | Person | active_resource.rb:18:1:18:22 | call to get | +| active_resource.rb:1:1:3:3 | Person | active_resource.rb:24:10:24:26 | call to find | +| active_resource.rb:1:1:3:3 | Person | active_resource.rb:26:9:26:20 | call to first | modelInstanceMethodCalls | active_resource.rb:6:1:6:10 | call to save | | active_resource.rb:9:1:9:13 | call to address= | @@ -50,3 +57,6 @@ collections | active_resource.rb:24:1:24:26 | ... = ... | | active_resource.rb:24:10:24:26 | call to find | | active_resource.rb:26:9:26:14 | people | +collectionSources +| active_resource.rb:23:10:23:19 | call to all | +| active_resource.rb:24:10:24:26 | call to find | diff --git a/ruby/ql/test/library-tests/frameworks/active_resource/ActiveResource.ql b/ruby/ql/test/library-tests/frameworks/active_resource/ActiveResource.ql index 1f2fd1efcf1..f1898ddbc98 100644 --- a/ruby/ql/test/library-tests/frameworks/active_resource/ActiveResource.ql +++ b/ruby/ql/test/library-tests/frameworks/active_resource/ActiveResource.ql @@ -3,7 +3,8 @@ import codeql.ruby.DataFlow import codeql.ruby.frameworks.ActiveResource query predicate modelClasses( - ActiveResource::ModelClass c, DataFlow::Node siteAssignCall, boolean disablesCertificateValidation + ActiveResource::ModelClassNode c, DataFlow::Node siteAssignCall, + boolean disablesCertificateValidation ) { c.getASiteAssignment() = siteAssignCall and if c.disablesCertificateValidation(siteAssignCall) @@ -13,8 +14,16 @@ query predicate modelClasses( query predicate modelClassMethodCalls(ActiveResource::ModelClassMethodCall c) { any() } -query predicate modelInstances(ActiveResource::ModelInstance c) { any() } +deprecated query predicate modelInstances(ActiveResource::ModelInstance c) { any() } + +query predicate modelInstancesAsSource( + ActiveResource::ModelClassNode cls, DataFlow::LocalSourceNode node +) { + node = cls.getAnInstanceReference().asSource() +} query predicate modelInstanceMethodCalls(ActiveResource::ModelInstanceMethodCall c) { any() } -query predicate collections(ActiveResource::Collection c) { any() } +deprecated query predicate collections(ActiveResource::Collection c) { any() } + +query predicate collectionSources(ActiveResource::CollectionSource c) { any() } diff --git a/ruby/ql/test/library-tests/frameworks/active_support/ActiveSupportDataFlow.expected b/ruby/ql/test/library-tests/frameworks/active_support/ActiveSupportDataFlow.expected index 7bce95e2b45..7f1f8d6c756 100644 --- a/ruby/ql/test/library-tests/frameworks/active_support/ActiveSupportDataFlow.expected +++ b/ruby/ql/test/library-tests/frameworks/active_support/ActiveSupportDataFlow.expected @@ -1,4 +1,5 @@ failures +testFailures | hash_extensions.rb:126:10:126:19 | call to sole | Unexpected result: hasValueFlow=b | edges | active_support.rb:10:5:10:5 | x | active_support.rb:11:10:11:10 | x | diff --git a/ruby/ql/test/library-tests/frameworks/active_support/ActiveSupportDataFlow.ql b/ruby/ql/test/library-tests/frameworks/active_support/ActiveSupportDataFlow.ql index 8a14d5f686e..7c19d2e3904 100644 --- a/ruby/ql/test/library-tests/frameworks/active_support/ActiveSupportDataFlow.ql +++ b/ruby/ql/test/library-tests/frameworks/active_support/ActiveSupportDataFlow.ql @@ -5,8 +5,9 @@ import codeql.ruby.AST import TestUtilities.InlineFlowTest import codeql.ruby.Frameworks +import DefaultFlowTest import PathGraph -from DataFlow::PathNode source, DataFlow::PathNode sink, DefaultValueFlowConf conf -where conf.hasFlowPath(source, sink) +from ValueFlow::PathNode source, ValueFlow::PathNode sink +where ValueFlow::flowPath(source, sink) select sink, source, sink, "$@", source, source.toString() diff --git a/ruby/ql/test/library-tests/frameworks/arel/Arel.expected b/ruby/ql/test/library-tests/frameworks/arel/Arel.expected index d34ccbcb1a1..4fdc2574ccc 100644 --- a/ruby/ql/test/library-tests/frameworks/arel/Arel.expected +++ b/ruby/ql/test/library-tests/frameworks/arel/Arel.expected @@ -1,3 +1,4 @@ failures +testFailures #select | arel.rb:3:8:3:18 | call to sql | arel.rb:2:7:2:14 | call to source | arel.rb:3:8:3:18 | call to sql | $@ | arel.rb:2:7:2:14 | call to source | call to source | diff --git a/ruby/ql/test/library-tests/frameworks/arel/Arel.ql b/ruby/ql/test/library-tests/frameworks/arel/Arel.ql index 01d7b5a5d1c..e1cc2782ceb 100644 --- a/ruby/ql/test/library-tests/frameworks/arel/Arel.ql +++ b/ruby/ql/test/library-tests/frameworks/arel/Arel.ql @@ -5,7 +5,8 @@ import codeql.ruby.frameworks.Arel import codeql.ruby.AST import TestUtilities.InlineFlowTest +import DefaultFlowTest -from DataFlow::PathNode source, DataFlow::PathNode sink, DefaultTaintFlowConf conf -where conf.hasFlowPath(source, sink) +from TaintFlow::PathNode source, TaintFlow::PathNode sink +where TaintFlow::flowPath(source, sink) select sink, source, sink, "$@", source, source.toString() diff --git a/ruby/ql/test/library-tests/frameworks/json/JsonDataFlow.expected b/ruby/ql/test/library-tests/frameworks/json/JsonDataFlow.expected index db04aefd22f..ad5802c2aae 100644 --- a/ruby/ql/test/library-tests/frameworks/json/JsonDataFlow.expected +++ b/ruby/ql/test/library-tests/frameworks/json/JsonDataFlow.expected @@ -1,4 +1,5 @@ failures +testFailures edges | json.rb:1:17:1:26 | call to source | json.rb:1:6:1:27 | call to parse | | json.rb:2:18:2:27 | call to source | json.rb:2:6:2:28 | call to parse! | diff --git a/ruby/ql/test/library-tests/frameworks/json/JsonDataFlow.ql b/ruby/ql/test/library-tests/frameworks/json/JsonDataFlow.ql index 11a25d33e71..6fe0aeda9b1 100644 --- a/ruby/ql/test/library-tests/frameworks/json/JsonDataFlow.ql +++ b/ruby/ql/test/library-tests/frameworks/json/JsonDataFlow.ql @@ -4,4 +4,5 @@ import TestUtilities.InlineFlowTest import codeql.ruby.Frameworks +import DefaultFlowTest import PathGraph diff --git a/ruby/ql/test/library-tests/frameworks/rack/Rack.expected b/ruby/ql/test/library-tests/frameworks/rack/Rack.expected index 5613aabd7a4..c55afeb7801 100644 --- a/ruby/ql/test/library-tests/frameworks/rack/Rack.expected +++ b/ruby/ql/test/library-tests/frameworks/rack/Rack.expected @@ -1,4 +1,11 @@ -| rack.rb:1:1:5:3 | HelloWorld | rack.rb:2:12:2:14 | env | -| rack.rb:7:1:16:3 | Proxy | rack.rb:12:12:12:18 | the_env | -| rack.rb:18:1:31:3 | Logger | rack.rb:24:12:24:14 | env | -| rack.rb:45:1:61:3 | Baz | rack.rb:46:12:46:14 | env | +rackApps +| rack.rb:1:1:10:3 | HelloWorld | rack.rb:2:12:2:14 | env | +| rack.rb:12:1:22:3 | Proxy | rack.rb:17:12:17:18 | the_env | +| rack.rb:24:1:37:3 | Logger | rack.rb:30:12:30:14 | env | +| rack.rb:39:1:45:3 | Redirector | rack.rb:40:12:40:14 | env | +| rack.rb:59:1:75:3 | Baz | rack.rb:60:12:60:14 | env | +rackResponseContentTypes +| rack.rb:8:5:8:38 | call to [] | rack.rb:7:34:7:45 | "text/plain" | +| rack.rb:20:5:20:27 | call to [] | rack.rb:19:28:19:38 | "text/html" | +redirectResponses +| rack.rb:43:5:43:45 | call to [] | rack.rb:42:30:42:40 | "/foo.html" | diff --git a/ruby/ql/test/library-tests/frameworks/rack/Rack.ql b/ruby/ql/test/library-tests/frameworks/rack/Rack.ql index 560b81c3839..9b5d0629a9f 100644 --- a/ruby/ql/test/library-tests/frameworks/rack/Rack.ql +++ b/ruby/ql/test/library-tests/frameworks/rack/Rack.ql @@ -1,4 +1,17 @@ +private import codeql.ruby.AST private import codeql.ruby.frameworks.Rack private import codeql.ruby.DataFlow -query predicate rackApps(Rack::AppCandidate c, DataFlow::ParameterNode env) { env = c.getEnv() } +query predicate rackApps(Rack::App::AppCandidate c, DataFlow::ParameterNode env) { + env = c.getEnv() +} + +query predicate rackResponseContentTypes( + Rack::Response::ResponseNode resp, DataFlow::Node contentType +) { + contentType = resp.getMimetypeOrContentTypeArg() +} + +query predicate redirectResponses(Rack::Response::RedirectResponse resp, DataFlow::Node location) { + location = resp.getRedirectLocation() +} diff --git a/ruby/ql/test/library-tests/frameworks/rack/rack.rb b/ruby/ql/test/library-tests/frameworks/rack/rack.rb index 03955455787..9f743496ad2 100644 --- a/ruby/ql/test/library-tests/frameworks/rack/rack.rb +++ b/ruby/ql/test/library-tests/frameworks/rack/rack.rb @@ -1,6 +1,11 @@ class HelloWorld def call(env) - [200, {'Content-Type' => 'text/plain'}, ['Hello World']] + status = 200 + if something_goes_wrong(env) + status = 500 + end + headers = {'Content-Type' => 'text/plain'} + [status, headers, ['Hello World']] end end @@ -11,6 +16,7 @@ class Proxy def call(the_env) status, headers, body = @app.call(the_env) + headers.content_type = "text/html" [status, headers, body] end end @@ -30,6 +36,14 @@ class Logger end end +class Redirector + def call(env) + status = 302 + headers = {'location' => '/foo.html'} + [status, headers, ['this is a redirect']] + end +end + class Foo def not_call(env) [1, 2, 3] diff --git a/ruby/ql/test/library-tests/frameworks/sinatra/Flow.expected b/ruby/ql/test/library-tests/frameworks/sinatra/Flow.expected index 2ace7832268..a2ea0e9a06e 100644 --- a/ruby/ql/test/library-tests/frameworks/sinatra/Flow.expected +++ b/ruby/ql/test/library-tests/frameworks/sinatra/Flow.expected @@ -1,4 +1,5 @@ failures +testFailures | views/index.erb:2:10:2:12 | call to foo | Unexpected result: hasTaintFlow= | edges | app.rb:75:5:75:8 | [post] self [@foo] | app.rb:76:32:76:35 | self [@foo] | diff --git a/ruby/ql/test/library-tests/frameworks/sinatra/Flow.ql b/ruby/ql/test/library-tests/frameworks/sinatra/Flow.ql index d54f73c9144..413511eac08 100644 --- a/ruby/ql/test/library-tests/frameworks/sinatra/Flow.ql +++ b/ruby/ql/test/library-tests/frameworks/sinatra/Flow.ql @@ -8,12 +8,16 @@ import PathGraph import codeql.ruby.frameworks.Sinatra import codeql.ruby.Concepts -class SinatraConf extends DefaultTaintFlowConf { - override predicate isSource(DataFlow::Node source) { +module SinatraConfig implements DataFlow::ConfigSig { + predicate isSource(DataFlow::Node source) { source instanceof Http::Server::RequestInputAccess::Range } + + predicate isSink(DataFlow::Node sink) { DefaultFlowConfig::isSink(sink) } } -from DataFlow::PathNode source, DataFlow::PathNode sink, SinatraConf conf -where conf.hasFlowPath(source, sink) +import FlowTest + +from TaintFlow::PathNode source, TaintFlow::PathNode sink +where TaintFlow::flowPath(source, sink) select sink, source, sink, "$@", source, source.toString() diff --git a/ruby/ql/test/query-tests/security/cwe-079/StoredXSS.expected b/ruby/ql/test/query-tests/security/cwe-079/StoredXSS.expected index 0eaf24029ef..460c7da3145 100644 --- a/ruby/ql/test/query-tests/security/cwe-079/StoredXSS.expected +++ b/ruby/ql/test/query-tests/security/cwe-079/StoredXSS.expected @@ -3,7 +3,6 @@ edges | app/controllers/foo/stores_controller.rb:8:5:8:6 | dt | app/controllers/foo/stores_controller.rb:13:55:13:56 | dt | | app/controllers/foo/stores_controller.rb:8:10:8:29 | call to read | app/controllers/foo/stores_controller.rb:8:5:8:6 | dt | | app/controllers/foo/stores_controller.rb:9:22:9:23 | dt | app/views/foo/stores/show.html.erb:37:3:37:16 | @instance_text | -| app/controllers/foo/stores_controller.rb:12:28:12:48 | call to raw_name | app/views/foo/stores/show.html.erb:82:5:82:24 | @other_user_raw_name | | app/controllers/foo/stores_controller.rb:13:55:13:56 | dt | app/views/foo/stores/show.html.erb:2:9:2:20 | call to display_text | | app/controllers/foo/stores_controller.rb:13:55:13:56 | dt | app/views/foo/stores/show.html.erb:5:9:5:21 | call to local_assigns [element :display_text] | | app/controllers/foo/stores_controller.rb:13:55:13:56 | dt | app/views/foo/stores/show.html.erb:9:9:9:21 | call to local_assigns [element :display_text] | @@ -22,7 +21,6 @@ nodes | app/controllers/foo/stores_controller.rb:8:5:8:6 | dt | semmle.label | dt | | app/controllers/foo/stores_controller.rb:8:10:8:29 | call to read | semmle.label | call to read | | app/controllers/foo/stores_controller.rb:9:22:9:23 | dt | semmle.label | dt | -| app/controllers/foo/stores_controller.rb:12:28:12:48 | call to raw_name | semmle.label | call to raw_name | | app/controllers/foo/stores_controller.rb:13:55:13:56 | dt | semmle.label | dt | | app/views/foo/bars/_widget.html.erb:5:9:5:20 | call to display_text | semmle.label | call to display_text | | app/views/foo/bars/_widget.html.erb:8:9:8:21 | call to local_assigns [element :display_text] | semmle.label | call to local_assigns [element :display_text] | @@ -39,11 +37,7 @@ nodes | app/views/foo/stores/show.html.erb:40:64:40:87 | ... + ... | semmle.label | ... + ... | | app/views/foo/stores/show.html.erb:40:76:40:87 | call to display_text | semmle.label | call to display_text | | app/views/foo/stores/show.html.erb:46:5:46:16 | call to handle | semmle.label | call to handle | -| app/views/foo/stores/show.html.erb:49:5:49:18 | call to raw_name | semmle.label | call to raw_name | | app/views/foo/stores/show.html.erb:63:3:63:18 | call to handle | semmle.label | call to handle | -| app/views/foo/stores/show.html.erb:69:3:69:20 | call to raw_name | semmle.label | call to raw_name | -| app/views/foo/stores/show.html.erb:79:5:79:22 | call to display_name | semmle.label | call to display_name | -| app/views/foo/stores/show.html.erb:82:5:82:24 | @other_user_raw_name | semmle.label | @other_user_raw_name | | app/views/foo/stores/show.html.erb:86:3:86:29 | call to sprintf | semmle.label | call to sprintf | | app/views/foo/stores/show.html.erb:86:17:86:28 | call to handle | semmle.label | call to handle | subpaths @@ -57,9 +51,5 @@ subpaths | app/views/foo/stores/show.html.erb:32:3:32:14 | call to display_text | app/controllers/foo/stores_controller.rb:8:10:8:29 | call to read | app/views/foo/stores/show.html.erb:32:3:32:14 | call to display_text | Stored cross-site scripting vulnerability due to $@. | app/controllers/foo/stores_controller.rb:8:10:8:29 | call to read | stored value | | app/views/foo/stores/show.html.erb:37:3:37:16 | @instance_text | app/controllers/foo/stores_controller.rb:8:10:8:29 | call to read | app/views/foo/stores/show.html.erb:37:3:37:16 | @instance_text | Stored cross-site scripting vulnerability due to $@. | app/controllers/foo/stores_controller.rb:8:10:8:29 | call to read | stored value | | app/views/foo/stores/show.html.erb:46:5:46:16 | call to handle | app/views/foo/stores/show.html.erb:46:5:46:16 | call to handle | app/views/foo/stores/show.html.erb:46:5:46:16 | call to handle | Stored cross-site scripting vulnerability due to $@. | app/views/foo/stores/show.html.erb:46:5:46:16 | call to handle | stored value | -| app/views/foo/stores/show.html.erb:49:5:49:18 | call to raw_name | app/views/foo/stores/show.html.erb:49:5:49:18 | call to raw_name | app/views/foo/stores/show.html.erb:49:5:49:18 | call to raw_name | Stored cross-site scripting vulnerability due to $@. | app/views/foo/stores/show.html.erb:49:5:49:18 | call to raw_name | stored value | | app/views/foo/stores/show.html.erb:63:3:63:18 | call to handle | app/views/foo/stores/show.html.erb:63:3:63:18 | call to handle | app/views/foo/stores/show.html.erb:63:3:63:18 | call to handle | Stored cross-site scripting vulnerability due to $@. | app/views/foo/stores/show.html.erb:63:3:63:18 | call to handle | stored value | -| app/views/foo/stores/show.html.erb:69:3:69:20 | call to raw_name | app/views/foo/stores/show.html.erb:69:3:69:20 | call to raw_name | app/views/foo/stores/show.html.erb:69:3:69:20 | call to raw_name | Stored cross-site scripting vulnerability due to $@. | app/views/foo/stores/show.html.erb:69:3:69:20 | call to raw_name | stored value | -| app/views/foo/stores/show.html.erb:79:5:79:22 | call to display_name | app/views/foo/stores/show.html.erb:79:5:79:22 | call to display_name | app/views/foo/stores/show.html.erb:79:5:79:22 | call to display_name | Stored cross-site scripting vulnerability due to $@. | app/views/foo/stores/show.html.erb:79:5:79:22 | call to display_name | stored value | -| app/views/foo/stores/show.html.erb:82:5:82:24 | @other_user_raw_name | app/controllers/foo/stores_controller.rb:12:28:12:48 | call to raw_name | app/views/foo/stores/show.html.erb:82:5:82:24 | @other_user_raw_name | Stored cross-site scripting vulnerability due to $@. | app/controllers/foo/stores_controller.rb:12:28:12:48 | call to raw_name | stored value | | app/views/foo/stores/show.html.erb:86:3:86:29 | call to sprintf | app/views/foo/stores/show.html.erb:86:17:86:28 | call to handle | app/views/foo/stores/show.html.erb:86:3:86:29 | call to sprintf | Stored cross-site scripting vulnerability due to $@. | app/views/foo/stores/show.html.erb:86:17:86:28 | call to handle | stored value | diff --git a/ruby/ql/test/query-tests/security/cwe-079/app/views/foo/stores/show.html.erb b/ruby/ql/test/query-tests/security/cwe-079/app/views/foo/stores/show.html.erb index 29656a15a3d..d8afec1c432 100644 --- a/ruby/ql/test/query-tests/security/cwe-079/app/views/foo/stores/show.html.erb +++ b/ruby/ql/test/query-tests/security/cwe-079/app/views/foo/stores/show.html.erb @@ -63,7 +63,7 @@ some_user.handle.html_safe %> -<%# BAD: Indirect to a database value without escaping %> +<%# BAD: Indirect to a database value without escaping (currently missed due to lack of 'self' handling in ORM tracking) %> <%= some_user = User.find 1 some_user.raw_name.html_safe @@ -75,10 +75,10 @@ some_user.handle %> -<%# BAD: Indirect to a database value without escaping %> +<%# BAD: Indirect to a database value without escaping (currently missed due to lack of 'self' handling in ORM tracking) %> <%= @user.display_name.html_safe %> -<%# BAD: Indirect to a database value without escaping %> +<%# BAD: Indirect to a database value without escaping (currently missed due to lack of 'self' handling in ORM tracking) %> <%= @other_user_raw_name.html_safe %> <%# BAD: Kernel.sprintf is a taint-step %> diff --git a/ruby/ql/test/query-tests/security/cwe-089/SqlInjection.expected b/ruby/ql/test/query-tests/security/cwe-089/SqlInjection.expected index 0cc0d213dcc..161cdcc7751 100644 --- a/ruby/ql/test/query-tests/security/cwe-089/SqlInjection.expected +++ b/ruby/ql/test/query-tests/security/cwe-089/SqlInjection.expected @@ -22,6 +22,7 @@ edges | ActiveRecordInjection.rb:70:38:70:50 | ...[...] | ActiveRecordInjection.rb:8:31:8:34 | pass | | ActiveRecordInjection.rb:74:41:74:46 | call to params | ActiveRecordInjection.rb:74:41:74:51 | ...[...] | | ActiveRecordInjection.rb:74:41:74:51 | ...[...] | ActiveRecordInjection.rb:74:32:74:54 | "id = '#{...}'" | +| ActiveRecordInjection.rb:79:23:79:28 | call to params | ActiveRecordInjection.rb:79:23:79:35 | ...[...] | | ActiveRecordInjection.rb:83:17:83:22 | call to params | ActiveRecordInjection.rb:83:17:83:31 | ...[...] | | ActiveRecordInjection.rb:84:19:84:24 | call to params | ActiveRecordInjection.rb:84:19:84:33 | ...[...] | | ActiveRecordInjection.rb:88:18:88:23 | call to params | ActiveRecordInjection.rb:88:18:88:35 | ...[...] | @@ -35,6 +36,7 @@ edges | ActiveRecordInjection.rb:103:11:103:17 | ...[...] | ActiveRecordInjection.rb:103:5:103:7 | uid | | ActiveRecordInjection.rb:104:5:104:9 | uidEq | ActiveRecordInjection.rb:108:20:108:32 | ... + ... | | ActiveRecordInjection.rb:141:21:141:26 | call to params | ActiveRecordInjection.rb:141:21:141:44 | ...[...] | +| ActiveRecordInjection.rb:141:21:141:26 | call to params | ActiveRecordInjection.rb:141:21:141:44 | ...[...] | | ActiveRecordInjection.rb:141:21:141:44 | ...[...] | ActiveRecordInjection.rb:20:22:20:30 | condition | | ActiveRecordInjection.rb:155:59:155:64 | call to params | ActiveRecordInjection.rb:155:59:155:74 | ...[...] | | ActiveRecordInjection.rb:155:59:155:74 | ...[...] | ActiveRecordInjection.rb:155:27:155:76 | "this is an unsafe annotation:..." | @@ -102,6 +104,8 @@ nodes | ActiveRecordInjection.rb:74:32:74:54 | "id = '#{...}'" | semmle.label | "id = '#{...}'" | | ActiveRecordInjection.rb:74:41:74:46 | call to params | semmle.label | call to params | | ActiveRecordInjection.rb:74:41:74:51 | ...[...] | semmle.label | ...[...] | +| ActiveRecordInjection.rb:79:23:79:28 | call to params | semmle.label | call to params | +| ActiveRecordInjection.rb:79:23:79:35 | ...[...] | semmle.label | ...[...] | | ActiveRecordInjection.rb:83:17:83:22 | call to params | semmle.label | call to params | | ActiveRecordInjection.rb:83:17:83:31 | ...[...] | semmle.label | ...[...] | | ActiveRecordInjection.rb:84:19:84:24 | call to params | semmle.label | call to params | @@ -123,6 +127,7 @@ nodes | ActiveRecordInjection.rb:108:20:108:32 | ... + ... | semmle.label | ... + ... | | ActiveRecordInjection.rb:141:21:141:26 | call to params | semmle.label | call to params | | ActiveRecordInjection.rb:141:21:141:44 | ...[...] | semmle.label | ...[...] | +| ActiveRecordInjection.rb:141:21:141:44 | ...[...] | semmle.label | ...[...] | | ActiveRecordInjection.rb:155:27:155:76 | "this is an unsafe annotation:..." | semmle.label | "this is an unsafe annotation:..." | | ActiveRecordInjection.rb:155:59:155:64 | call to params | semmle.label | call to params | | ActiveRecordInjection.rb:155:59:155:74 | ...[...] | semmle.label | ...[...] | @@ -172,6 +177,7 @@ subpaths | ActiveRecordInjection.rb:61:16:61:21 | <<-SQL | ActiveRecordInjection.rb:62:21:62:26 | call to params | ActiveRecordInjection.rb:61:16:61:21 | <<-SQL | This SQL query depends on a $@. | ActiveRecordInjection.rb:62:21:62:26 | call to params | user-provided value | | ActiveRecordInjection.rb:68:20:68:47 | "user.id = '#{...}'" | ActiveRecordInjection.rb:68:34:68:39 | call to params | ActiveRecordInjection.rb:68:20:68:47 | "user.id = '#{...}'" | This SQL query depends on a $@. | ActiveRecordInjection.rb:68:34:68:39 | call to params | user-provided value | | ActiveRecordInjection.rb:74:32:74:54 | "id = '#{...}'" | ActiveRecordInjection.rb:74:41:74:46 | call to params | ActiveRecordInjection.rb:74:32:74:54 | "id = '#{...}'" | This SQL query depends on a $@. | ActiveRecordInjection.rb:74:41:74:46 | call to params | user-provided value | +| ActiveRecordInjection.rb:79:23:79:35 | ...[...] | ActiveRecordInjection.rb:79:23:79:28 | call to params | ActiveRecordInjection.rb:79:23:79:35 | ...[...] | This SQL query depends on a $@. | ActiveRecordInjection.rb:79:23:79:28 | call to params | user-provided value | | ActiveRecordInjection.rb:83:17:83:31 | ...[...] | ActiveRecordInjection.rb:83:17:83:22 | call to params | ActiveRecordInjection.rb:83:17:83:31 | ...[...] | This SQL query depends on a $@. | ActiveRecordInjection.rb:83:17:83:22 | call to params | user-provided value | | ActiveRecordInjection.rb:84:19:84:33 | ...[...] | ActiveRecordInjection.rb:84:19:84:24 | call to params | ActiveRecordInjection.rb:84:19:84:33 | ...[...] | This SQL query depends on a $@. | ActiveRecordInjection.rb:84:19:84:24 | call to params | user-provided value | | ActiveRecordInjection.rb:88:18:88:35 | ...[...] | ActiveRecordInjection.rb:88:18:88:23 | call to params | ActiveRecordInjection.rb:88:18:88:35 | ...[...] | This SQL query depends on a $@. | ActiveRecordInjection.rb:88:18:88:23 | call to params | user-provided value | @@ -179,6 +185,7 @@ subpaths | ActiveRecordInjection.rb:94:18:94:35 | ...[...] | ActiveRecordInjection.rb:94:18:94:23 | call to params | ActiveRecordInjection.rb:94:18:94:35 | ...[...] | This SQL query depends on a $@. | ActiveRecordInjection.rb:94:18:94:23 | call to params | user-provided value | | ActiveRecordInjection.rb:96:23:96:47 | ...[...] | ActiveRecordInjection.rb:96:23:96:28 | call to params | ActiveRecordInjection.rb:96:23:96:47 | ...[...] | This SQL query depends on a $@. | ActiveRecordInjection.rb:96:23:96:28 | call to params | user-provided value | | ActiveRecordInjection.rb:108:20:108:32 | ... + ... | ActiveRecordInjection.rb:102:10:102:15 | call to params | ActiveRecordInjection.rb:108:20:108:32 | ... + ... | This SQL query depends on a $@. | ActiveRecordInjection.rb:102:10:102:15 | call to params | user-provided value | +| ActiveRecordInjection.rb:141:21:141:44 | ...[...] | ActiveRecordInjection.rb:141:21:141:26 | call to params | ActiveRecordInjection.rb:141:21:141:44 | ...[...] | This SQL query depends on a $@. | ActiveRecordInjection.rb:141:21:141:26 | call to params | user-provided value | | ActiveRecordInjection.rb:155:27:155:76 | "this is an unsafe annotation:..." | ActiveRecordInjection.rb:155:59:155:64 | call to params | ActiveRecordInjection.rb:155:27:155:76 | "this is an unsafe annotation:..." | This SQL query depends on a $@. | ActiveRecordInjection.rb:155:59:155:64 | call to params | user-provided value | | ActiveRecordInjection.rb:168:37:168:41 | query | ActiveRecordInjection.rb:173:5:173:10 | call to params | ActiveRecordInjection.rb:168:37:168:41 | query | This SQL query depends on a $@. | ActiveRecordInjection.rb:173:5:173:10 | call to params | user-provided value | | ActiveRecordInjection.rb:177:43:177:104 | "SELECT * FROM users WHERE id ..." | ActiveRecordInjection.rb:173:5:173:10 | call to params | ActiveRecordInjection.rb:177:43:177:104 | "SELECT * FROM users WHERE id ..." | This SQL query depends on a $@. | ActiveRecordInjection.rb:173:5:173:10 | call to params | user-provided value | @@ -189,4 +196,4 @@ subpaths | PgInjection.rb:20:22:20:25 | qry2 | PgInjection.rb:6:12:6:17 | call to params | PgInjection.rb:20:22:20:25 | qry2 | This SQL query depends on a $@. | PgInjection.rb:6:12:6:17 | call to params | user-provided value | | PgInjection.rb:21:28:21:31 | qry2 | PgInjection.rb:6:12:6:17 | call to params | PgInjection.rb:21:28:21:31 | qry2 | This SQL query depends on a $@. | PgInjection.rb:6:12:6:17 | call to params | user-provided value | | PgInjection.rb:32:29:32:32 | qry3 | PgInjection.rb:6:12:6:17 | call to params | PgInjection.rb:32:29:32:32 | qry3 | This SQL query depends on a $@. | PgInjection.rb:6:12:6:17 | call to params | user-provided value | -| PgInjection.rb:44:29:44:32 | qry3 | PgInjection.rb:6:12:6:17 | call to params | PgInjection.rb:44:29:44:32 | qry3 | This SQL query depends on a $@. | PgInjection.rb:6:12:6:17 | call to params | user-provided value | \ No newline at end of file +| PgInjection.rb:44:29:44:32 | qry3 | PgInjection.rb:6:12:6:17 | call to params | PgInjection.rb:44:29:44:32 | qry3 | This SQL query depends on a $@. | PgInjection.rb:6:12:6:17 | call to params | user-provided value | diff --git a/ruby/ql/test/query-tests/security/cwe-829/InsecureDownload.expected b/ruby/ql/test/query-tests/security/cwe-829/InsecureDownload.expected index 6e30aeeb235..82fbafcfc89 100644 --- a/ruby/ql/test/query-tests/security/cwe-829/InsecureDownload.expected +++ b/ruby/ql/test/query-tests/security/cwe-829/InsecureDownload.expected @@ -18,6 +18,7 @@ nodes | insecure_download.rb:43:22:43:56 | "http://example.org/unsafe.unk..." | semmle.label | "http://example.org/unsafe.unk..." | | insecure_download.rb:53:65:53:78 | "/myscript.sh" | semmle.label | "/myscript.sh" | subpaths +testFailures #select | insecure_download.rb:27:15:27:45 | "http://example.org/unsafe.APK" | insecure_download.rb:27:15:27:45 | "http://example.org/unsafe.APK" | insecure_download.rb:27:15:27:45 | "http://example.org/unsafe.APK" | $@ | insecure_download.rb:27:15:27:45 | "http://example.org/unsafe.APK" | "http://example.org/unsafe.APK" | | insecure_download.rb:27:15:27:45 | "http://example.org/unsafe.APK" | insecure_download.rb:27:15:27:45 | "http://example.org/unsafe.APK" | insecure_download.rb:27:15:27:45 | "http://example.org/unsafe.APK" | $@ | insecure_download.rb:27:15:27:45 | "http://example.org/unsafe.APK" | "http://example.org/unsafe.APK" | diff --git a/ruby/ql/test/query-tests/security/cwe-829/InsecureDownload.ql b/ruby/ql/test/query-tests/security/cwe-829/InsecureDownload.ql index 8690a0ec120..ab9ce62a21c 100644 --- a/ruby/ql/test/query-tests/security/cwe-829/InsecureDownload.ql +++ b/ruby/ql/test/query-tests/security/cwe-829/InsecureDownload.ql @@ -1,22 +1,25 @@ import codeql.ruby.AST import codeql.ruby.DataFlow -import PathGraph -import TestUtilities.InlineFlowTest import codeql.ruby.security.InsecureDownloadQuery +import Flow::PathGraph +import TestUtilities.InlineExpectationsTest +import TestUtilities.InlineFlowTestUtil -class FlowTest extends InlineFlowTest { - override DataFlow::Configuration getValueFlowConfig() { result = any(Configuration config) } +module FlowTest implements TestSig { + string getARelevantTag() { result = "BAD" } - override DataFlow::Configuration getTaintFlowConfig() { none() } - - override string getARelevantTag() { result = "BAD" } - - override predicate hasActualResult(Location location, string element, string tag, string value) { + predicate hasActualResult(Location location, string element, string tag, string value) { tag = "BAD" and - super.hasActualResult(location, element, "hasValueFlow", value) + exists(DataFlow::Node src, DataFlow::Node sink | Flow::flow(src, sink) | + sink.getLocation() = location and + element = sink.toString() and + if exists(getSourceArgString(src)) then value = getSourceArgString(src) else value = "" + ) } } -from DataFlow::PathNode source, DataFlow::PathNode sink, Configuration conf -where conf.hasFlowPath(source, sink) +import MakeTest + +from Flow::PathNode source, Flow::PathNode sink +where Flow::flowPath(source, sink) select sink, source, sink, "$@", source, source.toString() diff --git a/shared/mad/codeql/mad/ModelValidation.qll b/shared/mad/codeql/mad/ModelValidation.qll new file mode 100644 index 00000000000..d5108c2eeec --- /dev/null +++ b/shared/mad/codeql/mad/ModelValidation.qll @@ -0,0 +1,186 @@ +/** + * Provides classes and predicates related to validating models-as-data rows. + */ + +/** Provides predicates for determining if a model exists for a given `kind`. */ +signature module KindValidationConfigSig { + /** Holds if a summary model exists for the given `kind`. */ + predicate summaryKind(string kind); + + /** Holds if a sink model exists for the given `kind`. */ + predicate sinkKind(string kind); + + /** Holds if a source model exists for the given `kind`. */ + predicate sourceKind(string kind); + + /** Holds if a neutral model exists for the given `kind`. */ + default predicate neutralKind(string kind) { none() } +} + +/** Provides validation for models-as-data summary, sink, source, and neutral kinds. */ +module KindValidation { + /** A valid models-as-data sink kind. */ + private class ValidSinkKind extends string { + bindingset[this] + ValidSinkKind() { + this = + [ + // shared + "code-injection", "command-injection", "file-content-store", "html-injection", + "js-injection", "ldap-injection", "log-injection", "path-injection", "request-forgery", + "sql-injection", "url-redirection", + // Java-only currently, but may be shared in the future + "bean-validation", "fragment-injection", "groovy-injection", "hostname-verification", + "information-leak", "intent-redirection", "jexl-injection", "jndi-injection", + "mvel-injection", "ognl-injection", "pending-intents", "response-splitting", + "template-injection", "xpath-injection", "xslt-injection", + // JavaScript-only currently, but may be shared in the future + "mongodb.sink", "nosql-injection", "unsafe-deserialization", + // Swift-only currently, but may be shared in the future + "database-store", "format-string", "hash-iteration-count", "predicate-injection", + "preferences-store", "tls-protocol-version", "transmission", "webview-fetch", "xxe" + ] + or + this.matches([ + // shared + "encryption-%", "qltest%", "test-%", + // Java-only currently, but may be shared in the future + "regex-use%", + // JavaScript-only currently, but may be shared in the future + "credentials-%", + // Swift-only currently, but may be shared in the future + "%string-%length", "weak-hash-input-%" + ]) + } + } + + /** An outdated models-as-data sink kind. */ + private class OutdatedSinkKind extends string { + OutdatedSinkKind() { + this = + [ + "sql", "url-redirect", "xpath", "ssti", "logging", "groovy", "jexl", "mvel", "xslt", + "ldap", "pending-intent-sent", "intent-start", "set-hostname-verifier", + "header-splitting", "xss", "write-file", "create-file", "read-file", "open-url", + "jdbc-url", "command-line-injection", "code", "html", "remote", + "uncontrolled-format-string", "js-eval" + ] + } + + /** Gets a replacement kind for an outdated sink kind. */ + private string replacementKind() { + this = ["sql", "xpath", "groovy", "jexl", "mvel", "xslt", "ldap", "code", "html"] and + result = this + "-injection" + or + this = "js-eval" and result = "code-injection" + or + this = "url-redirect" and result = "url-redirection" + or + this = "ssti" and result = "template-injection" + or + this = "logging" and result = "log-injection" + or + this = "pending-intent-sent" and result = "pending-intents" + or + this = "intent-start" and result = "intent-redirection" + or + this = "set-hostname-verifier" and result = "hostname-verification" + or + this = "header-splitting" and result = "response-splitting" + or + this = "xss" and result = "html-injection\" or \"js-injection" + or + this = ["write-file", "remote"] and result = "file-content-store" + or + this = ["create-file", "read-file"] and result = "path-injection" + or + this = ["open-url", "jdbc-url"] and result = "request-forgery" + or + this = "command-line-injection" and result = "command-injection" + or + this = "uncontrolled-format-string" and result = "format-string" + } + + /** Gets an error message for an outdated sink kind. */ + string outdatedMessage() { + result = + "The kind \"" + this + "\" is outdated. Use \"" + this.replacementKind() + "\" instead." + } + } + + /** A valid models-as-data source kind. */ + private class ValidSourceKind extends string { + bindingset[this] + ValidSourceKind() { + this = + [ + // shared + "local", "remote", + // Java + "android-external-storage-dir", "contentprovider", + // C# + "file", "file-write", + // JavaScript + "database-access-result" + ] + or + this.matches([ + // shared + "qltest%", "test-%", + // Swift + "%string-%length" + ]) + } + } + + /** A valid models-as-data summary kind. */ + private class ValidSummaryKind extends string { + ValidSummaryKind() { + this = + [ + // shared + "taint", "value", + // Dynamic languages + "type" + ] + } + } + + /** A valid models-as-data neutral kind. */ + private class ValidNeutralKind extends string { + ValidNeutralKind() { + this = + [ + // Java/C# currently + "sink", "source", "summary" + ] + } + } + + /** Gets an error message relating to an invalid kind in a model. */ + string getInvalidModelKind() { + exists(string kind | Config::summaryKind(kind) | + not kind instanceof ValidSummaryKind and + result = "Invalid kind \"" + kind + "\" in summary model." + ) + or + exists(string kind, string msg | Config::sinkKind(kind) | + not kind instanceof ValidSinkKind and + msg = "Invalid kind \"" + kind + "\" in sink model." and + // The part of this message that refers to outdated sink kinds can be deleted after June 1st, 2024. + if kind instanceof OutdatedSinkKind + then result = msg + " " + kind.(OutdatedSinkKind).outdatedMessage() + else result = msg + ) + or + exists(string kind | Config::sourceKind(kind) | + not kind instanceof ValidSourceKind and + result = "Invalid kind \"" + kind + "\" in source model." + ) + or + exists(string kind | Config::neutralKind(kind) | + not kind instanceof ValidNeutralKind and + result = "Invalid kind \"" + kind + "\" in neutral model." + ) + } +} diff --git a/shared/mad/qlpack.yml b/shared/mad/qlpack.yml new file mode 100644 index 00000000000..27e6c8e9750 --- /dev/null +++ b/shared/mad/qlpack.yml @@ -0,0 +1,6 @@ +name: codeql/mad +version: 0.0.1-dev +groups: shared +library: true +dependencies: +warnOnImplicitThis: true diff --git a/shared/regex/CHANGELOG.md b/shared/regex/CHANGELOG.md index cc83ed1e68c..e45483b6d3c 100644 --- a/shared/regex/CHANGELOG.md +++ b/shared/regex/CHANGELOG.md @@ -1,3 +1,7 @@ +## 0.0.14 + +No user-facing changes. + ## 0.0.13 No user-facing changes. diff --git a/shared/regex/change-notes/released/0.0.14.md b/shared/regex/change-notes/released/0.0.14.md new file mode 100644 index 00000000000..63b4d50ca45 --- /dev/null +++ b/shared/regex/change-notes/released/0.0.14.md @@ -0,0 +1,3 @@ +## 0.0.14 + +No user-facing changes. diff --git a/shared/regex/codeql-pack.release.yml b/shared/regex/codeql-pack.release.yml index 044e54e4f7e..ca29e45d0a6 100644 --- a/shared/regex/codeql-pack.release.yml +++ b/shared/regex/codeql-pack.release.yml @@ -1,2 +1,2 @@ --- -lastReleaseVersion: 0.0.13 +lastReleaseVersion: 0.0.14 diff --git a/shared/regex/codeql/regex/nfa/NfaUtils.qll b/shared/regex/codeql/regex/nfa/NfaUtils.qll index 3fd4b97c829..005228e8970 100644 --- a/shared/regex/codeql/regex/nfa/NfaUtils.qll +++ b/shared/regex/codeql/regex/nfa/NfaUtils.qll @@ -451,7 +451,15 @@ module Make { } bindingset[char] - override predicate matches(string char) { not hasChildThatMatches(cc, char) } + override predicate matches(string char) { + not hasChildThatMatches(cc, char) and + ( + // detect unsupported char classes that doesn't match anything (e.g. `\p{L}` in ruby), and don't report any matches + hasChildThatMatches(cc, _) + or + not exists(cc.getAChild()) // [^] still matches everything + ) + } } /** @@ -536,7 +544,9 @@ module Make { bindingset[char] override predicate matches(string char) { - not classEscapeMatches(charClass.toLowerCase(), char) + not classEscapeMatches(charClass.toLowerCase(), char) and + // detect unsupported char classes (e.g. `\p{L}` in ruby), and don't report any matches + classEscapeMatches(charClass.toLowerCase(), _) } } @@ -854,6 +864,9 @@ module Make { */ RegExpTerm getRepr() { result = repr } + /** + * Holds if the term represented by this state is found at the specified location offsets. + */ predicate hasLocationInfo(string file, int line, int column, int endline, int endcolumn) { repr.hasLocationInfo(file, line, column, endline, endcolumn) } diff --git a/shared/regex/qlpack.yml b/shared/regex/qlpack.yml index 86b105c881a..03c1586d407 100644 --- a/shared/regex/qlpack.yml +++ b/shared/regex/qlpack.yml @@ -1,5 +1,5 @@ name: codeql/regex -version: 0.0.14-dev +version: 0.0.15-dev groups: shared library: true dependencies: diff --git a/shared/ssa/CHANGELOG.md b/shared/ssa/CHANGELOG.md index 5e42000c1d1..41f9216baff 100644 --- a/shared/ssa/CHANGELOG.md +++ b/shared/ssa/CHANGELOG.md @@ -1,3 +1,7 @@ +## 0.0.18 + +No user-facing changes. + ## 0.0.17 No user-facing changes. diff --git a/shared/ssa/change-notes/released/0.0.18.md b/shared/ssa/change-notes/released/0.0.18.md new file mode 100644 index 00000000000..86c60b8abe7 --- /dev/null +++ b/shared/ssa/change-notes/released/0.0.18.md @@ -0,0 +1,3 @@ +## 0.0.18 + +No user-facing changes. diff --git a/shared/ssa/codeql-pack.release.yml b/shared/ssa/codeql-pack.release.yml index cbc3d3cd493..a0d2bc59d97 100644 --- a/shared/ssa/codeql-pack.release.yml +++ b/shared/ssa/codeql-pack.release.yml @@ -1,2 +1,2 @@ --- -lastReleaseVersion: 0.0.17 +lastReleaseVersion: 0.0.18 diff --git a/shared/ssa/qlpack.yml b/shared/ssa/qlpack.yml index 55ebe316292..c3fdb224479 100644 --- a/shared/ssa/qlpack.yml +++ b/shared/ssa/qlpack.yml @@ -1,5 +1,5 @@ name: codeql/ssa -version: 0.0.18-dev +version: 0.0.19-dev groups: shared library: true warnOnImplicitThis: true diff --git a/shared/tutorial/CHANGELOG.md b/shared/tutorial/CHANGELOG.md index 02876619527..28a38e6333b 100644 --- a/shared/tutorial/CHANGELOG.md +++ b/shared/tutorial/CHANGELOG.md @@ -1,3 +1,7 @@ +## 0.0.11 + +No user-facing changes. + ## 0.0.10 No user-facing changes. diff --git a/shared/tutorial/change-notes/released/0.0.11.md b/shared/tutorial/change-notes/released/0.0.11.md new file mode 100644 index 00000000000..19a2a55bd68 --- /dev/null +++ b/shared/tutorial/change-notes/released/0.0.11.md @@ -0,0 +1,3 @@ +## 0.0.11 + +No user-facing changes. diff --git a/shared/tutorial/codeql-pack.release.yml b/shared/tutorial/codeql-pack.release.yml index b740014e5ae..e679dc42092 100644 --- a/shared/tutorial/codeql-pack.release.yml +++ b/shared/tutorial/codeql-pack.release.yml @@ -1,2 +1,2 @@ --- -lastReleaseVersion: 0.0.10 +lastReleaseVersion: 0.0.11 diff --git a/shared/tutorial/qlpack.yml b/shared/tutorial/qlpack.yml index af7544c0ae9..7dc19224a82 100644 --- a/shared/tutorial/qlpack.yml +++ b/shared/tutorial/qlpack.yml @@ -1,6 +1,6 @@ name: codeql/tutorial description: Library for the CodeQL detective tutorials, helping new users learn to write CodeQL queries. -version: 0.0.11-dev +version: 0.0.12-dev groups: shared library: true warnOnImplicitThis: true diff --git a/shared/typetracking/CHANGELOG.md b/shared/typetracking/CHANGELOG.md index c8729dc39f8..e87bb476477 100644 --- a/shared/typetracking/CHANGELOG.md +++ b/shared/typetracking/CHANGELOG.md @@ -1,3 +1,7 @@ +## 0.0.11 + +No user-facing changes. + ## 0.0.10 No user-facing changes. diff --git a/shared/typetracking/change-notes/released/0.0.11.md b/shared/typetracking/change-notes/released/0.0.11.md new file mode 100644 index 00000000000..19a2a55bd68 --- /dev/null +++ b/shared/typetracking/change-notes/released/0.0.11.md @@ -0,0 +1,3 @@ +## 0.0.11 + +No user-facing changes. diff --git a/shared/typetracking/codeql-pack.release.yml b/shared/typetracking/codeql-pack.release.yml index b740014e5ae..e679dc42092 100644 --- a/shared/typetracking/codeql-pack.release.yml +++ b/shared/typetracking/codeql-pack.release.yml @@ -1,2 +1,2 @@ --- -lastReleaseVersion: 0.0.10 +lastReleaseVersion: 0.0.11 diff --git a/shared/typetracking/qlpack.yml b/shared/typetracking/qlpack.yml index 10e32e39f99..09ae3c23605 100644 --- a/shared/typetracking/qlpack.yml +++ b/shared/typetracking/qlpack.yml @@ -1,5 +1,5 @@ name: codeql/typetracking -version: 0.0.11-dev +version: 0.0.12-dev groups: shared library: true dependencies: diff --git a/shared/typos/CHANGELOG.md b/shared/typos/CHANGELOG.md index 472d0ef41a5..9b3dcbace69 100644 --- a/shared/typos/CHANGELOG.md +++ b/shared/typos/CHANGELOG.md @@ -1,3 +1,7 @@ +## 0.0.18 + +No user-facing changes. + ## 0.0.17 No user-facing changes. diff --git a/shared/typos/change-notes/released/0.0.18.md b/shared/typos/change-notes/released/0.0.18.md new file mode 100644 index 00000000000..86c60b8abe7 --- /dev/null +++ b/shared/typos/change-notes/released/0.0.18.md @@ -0,0 +1,3 @@ +## 0.0.18 + +No user-facing changes. diff --git a/shared/typos/codeql-pack.release.yml b/shared/typos/codeql-pack.release.yml index cbc3d3cd493..a0d2bc59d97 100644 --- a/shared/typos/codeql-pack.release.yml +++ b/shared/typos/codeql-pack.release.yml @@ -1,2 +1,2 @@ --- -lastReleaseVersion: 0.0.17 +lastReleaseVersion: 0.0.18 diff --git a/shared/typos/qlpack.yml b/shared/typos/qlpack.yml index fa4fe52aace..65a104d1f01 100644 --- a/shared/typos/qlpack.yml +++ b/shared/typos/qlpack.yml @@ -1,5 +1,5 @@ name: codeql/typos -version: 0.0.18-dev +version: 0.0.19-dev groups: shared library: true warnOnImplicitThis: true diff --git a/shared/util/CHANGELOG.md b/shared/util/CHANGELOG.md index 99aa576343d..fe9befff25a 100644 --- a/shared/util/CHANGELOG.md +++ b/shared/util/CHANGELOG.md @@ -1,3 +1,7 @@ +## 0.0.11 + +No user-facing changes. + ## 0.0.10 No user-facing changes. diff --git a/shared/util/change-notes/released/0.0.11.md b/shared/util/change-notes/released/0.0.11.md new file mode 100644 index 00000000000..19a2a55bd68 --- /dev/null +++ b/shared/util/change-notes/released/0.0.11.md @@ -0,0 +1,3 @@ +## 0.0.11 + +No user-facing changes. diff --git a/shared/util/codeql-pack.release.yml b/shared/util/codeql-pack.release.yml index b740014e5ae..e679dc42092 100644 --- a/shared/util/codeql-pack.release.yml +++ b/shared/util/codeql-pack.release.yml @@ -1,2 +1,2 @@ --- -lastReleaseVersion: 0.0.10 +lastReleaseVersion: 0.0.11 diff --git a/shared/util/codeql/util/test/InlineExpectationsTest.qll b/shared/util/codeql/util/test/InlineExpectationsTest.qll index 7d3806cb4b5..ec2f30c4119 100644 --- a/shared/util/codeql/util/test/InlineExpectationsTest.qll +++ b/shared/util/codeql/util/test/InlineExpectationsTest.qll @@ -396,6 +396,63 @@ module Make { } } + /** + * A module that merges three test signatures. + */ + module MergeTests3 implements TestSig { + private module M = MergeTests, TestImpl3>; + + string getARelevantTag() { result = M::getARelevantTag() } + + predicate hasActualResult(Impl::Location location, string element, string tag, string value) { + M::hasActualResult(location, element, tag, value) + } + + predicate hasOptionalResult(Impl::Location location, string element, string tag, string value) { + M::hasOptionalResult(location, element, tag, value) + } + } + + /** + * A module that merges four test signatures. + */ + module MergeTests4 + implements TestSig + { + private module M = MergeTests, TestImpl4>; + + string getARelevantTag() { result = M::getARelevantTag() } + + predicate hasActualResult(Impl::Location location, string element, string tag, string value) { + M::hasActualResult(location, element, tag, value) + } + + predicate hasOptionalResult(Impl::Location location, string element, string tag, string value) { + M::hasOptionalResult(location, element, tag, value) + } + } + + /** + * A module that merges five test signatures. + */ + module MergeTests5< + TestSig TestImpl1, TestSig TestImpl2, TestSig TestImpl3, TestSig TestImpl4, TestSig TestImpl5> + implements TestSig + { + private module M = + MergeTests, TestImpl5>; + + string getARelevantTag() { result = M::getARelevantTag() } + + predicate hasActualResult(Impl::Location location, string element, string tag, string value) { + M::hasActualResult(location, element, tag, value) + } + + predicate hasOptionalResult(Impl::Location location, string element, string tag, string value) { + M::hasOptionalResult(location, element, tag, value) + } + } + private module LegacyImpl implements TestSig { string getARelevantTag() { result = any(InlineExpectationsTest t).getARelevantTag() } @@ -444,6 +501,19 @@ module Make { class FalseNegativeExpectation = LegacyTest::FalseNegativeTestExpectation; class InvalidExpectation = LegacyTest::InvalidTestExpectation; + + /** + * Holds if the expectation `tag=value` is found in one or more expectation comments. + * + * This can be used when writing tests where the set of possible values must be known in advance, + * for example, when testing a predicate for which `value` is part of the binding set. + */ + predicate hasExpectationWithValue(string tag, string value) { + exists(string tags | + getAnExpectation(_, _, _, tags, value) and + tag = tags.splitAt(",") + ) + } } /** diff --git a/shared/util/qlpack.yml b/shared/util/qlpack.yml index c044709ceee..5dce17506ce 100644 --- a/shared/util/qlpack.yml +++ b/shared/util/qlpack.yml @@ -1,5 +1,5 @@ name: codeql/util -version: 0.0.11-dev +version: 0.0.12-dev groups: shared library: true dependencies: diff --git a/shared/yaml/CHANGELOG.md b/shared/yaml/CHANGELOG.md index 9119d5fc839..390989ba76a 100644 --- a/shared/yaml/CHANGELOG.md +++ b/shared/yaml/CHANGELOG.md @@ -1,3 +1,7 @@ +## 0.0.3 + +No user-facing changes. + ## 0.0.2 No user-facing changes. diff --git a/shared/yaml/change-notes/released/0.0.3.md b/shared/yaml/change-notes/released/0.0.3.md new file mode 100644 index 00000000000..af7864fc7d5 --- /dev/null +++ b/shared/yaml/change-notes/released/0.0.3.md @@ -0,0 +1,3 @@ +## 0.0.3 + +No user-facing changes. diff --git a/shared/yaml/codeql-pack.release.yml b/shared/yaml/codeql-pack.release.yml index 55dc06fbd76..a24b693d1e7 100644 --- a/shared/yaml/codeql-pack.release.yml +++ b/shared/yaml/codeql-pack.release.yml @@ -1,2 +1,2 @@ --- -lastReleaseVersion: 0.0.2 +lastReleaseVersion: 0.0.3 diff --git a/shared/yaml/qlpack.yml b/shared/yaml/qlpack.yml index 6b9f33c9125..ffbf802a8c4 100644 --- a/shared/yaml/qlpack.yml +++ b/shared/yaml/qlpack.yml @@ -1,5 +1,5 @@ name: codeql/yaml -version: 0.0.3-dev +version: 0.0.4-dev groups: shared library: true warnOnImplicitThis: true diff --git a/swift/BUILD.bazel b/swift/BUILD.bazel index b5042dc19d8..83dff3f8033 100644 --- a/swift/BUILD.bazel +++ b/swift/BUILD.bazel @@ -38,11 +38,15 @@ pkg_files( pkg_filegroup( name = "extractor-pack-generic", srcs = [ - ":dbscheme_files", ":manifest", - "//swift/downgrades", "//swift/tools", - ], + ] + select({ + "@platforms//os:windows": [], + "//conditions:default": [ + ":dbscheme_files", + "//swift/downgrades", + ], + }), visibility = ["//visibility:public"], ) @@ -59,8 +63,8 @@ pkg_runfiles( ) pkg_runfiles( - name = "incompatible-os", - srcs = ["//swift/tools/autobuilder-diagnostics:incompatible-os"], + name = "diagnostics", + srcs = ["//swift/tools/diagnostics:autobuilder-incompatible-os"], prefix = "tools/" + codeql_platform, ) @@ -80,17 +84,20 @@ pkg_files( pkg_filegroup( name = "extractor-pack-arch", - srcs = [ - ":extractor", - ":swift-test-sdk-arch", - ":resource-dir-arch", - ] + select({ - "@platforms//os:linux": [ - ":incompatible-os", + srcs = select({ + "@platforms//os:windows": [], + "//conditions:default": [ + ":extractor", + ":resource-dir-arch", + ":swift-test-sdk-arch", ], + }) + select({ "@platforms//os:macos": [ ":xcode-autobuilder", ], + "//conditions:default": [ + ":diagnostics", + ], }), visibility = ["//visibility:public"], ) @@ -122,7 +129,7 @@ generate_cmake( "//swift/extractor:extractor.real", "//swift/logging/tests/assertion-diagnostics:assert-false", ] + select({ - "@platforms//os:linux": ["//swift/tools/autobuilder-diagnostics:incompatible-os"], + "@platforms//os:linux": ["//swift/tools/diagnostics:autobuilder-incompatible-os"], "@platforms//os:macos": ["//swift/xcode-autobuilder"], }), visibility = ["//visibility:public"], diff --git a/swift/CMakeLists.txt b/swift/CMakeLists.txt index ba4a30d5c4a..2b44cc58993 100644 --- a/swift/CMakeLists.txt +++ b/swift/CMakeLists.txt @@ -3,7 +3,7 @@ cmake_minimum_required(VERSION 3.21) -set(CMAKE_CXX_STANDARD 17) +set(CMAKE_CXX_STANDARD 20) set(CMAKE_CXX_EXTENSIONS OFF) set(CMAKE_C_COMPILER clang) diff --git a/swift/downgrades/qlpack.yml b/swift/downgrades/qlpack.yml index 3fc919124df..3de4eeebc7c 100644 --- a/swift/downgrades/qlpack.yml +++ b/swift/downgrades/qlpack.yml @@ -2,3 +2,4 @@ name: codeql/swift-downgrades groups: swift downgrades: . library: true +warnOnImplicitThis: true diff --git a/swift/extractor/main.cpp b/swift/extractor/main.cpp index de7fd703161..4f1dd990be9 100644 --- a/swift/extractor/main.cpp +++ b/swift/extractor/main.cpp @@ -85,6 +85,7 @@ class Observer : public swift::FrontendObserver { void parsedArgs(swift::CompilerInvocation& invocation) override { auto& options = invocation.getFrontendOptions(); + options.KeepASTContext = true; lockOutputSwiftModuleTraps(state, options); processFrontendOptions(state, options); } @@ -93,7 +94,7 @@ class Observer : public swift::FrontendObserver { instance.addDiagnosticConsumer(&diagConsumer); } - void performedSemanticAnalysis(swift::CompilerInstance& compiler) override { + void performedCompilation(swift::CompilerInstance& compiler) override { codeql::extractSwiftFiles(state, compiler); codeql::extractSwiftInvocation(state, compiler, invocationTrap); codeql::extractExtractLazyDeclarations(state, compiler); diff --git a/swift/integration-tests/qlpack.yml b/swift/integration-tests/qlpack.yml index c0030d14bdf..f0a64418576 100644 --- a/swift/integration-tests/qlpack.yml +++ b/swift/integration-tests/qlpack.yml @@ -4,3 +4,4 @@ dependencies: codeql/swift-all: ${workspace} tests: . extractor: swift +warnOnImplicitThis: true diff --git a/swift/logging/SwiftLogging.cpp b/swift/logging/SwiftLogging.cpp index 952021f740f..6c065e858c1 100644 --- a/swift/logging/SwiftLogging.cpp +++ b/swift/logging/SwiftLogging.cpp @@ -3,7 +3,11 @@ #include #include #include +#ifdef _WIN32 +#include +#else #include +#endif #include "absl/strings/str_cat.h" #define LEVEL_REGEX_PATTERN "trace|debug|info|warning|error|critical|no_logs" diff --git a/swift/ql/consistency-queries/qlpack.yml b/swift/ql/consistency-queries/qlpack.yml index 57ef2babccf..c1ee319c393 100644 --- a/swift/ql/consistency-queries/qlpack.yml +++ b/swift/ql/consistency-queries/qlpack.yml @@ -2,3 +2,4 @@ name: codeql/swift-consistency-queries groups: [swift, test, consistency-queries] dependencies: codeql/swift-all: ${workspace} +warnOnImplicitThis: true diff --git a/swift/ql/examples/qlpack.yml b/swift/ql/examples/qlpack.yml index ed3c6f12bac..c29a8b7783d 100644 --- a/swift/ql/examples/qlpack.yml +++ b/swift/ql/examples/qlpack.yml @@ -4,3 +4,4 @@ groups: - examples dependencies: codeql/swift-all: ${workspace} +warnOnImplicitThis: true diff --git a/swift/ql/lib/CHANGELOG.md b/swift/ql/lib/CHANGELOG.md new file mode 100644 index 00000000000..572ca004c63 --- /dev/null +++ b/swift/ql/lib/CHANGELOG.md @@ -0,0 +1,13 @@ +## 0.1.1 + +### Major Analysis Improvements + +* Incorporated the cross-language `SensitiveDataHeuristics.qll` heuristics library into the Swift `SensitiveExprs.qll` library. This adds a number of new heuristics enhancing detection from the library. + +### Minor Analysis Improvements + +* Some models for the `Data` class have been generalized to `DataProtocol` so that they apply more widely. + +### Bug Fixes + +* Fixed a number of inconsistencies in the abstract syntax tree (AST) and in the control-flow graph (CFG). This may lead to more results in queries that use these libraries, or libraries that depend on them (such as dataflow). diff --git a/swift/ql/lib/change-notes/2023-05-25-dataprotocol-models.md b/swift/ql/lib/change-notes/2023-05-25-dataprotocol-models.md deleted file mode 100644 index 6e26484f5dc..00000000000 --- a/swift/ql/lib/change-notes/2023-05-25-dataprotocol-models.md +++ /dev/null @@ -1,4 +0,0 @@ ---- -category: minorAnalysis ---- -* Some models for the `Data` class have been generalized to `DataProtocol` so that they apply more widely. \ No newline at end of file diff --git a/swift/ql/lib/change-notes/2023-05-25-fix-ast-and-cfg-inconsistencies.md b/swift/ql/lib/change-notes/2023-05-25-fix-ast-and-cfg-inconsistencies.md deleted file mode 100644 index 208486b8f27..00000000000 --- a/swift/ql/lib/change-notes/2023-05-25-fix-ast-and-cfg-inconsistencies.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -category: fix ---- - -* Fixed a number of inconsistencies in the abstract syntax tree (AST) and in the control-flow graph (CFG). This may lead to more results in queries that use these libraries, or libraries that depend on them (such as dataflow). diff --git a/swift/ql/lib/change-notes/2023-05-30-shared-sensitive.md b/swift/ql/lib/change-notes/2023-05-30-shared-sensitive.md deleted file mode 100644 index 03de16f4269..00000000000 --- a/swift/ql/lib/change-notes/2023-05-30-shared-sensitive.md +++ /dev/null @@ -1,4 +0,0 @@ ---- -category: majorAnalysis ---- -* Incorporated the cross-language `SensitiveDataHeuristics.qll` heuristics library into the Swift `SensitiveExprs.qll` library. This adds a number of new heuristics enhancing detection from the library. \ No newline at end of file diff --git a/swift/ql/lib/change-notes/2023-06-19-regex-library.md b/swift/ql/lib/change-notes/2023-06-19-regex-library.md new file mode 100644 index 00000000000..8f3f11725d9 --- /dev/null +++ b/swift/ql/lib/change-notes/2023-06-19-regex-library.md @@ -0,0 +1,6 @@ +--- +category: feature +--- + +* Added new libraries `Regex.qll` and `RegexTreeView.qll` for reasoning about regular expressions +in Swift code and places where they are evaluated. diff --git a/swift/ql/lib/change-notes/released/0.1.1.md b/swift/ql/lib/change-notes/released/0.1.1.md new file mode 100644 index 00000000000..572ca004c63 --- /dev/null +++ b/swift/ql/lib/change-notes/released/0.1.1.md @@ -0,0 +1,13 @@ +## 0.1.1 + +### Major Analysis Improvements + +* Incorporated the cross-language `SensitiveDataHeuristics.qll` heuristics library into the Swift `SensitiveExprs.qll` library. This adds a number of new heuristics enhancing detection from the library. + +### Minor Analysis Improvements + +* Some models for the `Data` class have been generalized to `DataProtocol` so that they apply more widely. + +### Bug Fixes + +* Fixed a number of inconsistencies in the abstract syntax tree (AST) and in the control-flow graph (CFG). This may lead to more results in queries that use these libraries, or libraries that depend on them (such as dataflow). diff --git a/swift/ql/lib/codeql-pack.release.yml b/swift/ql/lib/codeql-pack.release.yml new file mode 100644 index 00000000000..92d1505475f --- /dev/null +++ b/swift/ql/lib/codeql-pack.release.yml @@ -0,0 +1,2 @@ +--- +lastReleaseVersion: 0.1.1 diff --git a/swift/ql/lib/codeql/swift/dataflow/ExternalFlow.qll b/swift/ql/lib/codeql/swift/dataflow/ExternalFlow.qll index 82daf14a39a..ba680b1b138 100644 --- a/swift/ql/lib/codeql/swift/dataflow/ExternalFlow.qll +++ b/swift/ql/lib/codeql/swift/dataflow/ExternalFlow.qll @@ -74,6 +74,7 @@ private import internal.FlowSummaryImpl::Public private import internal.FlowSummaryImpl::Private::External private import internal.FlowSummaryImplSpecific private import FlowSummary as FlowSummary +private import codeql.mad.ModelValidation as SharedModelVal /** * A unit class for adding additional source model rows. @@ -263,14 +264,16 @@ module CsvValidation { ) } - private string getInvalidModelKind() { - exists(string row, string kind | summaryModel(row) | - kind = row.splitAt(";", 8) and - not kind = ["taint", "value"] and - result = "Invalid kind \"" + kind + "\" in summary model." - ) + private module KindValConfig implements SharedModelVal::KindValidationConfigSig { + predicate summaryKind(string kind) { summaryModel(_, _, _, _, _, _, _, _, kind, _) } + + predicate sinkKind(string kind) { sinkModel(_, _, _, _, _, _, _, kind, _) } + + predicate sourceKind(string kind) { sourceModel(_, _, _, _, _, _, _, kind, _) } } + private module KindVal = SharedModelVal::KindValidation; + private string getInvalidModelSubtype() { exists(string pred, string row | sourceModel(row) and pred = "source" @@ -335,7 +338,7 @@ module CsvValidation { msg = [ getInvalidModelSignature(), getInvalidModelInput(), getInvalidModelOutput(), - getInvalidModelSubtype(), getInvalidModelColumnCount(), getInvalidModelKind() + getInvalidModelSubtype(), getInvalidModelColumnCount(), KindVal::getInvalidModelKind() ] } } diff --git a/swift/ql/lib/codeql/swift/dataflow/internal/DataFlowImpl.qll b/swift/ql/lib/codeql/swift/dataflow/internal/DataFlowImpl.qll index 984c5ae2018..284fff191ae 100644 --- a/swift/ql/lib/codeql/swift/dataflow/internal/DataFlowImpl.qll +++ b/swift/ql/lib/codeql/swift/dataflow/internal/DataFlowImpl.qll @@ -2021,7 +2021,8 @@ module Impl { FlowCheckNode() { castNode(this.asNode()) or clearsContentCached(this.asNode(), _) or - expectsContentCached(this.asNode(), _) + expectsContentCached(this.asNode(), _) or + neverSkipInPathGraph(this.asNode()) } } diff --git a/swift/ql/lib/codeql/swift/dataflow/internal/DataFlowPrivate.qll b/swift/ql/lib/codeql/swift/dataflow/internal/DataFlowPrivate.qll index 02ae10b83af..6b830982d6f 100644 --- a/swift/ql/lib/codeql/swift/dataflow/internal/DataFlowPrivate.qll +++ b/swift/ql/lib/codeql/swift/dataflow/internal/DataFlowPrivate.qll @@ -282,7 +282,7 @@ private predicate hasPatternNode(PatternCfgNode n, Pattern p) { import Cached /** Holds if `n` should be hidden from path explanations. */ -predicate nodeIsHidden(Node n) { none() } +predicate nodeIsHidden(Node n) { n instanceof FlowSummaryNode } private module ParameterNodes { abstract class ParameterNodeImpl extends NodeImpl { @@ -849,6 +849,12 @@ class CastNode extends Node { CastNode() { none() } } +/** + * Holds if `n` should never be skipped over in the `PathGraph` and in path + * explanations. + */ +predicate neverSkipInPathGraph(Node n) { none() } + class DataFlowExpr = Expr; class DataFlowParameter = ParamDecl; diff --git a/swift/ql/lib/codeql/swift/regex/Regex.qll b/swift/ql/lib/codeql/swift/regex/Regex.qll new file mode 100644 index 00000000000..b97847a1ac6 --- /dev/null +++ b/swift/ql/lib/codeql/swift/regex/Regex.qll @@ -0,0 +1,134 @@ +/** + * Provides classes and predicates for reasoning about regular expressions. + */ + +import swift +import codeql.swift.regex.RegexTreeView +private import codeql.swift.dataflow.DataFlow +private import internal.ParseRegex + +/** + * A data flow configuration for tracking string literals that are used as + * regular expressions. + */ +private module RegexUseConfig implements DataFlow::ConfigSig { + predicate isSource(DataFlow::Node node) { node.asExpr() instanceof StringLiteralExpr } + + predicate isSink(DataFlow::Node node) { node.asExpr() = any(RegexEval eval).getRegexInput() } + + predicate isAdditionalFlowStep(DataFlow::Node nodeFrom, DataFlow::Node nodeTo) { + // flow through `Regex` initializer, i.e. from a string to a `Regex` object. + exists(CallExpr call | + ( + call.getStaticTarget().(Method).hasQualifiedName("Regex", ["init(_:)", "init(_:as:)"]) or + call.getStaticTarget() + .(Method) + .hasQualifiedName("NSRegularExpression", "init(pattern:options:)") + ) and + nodeFrom.asExpr() = call.getArgument(0).getExpr() and + nodeTo.asExpr() = call + ) + } +} + +private module RegexUseFlow = DataFlow::Global; + +/** + * A string literal that is used as a regular expression in a regular + * expression evaluation. For example the string literal `"(a|b).*"` in: + * ``` + * Regex("(a|b).*").firstMatch(in: myString) + * ``` + */ +private class ParsedStringRegex extends RegExp, StringLiteralExpr { + RegexEval eval; + + ParsedStringRegex() { + RegexUseFlow::flow(DataFlow::exprNode(this), DataFlow::exprNode(eval.getRegexInput())) + } + + /** + * Gets a call that evaluates this regular expression. + */ + RegexEval getEval() { result = eval } +} + +/** + * A call that evaluates a regular expression. For example, the call to `firstMatch` in: + * ``` + * Regex("(a|b).*").firstMatch(in: myString) + * ``` + */ +abstract class RegexEval extends CallExpr { + /** + * Gets the input to this call that is the regular expression being evaluated. + */ + abstract Expr getRegexInput(); + + /** + * Gets the input to this call that is the string the regular expression is evaluated on. + */ + abstract Expr getStringInput(); + + /** + * Gets a regular expression value that is evaluated here (if any can be identified). + */ + RegExp getARegex() { result.(ParsedStringRegex).getEval() = this } +} + +/** + * A call to a function that always evaluates a regular expression. + */ +private class AlwaysRegexEval extends RegexEval { + Expr regexInput; + Expr stringInput; + + AlwaysRegexEval() { + this.getStaticTarget() + .(Method) + .hasQualifiedName("Regex", ["firstMatch(in:)", "prefixMatch(in:)", "wholeMatch(in:)"]) and + regexInput = this.getQualifier() and + stringInput = this.getArgument(0).getExpr() + or + this.getStaticTarget() + .(Method) + .hasQualifiedName("NSRegularExpression", + [ + "numberOfMatches(in:options:range:)", "enumerateMatches(in:options:range:using:)", + "matches(in:options:range:)", "firstMatch(in:options:range:)", + "rangeOfFirstMatch(in:options:range:)", + "replaceMatches(in:options:range:withTemplate:)", + "stringByReplacingMatches(in:options:range:withTemplate:)" + ]) and + regexInput = this.getQualifier() and + stringInput = this.getArgument(0).getExpr() + or + this.getStaticTarget() + .(Method) + .hasQualifiedName("BidirectionalCollection", + [ + "contains(_:)", "firstMatch(of:)", "firstRange(of:)", "matches(of:)", + "prefixMatch(of:)", "ranges(of:)", + "split(separator:maxSplits:omittingEmptySubsequences:)", "starts(with:)", + "trimmingPrefix(_:)", "wholeMatch(of:)" + ]) and + regexInput = this.getArgument(0).getExpr() and + stringInput = this.getQualifier() + or + this.getStaticTarget() + .(Method) + .hasQualifiedName("RangeReplaceableCollection", + [ + "replace(_:maxReplacements:with:)", "replace(_:with:maxReplacements:)", + "replacing(_:maxReplacements:with:)", "replacing(_:subrange:maxReplacements:with:)", + "replacing(_:with:maxReplacements:)", "replacing(_:with:subrange:maxReplacements:)", + "trimPrefix(_:)" + ]) and + regexInput = this.getArgument(0).getExpr() and + stringInput = this.getQualifier() + } + + override Expr getRegexInput() { result = regexInput } + + override Expr getStringInput() { result = stringInput } +} diff --git a/swift/ql/lib/codeql/swift/regex/RegexTreeView.qll b/swift/ql/lib/codeql/swift/regex/RegexTreeView.qll new file mode 100644 index 00000000000..beaff7a4129 --- /dev/null +++ b/swift/ql/lib/codeql/swift/regex/RegexTreeView.qll @@ -0,0 +1,1226 @@ +/** + * Provides a class hierarchy corresponding to a parse tree of regular expressions. + */ + +import swift +private import internal.ParseRegex +private import codeql.util.Numbers +private import codeql.regex.nfa.NfaUtils as NfaUtils +private import codeql.regex.RegexTreeView +// exporting as RegexTreeView, and in the top-level scope. +import Impl as RegexTreeView +import Impl + +/** + * An element containing a regular expression term, that is, either + * a string literal (parsed as a regular expression) + * or another regular expression term. + * + * For sequences and alternations, we require at least one child. + * Otherwise, we wish to represent the term differently. + * This avoids multiple representations of the same term. + */ +private newtype TRegExpParent = + /** A string literal used as a regular expression */ + TRegExpLiteral(RegExp re) or + /** A quantified term */ + TRegExpQuantifier(RegExp re, int start, int end) { re.qualifiedItem(start, end, _, _) } or + /** A sequence term */ + TRegExpSequence(RegExp re, int start, int end) { re.sequence(start, end) } or + /** An alternation term */ + TRegExpAlt(RegExp re, int start, int end) { re.alternation(start, end) } or + /** A character class term */ + TRegExpCharacterClass(RegExp re, int start, int end) { re.charSet(start, end) } or + /** A character range term */ + TRegExpCharacterRange(RegExp re, int start, int end) { re.charRange(_, start, _, _, end) } or + /** A group term */ + TRegExpGroup(RegExp re, int start, int end) { re.group(start, end) } or + /** A special character */ + TRegExpSpecialChar(RegExp re, int start, int end) { re.specialCharacter(start, end, _) } or + /** A normal character */ + TRegExpNormalChar(RegExp re, int start, int end) { + re.normalCharacterSequence(start, end) + or + re.escapedCharacter(start, end) and + not re.specialCharacter(start, end, _) + } or + /** A back reference */ + TRegExpBackRef(RegExp re, int start, int end) { re.backreference(start, end) } or + /** A named character property */ + TRegExpNamedCharacterProperty(RegExp re, int start, int end) { + re.namedCharacterProperty(start, end, _) + } + +/** An implementation that statisfies the RegexTreeView signature. */ +private module Impl implements RegexTreeViewSig { + /** + * An element containing a regular expression term, that is, either + * a string literal (parsed as a regular expression) + * or another regular expression term. + */ + class RegExpParent extends TRegExpParent { + /** Gets a textual representation of this element. */ + string toString() { result = "RegExpParent" } + + /** Gets the `i`th child term. */ + RegExpTerm getChild(int i) { none() } + + /** Gets a child term . */ + final RegExpTerm getAChild() { result = this.getChild(_) } + + /** Gets the number of child terms. */ + int getNumChild() { result = count(this.getAChild()) } + + /** Gets the last child term of this element. */ + RegExpTerm getLastChild() { result = this.getChild(this.getNumChild() - 1) } + + /** + * Gets the name of a primary CodeQL class to which this regular + * expression term belongs. + */ + string getAPrimaryQlClass() { result = "RegExpParent" } + + /** + * Gets a comma-separated list of the names of the primary CodeQL classes to + * which this regular expression term belongs. + */ + final string getPrimaryQlClasses() { result = concat(this.getAPrimaryQlClass(), ",") } + } + + /** A string literal used as a regular expression */ + class RegExpLiteral extends TRegExpLiteral, RegExpParent { + RegExp re; + + RegExpLiteral() { this = TRegExpLiteral(re) } + + override RegExpTerm getChild(int i) { + i = 0 and result.getRegExp() = re and result.isRootTerm() + } + + /** Holds if dot, `.`, matches all characters, including newlines. */ + predicate isDotAll() { re.isDotAll() } + + /** Holds if this regex matching is case-insensitive for this regex. */ + predicate isIgnoreCase() { re.isIgnoreCase() } + + /** Get a string representing all modes for this regex. */ + string getFlags() { result = re.getFlags() } + + /** Gets the primary QL class for this regex. */ + override string getAPrimaryQlClass() { result = "RegExpLiteral" } + } + + /** + * A regular expression term, that is, a syntactic part of a regular expression. + */ + class RegExpTerm extends RegExpParent { + RegExp 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 = TRegExpGroup(re, start, end) + or + this = TRegExpQuantifier(re, start, end) + or + this = TRegExpSequence(re, start, end) and + exists(seqChild(re, start, end, 1)) // if a sequence does not have more than one element, it should be treated as that element instead. + or + this = TRegExpSpecialChar(re, start, end) + or + this = TRegExpNamedCharacterProperty(re, start, end) + } + + /** + * 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) + or + result = this.(RegExpNamedCharacterProperty).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 } + + /** Gets the associated `RegExp`. */ + RegExp getRegExp() { 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 + ) { + filepath = re.getFile().getAbsolutePath() and + startline = re.getLocation().getStartLine() and + startcolumn = re.getLocation().getStartColumn() + start + 1 and + endline = re.getLocation().getEndLine() and + endcolumn = re.getLocation().getStartColumn() + 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 single string this regular-expression term matches. + * + * This predicate is only defined for (sequences/groups of) constant regular + * expressions. In particular, terms involving zero-width assertions like `^` + * or `\b` are not considered to have a constant value. + * + * Note that this predicate does not take flags of the enclosing + * regular-expression literal into account. + */ + string getConstantValue() { none() } + + /** + * Gets a string that is matched by this regular-expression term. + */ + string getAMatchedString() { result = this.getConstantValue() } + + /** Gets the primary QL class for this term. */ + override string getAPrimaryQlClass() { result = "RegExpTerm" } + + /** Holds if this regular expression term can match the empty string. */ + predicate isNullable() { none() } + } + + /** + * A quantified regular expression term. + * + * Example: + * + * ``` + * ((ECMA|Java)[sS]cript)* + * ``` + */ + class RegExpQuantifier extends RegExpTerm, TRegExpQuantifier { + int part_end; + boolean may_repeat_forever; + + RegExpQuantifier() { + this = TRegExpQuantifier(re, start, end) and + re.qualifiedPart(start, part_end, end, _, may_repeat_forever) + } + + override RegExpTerm getChild(int i) { + i = 0 and + result.getRegExp() = re and + result.getStart() = start and + result.getEnd() = part_end + } + + /** Hols if this term may match an unlimited number of times. */ + predicate mayRepeatForever() { may_repeat_forever = true } + + /** Gets the qualifier for this term. That is e.g "?" for "a?". */ + string getQualifier() { result = re.getText().substring(part_end, end) } + + override string getAPrimaryQlClass() { result = "RegExpQuantifier" } + } + + /** + * A regular expression term that permits unlimited repetitions. + */ + class InfiniteRepetitionQuantifier extends RegExpQuantifier { + InfiniteRepetitionQuantifier() { this.mayRepeatForever() } + + override string getAPrimaryQlClass() { result = "InfiniteRepetitionQuantifier" } + } + + /** + * A star-quantified term. + * + * Example: + * + * ``` + * \w* + * ``` + */ + class RegExpStar extends InfiniteRepetitionQuantifier { + RegExpStar() { this.getQualifier().charAt(0) = "*" } + + override string getAPrimaryQlClass() { result = "RegExpStar" } + + override predicate isNullable() { any() } + } + + /** + * A plus-quantified term. + * + * Example: + * + * ``` + * \w+ + * ``` + */ + class RegExpPlus extends InfiniteRepetitionQuantifier { + RegExpPlus() { this.getQualifier().charAt(0) = "+" } + + override string getAPrimaryQlClass() { result = "RegExpPlus" } + + override predicate isNullable() { this.getAChild().isNullable() } + } + + /** + * An optional term. + * + * Example: + * + * ``` + * ;? + * ``` + */ + class RegExpOpt extends RegExpQuantifier { + RegExpOpt() { this.getQualifier().charAt(0) = "?" } + + override string getAPrimaryQlClass() { result = "RegExpOpt" } + + override predicate isNullable() { any() } + } + + /** + * 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) } + + override string getAPrimaryQlClass() { result = "RegExpRange" } + + /** 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 predicate isNullable() { this.getAChild().isNullable() or this.getLowerBound() = 0 } + } + + /** + * 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) and + exists(seqChild(re, start, end, 1)) // if a sequence does not have more than one element, it should be treated as that element instead. + } + + 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 getConstantValue() { result = this.getConstantValue(0) } + + /** + * Gets the single string matched by the `i`th child and all following + * children of this sequence, if any. + */ + private string getConstantValue(int i) { + i = this.getNumChild() and + result = "" + or + result = this.getChild(i).getConstantValue() + this.getConstantValue(i + 1) + } + + override string getAPrimaryQlClass() { result = "RegExpSequence" } + + override predicate isNullable() { + forall(RegExpTerm child | child = this.getAChild() | child.isNullable()) + } + } + + pragma[nomagic] + private int seqChildEnd(RegExp 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(RegExp re, int start, int end, int i) { + re.sequence(start, end) and + ( + i = 0 and + result.getRegExp() = re and + result.getStart() = start and + exists(int itemEnd | + re.item(start, itemEnd) and + result.getEnd() = itemEnd + ) + or + i > 0 and + result.getRegExp() = re and + exists(int itemStart | itemStart = seqChildEnd(re, start, end, i - 1) | + result.getStart() = itemStart and + re.item(itemStart, result.getEnd()) + ) + ) + } + + /** + * 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.getRegExp() = 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.getRegExp() = 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()) + ) + } + + /** Gets an alternative of this term. */ + RegExpTerm getAlternative() { result = this.getAChild() } + + override string getAMatchedString() { result = this.getAlternative().getAMatchedString() } + + override string getAPrimaryQlClass() { result = "RegExpAlt" } + + override predicate isNullable() { this.getAChild().isNullable() } + } + + /** + * A character escape in a regular expression. + * + * Example: + * + * ``` + * \. + * ``` + */ + 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", "v"] and not this.isUnicode() + } + + override string getAPrimaryQlClass() { 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", "\\x"] } + + /** + * Gets the unicode char for this escape. + * E.g. for `\u0061` this returns "a". + */ + private string getUnicode() { result = parseHexInt(this.getHexString()).toUnicode() } + + /** + * Gets the part of this escape that is a hexidecimal string. + */ + private string getHexString() { + this.isUnicode() and + if this.getText().matches(["\\x{%", "\\u{%"]) // \x{hh...} or \u{hh...} + then result = this.getText().substring(3, this.getText().length() - 1) + else result = this.getText().suffix(2) // \xhh or \uhhhh or \Uhhhhhhhh + } + } + + /** + * A word boundary, that is, a regular expression term of the form `\b`. + */ + class RegExpWordBoundary extends RegExpSpecialChar { + RegExpWordBoundary() { this.getChar() = "\\b" } + + override predicate isNullable() { none() } + } + + /** + * A non-word boundary, that is, a regular expression term of the form `\B`. + */ + class RegExpNonWordBoundary extends RegExpSpecialChar { + RegExpNonWordBoundary() { this.getChar() = "\\B" } + + override string getAPrimaryQlClass() { result = "RegExpNonWordBoundary" } + } + + /** + * 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"] } + + override RegExpTerm getChild(int i) { none() } + + override string getAPrimaryQlClass() { result = "RegExpCharacterClassEscape" } + + override predicate isNullable() { none() } + } + + /** + * A character class in a regular expression. + * + * Examples: + * + * ``` + * /[a-fA-F0-9]/ + * /[^abc]/ + * ``` + */ + 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 + result.getRegExp() = re and + exists(int itemStart, int itemEnd | + result.getStart() = itemStart and + re.charSetStart(start, itemStart) and + re.charSetChild(start, itemStart, itemEnd) and + result.getEnd() = itemEnd + ) + or + i > 0 and + result.getRegExp() = re and + exists(int itemStart | itemStart = this.getChild(i - 1).getEnd() | + result.getStart() = itemStart and + re.charSetChild(start, itemStart, result.getEnd()) + ) + } + + override string getAMatchedString() { + not this.isInverted() and result = this.getAChild().getAMatchedString() + } + + override string getAPrimaryQlClass() { result = "RegExpCharacterClass" } + + override predicate isNullable() { none() } + } + + /** + * 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.getRegExp() = re and + result.getStart() = start and + result.getEnd() = lower_end + or + i = 1 and + result.getRegExp() = re and + result.getStart() = upper_start and + result.getEnd() = end + } + + override string getAPrimaryQlClass() { result = "RegExpCharacterRange" } + + override predicate isNullable() { none() } + } + + /** + * A normal character in a regular expression, that is, a character + * without special meaning. This includes escaped characters. + * + * Examples: + * ``` + * t + * \t + * ``` + */ + additional 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 getAPrimaryQlClass() { 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() + or + this = TRegExpSpecialChar(re, start, end) and + re.inCharSet(start) and + value = this.(RegExpSpecialChar).getChar() + } + + /** + * 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 getConstantValue() { result = this.getValue() } + + override string getAPrimaryQlClass() { result = "RegExpConstant" } + + override predicate isNullable() { none() } + } + + /** + * 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.getRegExp() = re and + i = 0 and + re.groupContents(start, end, result.getStart(), result.getEnd()) + } + + override string getConstantValue() { result = this.getAChild().getConstantValue() } + + override string getAMatchedString() { result = this.getAChild().getAMatchedString() } + + override string getAPrimaryQlClass() { result = "RegExpGroup" } + + override predicate isNullable() { this.getAChild().isNullable() } + } + + /** + * A special character in a regular expression. + * + * Examples: + * ``` + * ^ + * $ + * . + * ``` + */ + additional 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 getAPrimaryQlClass() { result = "RegExpSpecialChar" } + } + + /** + * A dot regular expression. + * + * Example: + * + * ``` + * . + * ``` + */ + class RegExpDot extends RegExpSpecialChar { + RegExpDot() { this.getChar() = "." } + + override string getAPrimaryQlClass() { result = "RegExpDot" } + + override predicate isNullable() { none() } + } + + /** + * A term that matches a specific position between characters in the string. + * + * Example: + * + * ``` + * \A + * ``` + */ + class RegExpAnchor extends RegExpSpecialChar { + RegExpAnchor() { this.getChar() = ["^", "$", "\\A", "\\Z", "\\z"] } + + override string getAPrimaryQlClass() { result = "RegExpAnchor" } + } + + /** + * A dollar assertion `$` or `\Z` matching the end of a line. + * + * Example: + * + * ``` + * $ + * ``` + */ + class RegExpDollar extends RegExpAnchor { + RegExpDollar() { this.getChar() = ["$", "\\Z", "\\z"] } + + override string getAPrimaryQlClass() { result = "RegExpDollar" } + + override predicate isNullable() { any() } + } + + /** + * A caret assertion `^` or `\A` matching the beginning of a line. + * + * Example: + * + * ``` + * ^ + * ``` + */ + class RegExpCaret extends RegExpAnchor { + RegExpCaret() { this.getChar() = ["^", "\\A"] } + + override string getAPrimaryQlClass() { result = "RegExpCaret" } + + override predicate isNullable() { any() } + } + + /** + * A zero-width match, that is, either an empty group or an assertion. + * + * Examples: + * ``` + * () + * (?=\w) + * ``` + */ + additional class RegExpZeroWidthMatch extends RegExpGroup { + RegExpZeroWidthMatch() { re.zeroWidthMatch(start, end) } + + override RegExpTerm getChild(int i) { none() } + + override string getAPrimaryQlClass() { result = "RegExpZeroWidthMatch" } + + override predicate isNullable() { any() } + } + + /** + * 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.getLiteral() = this.getLiteral() and + ( + result.getNumber() = this.getNumber() or + result.getName() = this.getName() + ) + } + + override RegExpTerm getChild(int i) { none() } + + override string getAPrimaryQlClass() { result = "RegExpBackRef" } + + override predicate isNullable() { this.getGroup().isNullable() } + } + + /** + * A named character property. For example, the POSIX bracket expression + * `[[:digit:]]`. + */ + additional class RegExpNamedCharacterProperty extends RegExpTerm, TRegExpNamedCharacterProperty { + RegExpNamedCharacterProperty() { this = TRegExpNamedCharacterProperty(re, start, end) } + + override RegExpTerm getChild(int i) { none() } + + override string getAPrimaryQlClass() { result = "RegExpNamedCharacterProperty" } + + /** + * Gets the property name. For example, in `\p{Space}`, the result is + * `"Space"`. + */ + string getName() { result = re.getCharacterPropertyName(start, end) } + + /** + * Holds if the property is inverted. For example, it holds for `\p{^Digit}`, + * which matches non-digits. + */ + predicate isInverted() { re.namedCharacterPropertyIsInverted(start, end) } + } + + 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) + or + // TODO: expand to cover more properties + exists(RegExpNamedCharacterProperty escape | term = escape | + escape.getName().toLowerCase() = ["digit", "isdigit"] and + if escape.isInverted() then clazz = "D" else clazz = "d" + or + escape.getName().toLowerCase() = ["space", "isspace"] and + if escape.isInverted() then clazz = "S" else clazz = "s" + or + escape.getName().toLowerCase() = ["word", "isword"] and + if escape.isInverted() then clazz = "W" else clazz = "w" + ) + } + + /** + * Holds if the regular expression should not be considered. + */ + predicate isExcluded(RegExpParent parent) { none() } + + /** + * Holds if `term` is a possessive quantifier. + * Not currently implemented, 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 Swift. + */ + 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 Swift. + */ + predicate matchesAnySuffix(RegExpTerm term) { any() } + + /** + * 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() + } +} diff --git a/swift/ql/lib/codeql/swift/regex/internal/ParseRegex.qll b/swift/ql/lib/codeql/swift/regex/internal/ParseRegex.qll new file mode 100644 index 00000000000..7a837cd733e --- /dev/null +++ b/swift/ql/lib/codeql/swift/regex/internal/ParseRegex.qll @@ -0,0 +1,1049 @@ +/** + * Library for parsing Swift regular expressions. + * + * N.B. does not yet handle stripping whitespace and comments in regexes with + * the `x` (free-spacing) flag. + */ + +import swift + +/** + * A `Expr` containing a regular expression term, that is, either + * a regular expression literal, or a string literal used in a context where + * it is parsed as regular expression. + */ +abstract class RegExp extends Expr { + /** + * Holds if this `RegExp` has the `s` flag for multi-line matching. + */ + predicate isDotAll() { none() } + + /** + * Holds if this `RegExp` has the `i` flag for case-insensitive matching. + */ + predicate isIgnoreCase() { none() } + + /** + * Gets the flags for this `RegExp`, or the empty string if it has no flags. + */ + string getFlags() { result = "" } + + /** + * Helper predicate for `charSetStart(int start, int end)`. + * + * In order to identify left brackets ('[') which actually start a character class, + * we perform a left to right scan of the string. + * + * To avoid negative recursion we return a boolean. See `escaping`, + * the helper for `escapingChar`, for a clean use of this pattern. + * + * result is true for those start chars that actually mark a start of a char set. + */ + boolean charSetStart(int pos) { + exists(int index | + // is opening bracket + this.charSetDelimiter(index, pos) = true and + ( + // if this is the first bracket, `pos` starts a char set + index = 1 and result = true + or + // if the previous char set delimiter was not a closing bracket, `pos` does + // not start a char set. This is needed to handle cases such as `[[]` (a + // char set that matches the `[` char) + index > 1 and + not this.charSetDelimiter(index - 1, _) = false and + result = false + or + // special handling of cases such as `[][]` (the character-set of the characters `]` and `[`). + exists(int prevClosingBracketPos | + // previous bracket is a closing bracket + this.charSetDelimiter(index - 1, prevClosingBracketPos) = false and + if + // check if the character that comes before the previous closing bracket + // is an opening bracket (taking `^` into account) + // check if the character that comes before the previous closing bracket + // is an opening bracket (taking `^` into account) + exists(int posBeforePrevClosingBracket | + if this.getChar(prevClosingBracketPos - 1) = "^" + then posBeforePrevClosingBracket = prevClosingBracketPos - 2 + else posBeforePrevClosingBracket = prevClosingBracketPos - 1 + | + this.charSetDelimiter(index - 2, posBeforePrevClosingBracket) = true + ) + then + // brackets without anything in between is not valid character ranges, so + // the first closing bracket in `[]]` and `[^]]` does not count, + // + // and we should _not_ mark the second opening bracket in `[][]` and `[^][]` + // as starting a new char set. ^ ^ + exists(int posBeforePrevClosingBracket | + this.charSetDelimiter(index - 2, posBeforePrevClosingBracket) = true + | + result = this.charSetStart(posBeforePrevClosingBracket).booleanNot() + ) + else + // if not, `pos` does in fact mark a real start of a character range + result = true + ) + ) + ) + } + + /** + * Helper predicate for chars that could be character-set delimiters. + * Holds if the (non-escaped) char at `pos` in the string, is the (one-based) `index` occurrence of a bracket (`[` or `]`) in the string. + * Result if `true` is the char is `[`, and `false` if the char is `]`. + */ + boolean charSetDelimiter(int index, int pos) { + pos = + rank[index](int p | + (this.nonEscapedCharAt(p) = "[" or this.nonEscapedCharAt(p) = "]") and + // Brackets that are part of POSIX expressions should not count as + // char-set delimiters. + not exists(int x, int y | + this.posixStyleNamedCharacterProperty(x, y, _) and pos >= x and pos < y + ) + ) and + ( + this.nonEscapedCharAt(pos) = "[" and result = true + or + this.nonEscapedCharAt(pos) = "]" and result = false + ) + } + + /** Holds if a character set starts between `start` and `end`. */ + predicate charSetStart(int start, int end) { + this.charSetStart(start) = true and + ( + this.getChar(start + 1) = "^" and end = start + 2 + or + not this.getChar(start + 1) = "^" and end = start + 1 + ) + } + + /** + * Whether there is a character class, between start (inclusive) and end (exclusive). + */ + predicate charSet(int start, int end) { + exists(int innerStart, int innerEnd | + this.charSetStart(start, innerStart) and + not this.charSetStart(_, start) + | + end = innerEnd + 1 and + innerEnd = + min(int e | + e > innerStart and + this.nonEscapedCharAt(e) = "]" and + not exists(int x, int y | + this.posixStyleNamedCharacterProperty(x, y, _) and e >= x and e < y + ) + | + e + ) + ) + } + + /** + * Holds if the character set starting at `charsetStart` contains either + * a character or a `-` found between `start` and `end`. + */ + private predicate charSetToken(int charsetStart, int index, int tokenStart, int tokenEnd) { + tokenStart = + rank[index](int start, int end | this.charSetToken(charsetStart, start, end) | start) and + this.charSetToken(charsetStart, tokenStart, tokenEnd) + } + + /** + * Holds if the character set starting at `charsetStart` contains either + * a character or a `-` found between `start` and `end`. + */ + private predicate charSetToken(int charsetStart, int start, int end) { + this.charSetStart(charsetStart, start) and + ( + this.escapedCharacter(start, end) + or + this.namedCharacterProperty(start, end, _) + or + exists(this.nonEscapedCharAt(start)) and end = start + 1 + ) + or + this.charSetToken(charsetStart, _, start) and + ( + this.escapedCharacter(start, end) + or + this.namedCharacterProperty(start, end, _) + or + exists(this.nonEscapedCharAt(start)) and + end = start + 1 and + not this.getChar(start) = "]" + ) + } + + /** + * Holds if the character set starting at `charsetStart` contains either + * a character or a range found between `start` and `end`. + */ + predicate charSetChild(int charsetStart, int start, int end) { + this.charSetToken(charsetStart, start, end) and + not exists(int rangeStart, int rangeEnd | + this.charRange(charsetStart, rangeStart, _, _, rangeEnd) and + rangeStart <= start and + rangeEnd >= end + ) + or + this.charRange(charsetStart, start, _, _, end) + } + + /** + * Holds if the character set starting at `charsetStart` contains a character range + * with lower bound found between `start` and `lowerEnd` + * and upper bound found between `upperStart` and `end`. + */ + predicate charRange(int charsetStart, int start, int lowerEnd, int upperStart, int end) { + exists(int index | + this.charRangeEnd(charsetStart, index) = true and + this.charSetToken(charsetStart, index - 2, start, lowerEnd) and + this.charSetToken(charsetStart, index, upperStart, end) + ) + } + + /** + * Helper predicate for `charRange`. + * We can determine where character ranges end by a left to right sweep. + * + * To avoid negative recursion we return a boolean. See `escaping`, + * the helper for `escapingChar`, for a clean use of this pattern. + */ + private boolean charRangeEnd(int charsetStart, int index) { + this.charSetToken(charsetStart, index, _, _) and + ( + index in [1, 2] and result = false + or + index > 2 and + exists(int connectorStart | + this.charSetToken(charsetStart, index - 1, connectorStart, _) and + this.nonEscapedCharAt(connectorStart) = "-" and + result = + this.charRangeEnd(charsetStart, index - 2) + .booleanNot() + .booleanAnd(this.charRangeEnd(charsetStart, index - 1).booleanNot()) + ) + or + not exists(int connectorStart | + this.charSetToken(charsetStart, index - 1, connectorStart, _) and + this.nonEscapedCharAt(connectorStart) = "-" + ) and + result = false + ) + } + + /** Holds if the character at `pos` is a "\" that is actually escaping what comes after. */ + predicate escapingChar(int pos) { this.escaping(pos) = true } + + /** + * Helper predicate for `escapingChar`. + * In order to avoid negative recursion, we return a boolean. + * This way, we can refer to `escaping(pos - 1).booleanNot()` + * rather than to a negated version of `escaping(pos)`. + */ + private boolean escaping(int pos) { + pos = -1 and result = false + or + this.getChar(pos) = "\\" and result = this.escaping(pos - 1).booleanNot() + or + this.getChar(pos) != "\\" and result = false + } + + /** + * Gets the text of this regex. + */ + string getText() { result = this.(StringLiteralExpr).getValue() } + + /** Gets the `i`th character of this regex */ + string getChar(int i) { result = this.getText().charAt(i) } + + /** Gets the `i`th character of this regex, unless it is part of a character escape sequence. */ + string nonEscapedCharAt(int i) { + result = this.getText().charAt(i) and + not exists(int x, int y | this.escapedCharacter(x, y) and i in [x .. y - 1]) + } + + private predicate isOptionDivider(int i) { this.nonEscapedCharAt(i) = "|" } + + private predicate isGroupEnd(int i) { this.nonEscapedCharAt(i) = ")" and not this.inCharSet(i) } + + private predicate isGroupStart(int i) { this.nonEscapedCharAt(i) = "(" and not this.inCharSet(i) } + + /** + * Holds if the `i`th character could not be parsed. + */ + predicate failedToParse(int i) { + exists(this.getChar(i)) and + not exists(int start, int end | + this.topLevel(start, end) and + start <= i and + end > i + ) + } + + /** Matches named character properties such as `\p{Word}` and `[[:digit:]]` */ + predicate namedCharacterProperty(int start, int end, string name) { + this.pStyleNamedCharacterProperty(start, end, name) or + this.posixStyleNamedCharacterProperty(start, end, name) + } + + /** Gets the name of the character property in start,end */ + string getCharacterPropertyName(int start, int end) { + this.namedCharacterProperty(start, end, result) + } + + /** Matches a POSIX bracket expression such as `[:alnum:]` within a character class. */ + private predicate posixStyleNamedCharacterProperty(int start, int end, string name) { + this.getChar(start) = "[" and + this.getChar(start + 1) = ":" and + end = + min(int e | + e > start and + this.getChar(e - 2) = ":" and + this.getChar(e - 1) = "]" + | + e + ) and + exists(int nameStart | + this.getChar(start + 2) = "^" and nameStart = start + 3 + or + not this.getChar(start + 2) = "^" and nameStart = start + 2 + | + name = this.getText().substring(nameStart, end - 2) + ) + } + + /** + * Matches named character properties. For example: + * - `\p{Space}` + * - `\P{Digit}` upper-case P means inverted + * - `\p{^Word}` caret also means inverted (not supported in Swift `Regex` but it may be in + * other regex parsers or in future versions of Swift). + * + * These can occur both inside and outside of character classes. + */ + private predicate pStyleNamedCharacterProperty(int start, int end, string name) { + this.escapingChar(start) and + this.getChar(start + 1) in ["p", "P"] and + this.getChar(start + 2) = "{" and + this.getChar(end - 1) = "}" and + end > start and + not exists(int i | start + 2 < i and i < end - 1 | this.getChar(i) = "}") and + exists(int nameStart | + this.getChar(start + 3) = "^" and nameStart = start + 4 + or + not this.getChar(start + 3) = "^" and nameStart = start + 3 + | + name = this.getText().substring(nameStart, end - 1) + ) + } + + /** + * Holds if the named character property is inverted. Examples for which it holds: + * - `\P{Digit}` upper-case P means inverted + * - `\p{^Word}` caret also means inverted (not supported in Swift `Regex` but it may be in + * other regex parsers or in future versions of Swift). + * - `[[:^digit:]]` + * + * Examples for which it doesn't hold: + * - `\p{Word}` + * - `\P{^Space}` - upper-case P and caret cancel each other out + * - `[[:alnum:]]` + */ + predicate namedCharacterPropertyIsInverted(int start, int end) { + this.pStyleNamedCharacterProperty(start, end, _) and + exists(boolean upperP, boolean caret | + (if this.getChar(start + 1) = "P" then upperP = true else upperP = false) and + (if this.getChar(start + 3) = "^" then caret = true else caret = false) + | + upperP.booleanXor(caret) = true + ) + or + this.posixStyleNamedCharacterProperty(start, end, _) and + this.getChar(start + 3) = "^" + } + + /** + * Holds if an escaped character is found between `start` and `end`. + * Escaped characters include hex values, octal values and named escapes, + * but excludes backreferences. + */ + predicate escapedCharacter(int start, int end) { + this.escapingChar(start) and + not this.numberedBackreference(start, _, _) and + not this.namedBackreference(start, _, _) and + not this.pStyleNamedCharacterProperty(start, _, _) and + ( + // hex char \xhh + this.getChar(start + 1) = "x" and end = start + 4 + or + // wide hex char \uhhhh + this.getChar(start + 1) = "u" and end = start + 6 + or + // wide hex char \Uhhhhhhhh + this.getChar(start + 1) = "U" and end = start + 10 + or + // variable width hex char \x{hh...} or \u{hh...} (1-6 digits) + this.getChar(start + 1) = ["x", "u"] and + this.getChar(start + 2) = "{" and + this.getChar(end - 1) = "}" and + end > start and + end <= start + 10 and + not exists(int i | start + 2 < i and i < end - 1 | this.getChar(i) = "}") + or + // escape not handled above; update when adding a new case + not this.getChar(start + 1) in ["x", "u", "U"] and + not exists(this.getChar(start + 1).toInt()) and + end = start + 2 + ) + } + + /** + * Holds if the character at `index` is inside a character set. + */ + predicate inCharSet(int index) { + exists(int x, int y | this.charSet(x, y) and index in [x + 1 .. y - 2]) + } + + /** + * Holds if the character at `index` is inside a posix bracket. + */ + predicate inPosixBracket(int index) { + exists(int x, int y | + this.posixStyleNamedCharacterProperty(x, y, _) and index in [x + 1 .. y - 2] + ) + } + + /** + * 'simple' characters are any that don't alter the parsing of the regex. + */ + private predicate simpleCharacter(int start, int end) { + end = start + 1 and + not this.charSet(start, _) and + not this.charSet(_, start + 1) and + not exists(int x, int y | + this.posixStyleNamedCharacterProperty(x, y, _) and + start >= x and + end <= y + ) and + exists(string c | c = this.getChar(start) | + exists(int x, int y, int z | + this.charSet(x, z) and + this.charSetStart(x, y) + | + start = y + or + start = z - 2 + or + start > y and start < z - 2 and not this.charRange(_, _, start, end, _) + ) + or + not this.inCharSet(start) and + not c = "(" and + not c = "[" and + not c = ")" and + not c = "|" and + not this.qualifier(start, _, _, _) + ) + } + + /** + * Holds if a simple or escaped character is found between `start` and `end`. + */ + predicate character(int start, int end) { + ( + this.simpleCharacter(start, end) and + not exists(int x, int y | this.escapedCharacter(x, y) and x <= start and y >= end) + or + this.escapedCharacter(start, end) + ) and + not exists(int x, int y | this.groupStart(x, y) and x <= start and y >= end) and + not exists(int x, int y | this.backreference(x, y) and x <= start and y >= end) and + not exists(int x, int y | + this.pStyleNamedCharacterProperty(x, y, _) and x <= start and y >= end + ) and + not exists(int x, int y | this.multiples(x, y, _, _) and x <= start and y >= end) + } + + /** + * Holds if a normal character is found between `start` and `end`. + */ + predicate normalCharacter(int start, int end) { + end = start + 1 and + this.character(start, end) and + not this.specialCharacter(start, end, _) + } + + /** + * Holds if a special character is found between `start` and `end`. + */ + predicate specialCharacter(int start, int end, string char) { + this.character(start, end) and + not this.inCharSet(start) and + ( + end = start + 1 and + char = this.getChar(start) and + (char = "$" or char = "^" or char = ".") + or + end = start + 2 and + this.escapingChar(start) and + char = this.getText().substring(start, end) and + char = ["\\A", "\\Z", "\\z", "\\G", "\\b", "\\B"] + ) + } + + /** + * Holds if the range [start:end) consists of only 'normal' characters. + */ + predicate normalCharacterSequence(int start, int end) { + // a normal character inside a character set is interpreted on its own + this.normalCharacter(start, end) and + this.inCharSet(start) + or + // a maximal run of normal characters is considered as one constant + exists(int s, int e | + e = max(int i | this.normalCharacterRun(s, i)) and + not this.inCharSet(s) + | + // 'abc' can be considered one constant, but + // 'abc+' has to be broken up into 'ab' and 'c+', + // as the qualifier only applies to 'c'. + if this.qualifier(e, _, _, _) + then + end = e and start = e - 1 + or + end = e - 1 and start = s and start < end + else ( + end = e and + start = s + ) + ) + } + + private predicate normalCharacterRun(int start, int end) { + ( + this.normalCharacterRun(start, end - 1) + or + start = end - 1 and not this.normalCharacter(start - 1, start) + ) and + this.normalCharacter(end - 1, end) + } + + private predicate characterItem(int start, int end) { + this.normalCharacterSequence(start, end) or + this.escapedCharacter(start, end) or + this.specialCharacter(start, end, _) + } + + /** Whether the text in the range `start,end` is a group */ + predicate group(int start, int end) { + this.groupContents(start, end, _, _) + or + this.emptyGroup(start, end) + } + + /** Gets the number of the group in start,end */ + int getGroupNumber(int start, int end) { + this.group(start, end) and + not this.nonCapturingGroupStart(start, _) and + result = + count(int i | this.group(i, _) and i < start and not this.nonCapturingGroupStart(i, _)) + 1 + } + + /** Gets the name, if it has one, of the group in start,end */ + string getGroupName(int start, int end) { + this.group(start, end) and + exists(int nameEnd | + this.namedGroupStart(start, nameEnd) and + result = this.getText().substring(start + 3, nameEnd - 1) + ) + } + + /** Whether the text in the range start, end is a group and can match the empty string. */ + predicate zeroWidthMatch(int start, int end) { + this.emptyGroup(start, end) + or + this.negativeAssertionGroup(start, end) + or + this.positiveLookaheadAssertionGroup(start, end) + or + this.positiveLookbehindAssertionGroup(start, end) + } + + /** Holds if an empty group is found between `start` and `end`. */ + predicate emptyGroup(int start, int end) { + exists(int endm1 | end = endm1 + 1 | + this.groupStart(start, endm1) and + this.isGroupEnd(endm1) + ) + } + + private predicate emptyMatchAtStartGroup(int start, int end) { + this.emptyGroup(start, end) + or + this.negativeAssertionGroup(start, end) + or + this.positiveLookaheadAssertionGroup(start, end) + } + + private predicate emptyMatchAtEndGroup(int start, int end) { + this.emptyGroup(start, end) + or + this.negativeAssertionGroup(start, end) + or + this.positiveLookbehindAssertionGroup(start, end) + } + + private predicate negativeAssertionGroup(int start, int end) { + exists(int inStart | + this.negativeLookaheadAssertionStart(start, inStart) + or + this.negativeLookbehindAssertionStart(start, inStart) + | + this.groupContents(start, end, inStart, _) + ) + } + + /** Holds if a negative lookahead is found between `start` and `end` */ + predicate negativeLookaheadAssertionGroup(int start, int end) { + exists(int inStart | this.negativeLookaheadAssertionStart(start, inStart) | + this.groupContents(start, end, inStart, _) + ) + } + + /** Holds if a negative lookbehind is found between `start` and `end` */ + predicate negativeLookbehindAssertionGroup(int start, int end) { + exists(int inStart | this.negativeLookbehindAssertionStart(start, inStart) | + this.groupContents(start, end, inStart, _) + ) + } + + /** Holds if a positive lookahead is found between `start` and `end` */ + predicate positiveLookaheadAssertionGroup(int start, int end) { + exists(int inStart | this.lookaheadAssertionStart(start, inStart) | + this.groupContents(start, end, inStart, _) + ) + } + + /** Holds if a positive lookbehind is found between `start` and `end` */ + predicate positiveLookbehindAssertionGroup(int start, int end) { + exists(int inStart | this.lookbehindAssertionStart(start, inStart) | + this.groupContents(start, end, inStart, _) + ) + } + + private predicate groupStart(int start, int end) { + this.nonCapturingGroupStart(start, end) + or + this.namedGroupStart(start, end) + or + this.lookaheadAssertionStart(start, end) + or + this.negativeLookaheadAssertionStart(start, end) + or + this.lookbehindAssertionStart(start, end) + or + this.negativeLookbehindAssertionStart(start, end) + or + this.commentGroupStart(start, end) + or + this.simpleGroupStart(start, end) + } + + /** Matches the start of a non-capturing group, e.g. `(?:` */ + private predicate nonCapturingGroupStart(int start, int end) { + this.isGroupStart(start) and + this.getChar(start + 1) = "?" and + this.getChar(start + 2) = [":", "=", "<", "!", "#"] and + end = start + 3 + } + + /** Matches the start of a simple group, e.g. `(a+)`. */ + private predicate simpleGroupStart(int start, int end) { + this.isGroupStart(start) and + this.getChar(start + 1) != "?" and + end = start + 1 + } + + /** + * Matches the start of a named group, such as: + * - `(?\w+)` + * - `(?'name'\w+)` + */ + private predicate namedGroupStart(int start, int end) { + this.isGroupStart(start) and + this.getChar(start + 1) = "?" and + ( + this.getChar(start + 2) = "<" and + not this.getChar(start + 3) = "=" and // (?<=foo) is a positive lookbehind assertion + not this.getChar(start + 3) = "!" and // (? start + 3 and this.getChar(i) = ">") and + end = nameEnd + 1 + ) + or + this.getChar(start + 2) = "'" and + exists(int nameEnd | + nameEnd = min(int i | i > start + 2 and this.getChar(i) = "'") and end = nameEnd + 1 + ) + ) + } + + /** Matches the start of a positive lookahead assertion, i.e. `(?=`. */ + private predicate lookaheadAssertionStart(int start, int end) { + this.isGroupStart(start) and + this.getChar(start + 1) = "?" and + this.getChar(start + 2) = "=" and + end = start + 3 + } + + /** Matches the start of a negative lookahead assertion, i.e. `(?!`. */ + private predicate negativeLookaheadAssertionStart(int start, int end) { + this.isGroupStart(start) and + this.getChar(start + 1) = "?" and + this.getChar(start + 2) = "!" and + end = start + 3 + } + + /** Matches the start of a positive lookbehind assertion, i.e. `(?<=`. */ + private predicate lookbehindAssertionStart(int start, int end) { + this.isGroupStart(start) and + this.getChar(start + 1) = "?" and + this.getChar(start + 2) = "<" and + this.getChar(start + 3) = "=" and + end = start + 4 + } + + /** Matches the start of a negative lookbehind assertion, i.e. `(?`. */ + predicate namedBackreference(int start, int end, string name) { + this.escapingChar(start) and + this.getChar(start + 1) = "k" and + this.getChar(start + 2) = "<" and + exists(int nameEnd | nameEnd = min(int i | i > start + 3 and this.getChar(i) = ">") | + end = nameEnd + 1 and + name = this.getText().substring(start + 3, nameEnd) + ) + } + + /** Matches a numbered backreference, e.g. `\1`. */ + predicate numberedBackreference(int start, int end, int value) { + this.escapingChar(start) and + not this.getChar(start + 1) = "0" and + exists(string text, string svalue, int len | + end = start + len and + text = this.getText() and + len in [2 .. 3] + | + svalue = text.substring(start + 1, start + len) and + value = svalue.toInt() and + not exists(text.substring(start + 1, start + len + 1).toInt()) and + value > 0 + ) + } + + /** Whether the text in the range `start,end` is a back reference */ + predicate backreference(int start, int end) { + this.numberedBackreference(start, end, _) + or + this.namedBackreference(start, end, _) + } + + /** Gets the number of the back reference in start,end */ + int getBackRefNumber(int start, int end) { this.numberedBackreference(start, end, result) } + + /** Gets the name, if it has one, of the back reference in start,end */ + string getBackRefName(int start, int end) { this.namedBackreference(start, end, result) } + + private predicate baseItem(int start, int end) { + this.characterItem(start, end) and + not exists(int x, int y | this.charSet(x, y) and x <= start and y >= end) + or + this.group(start, end) + or + this.charSet(start, end) + or + this.backreference(start, end) + or + this.pStyleNamedCharacterProperty(start, end, _) + } + + private predicate qualifier(int start, int end, boolean maybeEmpty, boolean mayRepeatForever) { + this.shortQualifier(start, end, maybeEmpty, mayRepeatForever) and + not this.getChar(end) = "?" + or + exists(int shortEnd | this.shortQualifier(start, shortEnd, maybeEmpty, mayRepeatForever) | + if this.getChar(shortEnd) = "?" then end = shortEnd + 1 else end = shortEnd + ) + } + + private predicate shortQualifier(int start, int end, boolean maybeEmpty, boolean mayRepeatForever) { + ( + this.getChar(start) = "+" and maybeEmpty = false and mayRepeatForever = true + or + this.getChar(start) = "*" and maybeEmpty = true and mayRepeatForever = true + or + this.getChar(start) = "?" and maybeEmpty = true and mayRepeatForever = false + ) and + end = start + 1 + or + exists(string lower, string upper | + this.multiples(start, end, lower, upper) and + (if lower = "" or lower.toInt() = 0 then maybeEmpty = true else maybeEmpty = false) and + if upper = "" then mayRepeatForever = true else mayRepeatForever = false + ) + } + + /** + * Holds if a repetition quantifier is found between `start` and `end`, + * with the given lower and upper bounds. If a bound is omitted, the corresponding + * string is empty. + */ + predicate multiples(int start, int end, string lower, string upper) { + exists(string text, string match, string inner | + text = this.getText() and + end = start + match.length() and + inner = match.substring(1, match.length() - 1) + | + match = text.regexpFind("\\{[0-9]+\\}", _, start) and + lower = inner and + upper = lower + or + match = text.regexpFind("\\{[0-9]*,[0-9]*\\}", _, start) and + exists(int commaIndex | + commaIndex = inner.indexOf(",") and + lower = inner.prefix(commaIndex) and + upper = inner.suffix(commaIndex + 1) + ) + ) + } + + /** + * Whether the text in the range start,end is a qualified item, where item is a character, + * a character set or a group. + */ + predicate qualifiedItem(int start, int end, boolean maybeEmpty, boolean mayRepeatForever) { + this.qualifiedPart(start, _, end, maybeEmpty, mayRepeatForever) + } + + /** + * Holds if a qualified part is found between `start` and `partEnd` and the qualifier is + * found between `partEnd` and `end`. + * + * `maybeEmpty` is true if the part is optional. + * `mayRepeatForever` is true if the part may be repeated unboundedly. + */ + predicate qualifiedPart( + int start, int partEnd, int end, boolean maybeEmpty, boolean mayRepeatForever + ) { + this.baseItem(start, partEnd) and + this.qualifier(partEnd, end, maybeEmpty, mayRepeatForever) + } + + /** Holds if the range `start`, `end` contains a character, a quantifier, a character set or a group. */ + predicate item(int start, int end) { + this.qualifiedItem(start, end, _, _) + or + this.baseItem(start, end) and not this.qualifier(end, _, _, _) + } + + private predicate subsequence(int start, int end) { + ( + start = 0 or + this.groupStart(_, start) or + this.isOptionDivider(start - 1) + ) and + this.item(start, end) + or + exists(int mid | + this.subsequence(start, mid) and + this.item(mid, end) + ) + } + + /** + * Whether the text in the range start,end is a sequence of 1 or more items, where an item is a character, + * a character set or a group. + */ + predicate sequence(int start, int end) { + this.sequenceOrQualified(start, end) and + not this.qualifiedItem(start, end, _, _) + } + + private predicate sequenceOrQualified(int start, int end) { + this.subsequence(start, end) and + not this.itemStart(end) + } + + private predicate itemStart(int start) { + this.characterItem(start, _) or + this.isGroupStart(start) or + this.charSet(start, _) or + this.backreference(start, _) or + this.namedCharacterProperty(start, _, _) + } + + private predicate itemEnd(int end) { + this.characterItem(_, end) + or + exists(int endm1 | this.isGroupEnd(endm1) and end = endm1 + 1) + or + this.charSet(_, end) + or + this.qualifier(_, end, _, _) + } + + private predicate topLevel(int start, int end) { + this.subalternation(start, end, _) and + not this.isOptionDivider(end) + } + + private predicate subalternation(int start, int end, int itemStart) { + this.sequenceOrQualified(start, end) and + not this.isOptionDivider(start - 1) and + itemStart = start + or + start = end and + not this.itemEnd(start) and + this.isOptionDivider(end) and + itemStart = start + or + exists(int mid | + this.subalternation(start, mid, _) and + this.isOptionDivider(mid) and + itemStart = mid + 1 + | + this.sequenceOrQualified(itemStart, end) + or + not this.itemStart(end) and end = itemStart + ) + } + + /** + * Whether the text in the range start,end is an alternation + */ + predicate alternation(int start, int end) { + not this.inCharSet(start) and + this.topLevel(start, end) and + exists(int less | this.subalternation(start, less, _) and less < end) + } + + /** + * Whether the text in the range start,end is an alternation and the text in partStart, partEnd is one of the + * options in that alternation. + */ + predicate alternationOption(int start, int end, int partStart, int partEnd) { + this.alternation(start, end) and + this.subalternation(start, partEnd, partStart) + } + + /** A part of the regex that may match the start of the string. */ + private predicate firstPart(int start, int end) { + start = 0 and end = this.getText().length() + or + exists(int x | this.firstPart(x, end) | + this.emptyMatchAtStartGroup(x, start) + or + this.qualifiedItem(x, start, true, _) + or + // ^ and \A match the start of the string + this.specialCharacter(x, start, ["^", "\\A"]) + ) + or + exists(int y | this.firstPart(start, y) | + this.item(start, end) + or + this.qualifiedPart(start, end, y, _, _) + ) + or + exists(int x, int y | this.firstPart(x, y) | + this.groupContents(x, y, start, end) + or + this.alternationOption(x, y, start, end) + ) + } + + /** A part of the regex that may match the end of the string. */ + private predicate lastPart(int start, int end) { + start = 0 and end = this.getText().length() + or + exists(int y | this.lastPart(start, y) | + this.emptyMatchAtEndGroup(end, y) + or + this.qualifiedItem(end, y, true, _) + or + // $, \Z, and \z match the end of the string. + this.specialCharacter(end, y, ["$", "\\Z", "\\z"]) + ) + or + this.lastPart(_, end) and + this.item(start, end) + or + exists(int y | this.lastPart(start, y) | this.qualifiedPart(start, end, y, _, _)) + or + exists(int x, int y | this.lastPart(x, y) | + this.groupContents(x, y, start, end) + or + this.alternationOption(x, y, start, end) + ) + } + + /** + * Whether the item at [start, end) is one of the first items + * to be matched. + */ + predicate firstItem(int start, int end) { + ( + this.characterItem(start, end) + or + this.qualifiedItem(start, end, _, _) + or + this.charSet(start, end) + ) and + this.firstPart(start, end) + } + + /** + * Whether the item at [start, end) is one of the last items + * to be matched. + */ + predicate lastItem(int start, int end) { + ( + this.characterItem(start, end) + or + this.qualifiedItem(start, end, _, _) + or + this.charSet(start, end) + ) and + this.lastPart(start, end) + } +} diff --git a/swift/ql/lib/qlpack.yml b/swift/ql/lib/qlpack.yml index f45d347bad3..930916c2347 100644 --- a/swift/ql/lib/qlpack.yml +++ b/swift/ql/lib/qlpack.yml @@ -1,11 +1,13 @@ name: codeql/swift-all -version: 0.1.0 +version: 0.1.2-dev groups: swift extractor: swift dbscheme: swift.dbscheme upgrades: upgrades library: true dependencies: + codeql/regex: ${workspace} + codeql/mad: ${workspace} codeql/ssa: ${workspace} codeql/tutorial: ${workspace} codeql/util: ${workspace} diff --git a/swift/ql/src/change-notes/2023-05-25-string-length-conflation-fp.md b/swift/ql/src/CHANGELOG.md similarity index 72% rename from swift/ql/src/change-notes/2023-05-25-string-length-conflation-fp.md rename to swift/ql/src/CHANGELOG.md index 7166b5e9ed7..a682b626bb8 100644 --- a/swift/ql/src/change-notes/2023-05-25-string-length-conflation-fp.md +++ b/swift/ql/src/CHANGELOG.md @@ -1,4 +1,5 @@ ---- -category: minorAnalysis ---- +## 0.1.1 + +### Minor Analysis Improvements + * Fixed some false positive results from the `swift/string-length-conflation` query, caused by imprecise sinks. diff --git a/swift/ql/src/change-notes/2023-06-22-hide-summarized-nodes.md b/swift/ql/src/change-notes/2023-06-22-hide-summarized-nodes.md new file mode 100644 index 00000000000..3c192330ee4 --- /dev/null +++ b/swift/ql/src/change-notes/2023-06-22-hide-summarized-nodes.md @@ -0,0 +1,4 @@ +--- +category: fix +--- +* Functions and methods modeled as flow summaries are no longer shown in the path of `path-problem` queries. This results in more succinct paths for most security queries. diff --git a/swift/ql/src/change-notes/released/0.1.1.md b/swift/ql/src/change-notes/released/0.1.1.md new file mode 100644 index 00000000000..a682b626bb8 --- /dev/null +++ b/swift/ql/src/change-notes/released/0.1.1.md @@ -0,0 +1,5 @@ +## 0.1.1 + +### Minor Analysis Improvements + +* Fixed some false positive results from the `swift/string-length-conflation` query, caused by imprecise sinks. diff --git a/swift/ql/src/codeql-pack.release.yml b/swift/ql/src/codeql-pack.release.yml new file mode 100644 index 00000000000..92d1505475f --- /dev/null +++ b/swift/ql/src/codeql-pack.release.yml @@ -0,0 +1,2 @@ +--- +lastReleaseVersion: 0.1.1 diff --git a/swift/ql/src/qlpack.yml b/swift/ql/src/qlpack.yml index 2a0bee06578..9fac028053b 100644 --- a/swift/ql/src/qlpack.yml +++ b/swift/ql/src/qlpack.yml @@ -1,5 +1,5 @@ name: codeql/swift-queries -version: 0.1.0 +version: 0.1.2-dev groups: - swift - queries diff --git a/swift/ql/src/queries/Summary/SummaryStats.ql b/swift/ql/src/queries/Summary/SummaryStats.ql index 30122e666e3..49710791af8 100644 --- a/swift/ql/src/queries/Summary/SummaryStats.ql +++ b/swift/ql/src/queries/Summary/SummaryStats.ql @@ -11,6 +11,7 @@ import codeql.swift.dataflow.FlowSources import codeql.swift.security.SensitiveExprs import codeql.swift.dataflow.DataFlow import codeql.swift.dataflow.TaintTracking +import codeql.swift.regex.Regex /** * A taint configuration for tainted data reaching any node. @@ -56,6 +57,8 @@ predicate statistic(string what, string value) { what = "Dataflow nodes (tainted)" and value = taintedNodesCount().toString() or what = "Taint reach (per million nodes)" and value = taintReach().toString() + or + what = "Regular expression evals" and value = count(RegexEval e).toString() } from string what, string value diff --git a/swift/ql/test/extractor-tests/declarations/CONSISTENCY/CfgConsistency.expected b/swift/ql/test/extractor-tests/declarations/CONSISTENCY/CfgConsistency.expected deleted file mode 100644 index 9d02611e7a7..00000000000 --- a/swift/ql/test/extractor-tests/declarations/CONSISTENCY/CfgConsistency.expected +++ /dev/null @@ -1,6 +0,0 @@ -deadEnd -| file://:0:0:0:0 | ... = ... | -| file://:0:0:0:0 | ... = ... | -| file://:0:0:0:0 | ... = ... | -| file://:0:0:0:0 | ... = ... | -| file://:0:0:0:0 | ... = ... | diff --git a/swift/ql/test/extractor-tests/expressions/CONSISTENCY/CfgConsistency.expected b/swift/ql/test/extractor-tests/expressions/CONSISTENCY/CfgConsistency.expected deleted file mode 100644 index 069a6ecbc6a..00000000000 --- a/swift/ql/test/extractor-tests/expressions/CONSISTENCY/CfgConsistency.expected +++ /dev/null @@ -1,2 +0,0 @@ -deadEnd -| file://:0:0:0:0 | ... = ... | diff --git a/swift/ql/test/extractor-tests/generated/decl/EnumDecl/CONSISTENCY/CfgConsistency.expected b/swift/ql/test/extractor-tests/generated/decl/EnumDecl/CONSISTENCY/CfgConsistency.expected deleted file mode 100644 index 9d02611e7a7..00000000000 --- a/swift/ql/test/extractor-tests/generated/decl/EnumDecl/CONSISTENCY/CfgConsistency.expected +++ /dev/null @@ -1,6 +0,0 @@ -deadEnd -| file://:0:0:0:0 | ... = ... | -| file://:0:0:0:0 | ... = ... | -| file://:0:0:0:0 | ... = ... | -| file://:0:0:0:0 | ... = ... | -| file://:0:0:0:0 | ... = ... | diff --git a/swift/ql/test/extractor-tests/statements/CONSISTENCY/CfgConsistency.expected b/swift/ql/test/extractor-tests/statements/CONSISTENCY/CfgConsistency.expected deleted file mode 100644 index 069a6ecbc6a..00000000000 --- a/swift/ql/test/extractor-tests/statements/CONSISTENCY/CfgConsistency.expected +++ /dev/null @@ -1,2 +0,0 @@ -deadEnd -| file://:0:0:0:0 | ... = ... | diff --git a/swift/ql/test/library-tests/ast/CONSISTENCY/CfgConsistency.expected b/swift/ql/test/library-tests/ast/CONSISTENCY/CfgConsistency.expected index fdfaa9f18cd..9c49013832a 100644 --- a/swift/ql/test/library-tests/ast/CONSISTENCY/CfgConsistency.expected +++ b/swift/ql/test/library-tests/ast/CONSISTENCY/CfgConsistency.expected @@ -8,13 +8,4 @@ multipleSuccessors deadEnd | cfg.swift:33:49:33:60 | call to isZero(x:) | | cfg.swift:144:18:144:34 | ... .&&(_:_:) ... | -| file://:0:0:0:0 | ... = ... | -| file://:0:0:0:0 | ... = ... | -| file://:0:0:0:0 | ... = ... | -| file://:0:0:0:0 | ... = ... | -| file://:0:0:0:0 | ... = ... | -| file://:0:0:0:0 | ... = ... | -| file://:0:0:0:0 | ... = ... | -| file://:0:0:0:0 | ... = ... | -| file://:0:0:0:0 | ... = ... | | patterns.swift:16:10:16:14 | =~ ... | diff --git a/swift/ql/test/library-tests/ast/Errors.expected b/swift/ql/test/library-tests/ast/Errors.expected index e289df1fc15..a65eb1b2466 100644 --- a/swift/ql/test/library-tests/ast/Errors.expected +++ b/swift/ql/test/library-tests/ast/Errors.expected @@ -1,5 +1 @@ -| file://:0:0:0:0 | ... .combine(_:) | UnresolvedDotExpr | -| file://:0:0:0:0 | ... .combine(_:) | UnresolvedDotExpr | -| file://:0:0:0:0 | ... .combine(_:) | UnresolvedDotExpr | -| file://:0:0:0:0 | ... .combine(_:) | UnresolvedDotExpr | | patterns.swift:16:12:16:12 | OverloadedDeclRefExpr | OverloadedDeclRefExpr | diff --git a/swift/ql/test/library-tests/ast/PrintAst.expected b/swift/ql/test/library-tests/ast/PrintAst.expected index cb7091193f1..39320293cbc 100644 --- a/swift/ql/test/library-tests/ast/PrintAst.expected +++ b/swift/ql/test/library-tests/ast/PrintAst.expected @@ -2795,11 +2795,14 @@ cfg.swift: #-----| getSource(): [IntegerLiteralExpr] 1 #-----| getLabel(0): [CaseLabelItem] .B #-----| getPattern(): [EnumElementPattern] .B -#-----| getElement(2): [CallExpr] call to ... -#-----| getFunction(): [UnresolvedDotExpr] ... .combine(_:) -#-----| getBase(): [DeclRefExpr] hasher +#-----| getElement(2): [CallExpr] call to combine(_:) +#-----| getFunction(): [MethodLookupExpr] .combine(_:) +#-----| getBase(): [InOutExpr] &... +#-----| getSubExpr(): [DeclRefExpr] hasher +#-----| getMethodRef(): [DeclRefExpr] combine(_:) #-----| getArgument(0): [Argument] : discriminator #-----| getExpr(): [DeclRefExpr] discriminator +#-----| getExpr().getFullyConverted(): [LoadExpr] (Int) ... #-----| getMember(7): [ConcreteVarDecl] hashValue #-----| Type = Int #-----| getAccessor(0): [Accessor] get @@ -3482,11 +3485,14 @@ declarations.swift: #-----| getSource(): [IntegerLiteralExpr] 4 #-----| getLabel(0): [CaseLabelItem] .value5 #-----| getPattern(): [EnumElementPattern] .value5 -#-----| getElement(2): [CallExpr] call to ... -#-----| getFunction(): [UnresolvedDotExpr] ... .combine(_:) -#-----| getBase(): [DeclRefExpr] hasher +#-----| getElement(2): [CallExpr] call to combine(_:) +#-----| getFunction(): [MethodLookupExpr] .combine(_:) +#-----| getBase(): [InOutExpr] &... +#-----| getSubExpr(): [DeclRefExpr] hasher +#-----| getMethodRef(): [DeclRefExpr] combine(_:) #-----| getArgument(0): [Argument] : discriminator #-----| getExpr(): [DeclRefExpr] discriminator +#-----| getExpr().getFullyConverted(): [LoadExpr] (Int) ... #-----| getMember(10): [ConcreteVarDecl] hashValue #-----| Type = Int #-----| getAccessor(0): [Accessor] get @@ -4394,11 +4400,14 @@ expressions.swift: #-----| getSource(): [IntegerLiteralExpr] 0 #-----| getLabel(0): [CaseLabelItem] .failed #-----| getPattern(): [EnumElementPattern] .failed -#-----| getElement(2): [CallExpr] call to ... -#-----| getFunction(): [UnresolvedDotExpr] ... .combine(_:) -#-----| getBase(): [DeclRefExpr] hasher +#-----| getElement(2): [CallExpr] call to combine(_:) +#-----| getFunction(): [MethodLookupExpr] .combine(_:) +#-----| getBase(): [InOutExpr] &... +#-----| getSubExpr(): [DeclRefExpr] hasher +#-----| getMethodRef(): [DeclRefExpr] combine(_:) #-----| getArgument(0): [Argument] : discriminator #-----| getExpr(): [DeclRefExpr] discriminator +#-----| getExpr().getFullyConverted(): [LoadExpr] (Int) ... #-----| getMember(5): [ConcreteVarDecl] hashValue #-----| Type = Int #-----| getAccessor(0): [Accessor] get @@ -6656,11 +6665,14 @@ statements.swift: #-----| getSource(): [IntegerLiteralExpr] 0 #-----| getLabel(0): [CaseLabelItem] .failed #-----| getPattern(): [EnumElementPattern] .failed -#-----| getElement(2): [CallExpr] call to ... -#-----| getFunction(): [UnresolvedDotExpr] ... .combine(_:) -#-----| getBase(): [DeclRefExpr] hasher +#-----| getElement(2): [CallExpr] call to combine(_:) +#-----| getFunction(): [MethodLookupExpr] .combine(_:) +#-----| getBase(): [InOutExpr] &... +#-----| getSubExpr(): [DeclRefExpr] hasher +#-----| getMethodRef(): [DeclRefExpr] combine(_:) #-----| getArgument(0): [Argument] : discriminator #-----| getExpr(): [DeclRefExpr] discriminator +#-----| getExpr().getFullyConverted(): [LoadExpr] (Int) ... #-----| getMember(5): [ConcreteVarDecl] hashValue #-----| Type = Int #-----| getAccessor(0): [Accessor] get diff --git a/swift/ql/test/library-tests/controlflow/graph/CONSISTENCY/CfgConsistency.expected b/swift/ql/test/library-tests/controlflow/graph/CONSISTENCY/CfgConsistency.expected index 0c4047134f3..81ad590aaab 100644 --- a/swift/ql/test/library-tests/controlflow/graph/CONSISTENCY/CfgConsistency.expected +++ b/swift/ql/test/library-tests/controlflow/graph/CONSISTENCY/CfgConsistency.expected @@ -8,5 +8,3 @@ multipleSuccessors deadEnd | cfg.swift:33:49:33:60 | call to isZero(x:) | | cfg.swift:144:18:144:34 | ... .&&(_:_:) ... | -| file://:0:0:0:0 | ... = ... | -| file://:0:0:0:0 | ... = ... | diff --git a/swift/ql/test/library-tests/dataflow/dataflow/DataFlow.expected b/swift/ql/test/library-tests/dataflow/dataflow/DataFlow.expected index b725e5e7298..bebfd102871 100644 --- a/swift/ql/test/library-tests/dataflow/dataflow/DataFlow.expected +++ b/swift/ql/test/library-tests/dataflow/dataflow/DataFlow.expected @@ -1,6 +1,4 @@ edges -| file://:0:0:0:0 | [summary param] this in signum() | file://:0:0:0:0 | [summary] to write: return (return) in signum() | -| file://:0:0:0:0 | [summary param] this in signum() [some:0] | file://:0:0:0:0 | [summary] to write: return (return) in signum() [some:0] | | file://:0:0:0:0 | self [a, x] | file://:0:0:0:0 | .a [x] | | file://:0:0:0:0 | self [str] | file://:0:0:0:0 | .str | | file://:0:0:0:0 | self [x, some:0] | file://:0:0:0:0 | .x [some:0] | @@ -122,9 +120,7 @@ edges | test.swift:263:13:263:28 | call to optionalSource() [some:0] | test.swift:298:11:298:15 | let ...? [some:0] | | test.swift:263:13:263:28 | call to optionalSource() [some:0] | test.swift:306:13:306:24 | .some(...) [some:0] | | test.swift:263:13:263:28 | call to optionalSource() [some:0] | test.swift:314:10:314:21 | .some(...) [some:0] | -| test.swift:270:15:270:22 | call to source() | file://:0:0:0:0 | [summary param] this in signum() | | test.swift:270:15:270:22 | call to source() | test.swift:270:15:270:31 | call to signum() | -| test.swift:271:15:271:16 | ...? | file://:0:0:0:0 | [summary param] this in signum() | | test.swift:271:15:271:16 | ...? | test.swift:271:15:271:25 | call to signum() | | test.swift:271:15:271:25 | call to signum() | test.swift:271:15:271:25 | OptionalEvaluationExpr | | test.swift:280:31:280:38 | call to source() | test.swift:280:15:280:38 | ... ? ... : ... | @@ -133,15 +129,12 @@ edges | test.swift:284:12:284:12 | z | test.swift:285:19:285:19 | z | | test.swift:291:8:291:12 | let ...? [some:0] | test.swift:291:12:291:12 | z | | test.swift:291:12:291:12 | z | test.swift:292:19:292:19 | z | -| test.swift:291:16:291:17 | ...? | file://:0:0:0:0 | [summary param] this in signum() | | test.swift:291:16:291:17 | ...? | test.swift:291:16:291:26 | call to signum() | -| test.swift:291:16:291:17 | ...? [some:0] | file://:0:0:0:0 | [summary param] this in signum() [some:0] | | test.swift:291:16:291:17 | ...? [some:0] | test.swift:291:16:291:26 | call to signum() [some:0] | | test.swift:291:16:291:26 | call to signum() | test.swift:291:16:291:26 | call to signum() [some:0] | | test.swift:291:16:291:26 | call to signum() [some:0] | test.swift:291:8:291:12 | let ...? [some:0] | | test.swift:298:11:298:15 | let ...? [some:0] | test.swift:298:15:298:15 | z1 | | test.swift:298:15:298:15 | z1 | test.swift:300:15:300:15 | z1 | -| test.swift:303:15:303:16 | ...! | file://:0:0:0:0 | [summary param] this in signum() | | test.swift:303:15:303:16 | ...! | test.swift:303:15:303:25 | call to signum() | | test.swift:306:13:306:24 | .some(...) [some:0] | test.swift:306:23:306:23 | z | | test.swift:306:23:306:23 | z | test.swift:307:19:307:19 | z | @@ -283,10 +276,6 @@ nodes | file://:0:0:0:0 | .x [some:0] | semmle.label | .x [some:0] | | file://:0:0:0:0 | [post] self [x, some:0] | semmle.label | [post] self [x, some:0] | | file://:0:0:0:0 | [post] self [x] | semmle.label | [post] self [x] | -| file://:0:0:0:0 | [summary param] this in signum() | semmle.label | [summary param] this in signum() | -| file://:0:0:0:0 | [summary param] this in signum() [some:0] | semmle.label | [summary param] this in signum() [some:0] | -| file://:0:0:0:0 | [summary] to write: return (return) in signum() | semmle.label | [summary] to write: return (return) in signum() | -| file://:0:0:0:0 | [summary] to write: return (return) in signum() [some:0] | semmle.label | [summary] to write: return (return) in signum() [some:0] | | file://:0:0:0:0 | self [a, x] | semmle.label | self [a, x] | | file://:0:0:0:0 | self [str] | semmle.label | self [str] | | file://:0:0:0:0 | self [x, some:0] | semmle.label | self [x, some:0] | @@ -599,11 +588,6 @@ subpaths | test.swift:218:11:218:18 | call to source() | test.swift:169:12:169:22 | value | test.swift:170:5:170:5 | [post] self [x] | test.swift:218:3:218:5 | [post] getter for .a [x] | | test.swift:219:13:219:13 | b [a, x] | test.swift:185:7:185:7 | self [a, x] | file://:0:0:0:0 | .a [x] | test.swift:219:13:219:15 | .a [x] | | test.swift:219:13:219:15 | .a [x] | test.swift:163:7:163:7 | self [x] | file://:0:0:0:0 | .x | test.swift:219:13:219:17 | .x | -| test.swift:270:15:270:22 | call to source() | file://:0:0:0:0 | [summary param] this in signum() | file://:0:0:0:0 | [summary] to write: return (return) in signum() | test.swift:270:15:270:31 | call to signum() | -| test.swift:271:15:271:16 | ...? | file://:0:0:0:0 | [summary param] this in signum() | file://:0:0:0:0 | [summary] to write: return (return) in signum() | test.swift:271:15:271:25 | call to signum() | -| test.swift:291:16:291:17 | ...? | file://:0:0:0:0 | [summary param] this in signum() | file://:0:0:0:0 | [summary] to write: return (return) in signum() | test.swift:291:16:291:26 | call to signum() | -| test.swift:291:16:291:17 | ...? [some:0] | file://:0:0:0:0 | [summary param] this in signum() [some:0] | file://:0:0:0:0 | [summary] to write: return (return) in signum() [some:0] | test.swift:291:16:291:26 | call to signum() [some:0] | -| test.swift:303:15:303:16 | ...! | file://:0:0:0:0 | [summary param] this in signum() | file://:0:0:0:0 | [summary] to write: return (return) in signum() | test.swift:303:15:303:25 | call to signum() | | test.swift:515:12:515:12 | x [some:0] | test.swift:509:9:509:9 | value [some:0] | file://:0:0:0:0 | [post] self [x, some:0] | test.swift:515:5:515:5 | [post] cx [x, some:0] | | test.swift:519:20:519:20 | cx [x, some:0] | test.swift:509:9:509:9 | self [x, some:0] | file://:0:0:0:0 | .x [some:0] | test.swift:519:20:519:23 | .x [some:0] | | test.swift:543:20:543:28 | call to source3() | test.swift:536:10:536:13 | s | test.swift:537:7:537:7 | [post] self [str] | test.swift:543:7:543:7 | [post] self [str] | diff --git a/swift/ql/test/library-tests/dataflow/taint/libraries/CONSISTENCY/CfgConsistency.expected b/swift/ql/test/library-tests/dataflow/taint/libraries/CONSISTENCY/CfgConsistency.expected deleted file mode 100644 index 853828d4f77..00000000000 --- a/swift/ql/test/library-tests/dataflow/taint/libraries/CONSISTENCY/CfgConsistency.expected +++ /dev/null @@ -1,5 +0,0 @@ -deadEnd -| file://:0:0:0:0 | ... = ... | -| file://:0:0:0:0 | ... = ... | -| file://:0:0:0:0 | ... = ... | -| file://:0:0:0:0 | ... = ... | diff --git a/swift/ql/test/library-tests/elements/decl/enumdecl/CONSISTENCY/CfgConsistency.expected b/swift/ql/test/library-tests/elements/decl/enumdecl/CONSISTENCY/CfgConsistency.expected deleted file mode 100644 index 4de95c00602..00000000000 --- a/swift/ql/test/library-tests/elements/decl/enumdecl/CONSISTENCY/CfgConsistency.expected +++ /dev/null @@ -1,10 +0,0 @@ -deadEnd -| file://:0:0:0:0 | ... = ... | -| file://:0:0:0:0 | ... = ... | -| file://:0:0:0:0 | ... = ... | -| file://:0:0:0:0 | ... = ... | -| file://:0:0:0:0 | ... = ... | -| file://:0:0:0:0 | ... = ... | -| file://:0:0:0:0 | ... = ... | -| file://:0:0:0:0 | ... = ... | -| file://:0:0:0:0 | ... = ... | diff --git a/swift/ql/test/library-tests/elements/decl/function/CONSISTENCY/CfgConsistency.expected b/swift/ql/test/library-tests/elements/decl/function/CONSISTENCY/CfgConsistency.expected deleted file mode 100644 index 280e2eeab41..00000000000 --- a/swift/ql/test/library-tests/elements/decl/function/CONSISTENCY/CfgConsistency.expected +++ /dev/null @@ -1,3 +0,0 @@ -deadEnd -| file://:0:0:0:0 | ... = ... | -| file://:0:0:0:0 | ... = ... | diff --git a/swift/ql/test/library-tests/regex/parse.expected b/swift/ql/test/library-tests/regex/parse.expected new file mode 100644 index 00000000000..34c746bf94d --- /dev/null +++ b/swift/ql/test/library-tests/regex/parse.expected @@ -0,0 +1,6485 @@ +redos_variants.swift: +# 44| [RegExpDot] . + +# 44| [RegExpStar] .* +#-----| 0 -> [RegExpDot] . + +# 46| [RegExpConstant, RegExpNormalChar] a + +# 46| [RegExpStar] a* +#-----| 0 -> [RegExpConstant, RegExpNormalChar] a + +# 46| [RegExpSequence] a*b +#-----| 0 -> [RegExpStar] a* +#-----| 1 -> [RegExpConstant, RegExpNormalChar] b + +# 46| [RegExpConstant, RegExpNormalChar] b + +# 47| [RegExpGroup] (a*) +#-----| 0 -> [RegExpStar] a* + +# 47| [RegExpSequence] (a*)b +#-----| 0 -> [RegExpGroup] (a*) +#-----| 1 -> [RegExpConstant, RegExpNormalChar] b + +# 47| [RegExpConstant, RegExpNormalChar] a + +# 47| [RegExpStar] a* +#-----| 0 -> [RegExpConstant, RegExpNormalChar] a + +# 47| [RegExpConstant, RegExpNormalChar] b + +# 48| [RegExpGroup] (a) +#-----| 0 -> [RegExpConstant, RegExpNormalChar] a + +# 48| [RegExpStar] (a)* +#-----| 0 -> [RegExpGroup] (a) + +# 48| [RegExpSequence] (a)*b +#-----| 0 -> [RegExpStar] (a)* +#-----| 1 -> [RegExpConstant, RegExpNormalChar] b + +# 48| [RegExpConstant, RegExpNormalChar] a + +# 48| [RegExpConstant, RegExpNormalChar] b + +# 49| [RegExpGroup] (a*) +#-----| 0 -> [RegExpStar] a* + +# 49| [RegExpStar] (a*)* +#-----| 0 -> [RegExpGroup] (a*) + +# 49| [RegExpSequence] (a*)*b +#-----| 0 -> [RegExpStar] (a*)* +#-----| 1 -> [RegExpConstant, RegExpNormalChar] b + +# 49| [RegExpConstant, RegExpNormalChar] a + +# 49| [RegExpStar] a* +#-----| 0 -> [RegExpConstant, RegExpNormalChar] a + +# 49| [RegExpConstant, RegExpNormalChar] b + +# 50| [RegExpGroup] ((a*)*b) +#-----| 0 -> [RegExpSequence] (a*)*b + +# 50| [RegExpGroup] (a*) +#-----| 0 -> [RegExpStar] a* + +# 50| [RegExpStar] (a*)* +#-----| 0 -> [RegExpGroup] (a*) + +# 50| [RegExpSequence] (a*)*b +#-----| 0 -> [RegExpStar] (a*)* +#-----| 1 -> [RegExpConstant, RegExpNormalChar] b + +# 50| [RegExpConstant, RegExpNormalChar] a + +# 50| [RegExpStar] a* +#-----| 0 -> [RegExpConstant, RegExpNormalChar] a + +# 50| [RegExpConstant, RegExpNormalChar] b + +# 52| [RegExpGroup] (a|aa?) +#-----| 0 -> [RegExpAlt] a|aa? + +# 52| [RegExpSequence] (a|aa?)b +#-----| 0 -> [RegExpGroup] (a|aa?) +#-----| 1 -> [RegExpConstant, RegExpNormalChar] b + +# 52| [RegExpConstant, RegExpNormalChar] a + +# 52| [RegExpAlt] a|aa? +#-----| 0 -> [RegExpConstant, RegExpNormalChar] a +#-----| 1 -> [RegExpSequence] aa? + +# 52| [RegExpConstant, RegExpNormalChar] a + +# 52| [RegExpSequence] aa? +#-----| 0 -> [RegExpConstant, RegExpNormalChar] a +#-----| 1 -> [RegExpOpt] a? + +# 52| [RegExpConstant, RegExpNormalChar] a + +# 52| [RegExpOpt] a? +#-----| 0 -> [RegExpConstant, RegExpNormalChar] a + +# 52| [RegExpConstant, RegExpNormalChar] b + +# 53| [RegExpGroup] (a|aa?) +#-----| 0 -> [RegExpAlt] a|aa? + +# 53| [RegExpStar] (a|aa?)* +#-----| 0 -> [RegExpGroup] (a|aa?) + +# 53| [RegExpSequence] (a|aa?)*b +#-----| 0 -> [RegExpStar] (a|aa?)* +#-----| 1 -> [RegExpConstant, RegExpNormalChar] b + +# 53| [RegExpConstant, RegExpNormalChar] a + +# 53| [RegExpAlt] a|aa? +#-----| 0 -> [RegExpConstant, RegExpNormalChar] a +#-----| 1 -> [RegExpSequence] aa? + +# 53| [RegExpConstant, RegExpNormalChar] a + +# 53| [RegExpSequence] aa? +#-----| 0 -> [RegExpConstant, RegExpNormalChar] a +#-----| 1 -> [RegExpOpt] a? + +# 53| [RegExpConstant, RegExpNormalChar] a + +# 53| [RegExpOpt] a? +#-----| 0 -> [RegExpConstant, RegExpNormalChar] a + +# 53| [RegExpConstant, RegExpNormalChar] b + +# 58| [RegExpCaret] ^ + +# 58| [RegExpSequence] ^_(__|.)+_$ +#-----| 0 -> [RegExpCaret] ^ +#-----| 1 -> [RegExpConstant, RegExpNormalChar] _ +#-----| 2 -> [RegExpPlus] (__|.)+ +#-----| 3 -> [RegExpConstant, RegExpNormalChar] _ +#-----| 4 -> [RegExpDollar] $ + +# 58| [RegExpConstant, RegExpNormalChar] _ + +# 58| [RegExpGroup] (__|.) +#-----| 0 -> [RegExpAlt] __|. + +# 58| [RegExpPlus] (__|.)+ +#-----| 0 -> [RegExpGroup] (__|.) + +# 58| [RegExpConstant, RegExpNormalChar] __ + +# 58| [RegExpAlt] __|. +#-----| 0 -> [RegExpConstant, RegExpNormalChar] __ +#-----| 1 -> [RegExpDot] . + +# 58| [RegExpDot] . + +# 58| [RegExpConstant, RegExpNormalChar] _ + +# 58| [RegExpDollar] $ + +# 59| [RegExpCaret] ^ + +# 59| [RegExpSequence] ^_(__|[^_])+_$ +#-----| 0 -> [RegExpCaret] ^ +#-----| 1 -> [RegExpConstant, RegExpNormalChar] _ +#-----| 2 -> [RegExpPlus] (__|[^_])+ +#-----| 3 -> [RegExpConstant, RegExpNormalChar] _ +#-----| 4 -> [RegExpDollar] $ + +# 59| [RegExpConstant, RegExpNormalChar] _ + +# 59| [RegExpGroup] (__|[^_]) +#-----| 0 -> [RegExpAlt] __|[^_] + +# 59| [RegExpPlus] (__|[^_])+ +#-----| 0 -> [RegExpGroup] (__|[^_]) + +# 59| [RegExpConstant, RegExpNormalChar] __ + +# 59| [RegExpAlt] __|[^_] +#-----| 0 -> [RegExpConstant, RegExpNormalChar] __ +#-----| 1 -> [RegExpCharacterClass] [^_] + +# 59| [RegExpCharacterClass] [^_] +#-----| 0 -> [RegExpConstant, RegExpNormalChar] _ + +# 59| [RegExpConstant, RegExpNormalChar] _ + +# 59| [RegExpConstant, RegExpNormalChar] _ + +# 59| [RegExpDollar] $ + +# 66| [RegExpCaret] ^ + +# 66| [RegExpSequence] ^\b_((?:__|[\s\S])+?)_\b +#-----| 0 -> [RegExpCaret] ^ +#-----| 1 -> [RegExpSpecialChar] \b +#-----| 2 -> [RegExpConstant, RegExpNormalChar] _ +#-----| 3 -> [RegExpGroup] ((?:__|[\s\S])+?) +#-----| 4 -> [RegExpConstant, RegExpNormalChar] _ +#-----| 5 -> [RegExpSpecialChar] \b + +# 66| [RegExpAlt] ^\b_((?:__|[\s\S])+?)_\b|^\*((?:\*\*|[\s\S])+?)\*(?!\*) +#-----| 0 -> [RegExpSequence] ^\b_((?:__|[\s\S])+?)_\b +#-----| 1 -> [RegExpSequence] ^\*((?:\*\*|[\s\S])+?)\*(?!\*) + +# 66| [RegExpSpecialChar] \b + +# 66| [RegExpConstant, RegExpNormalChar] _ + +# 66| [RegExpGroup] ((?:__|[\s\S])+?) +#-----| 0 -> [RegExpPlus] (?:__|[\s\S])+? + +# 66| [RegExpGroup] (?:__|[\s\S]) +#-----| 0 -> [RegExpAlt] __|[\s\S] + +# 66| [RegExpPlus] (?:__|[\s\S])+? +#-----| 0 -> [RegExpGroup] (?:__|[\s\S]) + +# 66| [RegExpConstant, RegExpNormalChar] __ + +# 66| [RegExpAlt] __|[\s\S] +#-----| 0 -> [RegExpConstant, RegExpNormalChar] __ +#-----| 1 -> [RegExpCharacterClass] [\s\S] + +# 66| [RegExpCharacterClass] [\s\S] +#-----| 0 -> [RegExpCharacterClassEscape] \s +#-----| 1 -> [RegExpCharacterClassEscape] \S + +# 66| [RegExpCharacterClassEscape] \s + +# 66| [RegExpCharacterClassEscape] \S + +# 66| [RegExpConstant, RegExpNormalChar] _ + +# 66| [RegExpSpecialChar] \b + +# 66| [RegExpCaret] ^ + +# 66| [RegExpSequence] ^\*((?:\*\*|[\s\S])+?)\*(?!\*) +#-----| 0 -> [RegExpCaret] ^ +#-----| 1 -> [RegExpConstant, RegExpEscape] \* +#-----| 2 -> [RegExpGroup] ((?:\*\*|[\s\S])+?) +#-----| 3 -> [RegExpConstant, RegExpEscape] \* +#-----| 4 -> [RegExpNegativeLookahead] (?!\*) + +# 66| [RegExpConstant, RegExpEscape] \* + +# 66| [RegExpGroup] ((?:\*\*|[\s\S])+?) +#-----| 0 -> [RegExpPlus] (?:\*\*|[\s\S])+? + +# 66| [RegExpGroup] (?:\*\*|[\s\S]) +#-----| 0 -> [RegExpAlt] \*\*|[\s\S] + +# 66| [RegExpPlus] (?:\*\*|[\s\S])+? +#-----| 0 -> [RegExpGroup] (?:\*\*|[\s\S]) + +# 66| [RegExpConstant, RegExpEscape] \* + +# 66| [RegExpSequence] \*\* +#-----| 0 -> [RegExpConstant, RegExpEscape] \* +#-----| 1 -> [RegExpConstant, RegExpEscape] \* + +# 66| [RegExpAlt] \*\*|[\s\S] +#-----| 0 -> [RegExpSequence] \*\* +#-----| 1 -> [RegExpCharacterClass] [\s\S] + +# 66| [RegExpConstant, RegExpEscape] \* + +# 66| [RegExpCharacterClass] [\s\S] +#-----| 0 -> [RegExpCharacterClassEscape] \s +#-----| 1 -> [RegExpCharacterClassEscape] \S + +# 66| [RegExpCharacterClassEscape] \s + +# 66| [RegExpCharacterClassEscape] \S + +# 66| [RegExpConstant, RegExpEscape] \* + +# 66| [RegExpNegativeLookahead] (?!\*) + +# 66| [RegExpConstant, RegExpEscape] \* + +# 69| [RegExpCaret] ^ + +# 69| [RegExpSequence] ^\b_((?:__|[\s\S])+?)_\b +#-----| 0 -> [RegExpCaret] ^ +#-----| 1 -> [RegExpSpecialChar] \b +#-----| 2 -> [RegExpConstant, RegExpNormalChar] _ +#-----| 3 -> [RegExpGroup] ((?:__|[\s\S])+?) +#-----| 4 -> [RegExpConstant, RegExpNormalChar] _ +#-----| 5 -> [RegExpSpecialChar] \b + +# 69| [RegExpAlt] ^\b_((?:__|[\s\S])+?)_\b|^\*((?:\*\*|[\s\S])+?)\*(?!\*) +#-----| 0 -> [RegExpSequence] ^\b_((?:__|[\s\S])+?)_\b +#-----| 1 -> [RegExpSequence] ^\*((?:\*\*|[\s\S])+?)\*(?!\*) + +# 69| [RegExpSpecialChar] \b + +# 69| [RegExpConstant, RegExpNormalChar] _ + +# 69| [RegExpGroup] ((?:__|[\s\S])+?) +#-----| 0 -> [RegExpPlus] (?:__|[\s\S])+? + +# 69| [RegExpGroup] (?:__|[\s\S]) +#-----| 0 -> [RegExpAlt] __|[\s\S] + +# 69| [RegExpPlus] (?:__|[\s\S])+? +#-----| 0 -> [RegExpGroup] (?:__|[\s\S]) + +# 69| [RegExpConstant, RegExpNormalChar] __ + +# 69| [RegExpAlt] __|[\s\S] +#-----| 0 -> [RegExpConstant, RegExpNormalChar] __ +#-----| 1 -> [RegExpCharacterClass] [\s\S] + +# 69| [RegExpCharacterClass] [\s\S] +#-----| 0 -> [RegExpCharacterClassEscape] \s +#-----| 1 -> [RegExpCharacterClassEscape] \S + +# 69| [RegExpCharacterClassEscape] \s + +# 69| [RegExpCharacterClassEscape] \S + +# 69| [RegExpConstant, RegExpNormalChar] _ + +# 69| [RegExpSpecialChar] \b + +# 69| [RegExpCaret] ^ + +# 69| [RegExpSequence] ^\*((?:\*\*|[\s\S])+?)\*(?!\*) +#-----| 0 -> [RegExpCaret] ^ +#-----| 1 -> [RegExpConstant, RegExpEscape] \* +#-----| 2 -> [RegExpGroup] ((?:\*\*|[\s\S])+?) +#-----| 3 -> [RegExpConstant, RegExpEscape] \* +#-----| 4 -> [RegExpNegativeLookahead] (?!\*) + +# 69| [RegExpConstant, RegExpEscape] \* + +# 69| [RegExpGroup] ((?:\*\*|[\s\S])+?) +#-----| 0 -> [RegExpPlus] (?:\*\*|[\s\S])+? + +# 69| [RegExpGroup] (?:\*\*|[\s\S]) +#-----| 0 -> [RegExpAlt] \*\*|[\s\S] + +# 69| [RegExpPlus] (?:\*\*|[\s\S])+? +#-----| 0 -> [RegExpGroup] (?:\*\*|[\s\S]) + +# 69| [RegExpConstant, RegExpEscape] \* + +# 69| [RegExpSequence] \*\* +#-----| 0 -> [RegExpConstant, RegExpEscape] \* +#-----| 1 -> [RegExpConstant, RegExpEscape] \* + +# 69| [RegExpAlt] \*\*|[\s\S] +#-----| 0 -> [RegExpSequence] \*\* +#-----| 1 -> [RegExpCharacterClass] [\s\S] + +# 69| [RegExpConstant, RegExpEscape] \* + +# 69| [RegExpCharacterClass] [\s\S] +#-----| 0 -> [RegExpCharacterClassEscape] \s +#-----| 1 -> [RegExpCharacterClassEscape] \S + +# 69| [RegExpCharacterClassEscape] \s + +# 69| [RegExpCharacterClassEscape] \S + +# 69| [RegExpConstant, RegExpEscape] \* + +# 69| [RegExpNegativeLookahead] (?!\*) + +# 69| [RegExpConstant, RegExpEscape] \* + +# 74| [RegExpCaret] ^ + +# 74| [RegExpSequence] ^\b_((?:__|[^_])+?)_\b +#-----| 0 -> [RegExpCaret] ^ +#-----| 1 -> [RegExpSpecialChar] \b +#-----| 2 -> [RegExpConstant, RegExpNormalChar] _ +#-----| 3 -> [RegExpGroup] ((?:__|[^_])+?) +#-----| 4 -> [RegExpConstant, RegExpNormalChar] _ +#-----| 5 -> [RegExpSpecialChar] \b + +# 74| [RegExpAlt] ^\b_((?:__|[^_])+?)_\b|^\*((?:\*\*|[^*])+?)\*(?!\*) +#-----| 0 -> [RegExpSequence] ^\b_((?:__|[^_])+?)_\b +#-----| 1 -> [RegExpSequence] ^\*((?:\*\*|[^*])+?)\*(?!\*) + +# 74| [RegExpSpecialChar] \b + +# 74| [RegExpConstant, RegExpNormalChar] _ + +# 74| [RegExpGroup] ((?:__|[^_])+?) +#-----| 0 -> [RegExpPlus] (?:__|[^_])+? + +# 74| [RegExpGroup] (?:__|[^_]) +#-----| 0 -> [RegExpAlt] __|[^_] + +# 74| [RegExpPlus] (?:__|[^_])+? +#-----| 0 -> [RegExpGroup] (?:__|[^_]) + +# 74| [RegExpConstant, RegExpNormalChar] __ + +# 74| [RegExpAlt] __|[^_] +#-----| 0 -> [RegExpConstant, RegExpNormalChar] __ +#-----| 1 -> [RegExpCharacterClass] [^_] + +# 74| [RegExpCharacterClass] [^_] +#-----| 0 -> [RegExpConstant, RegExpNormalChar] _ + +# 74| [RegExpConstant, RegExpNormalChar] _ + +# 74| [RegExpConstant, RegExpNormalChar] _ + +# 74| [RegExpSpecialChar] \b + +# 74| [RegExpCaret] ^ + +# 74| [RegExpSequence] ^\*((?:\*\*|[^*])+?)\*(?!\*) +#-----| 0 -> [RegExpCaret] ^ +#-----| 1 -> [RegExpConstant, RegExpEscape] \* +#-----| 2 -> [RegExpGroup] ((?:\*\*|[^*])+?) +#-----| 3 -> [RegExpConstant, RegExpEscape] \* +#-----| 4 -> [RegExpNegativeLookahead] (?!\*) + +# 74| [RegExpConstant, RegExpEscape] \* + +# 74| [RegExpGroup] ((?:\*\*|[^*])+?) +#-----| 0 -> [RegExpPlus] (?:\*\*|[^*])+? + +# 74| [RegExpGroup] (?:\*\*|[^*]) +#-----| 0 -> [RegExpAlt] \*\*|[^*] + +# 74| [RegExpPlus] (?:\*\*|[^*])+? +#-----| 0 -> [RegExpGroup] (?:\*\*|[^*]) + +# 74| [RegExpConstant, RegExpEscape] \* + +# 74| [RegExpSequence] \*\* +#-----| 0 -> [RegExpConstant, RegExpEscape] \* +#-----| 1 -> [RegExpConstant, RegExpEscape] \* + +# 74| [RegExpAlt] \*\*|[^*] +#-----| 0 -> [RegExpSequence] \*\* +#-----| 1 -> [RegExpCharacterClass] [^*] + +# 74| [RegExpConstant, RegExpEscape] \* + +# 74| [RegExpCharacterClass] [^*] +#-----| 0 -> [RegExpConstant, RegExpNormalChar] * + +# 74| [RegExpConstant, RegExpNormalChar] * + +# 74| [RegExpConstant, RegExpEscape] \* + +# 74| [RegExpNegativeLookahead] (?!\*) + +# 74| [RegExpConstant, RegExpEscape] \* + +# 79| [RegExpGroup] (.*,) +#-----| 0 -> [RegExpSequence] .*, + +# 79| [RegExpPlus] (.*,)+ +#-----| 0 -> [RegExpGroup] (.*,) + +# 79| [RegExpSequence] (.*,)+.+ +#-----| 0 -> [RegExpPlus] (.*,)+ +#-----| 1 -> [RegExpPlus] .+ + +# 79| [RegExpDot] . + +# 79| [RegExpStar] .* +#-----| 0 -> [RegExpDot] . + +# 79| [RegExpSequence] .*, +#-----| 0 -> [RegExpStar] .* +#-----| 1 -> [RegExpConstant, RegExpNormalChar] , + +# 79| [RegExpConstant, RegExpNormalChar] , + +# 79| [RegExpDot] . + +# 79| [RegExpPlus] .+ +#-----| 0 -> [RegExpDot] . + +# 85| [RegExpCaret] ^ + +# 85| [RegExpSequence] ^(?:\s+(?:"(?:[^"\\]|\\\\|\\.)+"|'(?:[^'\\]|\\\\|\\.)+'|\((?:[^)\\]|\\\\|\\.)+\)))? +#-----| 0 -> [RegExpCaret] ^ +#-----| 1 -> [RegExpOpt] (?:\s+(?:"(?:[^"\\]|\\\\|\\.)+"|'(?:[^'\\]|\\\\|\\.)+'|\((?:[^)\\]|\\\\|\\.)+\)))? + +# 85| [RegExpGroup] (?:\s+(?:"(?:[^"\\]|\\\\|\\.)+"|'(?:[^'\\]|\\\\|\\.)+'|\((?:[^)\\]|\\\\|\\.)+\))) +#-----| 0 -> [RegExpSequence] \s+(?:"(?:[^"\\]|\\\\|\\.)+"|'(?:[^'\\]|\\\\|\\.)+'|\((?:[^)\\]|\\\\|\\.)+\)) + +# 85| [RegExpOpt] (?:\s+(?:"(?:[^"\\]|\\\\|\\.)+"|'(?:[^'\\]|\\\\|\\.)+'|\((?:[^)\\]|\\\\|\\.)+\)))? +#-----| 0 -> [RegExpGroup] (?:\s+(?:"(?:[^"\\]|\\\\|\\.)+"|'(?:[^'\\]|\\\\|\\.)+'|\((?:[^)\\]|\\\\|\\.)+\))) + +# 85| [RegExpCharacterClassEscape] \s + +# 85| [RegExpPlus] \s+ +#-----| 0 -> [RegExpCharacterClassEscape] \s + +# 85| [RegExpSequence] \s+(?:"(?:[^"\\]|\\\\|\\.)+"|'(?:[^'\\]|\\\\|\\.)+'|\((?:[^)\\]|\\\\|\\.)+\)) +#-----| 0 -> [RegExpPlus] \s+ +#-----| 1 -> [RegExpGroup] (?:"(?:[^"\\]|\\\\|\\.)+"|'(?:[^'\\]|\\\\|\\.)+'|\((?:[^)\\]|\\\\|\\.)+\)) + +# 85| [RegExpGroup] (?:"(?:[^"\\]|\\\\|\\.)+"|'(?:[^'\\]|\\\\|\\.)+'|\((?:[^)\\]|\\\\|\\.)+\)) +#-----| 0 -> [RegExpAlt] "(?:[^"\\]|\\\\|\\.)+"|'(?:[^'\\]|\\\\|\\.)+'|\((?:[^)\\]|\\\\|\\.)+\) + +# 85| [RegExpConstant, RegExpNormalChar] " + +# 85| [RegExpSequence] "(?:[^"\\]|\\\\|\\.)+" +#-----| 0 -> [RegExpConstant, RegExpNormalChar] " +#-----| 1 -> [RegExpPlus] (?:[^"\\]|\\\\|\\.)+ +#-----| 2 -> [RegExpConstant, RegExpNormalChar] " + +# 85| [RegExpAlt] "(?:[^"\\]|\\\\|\\.)+"|'(?:[^'\\]|\\\\|\\.)+'|\((?:[^)\\]|\\\\|\\.)+\) +#-----| 0 -> [RegExpSequence] "(?:[^"\\]|\\\\|\\.)+" +#-----| 1 -> [RegExpSequence] '(?:[^'\\]|\\\\|\\.)+' +#-----| 2 -> [RegExpSequence] \((?:[^)\\]|\\\\|\\.)+\) + +# 85| [RegExpGroup] (?:[^"\\]|\\\\|\\.) +#-----| 0 -> [RegExpAlt] [^"\\]|\\\\|\\. + +# 85| [RegExpPlus] (?:[^"\\]|\\\\|\\.)+ +#-----| 0 -> [RegExpGroup] (?:[^"\\]|\\\\|\\.) + +# 85| [RegExpCharacterClass] [^"\\] +#-----| 0 -> [RegExpConstant, RegExpNormalChar] " +#-----| 1 -> [RegExpConstant, RegExpEscape] \\ + +# 85| [RegExpAlt] [^"\\]|\\\\|\\. +#-----| 0 -> [RegExpCharacterClass] [^"\\] +#-----| 1 -> [RegExpSequence] \\\\ +#-----| 2 -> [RegExpSequence] \\. + +# 85| [RegExpConstant, RegExpNormalChar] " + +# 85| [RegExpConstant, RegExpEscape] \\ + +# 85| [RegExpConstant, RegExpEscape] \\ + +# 85| [RegExpSequence] \\\\ +#-----| 0 -> [RegExpConstant, RegExpEscape] \\ +#-----| 1 -> [RegExpConstant, RegExpEscape] \\ + +# 85| [RegExpConstant, RegExpEscape] \\ + +# 85| [RegExpConstant, RegExpEscape] \\ + +# 85| [RegExpSequence] \\. +#-----| 0 -> [RegExpConstant, RegExpEscape] \\ +#-----| 1 -> [RegExpDot] . + +# 85| [RegExpDot] . + +# 85| [RegExpConstant, RegExpNormalChar] " + +# 85| [RegExpConstant, RegExpNormalChar] ' + +# 85| [RegExpSequence] '(?:[^'\\]|\\\\|\\.)+' +#-----| 0 -> [RegExpConstant, RegExpNormalChar] ' +#-----| 1 -> [RegExpPlus] (?:[^'\\]|\\\\|\\.)+ +#-----| 2 -> [RegExpConstant, RegExpNormalChar] ' + +# 85| [RegExpGroup] (?:[^'\\]|\\\\|\\.) +#-----| 0 -> [RegExpAlt] [^'\\]|\\\\|\\. + +# 85| [RegExpPlus] (?:[^'\\]|\\\\|\\.)+ +#-----| 0 -> [RegExpGroup] (?:[^'\\]|\\\\|\\.) + +# 85| [RegExpCharacterClass] [^'\\] +#-----| 0 -> [RegExpConstant, RegExpNormalChar] ' +#-----| 1 -> [RegExpConstant, RegExpEscape] \\ + +# 85| [RegExpAlt] [^'\\]|\\\\|\\. +#-----| 0 -> [RegExpCharacterClass] [^'\\] +#-----| 1 -> [RegExpSequence] \\\\ +#-----| 2 -> [RegExpSequence] \\. + +# 85| [RegExpConstant, RegExpNormalChar] ' + +# 85| [RegExpConstant, RegExpEscape] \\ + +# 85| [RegExpConstant, RegExpEscape] \\ + +# 85| [RegExpSequence] \\\\ +#-----| 0 -> [RegExpConstant, RegExpEscape] \\ +#-----| 1 -> [RegExpConstant, RegExpEscape] \\ + +# 85| [RegExpConstant, RegExpEscape] \\ + +# 85| [RegExpConstant, RegExpEscape] \\ + +# 85| [RegExpSequence] \\. +#-----| 0 -> [RegExpConstant, RegExpEscape] \\ +#-----| 1 -> [RegExpDot] . + +# 85| [RegExpDot] . + +# 85| [RegExpConstant, RegExpNormalChar] ' + +# 85| [RegExpConstant, RegExpEscape] \( + +# 85| [RegExpSequence] \((?:[^)\\]|\\\\|\\.)+\) +#-----| 0 -> [RegExpConstant, RegExpEscape] \( +#-----| 1 -> [RegExpPlus] (?:[^)\\]|\\\\|\\.)+ +#-----| 2 -> [RegExpConstant, RegExpEscape] \) + +# 85| [RegExpGroup] (?:[^)\\]|\\\\|\\.) +#-----| 0 -> [RegExpAlt] [^)\\]|\\\\|\\. + +# 85| [RegExpPlus] (?:[^)\\]|\\\\|\\.)+ +#-----| 0 -> [RegExpGroup] (?:[^)\\]|\\\\|\\.) + +# 85| [RegExpCharacterClass] [^)\\] +#-----| 0 -> [RegExpConstant, RegExpNormalChar] ) +#-----| 1 -> [RegExpConstant, RegExpEscape] \\ + +# 85| [RegExpAlt] [^)\\]|\\\\|\\. +#-----| 0 -> [RegExpCharacterClass] [^)\\] +#-----| 1 -> [RegExpSequence] \\\\ +#-----| 2 -> [RegExpSequence] \\. + +# 85| [RegExpConstant, RegExpNormalChar] ) + +# 85| [RegExpConstant, RegExpEscape] \\ + +# 85| [RegExpConstant, RegExpEscape] \\ + +# 85| [RegExpSequence] \\\\ +#-----| 0 -> [RegExpConstant, RegExpEscape] \\ +#-----| 1 -> [RegExpConstant, RegExpEscape] \\ + +# 85| [RegExpConstant, RegExpEscape] \\ + +# 85| [RegExpConstant, RegExpEscape] \\ + +# 85| [RegExpSequence] \\. +#-----| 0 -> [RegExpConstant, RegExpEscape] \\ +#-----| 1 -> [RegExpDot] . + +# 85| [RegExpDot] . + +# 85| [RegExpConstant, RegExpEscape] \) + +# 90| [RegExpCaret] ^ + +# 90| [RegExpSequence] ^ *(\S.*\|.*)\n *([-:]+ *\|[-| :]*)\n((?:.*\|.*(?:\n|$))*)\n* +#-----| 0 -> [RegExpCaret] ^ +#-----| 1 -> [RegExpStar] * +#-----| 2 -> [RegExpGroup] (\S.*\|.*) +#-----| 3 -> [RegExpConstant, RegExpEscape] \n +#-----| 4 -> [RegExpStar] * +#-----| 5 -> [RegExpGroup] ([-:]+ *\|[-| :]*) +#-----| 6 -> [RegExpConstant, RegExpEscape] \n +#-----| 7 -> [RegExpGroup] ((?:.*\|.*(?:\n|$))*) +#-----| 8 -> [RegExpStar] \n* + +# 90| [RegExpConstant, RegExpNormalChar] + +# 90| [RegExpStar] * +#-----| 0 -> [RegExpConstant, RegExpNormalChar] + +# 90| [RegExpGroup] (\S.*\|.*) +#-----| 0 -> [RegExpSequence] \S.*\|.* + +# 90| [RegExpCharacterClassEscape] \S + +# 90| [RegExpSequence] \S.*\|.* +#-----| 0 -> [RegExpCharacterClassEscape] \S +#-----| 1 -> [RegExpStar] .* +#-----| 2 -> [RegExpConstant, RegExpEscape] \| +#-----| 3 -> [RegExpStar] .* + +# 90| [RegExpDot] . + +# 90| [RegExpStar] .* +#-----| 0 -> [RegExpDot] . + +# 90| [RegExpConstant, RegExpEscape] \| + +# 90| [RegExpDot] . + +# 90| [RegExpStar] .* +#-----| 0 -> [RegExpDot] . + +# 90| [RegExpConstant, RegExpEscape] \n + +# 90| [RegExpConstant, RegExpNormalChar] + +# 90| [RegExpStar] * +#-----| 0 -> [RegExpConstant, RegExpNormalChar] + +# 90| [RegExpGroup] ([-:]+ *\|[-| :]*) +#-----| 0 -> [RegExpSequence] [-:]+ *\|[-| :]* + +# 90| [RegExpCharacterClass] [-:] +#-----| 0 -> [RegExpConstant, RegExpNormalChar] - +#-----| 1 -> [RegExpConstant, RegExpNormalChar] : + +# 90| [RegExpPlus] [-:]+ +#-----| 0 -> [RegExpCharacterClass] [-:] + +# 90| [RegExpSequence] [-:]+ *\|[-| :]* +#-----| 0 -> [RegExpPlus] [-:]+ +#-----| 1 -> [RegExpStar] * +#-----| 2 -> [RegExpConstant, RegExpEscape] \| +#-----| 3 -> [RegExpStar] [-| :]* + +# 90| [RegExpConstant, RegExpNormalChar] - + +# 90| [RegExpConstant, RegExpNormalChar] : + +# 90| [RegExpConstant, RegExpNormalChar] + +# 90| [RegExpStar] * +#-----| 0 -> [RegExpConstant, RegExpNormalChar] + +# 90| [RegExpConstant, RegExpEscape] \| + +# 90| [RegExpCharacterClass] [-| :] +#-----| 0 -> [RegExpConstant, RegExpNormalChar] - +#-----| 1 -> [RegExpConstant, RegExpNormalChar] | +#-----| 2 -> [RegExpConstant, RegExpNormalChar] +#-----| 3 -> [RegExpConstant, RegExpNormalChar] : + +# 90| [RegExpStar] [-| :]* +#-----| 0 -> [RegExpCharacterClass] [-| :] + +# 90| [RegExpConstant, RegExpNormalChar] - + +# 90| [RegExpConstant, RegExpNormalChar] | + +# 90| [RegExpConstant, RegExpNormalChar] + +# 90| [RegExpConstant, RegExpNormalChar] : + +# 90| [RegExpConstant, RegExpEscape] \n + +# 90| [RegExpGroup] ((?:.*\|.*(?:\n|$))*) +#-----| 0 -> [RegExpStar] (?:.*\|.*(?:\n|$))* + +# 90| [RegExpGroup] (?:.*\|.*(?:\n|$)) +#-----| 0 -> [RegExpSequence] .*\|.*(?:\n|$) + +# 90| [RegExpStar] (?:.*\|.*(?:\n|$))* +#-----| 0 -> [RegExpGroup] (?:.*\|.*(?:\n|$)) + +# 90| [RegExpDot] . + +# 90| [RegExpStar] .* +#-----| 0 -> [RegExpDot] . + +# 90| [RegExpSequence] .*\|.*(?:\n|$) +#-----| 0 -> [RegExpStar] .* +#-----| 1 -> [RegExpConstant, RegExpEscape] \| +#-----| 2 -> [RegExpStar] .* +#-----| 3 -> [RegExpGroup] (?:\n|$) + +# 90| [RegExpConstant, RegExpEscape] \| + +# 90| [RegExpDot] . + +# 90| [RegExpStar] .* +#-----| 0 -> [RegExpDot] . + +# 90| [RegExpGroup] (?:\n|$) +#-----| 0 -> [RegExpAlt] \n|$ + +# 90| [RegExpConstant, RegExpEscape] \n + +# 90| [RegExpAlt] \n|$ +#-----| 0 -> [RegExpConstant, RegExpEscape] \n +#-----| 1 -> [RegExpDollar] $ + +# 90| [RegExpDollar] $ + +# 90| [RegExpConstant, RegExpEscape] \n + +# 90| [RegExpStar] \n* +#-----| 0 -> [RegExpConstant, RegExpEscape] \n + +# 96| [RegExpConstant, RegExpEscape] \/ + +# 96| [RegExpSequence] \/(?![ *])(\\\/|.)*?\/[gim]*(?=\W|$) +#-----| 0 -> [RegExpConstant, RegExpEscape] \/ +#-----| 1 -> [RegExpNegativeLookahead] (?![ *]) +#-----| 2 -> [RegExpStar] (\\\/|.)*? +#-----| 3 -> [RegExpConstant, RegExpEscape] \/ +#-----| 4 -> [RegExpStar] [gim]* +#-----| 5 -> [RegExpPositiveLookahead] (?=\W|$) + +# 96| [RegExpNegativeLookahead] (?![ *]) + +# 96| [RegExpCharacterClass] [ *] +#-----| 0 -> [RegExpConstant, RegExpNormalChar] +#-----| 1 -> [RegExpConstant, RegExpNormalChar] * + +# 96| [RegExpConstant, RegExpNormalChar] + +# 96| [RegExpConstant, RegExpNormalChar] * + +# 96| [RegExpGroup] (\\\/|.) +#-----| 0 -> [RegExpAlt] \\\/|. + +# 96| [RegExpStar] (\\\/|.)*? +#-----| 0 -> [RegExpGroup] (\\\/|.) + +# 96| [RegExpConstant, RegExpEscape] \\ + +# 96| [RegExpSequence] \\\/ +#-----| 0 -> [RegExpConstant, RegExpEscape] \\ +#-----| 1 -> [RegExpConstant, RegExpEscape] \/ + +# 96| [RegExpAlt] \\\/|. +#-----| 0 -> [RegExpSequence] \\\/ +#-----| 1 -> [RegExpDot] . + +# 96| [RegExpConstant, RegExpEscape] \/ + +# 96| [RegExpDot] . + +# 96| [RegExpConstant, RegExpEscape] \/ + +# 96| [RegExpCharacterClass] [gim] +#-----| 0 -> [RegExpConstant, RegExpNormalChar] g +#-----| 1 -> [RegExpConstant, RegExpNormalChar] i +#-----| 2 -> [RegExpConstant, RegExpNormalChar] m + +# 96| [RegExpStar] [gim]* +#-----| 0 -> [RegExpCharacterClass] [gim] + +# 96| [RegExpConstant, RegExpNormalChar] g + +# 96| [RegExpConstant, RegExpNormalChar] i + +# 96| [RegExpConstant, RegExpNormalChar] m + +# 96| [RegExpPositiveLookahead] (?=\W|$) + +# 96| [RegExpCharacterClassEscape] \W + +# 96| [RegExpAlt] \W|$ +#-----| 0 -> [RegExpCharacterClassEscape] \W +#-----| 1 -> [RegExpDollar] $ + +# 96| [RegExpDollar] $ + +# 102| [RegExpCaret] ^ + +# 102| [RegExpSequence] ^([\s\[\{\(]|#.*)*$ +#-----| 0 -> [RegExpCaret] ^ +#-----| 1 -> [RegExpStar] ([\s\[\{\(]|#.*)* +#-----| 2 -> [RegExpDollar] $ + +# 102| [RegExpGroup] ([\s\[\{\(]|#.*) +#-----| 0 -> [RegExpAlt] [\s\[\{\(]|#.* + +# 102| [RegExpStar] ([\s\[\{\(]|#.*)* +#-----| 0 -> [RegExpGroup] ([\s\[\{\(]|#.*) + +# 102| [RegExpCharacterClass] [\s\[\{\(] +#-----| 0 -> [RegExpCharacterClassEscape] \s +#-----| 1 -> [RegExpConstant, RegExpEscape] \[ +#-----| 2 -> [RegExpConstant, RegExpEscape] \{ +#-----| 3 -> [RegExpConstant, RegExpEscape] \( + +# 102| [RegExpAlt] [\s\[\{\(]|#.* +#-----| 0 -> [RegExpCharacterClass] [\s\[\{\(] +#-----| 1 -> [RegExpSequence] #.* + +# 102| [RegExpCharacterClassEscape] \s + +# 102| [RegExpConstant, RegExpEscape] \[ + +# 102| [RegExpConstant, RegExpEscape] \{ + +# 102| [RegExpConstant, RegExpEscape] \( + +# 102| [RegExpConstant, RegExpNormalChar] # + +# 102| [RegExpSequence] #.* +#-----| 0 -> [RegExpConstant, RegExpNormalChar] # +#-----| 1 -> [RegExpStar] .* + +# 102| [RegExpDot] . + +# 102| [RegExpStar] .* +#-----| 0 -> [RegExpDot] . + +# 102| [RegExpDollar] $ + +# 108| [RegExpCaret] ^ + +# 108| [RegExpSequence] ^[\_$a-z][\_$a-z0-9]*(\[.*?\])*(\.[\_$a-z][\_$a-z0-9]*(\[.*?\])*)*$ +#-----| 0 -> [RegExpCaret] ^ +#-----| 1 -> [RegExpCharacterClass] [\_$a-z] +#-----| 2 -> [RegExpStar] [\_$a-z0-9]* +#-----| 3 -> [RegExpStar] (\[.*?\])* +#-----| 4 -> [RegExpStar] (\.[\_$a-z][\_$a-z0-9]*(\[.*?\])*)* +#-----| 5 -> [RegExpDollar] $ + +# 108| [RegExpCharacterClass] [\_$a-z] +#-----| 0 -> [RegExpConstant, RegExpEscape] \_ +#-----| 1 -> [RegExpConstant, RegExpNormalChar] $ +#-----| 2 -> [RegExpCharacterRange] a-z + +# 108| [RegExpConstant, RegExpEscape] \_ + +# 108| [RegExpConstant, RegExpNormalChar] $ + +# 108| [RegExpConstant, RegExpNormalChar] a + +# 108| [RegExpCharacterRange] a-z +#-----| 0 -> [RegExpConstant, RegExpNormalChar] a +#-----| 1 -> [RegExpConstant, RegExpNormalChar] z + +# 108| [RegExpConstant, RegExpNormalChar] z + +# 108| [RegExpCharacterClass] [\_$a-z0-9] +#-----| 0 -> [RegExpConstant, RegExpEscape] \_ +#-----| 1 -> [RegExpConstant, RegExpNormalChar] $ +#-----| 2 -> [RegExpCharacterRange] a-z +#-----| 3 -> [RegExpCharacterRange] 0-9 + +# 108| [RegExpStar] [\_$a-z0-9]* +#-----| 0 -> [RegExpCharacterClass] [\_$a-z0-9] + +# 108| [RegExpConstant, RegExpEscape] \_ + +# 108| [RegExpConstant, RegExpNormalChar] $ + +# 108| [RegExpConstant, RegExpNormalChar] a + +# 108| [RegExpCharacterRange] a-z +#-----| 0 -> [RegExpConstant, RegExpNormalChar] a +#-----| 1 -> [RegExpConstant, RegExpNormalChar] z + +# 108| [RegExpConstant, RegExpNormalChar] z + +# 108| [RegExpConstant, RegExpNormalChar] 0 + +# 108| [RegExpCharacterRange] 0-9 +#-----| 0 -> [RegExpConstant, RegExpNormalChar] 0 +#-----| 1 -> [RegExpConstant, RegExpNormalChar] 9 + +# 108| [RegExpConstant, RegExpNormalChar] 9 + +# 108| [RegExpGroup] (\[.*?\]) +#-----| 0 -> [RegExpSequence] \[.*?\] + +# 108| [RegExpStar] (\[.*?\])* +#-----| 0 -> [RegExpGroup] (\[.*?\]) + +# 108| [RegExpConstant, RegExpEscape] \[ + +# 108| [RegExpSequence] \[.*?\] +#-----| 0 -> [RegExpConstant, RegExpEscape] \[ +#-----| 1 -> [RegExpStar] .*? +#-----| 2 -> [RegExpConstant, RegExpEscape] \] + +# 108| [RegExpDot] . + +# 108| [RegExpStar] .*? +#-----| 0 -> [RegExpDot] . + +# 108| [RegExpConstant, RegExpEscape] \] + +# 108| [RegExpGroup] (\.[\_$a-z][\_$a-z0-9]*(\[.*?\])*) +#-----| 0 -> [RegExpSequence] \.[\_$a-z][\_$a-z0-9]*(\[.*?\])* + +# 108| [RegExpStar] (\.[\_$a-z][\_$a-z0-9]*(\[.*?\])*)* +#-----| 0 -> [RegExpGroup] (\.[\_$a-z][\_$a-z0-9]*(\[.*?\])*) + +# 108| [RegExpConstant, RegExpEscape] \. + +# 108| [RegExpSequence] \.[\_$a-z][\_$a-z0-9]*(\[.*?\])* +#-----| 0 -> [RegExpConstant, RegExpEscape] \. +#-----| 1 -> [RegExpCharacterClass] [\_$a-z] +#-----| 2 -> [RegExpStar] [\_$a-z0-9]* +#-----| 3 -> [RegExpStar] (\[.*?\])* + +# 108| [RegExpCharacterClass] [\_$a-z] +#-----| 0 -> [RegExpConstant, RegExpEscape] \_ +#-----| 1 -> [RegExpConstant, RegExpNormalChar] $ +#-----| 2 -> [RegExpCharacterRange] a-z + +# 108| [RegExpConstant, RegExpEscape] \_ + +# 108| [RegExpConstant, RegExpNormalChar] $ + +# 108| [RegExpConstant, RegExpNormalChar] a + +# 108| [RegExpCharacterRange] a-z +#-----| 0 -> [RegExpConstant, RegExpNormalChar] a +#-----| 1 -> [RegExpConstant, RegExpNormalChar] z + +# 108| [RegExpConstant, RegExpNormalChar] z + +# 108| [RegExpCharacterClass] [\_$a-z0-9] +#-----| 0 -> [RegExpConstant, RegExpEscape] \_ +#-----| 1 -> [RegExpConstant, RegExpNormalChar] $ +#-----| 2 -> [RegExpCharacterRange] a-z +#-----| 3 -> [RegExpCharacterRange] 0-9 + +# 108| [RegExpStar] [\_$a-z0-9]* +#-----| 0 -> [RegExpCharacterClass] [\_$a-z0-9] + +# 108| [RegExpConstant, RegExpEscape] \_ + +# 108| [RegExpConstant, RegExpNormalChar] $ + +# 108| [RegExpConstant, RegExpNormalChar] a + +# 108| [RegExpCharacterRange] a-z +#-----| 0 -> [RegExpConstant, RegExpNormalChar] a +#-----| 1 -> [RegExpConstant, RegExpNormalChar] z + +# 108| [RegExpConstant, RegExpNormalChar] z + +# 108| [RegExpConstant, RegExpNormalChar] 0 + +# 108| [RegExpCharacterRange] 0-9 +#-----| 0 -> [RegExpConstant, RegExpNormalChar] 0 +#-----| 1 -> [RegExpConstant, RegExpNormalChar] 9 + +# 108| [RegExpConstant, RegExpNormalChar] 9 + +# 108| [RegExpGroup] (\[.*?\]) +#-----| 0 -> [RegExpSequence] \[.*?\] + +# 108| [RegExpStar] (\[.*?\])* +#-----| 0 -> [RegExpGroup] (\[.*?\]) + +# 108| [RegExpConstant, RegExpEscape] \[ + +# 108| [RegExpSequence] \[.*?\] +#-----| 0 -> [RegExpConstant, RegExpEscape] \[ +#-----| 1 -> [RegExpStar] .*? +#-----| 2 -> [RegExpConstant, RegExpEscape] \] + +# 108| [RegExpDot] . + +# 108| [RegExpStar] .*? +#-----| 0 -> [RegExpDot] . + +# 108| [RegExpConstant, RegExpEscape] \] + +# 108| [RegExpDollar] $ + +# 114| [RegExpGroup] (([\w#:.~>+()\s-]+|\*|\[.*?\])+) +#-----| 0 -> [RegExpPlus] ([\w#:.~>+()\s-]+|\*|\[.*?\])+ + +# 114| [RegExpSequence] (([\w#:.~>+()\s-]+|\*|\[.*?\])+)\s*(,|$) +#-----| 0 -> [RegExpGroup] (([\w#:.~>+()\s-]+|\*|\[.*?\])+) +#-----| 1 -> [RegExpStar] \s* +#-----| 2 -> [RegExpGroup] (,|$) + +# 114| [RegExpGroup] ([\w#:.~>+()\s-]+|\*|\[.*?\]) +#-----| 0 -> [RegExpAlt] [\w#:.~>+()\s-]+|\*|\[.*?\] + +# 114| [RegExpPlus] ([\w#:.~>+()\s-]+|\*|\[.*?\])+ +#-----| 0 -> [RegExpGroup] ([\w#:.~>+()\s-]+|\*|\[.*?\]) + +# 114| [RegExpCharacterClass] [\w#:.~>+()\s-] +#-----| 0 -> [RegExpCharacterClassEscape] \w +#-----| 1 -> [RegExpConstant, RegExpNormalChar] # +#-----| 2 -> [RegExpConstant, RegExpNormalChar] : +#-----| 3 -> [RegExpConstant, RegExpNormalChar] . +#-----| 4 -> [RegExpConstant, RegExpNormalChar] ~ +#-----| 5 -> [RegExpConstant, RegExpNormalChar] > +#-----| 6 -> [RegExpConstant, RegExpNormalChar] + +#-----| 7 -> [RegExpConstant, RegExpNormalChar] ( +#-----| 8 -> [RegExpConstant, RegExpNormalChar] ) +#-----| 9 -> [RegExpCharacterClassEscape] \s +#-----| 10 -> [RegExpConstant, RegExpNormalChar] - + +# 114| [RegExpPlus] [\w#:.~>+()\s-]+ +#-----| 0 -> [RegExpCharacterClass] [\w#:.~>+()\s-] + +# 114| [RegExpAlt] [\w#:.~>+()\s-]+|\*|\[.*?\] +#-----| 0 -> [RegExpPlus] [\w#:.~>+()\s-]+ +#-----| 1 -> [RegExpConstant, RegExpEscape] \* +#-----| 2 -> [RegExpSequence] \[.*?\] + +# 114| [RegExpCharacterClassEscape] \w + +# 114| [RegExpConstant, RegExpNormalChar] # + +# 114| [RegExpConstant, RegExpNormalChar] : + +# 114| [RegExpConstant, RegExpNormalChar] . + +# 114| [RegExpConstant, RegExpNormalChar] ~ + +# 114| [RegExpConstant, RegExpNormalChar] > + +# 114| [RegExpConstant, RegExpNormalChar] + + +# 114| [RegExpConstant, RegExpNormalChar] ( + +# 114| [RegExpConstant, RegExpNormalChar] ) + +# 114| [RegExpCharacterClassEscape] \s + +# 114| [RegExpConstant, RegExpNormalChar] - + +# 114| [RegExpConstant, RegExpEscape] \* + +# 114| [RegExpConstant, RegExpEscape] \[ + +# 114| [RegExpSequence] \[.*?\] +#-----| 0 -> [RegExpConstant, RegExpEscape] \[ +#-----| 1 -> [RegExpStar] .*? +#-----| 2 -> [RegExpConstant, RegExpEscape] \] + +# 114| [RegExpDot] . + +# 114| [RegExpStar] .*? +#-----| 0 -> [RegExpDot] . + +# 114| [RegExpConstant, RegExpEscape] \] + +# 114| [RegExpCharacterClassEscape] \s + +# 114| [RegExpStar] \s* +#-----| 0 -> [RegExpCharacterClassEscape] \s + +# 114| [RegExpGroup] (,|$) +#-----| 0 -> [RegExpAlt] ,|$ + +# 114| [RegExpConstant, RegExpNormalChar] , + +# 114| [RegExpAlt] ,|$ +#-----| 0 -> [RegExpConstant, RegExpNormalChar] , +#-----| 1 -> [RegExpDollar] $ + +# 114| [RegExpDollar] $ + +# 120| [RegExpGroup] ("|') +#-----| 0 -> [RegExpAlt] "|' + +# 120| [RegExpSequence] ("|')(\\?.)*?\1 +#-----| 0 -> [RegExpGroup] ("|') +#-----| 1 -> [RegExpStar] (\\?.)*? +#-----| 2 -> [RegExpBackRef] \1 + +# 120| [RegExpConstant, RegExpNormalChar] " + +# 120| [RegExpAlt] "|' +#-----| 0 -> [RegExpConstant, RegExpNormalChar] " +#-----| 1 -> [RegExpConstant, RegExpNormalChar] ' + +# 120| [RegExpConstant, RegExpNormalChar] ' + +# 120| [RegExpGroup] (\\?.) +#-----| 0 -> [RegExpSequence] \\?. + +# 120| [RegExpStar] (\\?.)*? +#-----| 0 -> [RegExpGroup] (\\?.) + +# 120| [RegExpConstant, RegExpEscape] \\ + +# 120| [RegExpOpt] \\? +#-----| 0 -> [RegExpConstant, RegExpEscape] \\ + +# 120| [RegExpSequence] \\?. +#-----| 0 -> [RegExpOpt] \\? +#-----| 1 -> [RegExpDot] . + +# 120| [RegExpDot] . + +# 120| [RegExpBackRef] \1 + +# 125| [RegExpGroup] (\r\n|\r|\n) +#-----| 0 -> [RegExpAlt] \r\n|\r|\n + +# 125| [RegExpPlus] (\r\n|\r|\n)+ +#-----| 0 -> [RegExpGroup] (\r\n|\r|\n) + +# 125| [RegExpConstant, RegExpEscape] \r + +# 125| [RegExpSequence] \r\n +#-----| 0 -> [RegExpConstant, RegExpEscape] \r +#-----| 1 -> [RegExpConstant, RegExpEscape] \n + +# 125| [RegExpAlt] \r\n|\r|\n +#-----| 0 -> [RegExpSequence] \r\n +#-----| 1 -> [RegExpConstant, RegExpEscape] \r +#-----| 2 -> [RegExpConstant, RegExpEscape] \n + +# 125| [RegExpConstant, RegExpEscape] \n + +# 125| [RegExpConstant, RegExpEscape] \r + +# 125| [RegExpConstant, RegExpEscape] \n + +# 128| [RegExpGroup] (a|.) +#-----| 0 -> [RegExpAlt] a|. + +# 128| [RegExpStar] (a|.)* +#-----| 0 -> [RegExpGroup] (a|.) + +# 128| [RegExpConstant, RegExpNormalChar] a + +# 128| [RegExpAlt] a|. +#-----| 0 -> [RegExpConstant, RegExpNormalChar] a +#-----| 1 -> [RegExpDot] . + +# 128| [RegExpDot] . + +# 132| [RegExpCaret] ^ + +# 132| [RegExpSequence] ^([a-z]+)+$ +#-----| 0 -> [RegExpCaret] ^ +#-----| 1 -> [RegExpPlus] ([a-z]+)+ +#-----| 2 -> [RegExpDollar] $ + +# 132| [RegExpGroup] ([a-z]+) +#-----| 0 -> [RegExpPlus] [a-z]+ + +# 132| [RegExpPlus] ([a-z]+)+ +#-----| 0 -> [RegExpGroup] ([a-z]+) + +# 132| [RegExpCharacterClass] [a-z] +#-----| 0 -> [RegExpCharacterRange] a-z + +# 132| [RegExpPlus] [a-z]+ +#-----| 0 -> [RegExpCharacterClass] [a-z] + +# 132| [RegExpConstant, RegExpNormalChar] a + +# 132| [RegExpCharacterRange] a-z +#-----| 0 -> [RegExpConstant, RegExpNormalChar] a +#-----| 1 -> [RegExpConstant, RegExpNormalChar] z + +# 132| [RegExpConstant, RegExpNormalChar] z + +# 132| [RegExpDollar] $ + +# 133| [RegExpCaret] ^ + +# 133| [RegExpSequence] ^([a-z]*)*$ +#-----| 0 -> [RegExpCaret] ^ +#-----| 1 -> [RegExpStar] ([a-z]*)* +#-----| 2 -> [RegExpDollar] $ + +# 133| [RegExpGroup] ([a-z]*) +#-----| 0 -> [RegExpStar] [a-z]* + +# 133| [RegExpStar] ([a-z]*)* +#-----| 0 -> [RegExpGroup] ([a-z]*) + +# 133| [RegExpCharacterClass] [a-z] +#-----| 0 -> [RegExpCharacterRange] a-z + +# 133| [RegExpStar] [a-z]* +#-----| 0 -> [RegExpCharacterClass] [a-z] + +# 133| [RegExpConstant, RegExpNormalChar] a + +# 133| [RegExpCharacterRange] a-z +#-----| 0 -> [RegExpConstant, RegExpNormalChar] a +#-----| 1 -> [RegExpConstant, RegExpNormalChar] z + +# 133| [RegExpConstant, RegExpNormalChar] z + +# 133| [RegExpDollar] $ + +# 134| [RegExpCaret] ^ + +# 134| [RegExpSequence] ^([a-zA-Z0-9])(([\\.-]|[_]+)?([a-zA-Z0-9]+))*(@){1}[a-z0-9]+[.]{1}(([a-z]{2,3})|([a-z]{2,3}[.]{1}[a-z]{2,3}))$ +#-----| 0 -> [RegExpCaret] ^ +#-----| 1 -> [RegExpGroup] ([a-zA-Z0-9]) +#-----| 2 -> [RegExpStar] (([\\.-]|[_]+)?([a-zA-Z0-9]+))* +#-----| 3 -> [RegExpRange] (@){1} +#-----| 4 -> [RegExpPlus] [a-z0-9]+ +#-----| 5 -> [RegExpRange] [.]{1} +#-----| 6 -> [RegExpGroup] (([a-z]{2,3})|([a-z]{2,3}[.]{1}[a-z]{2,3})) +#-----| 7 -> [RegExpDollar] $ + +# 134| [RegExpGroup] ([a-zA-Z0-9]) +#-----| 0 -> [RegExpCharacterClass] [a-zA-Z0-9] + +# 134| [RegExpCharacterClass] [a-zA-Z0-9] +#-----| 0 -> [RegExpCharacterRange] a-z +#-----| 1 -> [RegExpCharacterRange] A-Z +#-----| 2 -> [RegExpCharacterRange] 0-9 + +# 134| [RegExpConstant, RegExpNormalChar] a + +# 134| [RegExpCharacterRange] a-z +#-----| 0 -> [RegExpConstant, RegExpNormalChar] a +#-----| 1 -> [RegExpConstant, RegExpNormalChar] z + +# 134| [RegExpConstant, RegExpNormalChar] z + +# 134| [RegExpConstant, RegExpNormalChar] A + +# 134| [RegExpCharacterRange] A-Z +#-----| 0 -> [RegExpConstant, RegExpNormalChar] A +#-----| 1 -> [RegExpConstant, RegExpNormalChar] Z + +# 134| [RegExpConstant, RegExpNormalChar] Z + +# 134| [RegExpConstant, RegExpNormalChar] 0 + +# 134| [RegExpCharacterRange] 0-9 +#-----| 0 -> [RegExpConstant, RegExpNormalChar] 0 +#-----| 1 -> [RegExpConstant, RegExpNormalChar] 9 + +# 134| [RegExpConstant, RegExpNormalChar] 9 + +# 134| [RegExpGroup] (([\\.-]|[_]+)?([a-zA-Z0-9]+)) +#-----| 0 -> [RegExpSequence] ([\\.-]|[_]+)?([a-zA-Z0-9]+) + +# 134| [RegExpStar] (([\\.-]|[_]+)?([a-zA-Z0-9]+))* +#-----| 0 -> [RegExpGroup] (([\\.-]|[_]+)?([a-zA-Z0-9]+)) + +# 134| [RegExpGroup] ([\\.-]|[_]+) +#-----| 0 -> [RegExpAlt] [\\.-]|[_]+ + +# 134| [RegExpOpt] ([\\.-]|[_]+)? +#-----| 0 -> [RegExpGroup] ([\\.-]|[_]+) + +# 134| [RegExpSequence] ([\\.-]|[_]+)?([a-zA-Z0-9]+) +#-----| 0 -> [RegExpOpt] ([\\.-]|[_]+)? +#-----| 1 -> [RegExpGroup] ([a-zA-Z0-9]+) + +# 134| [RegExpCharacterClass] [\\.-] +#-----| 0 -> [RegExpConstant, RegExpEscape] \\ +#-----| 1 -> [RegExpConstant, RegExpNormalChar] . +#-----| 2 -> [RegExpConstant, RegExpNormalChar] - + +# 134| [RegExpAlt] [\\.-]|[_]+ +#-----| 0 -> [RegExpCharacterClass] [\\.-] +#-----| 1 -> [RegExpPlus] [_]+ + +# 134| [RegExpConstant, RegExpEscape] \\ + +# 134| [RegExpConstant, RegExpNormalChar] . + +# 134| [RegExpConstant, RegExpNormalChar] - + +# 134| [RegExpCharacterClass] [_] +#-----| 0 -> [RegExpConstant, RegExpNormalChar] _ + +# 134| [RegExpPlus] [_]+ +#-----| 0 -> [RegExpCharacterClass] [_] + +# 134| [RegExpConstant, RegExpNormalChar] _ + +# 134| [RegExpGroup] ([a-zA-Z0-9]+) +#-----| 0 -> [RegExpPlus] [a-zA-Z0-9]+ + +# 134| [RegExpCharacterClass] [a-zA-Z0-9] +#-----| 0 -> [RegExpCharacterRange] a-z +#-----| 1 -> [RegExpCharacterRange] A-Z +#-----| 2 -> [RegExpCharacterRange] 0-9 + +# 134| [RegExpPlus] [a-zA-Z0-9]+ +#-----| 0 -> [RegExpCharacterClass] [a-zA-Z0-9] + +# 134| [RegExpConstant, RegExpNormalChar] a + +# 134| [RegExpCharacterRange] a-z +#-----| 0 -> [RegExpConstant, RegExpNormalChar] a +#-----| 1 -> [RegExpConstant, RegExpNormalChar] z + +# 134| [RegExpConstant, RegExpNormalChar] z + +# 134| [RegExpConstant, RegExpNormalChar] A + +# 134| [RegExpCharacterRange] A-Z +#-----| 0 -> [RegExpConstant, RegExpNormalChar] A +#-----| 1 -> [RegExpConstant, RegExpNormalChar] Z + +# 134| [RegExpConstant, RegExpNormalChar] Z + +# 134| [RegExpConstant, RegExpNormalChar] 0 + +# 134| [RegExpCharacterRange] 0-9 +#-----| 0 -> [RegExpConstant, RegExpNormalChar] 0 +#-----| 1 -> [RegExpConstant, RegExpNormalChar] 9 + +# 134| [RegExpConstant, RegExpNormalChar] 9 + +# 134| [RegExpGroup] (@) +#-----| 0 -> [RegExpConstant, RegExpNormalChar] @ + +# 134| [RegExpRange] (@){1} +#-----| 0 -> [RegExpGroup] (@) + +# 134| [RegExpConstant, RegExpNormalChar] @ + +# 134| [RegExpCharacterClass] [a-z0-9] +#-----| 0 -> [RegExpCharacterRange] a-z +#-----| 1 -> [RegExpCharacterRange] 0-9 + +# 134| [RegExpPlus] [a-z0-9]+ +#-----| 0 -> [RegExpCharacterClass] [a-z0-9] + +# 134| [RegExpConstant, RegExpNormalChar] a + +# 134| [RegExpCharacterRange] a-z +#-----| 0 -> [RegExpConstant, RegExpNormalChar] a +#-----| 1 -> [RegExpConstant, RegExpNormalChar] z + +# 134| [RegExpConstant, RegExpNormalChar] z + +# 134| [RegExpConstant, RegExpNormalChar] 0 + +# 134| [RegExpCharacterRange] 0-9 +#-----| 0 -> [RegExpConstant, RegExpNormalChar] 0 +#-----| 1 -> [RegExpConstant, RegExpNormalChar] 9 + +# 134| [RegExpConstant, RegExpNormalChar] 9 + +# 134| [RegExpCharacterClass] [.] +#-----| 0 -> [RegExpConstant, RegExpNormalChar] . + +# 134| [RegExpRange] [.]{1} +#-----| 0 -> [RegExpCharacterClass] [.] + +# 134| [RegExpConstant, RegExpNormalChar] . + +# 134| [RegExpGroup] (([a-z]{2,3})|([a-z]{2,3}[.]{1}[a-z]{2,3})) +#-----| 0 -> [RegExpAlt] ([a-z]{2,3})|([a-z]{2,3}[.]{1}[a-z]{2,3}) + +# 134| [RegExpGroup] ([a-z]{2,3}) +#-----| 0 -> [RegExpRange] [a-z]{2,3} + +# 134| [RegExpAlt] ([a-z]{2,3})|([a-z]{2,3}[.]{1}[a-z]{2,3}) +#-----| 0 -> [RegExpGroup] ([a-z]{2,3}) +#-----| 1 -> [RegExpGroup] ([a-z]{2,3}[.]{1}[a-z]{2,3}) + +# 134| [RegExpCharacterClass] [a-z] +#-----| 0 -> [RegExpCharacterRange] a-z + +# 134| [RegExpRange] [a-z]{2,3} +#-----| 0 -> [RegExpCharacterClass] [a-z] + +# 134| [RegExpConstant, RegExpNormalChar] a + +# 134| [RegExpCharacterRange] a-z +#-----| 0 -> [RegExpConstant, RegExpNormalChar] a +#-----| 1 -> [RegExpConstant, RegExpNormalChar] z + +# 134| [RegExpConstant, RegExpNormalChar] z + +# 134| [RegExpGroup] ([a-z]{2,3}[.]{1}[a-z]{2,3}) +#-----| 0 -> [RegExpSequence] [a-z]{2,3}[.]{1}[a-z]{2,3} + +# 134| [RegExpCharacterClass] [a-z] +#-----| 0 -> [RegExpCharacterRange] a-z + +# 134| [RegExpRange] [a-z]{2,3} +#-----| 0 -> [RegExpCharacterClass] [a-z] + +# 134| [RegExpSequence] [a-z]{2,3}[.]{1}[a-z]{2,3} +#-----| 0 -> [RegExpRange] [a-z]{2,3} +#-----| 1 -> [RegExpRange] [.]{1} +#-----| 2 -> [RegExpRange] [a-z]{2,3} + +# 134| [RegExpConstant, RegExpNormalChar] a + +# 134| [RegExpCharacterRange] a-z +#-----| 0 -> [RegExpConstant, RegExpNormalChar] a +#-----| 1 -> [RegExpConstant, RegExpNormalChar] z + +# 134| [RegExpConstant, RegExpNormalChar] z + +# 134| [RegExpCharacterClass] [.] +#-----| 0 -> [RegExpConstant, RegExpNormalChar] . + +# 134| [RegExpRange] [.]{1} +#-----| 0 -> [RegExpCharacterClass] [.] + +# 134| [RegExpConstant, RegExpNormalChar] . + +# 134| [RegExpCharacterClass] [a-z] +#-----| 0 -> [RegExpCharacterRange] a-z + +# 134| [RegExpRange] [a-z]{2,3} +#-----| 0 -> [RegExpCharacterClass] [a-z] + +# 134| [RegExpConstant, RegExpNormalChar] a + +# 134| [RegExpCharacterRange] a-z +#-----| 0 -> [RegExpConstant, RegExpNormalChar] a +#-----| 1 -> [RegExpConstant, RegExpNormalChar] z + +# 134| [RegExpConstant, RegExpNormalChar] z + +# 134| [RegExpDollar] $ + +# 135| [RegExpCaret] ^ + +# 135| [RegExpSequence] ^(([a-z])+.)+[A-Z]([a-z])+$ +#-----| 0 -> [RegExpCaret] ^ +#-----| 1 -> [RegExpPlus] (([a-z])+.)+ +#-----| 2 -> [RegExpCharacterClass] [A-Z] +#-----| 3 -> [RegExpPlus] ([a-z])+ +#-----| 4 -> [RegExpDollar] $ + +# 135| [RegExpGroup] (([a-z])+.) +#-----| 0 -> [RegExpSequence] ([a-z])+. + +# 135| [RegExpPlus] (([a-z])+.)+ +#-----| 0 -> [RegExpGroup] (([a-z])+.) + +# 135| [RegExpGroup] ([a-z]) +#-----| 0 -> [RegExpCharacterClass] [a-z] + +# 135| [RegExpPlus] ([a-z])+ +#-----| 0 -> [RegExpGroup] ([a-z]) + +# 135| [RegExpSequence] ([a-z])+. +#-----| 0 -> [RegExpPlus] ([a-z])+ +#-----| 1 -> [RegExpDot] . + +# 135| [RegExpCharacterClass] [a-z] +#-----| 0 -> [RegExpCharacterRange] a-z + +# 135| [RegExpConstant, RegExpNormalChar] a + +# 135| [RegExpCharacterRange] a-z +#-----| 0 -> [RegExpConstant, RegExpNormalChar] a +#-----| 1 -> [RegExpConstant, RegExpNormalChar] z + +# 135| [RegExpConstant, RegExpNormalChar] z + +# 135| [RegExpDot] . + +# 135| [RegExpCharacterClass] [A-Z] +#-----| 0 -> [RegExpCharacterRange] A-Z + +# 135| [RegExpConstant, RegExpNormalChar] A + +# 135| [RegExpCharacterRange] A-Z +#-----| 0 -> [RegExpConstant, RegExpNormalChar] A +#-----| 1 -> [RegExpConstant, RegExpNormalChar] Z + +# 135| [RegExpConstant, RegExpNormalChar] Z + +# 135| [RegExpGroup] ([a-z]) +#-----| 0 -> [RegExpCharacterClass] [a-z] + +# 135| [RegExpPlus] ([a-z])+ +#-----| 0 -> [RegExpGroup] ([a-z]) + +# 135| [RegExpCharacterClass] [a-z] +#-----| 0 -> [RegExpCharacterRange] a-z + +# 135| [RegExpConstant, RegExpNormalChar] a + +# 135| [RegExpCharacterRange] a-z +#-----| 0 -> [RegExpConstant, RegExpNormalChar] a +#-----| 1 -> [RegExpConstant, RegExpNormalChar] z + +# 135| [RegExpConstant, RegExpNormalChar] z + +# 135| [RegExpDollar] $ + +# 139| [RegExpGroup] (b|a?b) +#-----| 0 -> [RegExpAlt] b|a?b + +# 139| [RegExpStar] (b|a?b)* +#-----| 0 -> [RegExpGroup] (b|a?b) + +# 139| [RegExpSequence] (b|a?b)*c +#-----| 0 -> [RegExpStar] (b|a?b)* +#-----| 1 -> [RegExpConstant, RegExpNormalChar] c + +# 139| [RegExpConstant, RegExpNormalChar] b + +# 139| [RegExpAlt] b|a?b +#-----| 0 -> [RegExpConstant, RegExpNormalChar] b +#-----| 1 -> [RegExpSequence] a?b + +# 139| [RegExpConstant, RegExpNormalChar] a + +# 139| [RegExpOpt] a? +#-----| 0 -> [RegExpConstant, RegExpNormalChar] a + +# 139| [RegExpSequence] a?b +#-----| 0 -> [RegExpOpt] a? +#-----| 1 -> [RegExpConstant, RegExpNormalChar] b + +# 139| [RegExpConstant, RegExpNormalChar] b + +# 139| [RegExpConstant, RegExpNormalChar] c + +# 142| [RegExpGroup] (.|\n) +#-----| 0 -> [RegExpAlt] .|\n + +# 142| [RegExpStar] (.|\n)* +#-----| 0 -> [RegExpGroup] (.|\n) + +# 142| [RegExpSequence] (.|\n)*! +#-----| 0 -> [RegExpStar] (.|\n)* +#-----| 1 -> [RegExpConstant, RegExpNormalChar] ! + +# 142| [RegExpDot] . + +# 142| [RegExpAlt] .|\n +#-----| 0 -> [RegExpDot] . +#-----| 1 -> [RegExpConstant, RegExpEscape] \n + +# 142| [RegExpConstant, RegExpEscape] \n + +# 142| [RegExpConstant, RegExpNormalChar] ! + +# 146| [RegExpConstant, RegExpNormalChar] s + +# 146| [RegExpGroup] (.|\n) +#-----| 0 -> [RegExpAlt] .|\n + +# 146| [RegExpStar] (.|\n)* +#-----| 0 -> [RegExpGroup] (.|\n) + +# 146| [RegExpDot] . + +# 146| [RegExpAlt] .|\n +#-----| 0 -> [RegExpDot] . +#-----| 1 -> [RegExpConstant, RegExpEscape] \n + +# 146| [RegExpConstant, RegExpEscape] \n + +# 146| [RegExpConstant, RegExpNormalChar] ! + +# 149| [RegExpGroup] ([\w.]+) +#-----| 0 -> [RegExpPlus] [\w.]+ + +# 149| [RegExpStar] ([\w.]+)* +#-----| 0 -> [RegExpGroup] ([\w.]+) + +# 149| [RegExpCharacterClass] [\w.] +#-----| 0 -> [RegExpCharacterClassEscape] \w +#-----| 1 -> [RegExpConstant, RegExpNormalChar] . + +# 149| [RegExpPlus] [\w.]+ +#-----| 0 -> [RegExpCharacterClass] [\w.] + +# 149| [RegExpCharacterClassEscape] \w + +# 149| [RegExpConstant, RegExpNormalChar] . + +# 152| [RegExpGroup] ([\w.]+) +#-----| 0 -> [RegExpPlus] [\w.]+ + +# 152| [RegExpStar] ([\w.]+)* +#-----| 0 -> [RegExpGroup] ([\w.]+) + +# 152| [RegExpCharacterClass] [\w.] +#-----| 0 -> [RegExpCharacterClassEscape] \w +#-----| 1 -> [RegExpConstant, RegExpNormalChar] . + +# 152| [RegExpPlus] [\w.]+ +#-----| 0 -> [RegExpCharacterClass] [\w.] + +# 152| [RegExpCharacterClassEscape] \w + +# 152| [RegExpConstant, RegExpNormalChar] . + +# 156| [RegExpGroup] (([\s\S]|[^a])*) +#-----| 0 -> [RegExpStar] ([\s\S]|[^a])* + +# 156| [RegExpSequence] (([\s\S]|[^a])*)" +#-----| 0 -> [RegExpGroup] (([\s\S]|[^a])*) +#-----| 1 -> [RegExpConstant, RegExpNormalChar] " + +# 156| [RegExpGroup] ([\s\S]|[^a]) +#-----| 0 -> [RegExpAlt] [\s\S]|[^a] + +# 156| [RegExpStar] ([\s\S]|[^a])* +#-----| 0 -> [RegExpGroup] ([\s\S]|[^a]) + +# 156| [RegExpCharacterClass] [\s\S] +#-----| 0 -> [RegExpCharacterClassEscape] \s +#-----| 1 -> [RegExpCharacterClassEscape] \S + +# 156| [RegExpAlt] [\s\S]|[^a] +#-----| 0 -> [RegExpCharacterClass] [\s\S] +#-----| 1 -> [RegExpCharacterClass] [^a] + +# 156| [RegExpCharacterClassEscape] \s + +# 156| [RegExpCharacterClassEscape] \S + +# 156| [RegExpCharacterClass] [^a] +#-----| 0 -> [RegExpConstant, RegExpNormalChar] a + +# 156| [RegExpConstant, RegExpNormalChar] a + +# 156| [RegExpConstant, RegExpNormalChar] " + +# 159| [RegExpGroup] ([^"']+) +#-----| 0 -> [RegExpPlus] [^"']+ + +# 159| [RegExpStar] ([^"']+)* +#-----| 0 -> [RegExpGroup] ([^"']+) + +# 159| [RegExpCharacterClass] [^"'] +#-----| 0 -> [RegExpConstant, RegExpNormalChar] " +#-----| 1 -> [RegExpConstant, RegExpNormalChar] ' + +# 159| [RegExpPlus] [^"']+ +#-----| 0 -> [RegExpCharacterClass] [^"'] + +# 159| [RegExpConstant, RegExpNormalChar] " + +# 159| [RegExpConstant, RegExpNormalChar] ' + +# 163| [RegExpGroup] ((.|[^a])*) +#-----| 0 -> [RegExpStar] (.|[^a])* + +# 163| [RegExpSequence] ((.|[^a])*)" +#-----| 0 -> [RegExpGroup] ((.|[^a])*) +#-----| 1 -> [RegExpConstant, RegExpNormalChar] " + +# 163| [RegExpGroup] (.|[^a]) +#-----| 0 -> [RegExpAlt] .|[^a] + +# 163| [RegExpStar] (.|[^a])* +#-----| 0 -> [RegExpGroup] (.|[^a]) + +# 163| [RegExpDot] . + +# 163| [RegExpAlt] .|[^a] +#-----| 0 -> [RegExpDot] . +#-----| 1 -> [RegExpCharacterClass] [^a] + +# 163| [RegExpCharacterClass] [^a] +#-----| 0 -> [RegExpConstant, RegExpNormalChar] a + +# 163| [RegExpConstant, RegExpNormalChar] a + +# 163| [RegExpConstant, RegExpNormalChar] " + +# 166| [RegExpGroup] ((a|[^a])*) +#-----| 0 -> [RegExpStar] (a|[^a])* + +# 166| [RegExpSequence] ((a|[^a])*)" +#-----| 0 -> [RegExpGroup] ((a|[^a])*) +#-----| 1 -> [RegExpConstant, RegExpNormalChar] " + +# 166| [RegExpGroup] (a|[^a]) +#-----| 0 -> [RegExpAlt] a|[^a] + +# 166| [RegExpStar] (a|[^a])* +#-----| 0 -> [RegExpGroup] (a|[^a]) + +# 166| [RegExpConstant, RegExpNormalChar] a + +# 166| [RegExpAlt] a|[^a] +#-----| 0 -> [RegExpConstant, RegExpNormalChar] a +#-----| 1 -> [RegExpCharacterClass] [^a] + +# 166| [RegExpCharacterClass] [^a] +#-----| 0 -> [RegExpConstant, RegExpNormalChar] a + +# 166| [RegExpConstant, RegExpNormalChar] a + +# 166| [RegExpConstant, RegExpNormalChar] " + +# 170| [RegExpGroup] ((b|[^a])*) +#-----| 0 -> [RegExpStar] (b|[^a])* + +# 170| [RegExpSequence] ((b|[^a])*)" +#-----| 0 -> [RegExpGroup] ((b|[^a])*) +#-----| 1 -> [RegExpConstant, RegExpNormalChar] " + +# 170| [RegExpGroup] (b|[^a]) +#-----| 0 -> [RegExpAlt] b|[^a] + +# 170| [RegExpStar] (b|[^a])* +#-----| 0 -> [RegExpGroup] (b|[^a]) + +# 170| [RegExpConstant, RegExpNormalChar] b + +# 170| [RegExpAlt] b|[^a] +#-----| 0 -> [RegExpConstant, RegExpNormalChar] b +#-----| 1 -> [RegExpCharacterClass] [^a] + +# 170| [RegExpCharacterClass] [^a] +#-----| 0 -> [RegExpConstant, RegExpNormalChar] a + +# 170| [RegExpConstant, RegExpNormalChar] a + +# 170| [RegExpConstant, RegExpNormalChar] " + +# 174| [RegExpGroup] ((G|[^a])*) +#-----| 0 -> [RegExpStar] (G|[^a])* + +# 174| [RegExpSequence] ((G|[^a])*)" +#-----| 0 -> [RegExpGroup] ((G|[^a])*) +#-----| 1 -> [RegExpConstant, RegExpNormalChar] " + +# 174| [RegExpGroup] (G|[^a]) +#-----| 0 -> [RegExpAlt] G|[^a] + +# 174| [RegExpStar] (G|[^a])* +#-----| 0 -> [RegExpGroup] (G|[^a]) + +# 174| [RegExpConstant, RegExpNormalChar] G + +# 174| [RegExpAlt] G|[^a] +#-----| 0 -> [RegExpConstant, RegExpNormalChar] G +#-----| 1 -> [RegExpCharacterClass] [^a] + +# 174| [RegExpCharacterClass] [^a] +#-----| 0 -> [RegExpConstant, RegExpNormalChar] a + +# 174| [RegExpConstant, RegExpNormalChar] a + +# 174| [RegExpConstant, RegExpNormalChar] " + +# 178| [RegExpGroup] (([0-9]|[^a])*) +#-----| 0 -> [RegExpStar] ([0-9]|[^a])* + +# 178| [RegExpSequence] (([0-9]|[^a])*)" +#-----| 0 -> [RegExpGroup] (([0-9]|[^a])*) +#-----| 1 -> [RegExpConstant, RegExpNormalChar] " + +# 178| [RegExpGroup] ([0-9]|[^a]) +#-----| 0 -> [RegExpAlt] [0-9]|[^a] + +# 178| [RegExpStar] ([0-9]|[^a])* +#-----| 0 -> [RegExpGroup] ([0-9]|[^a]) + +# 178| [RegExpCharacterClass] [0-9] +#-----| 0 -> [RegExpCharacterRange] 0-9 + +# 178| [RegExpAlt] [0-9]|[^a] +#-----| 0 -> [RegExpCharacterClass] [0-9] +#-----| 1 -> [RegExpCharacterClass] [^a] + +# 178| [RegExpConstant, RegExpNormalChar] 0 + +# 178| [RegExpCharacterRange] 0-9 +#-----| 0 -> [RegExpConstant, RegExpNormalChar] 0 +#-----| 1 -> [RegExpConstant, RegExpNormalChar] 9 + +# 178| [RegExpConstant, RegExpNormalChar] 9 + +# 178| [RegExpCharacterClass] [^a] +#-----| 0 -> [RegExpConstant, RegExpNormalChar] a + +# 178| [RegExpConstant, RegExpNormalChar] a + +# 178| [RegExpConstant, RegExpNormalChar] " + +# 182| [RegExpGroup] (?:=(?:([!#\$%&'\*\+\-\.\^_`\|~0-9A-Za-z]+)|"((?:\\[\x00-\x7f]|[^\x00-\x08\x0a-\x1f\x7f"])*)")) +#-----| 0 -> [RegExpSequence] =(?:([!#\$%&'\*\+\-\.\^_`\|~0-9A-Za-z]+)|"((?:\\[\x00-\x7f]|[^\x00-\x08\x0a-\x1f\x7f"])*)") + +# 182| [RegExpOpt] (?:=(?:([!#\$%&'\*\+\-\.\^_`\|~0-9A-Za-z]+)|"((?:\\[\x00-\x7f]|[^\x00-\x08\x0a-\x1f\x7f"])*)"))? +#-----| 0 -> [RegExpGroup] (?:=(?:([!#\$%&'\*\+\-\.\^_`\|~0-9A-Za-z]+)|"((?:\\[\x00-\x7f]|[^\x00-\x08\x0a-\x1f\x7f"])*)")) + +# 182| [RegExpConstant, RegExpNormalChar] = + +# 182| [RegExpSequence] =(?:([!#\$%&'\*\+\-\.\^_`\|~0-9A-Za-z]+)|"((?:\\[\x00-\x7f]|[^\x00-\x08\x0a-\x1f\x7f"])*)") +#-----| 0 -> [RegExpConstant, RegExpNormalChar] = +#-----| 1 -> [RegExpGroup] (?:([!#\$%&'\*\+\-\.\^_`\|~0-9A-Za-z]+)|"((?:\\[\x00-\x7f]|[^\x00-\x08\x0a-\x1f\x7f"])*)") + +# 182| [RegExpGroup] (?:([!#\$%&'\*\+\-\.\^_`\|~0-9A-Za-z]+)|"((?:\\[\x00-\x7f]|[^\x00-\x08\x0a-\x1f\x7f"])*)") +#-----| 0 -> [RegExpAlt] ([!#\$%&'\*\+\-\.\^_`\|~0-9A-Za-z]+)|"((?:\\[\x00-\x7f]|[^\x00-\x08\x0a-\x1f\x7f"])*)" + +# 182| [RegExpGroup] ([!#\$%&'\*\+\-\.\^_`\|~0-9A-Za-z]+) +#-----| 0 -> [RegExpPlus] [!#\$%&'\*\+\-\.\^_`\|~0-9A-Za-z]+ + +# 182| [RegExpAlt] ([!#\$%&'\*\+\-\.\^_`\|~0-9A-Za-z]+)|"((?:\\[\x00-\x7f]|[^\x00-\x08\x0a-\x1f\x7f"])*)" +#-----| 0 -> [RegExpGroup] ([!#\$%&'\*\+\-\.\^_`\|~0-9A-Za-z]+) +#-----| 1 -> [RegExpSequence] "((?:\\[\x00-\x7f]|[^\x00-\x08\x0a-\x1f\x7f"])*)" + +# 182| [RegExpCharacterClass] [!#\$%&'\*\+\-\.\^_`\|~0-9A-Za-z] +#-----| 0 -> [RegExpConstant, RegExpNormalChar] ! +#-----| 1 -> [RegExpConstant, RegExpNormalChar] # +#-----| 2 -> [RegExpConstant, RegExpEscape] \$ +#-----| 3 -> [RegExpConstant, RegExpNormalChar] % +#-----| 4 -> [RegExpConstant, RegExpNormalChar] & +#-----| 5 -> [RegExpConstant, RegExpNormalChar] ' +#-----| 6 -> [RegExpConstant, RegExpEscape] \* +#-----| 7 -> [RegExpConstant, RegExpEscape] \+ +#-----| 8 -> [RegExpConstant, RegExpEscape] \- +#-----| 9 -> [RegExpConstant, RegExpEscape] \. +#-----| 10 -> [RegExpConstant, RegExpEscape] \^ +#-----| 11 -> [RegExpConstant, RegExpNormalChar] _ +#-----| 12 -> [RegExpConstant, RegExpNormalChar] ` +#-----| 13 -> [RegExpConstant, RegExpEscape] \| +#-----| 14 -> [RegExpConstant, RegExpNormalChar] ~ +#-----| 15 -> [RegExpCharacterRange] 0-9 +#-----| 16 -> [RegExpCharacterRange] A-Z +#-----| 17 -> [RegExpCharacterRange] a-z + +# 182| [RegExpPlus] [!#\$%&'\*\+\-\.\^_`\|~0-9A-Za-z]+ +#-----| 0 -> [RegExpCharacterClass] [!#\$%&'\*\+\-\.\^_`\|~0-9A-Za-z] + +# 182| [RegExpConstant, RegExpNormalChar] ! + +# 182| [RegExpConstant, RegExpNormalChar] # + +# 182| [RegExpConstant, RegExpEscape] \$ + +# 182| [RegExpConstant, RegExpNormalChar] % + +# 182| [RegExpConstant, RegExpNormalChar] & + +# 182| [RegExpConstant, RegExpNormalChar] ' + +# 182| [RegExpConstant, RegExpEscape] \* + +# 182| [RegExpConstant, RegExpEscape] \+ + +# 182| [RegExpConstant, RegExpEscape] \- + +# 182| [RegExpConstant, RegExpEscape] \. + +# 182| [RegExpConstant, RegExpEscape] \^ + +# 182| [RegExpConstant, RegExpNormalChar] _ + +# 182| [RegExpConstant, RegExpNormalChar] ` + +# 182| [RegExpConstant, RegExpEscape] \| + +# 182| [RegExpConstant, RegExpNormalChar] ~ + +# 182| [RegExpConstant, RegExpNormalChar] 0 + +# 182| [RegExpCharacterRange] 0-9 +#-----| 0 -> [RegExpConstant, RegExpNormalChar] 0 +#-----| 1 -> [RegExpConstant, RegExpNormalChar] 9 + +# 182| [RegExpConstant, RegExpNormalChar] 9 + +# 182| [RegExpConstant, RegExpNormalChar] A + +# 182| [RegExpCharacterRange] A-Z +#-----| 0 -> [RegExpConstant, RegExpNormalChar] A +#-----| 1 -> [RegExpConstant, RegExpNormalChar] Z + +# 182| [RegExpConstant, RegExpNormalChar] Z + +# 182| [RegExpConstant, RegExpNormalChar] a + +# 182| [RegExpCharacterRange] a-z +#-----| 0 -> [RegExpConstant, RegExpNormalChar] a +#-----| 1 -> [RegExpConstant, RegExpNormalChar] z + +# 182| [RegExpConstant, RegExpNormalChar] z + +# 182| [RegExpConstant, RegExpNormalChar] " + +# 182| [RegExpSequence] "((?:\\[\x00-\x7f]|[^\x00-\x08\x0a-\x1f\x7f"])*)" +#-----| 0 -> [RegExpConstant, RegExpNormalChar] " +#-----| 1 -> [RegExpGroup] ((?:\\[\x00-\x7f]|[^\x00-\x08\x0a-\x1f\x7f"])*) +#-----| 2 -> [RegExpConstant, RegExpNormalChar] " + +# 182| [RegExpGroup] ((?:\\[\x00-\x7f]|[^\x00-\x08\x0a-\x1f\x7f"])*) +#-----| 0 -> [RegExpStar] (?:\\[\x00-\x7f]|[^\x00-\x08\x0a-\x1f\x7f"])* + +# 182| [RegExpGroup] (?:\\[\x00-\x7f]|[^\x00-\x08\x0a-\x1f\x7f"]) +#-----| 0 -> [RegExpAlt] \\[\x00-\x7f]|[^\x00-\x08\x0a-\x1f\x7f"] + +# 182| [RegExpStar] (?:\\[\x00-\x7f]|[^\x00-\x08\x0a-\x1f\x7f"])* +#-----| 0 -> [RegExpGroup] (?:\\[\x00-\x7f]|[^\x00-\x08\x0a-\x1f\x7f"]) + +# 182| [RegExpConstant, RegExpEscape] \\ + +# 182| [RegExpSequence] \\[\x00-\x7f] +#-----| 0 -> [RegExpConstant, RegExpEscape] \\ +#-----| 1 -> [RegExpCharacterClass] [\x00-\x7f] + +# 182| [RegExpAlt] \\[\x00-\x7f]|[^\x00-\x08\x0a-\x1f\x7f"] +#-----| 0 -> [RegExpSequence] \\[\x00-\x7f] +#-----| 1 -> [RegExpCharacterClass] [^\x00-\x08\x0a-\x1f\x7f"] + +# 182| [RegExpCharacterClass] [\x00-\x7f] +#-----| 0 -> [RegExpCharacterRange] \x00-\x7f + +# 182| [RegExpConstant, RegExpEscape] \x00 + +# 182| [RegExpCharacterRange] \x00-\x7f +#-----| 0 -> [RegExpConstant, RegExpEscape] \x00 +#-----| 1 -> [RegExpConstant, RegExpEscape] \x7f + +# 182| [RegExpConstant, RegExpEscape] \x7f + +# 182| [RegExpCharacterClass] [^\x00-\x08\x0a-\x1f\x7f"] +#-----| 0 -> [RegExpCharacterRange] \x00-\x08 +#-----| 1 -> [RegExpCharacterRange] \x0a-\x1f +#-----| 2 -> [RegExpConstant, RegExpEscape] \x7f +#-----| 3 -> [RegExpConstant, RegExpNormalChar] " + +# 182| [RegExpConstant, RegExpEscape] \x00 + +# 182| [RegExpCharacterRange] \x00-\x08 +#-----| 0 -> [RegExpConstant, RegExpEscape] \x00 +#-----| 1 -> [RegExpConstant, RegExpEscape] \x08 + +# 182| [RegExpConstant, RegExpEscape] \x08 + +# 182| [RegExpConstant, RegExpEscape] \x0a + +# 182| [RegExpCharacterRange] \x0a-\x1f +#-----| 0 -> [RegExpConstant, RegExpEscape] \x0a +#-----| 1 -> [RegExpConstant, RegExpEscape] \x1f + +# 182| [RegExpConstant, RegExpEscape] \x1f + +# 182| [RegExpConstant, RegExpEscape] \x7f + +# 182| [RegExpConstant, RegExpNormalChar] " + +# 182| [RegExpConstant, RegExpNormalChar] " + +# 186| [RegExpConstant, RegExpNormalChar] " + +# 186| [RegExpSequence] "((?:\\[\x00-\x7f]|[^\x00-\x08\x0a-\x1f\x7f"])*)" +#-----| 0 -> [RegExpConstant, RegExpNormalChar] " +#-----| 1 -> [RegExpGroup] ((?:\\[\x00-\x7f]|[^\x00-\x08\x0a-\x1f\x7f"])*) +#-----| 2 -> [RegExpConstant, RegExpNormalChar] " + +# 186| [RegExpGroup] ((?:\\[\x00-\x7f]|[^\x00-\x08\x0a-\x1f\x7f"])*) +#-----| 0 -> [RegExpStar] (?:\\[\x00-\x7f]|[^\x00-\x08\x0a-\x1f\x7f"])* + +# 186| [RegExpGroup] (?:\\[\x00-\x7f]|[^\x00-\x08\x0a-\x1f\x7f"]) +#-----| 0 -> [RegExpAlt] \\[\x00-\x7f]|[^\x00-\x08\x0a-\x1f\x7f"] + +# 186| [RegExpStar] (?:\\[\x00-\x7f]|[^\x00-\x08\x0a-\x1f\x7f"])* +#-----| 0 -> [RegExpGroup] (?:\\[\x00-\x7f]|[^\x00-\x08\x0a-\x1f\x7f"]) + +# 186| [RegExpConstant, RegExpEscape] \\ + +# 186| [RegExpSequence] \\[\x00-\x7f] +#-----| 0 -> [RegExpConstant, RegExpEscape] \\ +#-----| 1 -> [RegExpCharacterClass] [\x00-\x7f] + +# 186| [RegExpAlt] \\[\x00-\x7f]|[^\x00-\x08\x0a-\x1f\x7f"] +#-----| 0 -> [RegExpSequence] \\[\x00-\x7f] +#-----| 1 -> [RegExpCharacterClass] [^\x00-\x08\x0a-\x1f\x7f"] + +# 186| [RegExpCharacterClass] [\x00-\x7f] +#-----| 0 -> [RegExpCharacterRange] \x00-\x7f + +# 186| [RegExpConstant, RegExpEscape] \x00 + +# 186| [RegExpCharacterRange] \x00-\x7f +#-----| 0 -> [RegExpConstant, RegExpEscape] \x00 +#-----| 1 -> [RegExpConstant, RegExpEscape] \x7f + +# 186| [RegExpConstant, RegExpEscape] \x7f + +# 186| [RegExpCharacterClass] [^\x00-\x08\x0a-\x1f\x7f"] +#-----| 0 -> [RegExpCharacterRange] \x00-\x08 +#-----| 1 -> [RegExpCharacterRange] \x0a-\x1f +#-----| 2 -> [RegExpConstant, RegExpEscape] \x7f +#-----| 3 -> [RegExpConstant, RegExpNormalChar] " + +# 186| [RegExpConstant, RegExpEscape] \x00 + +# 186| [RegExpCharacterRange] \x00-\x08 +#-----| 0 -> [RegExpConstant, RegExpEscape] \x00 +#-----| 1 -> [RegExpConstant, RegExpEscape] \x08 + +# 186| [RegExpConstant, RegExpEscape] \x08 + +# 186| [RegExpConstant, RegExpEscape] \x0a + +# 186| [RegExpCharacterRange] \x0a-\x1f +#-----| 0 -> [RegExpConstant, RegExpEscape] \x0a +#-----| 1 -> [RegExpConstant, RegExpEscape] \x1f + +# 186| [RegExpConstant, RegExpEscape] \x1f + +# 186| [RegExpConstant, RegExpEscape] \x7f + +# 186| [RegExpConstant, RegExpNormalChar] " + +# 186| [RegExpConstant, RegExpNormalChar] " + +# 189| [RegExpConstant, RegExpNormalChar] " + +# 189| [RegExpSequence] "((?:\\[\x00-\x7f]|[^\x00-\x08\x0a-\x1f\x7f"\\])*)" +#-----| 0 -> [RegExpConstant, RegExpNormalChar] " +#-----| 1 -> [RegExpGroup] ((?:\\[\x00-\x7f]|[^\x00-\x08\x0a-\x1f\x7f"\\])*) +#-----| 2 -> [RegExpConstant, RegExpNormalChar] " + +# 189| [RegExpGroup] ((?:\\[\x00-\x7f]|[^\x00-\x08\x0a-\x1f\x7f"\\])*) +#-----| 0 -> [RegExpStar] (?:\\[\x00-\x7f]|[^\x00-\x08\x0a-\x1f\x7f"\\])* + +# 189| [RegExpGroup] (?:\\[\x00-\x7f]|[^\x00-\x08\x0a-\x1f\x7f"\\]) +#-----| 0 -> [RegExpAlt] \\[\x00-\x7f]|[^\x00-\x08\x0a-\x1f\x7f"\\] + +# 189| [RegExpStar] (?:\\[\x00-\x7f]|[^\x00-\x08\x0a-\x1f\x7f"\\])* +#-----| 0 -> [RegExpGroup] (?:\\[\x00-\x7f]|[^\x00-\x08\x0a-\x1f\x7f"\\]) + +# 189| [RegExpConstant, RegExpEscape] \\ + +# 189| [RegExpSequence] \\[\x00-\x7f] +#-----| 0 -> [RegExpConstant, RegExpEscape] \\ +#-----| 1 -> [RegExpCharacterClass] [\x00-\x7f] + +# 189| [RegExpAlt] \\[\x00-\x7f]|[^\x00-\x08\x0a-\x1f\x7f"\\] +#-----| 0 -> [RegExpSequence] \\[\x00-\x7f] +#-----| 1 -> [RegExpCharacterClass] [^\x00-\x08\x0a-\x1f\x7f"\\] + +# 189| [RegExpCharacterClass] [\x00-\x7f] +#-----| 0 -> [RegExpCharacterRange] \x00-\x7f + +# 189| [RegExpConstant, RegExpEscape] \x00 + +# 189| [RegExpCharacterRange] \x00-\x7f +#-----| 0 -> [RegExpConstant, RegExpEscape] \x00 +#-----| 1 -> [RegExpConstant, RegExpEscape] \x7f + +# 189| [RegExpConstant, RegExpEscape] \x7f + +# 189| [RegExpCharacterClass] [^\x00-\x08\x0a-\x1f\x7f"\\] +#-----| 0 -> [RegExpCharacterRange] \x00-\x08 +#-----| 1 -> [RegExpCharacterRange] \x0a-\x1f +#-----| 2 -> [RegExpConstant, RegExpEscape] \x7f +#-----| 3 -> [RegExpConstant, RegExpNormalChar] " +#-----| 4 -> [RegExpConstant, RegExpEscape] \\ + +# 189| [RegExpConstant, RegExpEscape] \x00 + +# 189| [RegExpCharacterRange] \x00-\x08 +#-----| 0 -> [RegExpConstant, RegExpEscape] \x00 +#-----| 1 -> [RegExpConstant, RegExpEscape] \x08 + +# 189| [RegExpConstant, RegExpEscape] \x08 + +# 189| [RegExpConstant, RegExpEscape] \x0a + +# 189| [RegExpCharacterRange] \x0a-\x1f +#-----| 0 -> [RegExpConstant, RegExpEscape] \x0a +#-----| 1 -> [RegExpConstant, RegExpEscape] \x1f + +# 189| [RegExpConstant, RegExpEscape] \x1f + +# 189| [RegExpConstant, RegExpEscape] \x7f + +# 189| [RegExpConstant, RegExpNormalChar] " + +# 189| [RegExpConstant, RegExpEscape] \\ + +# 189| [RegExpConstant, RegExpNormalChar] " + +# 193| [RegExpGroup] (([a-z]|[d-h])*) +#-----| 0 -> [RegExpStar] ([a-z]|[d-h])* + +# 193| [RegExpSequence] (([a-z]|[d-h])*)" +#-----| 0 -> [RegExpGroup] (([a-z]|[d-h])*) +#-----| 1 -> [RegExpConstant, RegExpNormalChar] " + +# 193| [RegExpGroup] ([a-z]|[d-h]) +#-----| 0 -> [RegExpAlt] [a-z]|[d-h] + +# 193| [RegExpStar] ([a-z]|[d-h])* +#-----| 0 -> [RegExpGroup] ([a-z]|[d-h]) + +# 193| [RegExpCharacterClass] [a-z] +#-----| 0 -> [RegExpCharacterRange] a-z + +# 193| [RegExpAlt] [a-z]|[d-h] +#-----| 0 -> [RegExpCharacterClass] [a-z] +#-----| 1 -> [RegExpCharacterClass] [d-h] + +# 193| [RegExpConstant, RegExpNormalChar] a + +# 193| [RegExpCharacterRange] a-z +#-----| 0 -> [RegExpConstant, RegExpNormalChar] a +#-----| 1 -> [RegExpConstant, RegExpNormalChar] z + +# 193| [RegExpConstant, RegExpNormalChar] z + +# 193| [RegExpCharacterClass] [d-h] +#-----| 0 -> [RegExpCharacterRange] d-h + +# 193| [RegExpConstant, RegExpNormalChar] d + +# 193| [RegExpCharacterRange] d-h +#-----| 0 -> [RegExpConstant, RegExpNormalChar] d +#-----| 1 -> [RegExpConstant, RegExpNormalChar] h + +# 193| [RegExpConstant, RegExpNormalChar] h + +# 193| [RegExpConstant, RegExpNormalChar] " + +# 197| [RegExpGroup] (([^a-z]|[^0-9])*) +#-----| 0 -> [RegExpStar] ([^a-z]|[^0-9])* + +# 197| [RegExpSequence] (([^a-z]|[^0-9])*)" +#-----| 0 -> [RegExpGroup] (([^a-z]|[^0-9])*) +#-----| 1 -> [RegExpConstant, RegExpNormalChar] " + +# 197| [RegExpGroup] ([^a-z]|[^0-9]) +#-----| 0 -> [RegExpAlt] [^a-z]|[^0-9] + +# 197| [RegExpStar] ([^a-z]|[^0-9])* +#-----| 0 -> [RegExpGroup] ([^a-z]|[^0-9]) + +# 197| [RegExpCharacterClass] [^a-z] +#-----| 0 -> [RegExpCharacterRange] a-z + +# 197| [RegExpAlt] [^a-z]|[^0-9] +#-----| 0 -> [RegExpCharacterClass] [^a-z] +#-----| 1 -> [RegExpCharacterClass] [^0-9] + +# 197| [RegExpConstant, RegExpNormalChar] a + +# 197| [RegExpCharacterRange] a-z +#-----| 0 -> [RegExpConstant, RegExpNormalChar] a +#-----| 1 -> [RegExpConstant, RegExpNormalChar] z + +# 197| [RegExpConstant, RegExpNormalChar] z + +# 197| [RegExpCharacterClass] [^0-9] +#-----| 0 -> [RegExpCharacterRange] 0-9 + +# 197| [RegExpConstant, RegExpNormalChar] 0 + +# 197| [RegExpCharacterRange] 0-9 +#-----| 0 -> [RegExpConstant, RegExpNormalChar] 0 +#-----| 1 -> [RegExpConstant, RegExpNormalChar] 9 + +# 197| [RegExpConstant, RegExpNormalChar] 9 + +# 197| [RegExpConstant, RegExpNormalChar] " + +# 201| [RegExpGroup] ((\d|[0-9])*) +#-----| 0 -> [RegExpStar] (\d|[0-9])* + +# 201| [RegExpSequence] ((\d|[0-9])*)" +#-----| 0 -> [RegExpGroup] ((\d|[0-9])*) +#-----| 1 -> [RegExpConstant, RegExpNormalChar] " + +# 201| [RegExpGroup] (\d|[0-9]) +#-----| 0 -> [RegExpAlt] \d|[0-9] + +# 201| [RegExpStar] (\d|[0-9])* +#-----| 0 -> [RegExpGroup] (\d|[0-9]) + +# 201| [RegExpCharacterClassEscape] \d + +# 201| [RegExpAlt] \d|[0-9] +#-----| 0 -> [RegExpCharacterClassEscape] \d +#-----| 1 -> [RegExpCharacterClass] [0-9] + +# 201| [RegExpCharacterClass] [0-9] +#-----| 0 -> [RegExpCharacterRange] 0-9 + +# 201| [RegExpConstant, RegExpNormalChar] 0 + +# 201| [RegExpCharacterRange] 0-9 +#-----| 0 -> [RegExpConstant, RegExpNormalChar] 0 +#-----| 1 -> [RegExpConstant, RegExpNormalChar] 9 + +# 201| [RegExpConstant, RegExpNormalChar] 9 + +# 201| [RegExpConstant, RegExpNormalChar] " + +# 205| [RegExpGroup] ((\s|\s)*) +#-----| 0 -> [RegExpStar] (\s|\s)* + +# 205| [RegExpSequence] ((\s|\s)*)" +#-----| 0 -> [RegExpGroup] ((\s|\s)*) +#-----| 1 -> [RegExpConstant, RegExpNormalChar] " + +# 205| [RegExpGroup] (\s|\s) +#-----| 0 -> [RegExpAlt] \s|\s + +# 205| [RegExpStar] (\s|\s)* +#-----| 0 -> [RegExpGroup] (\s|\s) + +# 205| [RegExpCharacterClassEscape] \s + +# 205| [RegExpAlt] \s|\s +#-----| 0 -> [RegExpCharacterClassEscape] \s +#-----| 1 -> [RegExpCharacterClassEscape] \s + +# 205| [RegExpCharacterClassEscape] \s + +# 205| [RegExpConstant, RegExpNormalChar] " + +# 209| [RegExpGroup] ((\w|G)*) +#-----| 0 -> [RegExpStar] (\w|G)* + +# 209| [RegExpSequence] ((\w|G)*)" +#-----| 0 -> [RegExpGroup] ((\w|G)*) +#-----| 1 -> [RegExpConstant, RegExpNormalChar] " + +# 209| [RegExpGroup] (\w|G) +#-----| 0 -> [RegExpAlt] \w|G + +# 209| [RegExpStar] (\w|G)* +#-----| 0 -> [RegExpGroup] (\w|G) + +# 209| [RegExpCharacterClassEscape] \w + +# 209| [RegExpAlt] \w|G +#-----| 0 -> [RegExpCharacterClassEscape] \w +#-----| 1 -> [RegExpConstant, RegExpNormalChar] G + +# 209| [RegExpConstant, RegExpNormalChar] G + +# 209| [RegExpConstant, RegExpNormalChar] " + +# 212| [RegExpGroup] ((\s|\d)*) +#-----| 0 -> [RegExpStar] (\s|\d)* + +# 212| [RegExpSequence] ((\s|\d)*)" +#-----| 0 -> [RegExpGroup] ((\s|\d)*) +#-----| 1 -> [RegExpConstant, RegExpNormalChar] " + +# 212| [RegExpGroup] (\s|\d) +#-----| 0 -> [RegExpAlt] \s|\d + +# 212| [RegExpStar] (\s|\d)* +#-----| 0 -> [RegExpGroup] (\s|\d) + +# 212| [RegExpCharacterClassEscape] \s + +# 212| [RegExpAlt] \s|\d +#-----| 0 -> [RegExpCharacterClassEscape] \s +#-----| 1 -> [RegExpCharacterClassEscape] \d + +# 212| [RegExpCharacterClassEscape] \d + +# 212| [RegExpConstant, RegExpNormalChar] " + +# 216| [RegExpGroup] ((\d|\d)*) +#-----| 0 -> [RegExpStar] (\d|\d)* + +# 216| [RegExpSequence] ((\d|\d)*)" +#-----| 0 -> [RegExpGroup] ((\d|\d)*) +#-----| 1 -> [RegExpConstant, RegExpNormalChar] " + +# 216| [RegExpGroup] (\d|\d) +#-----| 0 -> [RegExpAlt] \d|\d + +# 216| [RegExpStar] (\d|\d)* +#-----| 0 -> [RegExpGroup] (\d|\d) + +# 216| [RegExpCharacterClassEscape] \d + +# 216| [RegExpAlt] \d|\d +#-----| 0 -> [RegExpCharacterClassEscape] \d +#-----| 1 -> [RegExpCharacterClassEscape] \d + +# 216| [RegExpCharacterClassEscape] \d + +# 216| [RegExpConstant, RegExpNormalChar] " + +# 220| [RegExpGroup] ((\d|\w)*) +#-----| 0 -> [RegExpStar] (\d|\w)* + +# 220| [RegExpSequence] ((\d|\w)*)" +#-----| 0 -> [RegExpGroup] ((\d|\w)*) +#-----| 1 -> [RegExpConstant, RegExpNormalChar] " + +# 220| [RegExpGroup] (\d|\w) +#-----| 0 -> [RegExpAlt] \d|\w + +# 220| [RegExpStar] (\d|\w)* +#-----| 0 -> [RegExpGroup] (\d|\w) + +# 220| [RegExpCharacterClassEscape] \d + +# 220| [RegExpAlt] \d|\w +#-----| 0 -> [RegExpCharacterClassEscape] \d +#-----| 1 -> [RegExpCharacterClassEscape] \w + +# 220| [RegExpCharacterClassEscape] \w + +# 220| [RegExpConstant, RegExpNormalChar] " + +# 224| [RegExpGroup] ((\d|5)*) +#-----| 0 -> [RegExpStar] (\d|5)* + +# 224| [RegExpSequence] ((\d|5)*)" +#-----| 0 -> [RegExpGroup] ((\d|5)*) +#-----| 1 -> [RegExpConstant, RegExpNormalChar] " + +# 224| [RegExpGroup] (\d|5) +#-----| 0 -> [RegExpAlt] \d|5 + +# 224| [RegExpStar] (\d|5)* +#-----| 0 -> [RegExpGroup] (\d|5) + +# 224| [RegExpCharacterClassEscape] \d + +# 224| [RegExpAlt] \d|5 +#-----| 0 -> [RegExpCharacterClassEscape] \d +#-----| 1 -> [RegExpConstant, RegExpNormalChar] 5 + +# 224| [RegExpConstant, RegExpNormalChar] 5 + +# 224| [RegExpConstant, RegExpNormalChar] " + +# 228| [RegExpGroup] ((\s|[\f])*) +#-----| 0 -> [RegExpStar] (\s|[\f])* + +# 228| [RegExpSequence] ((\s|[\f])*)" +#-----| 0 -> [RegExpGroup] ((\s|[\f])*) +#-----| 1 -> [RegExpConstant, RegExpNormalChar] " + +# 228| [RegExpGroup] (\s|[\f]) +#-----| 0 -> [RegExpAlt] \s|[\f] + +# 228| [RegExpStar] (\s|[\f])* +#-----| 0 -> [RegExpGroup] (\s|[\f]) + +# 228| [RegExpCharacterClassEscape] \s + +# 228| [RegExpAlt] \s|[\f] +#-----| 0 -> [RegExpCharacterClassEscape] \s +#-----| 1 -> [RegExpCharacterClass] [\f] + +# 228| [RegExpCharacterClass] [\f] +#-----| 0 -> [RegExpConstant, RegExpEscape] \f + +# 228| [RegExpConstant, RegExpEscape] \f + +# 228| [RegExpConstant, RegExpNormalChar] " + +# 232| [RegExpGroup] ((\s|[\v]|\\v)*) +#-----| 0 -> [RegExpStar] (\s|[\v]|\\v)* + +# 232| [RegExpSequence] ((\s|[\v]|\\v)*)" +#-----| 0 -> [RegExpGroup] ((\s|[\v]|\\v)*) +#-----| 1 -> [RegExpConstant, RegExpNormalChar] " + +# 232| [RegExpGroup] (\s|[\v]|\\v) +#-----| 0 -> [RegExpAlt] \s|[\v]|\\v + +# 232| [RegExpStar] (\s|[\v]|\\v)* +#-----| 0 -> [RegExpGroup] (\s|[\v]|\\v) + +# 232| [RegExpCharacterClassEscape] \s + +# 232| [RegExpAlt] \s|[\v]|\\v +#-----| 0 -> [RegExpCharacterClassEscape] \s +#-----| 1 -> [RegExpCharacterClass] [\v] +#-----| 2 -> [RegExpSequence] \\v + +# 232| [RegExpCharacterClass] [\v] +#-----| 0 -> [RegExpConstant, RegExpEscape] \v + +# 232| [RegExpConstant, RegExpEscape] \v + +# 232| [RegExpConstant, RegExpEscape] \\ + +# 232| [RegExpSequence] \\v +#-----| 0 -> [RegExpConstant, RegExpEscape] \\ +#-----| 1 -> [RegExpConstant, RegExpNormalChar] v + +# 232| [RegExpConstant, RegExpNormalChar] v + +# 232| [RegExpConstant, RegExpNormalChar] " + +# 236| [RegExpGroup] ((\f|[\f])*) +#-----| 0 -> [RegExpStar] (\f|[\f])* + +# 236| [RegExpSequence] ((\f|[\f])*)" +#-----| 0 -> [RegExpGroup] ((\f|[\f])*) +#-----| 1 -> [RegExpConstant, RegExpNormalChar] " + +# 236| [RegExpGroup] (\f|[\f]) +#-----| 0 -> [RegExpAlt] \f|[\f] + +# 236| [RegExpStar] (\f|[\f])* +#-----| 0 -> [RegExpGroup] (\f|[\f]) + +# 236| [RegExpConstant, RegExpEscape] \f + +# 236| [RegExpAlt] \f|[\f] +#-----| 0 -> [RegExpConstant, RegExpEscape] \f +#-----| 1 -> [RegExpCharacterClass] [\f] + +# 236| [RegExpCharacterClass] [\f] +#-----| 0 -> [RegExpConstant, RegExpEscape] \f + +# 236| [RegExpConstant, RegExpEscape] \f + +# 236| [RegExpConstant, RegExpNormalChar] " + +# 240| [RegExpGroup] ((\W|\D)*) +#-----| 0 -> [RegExpStar] (\W|\D)* + +# 240| [RegExpSequence] ((\W|\D)*)" +#-----| 0 -> [RegExpGroup] ((\W|\D)*) +#-----| 1 -> [RegExpConstant, RegExpNormalChar] " + +# 240| [RegExpGroup] (\W|\D) +#-----| 0 -> [RegExpAlt] \W|\D + +# 240| [RegExpStar] (\W|\D)* +#-----| 0 -> [RegExpGroup] (\W|\D) + +# 240| [RegExpCharacterClassEscape] \W + +# 240| [RegExpAlt] \W|\D +#-----| 0 -> [RegExpCharacterClassEscape] \W +#-----| 1 -> [RegExpCharacterClassEscape] \D + +# 240| [RegExpCharacterClassEscape] \D + +# 240| [RegExpConstant, RegExpNormalChar] " + +# 244| [RegExpGroup] ((\S|\w)*) +#-----| 0 -> [RegExpStar] (\S|\w)* + +# 244| [RegExpSequence] ((\S|\w)*)" +#-----| 0 -> [RegExpGroup] ((\S|\w)*) +#-----| 1 -> [RegExpConstant, RegExpNormalChar] " + +# 244| [RegExpGroup] (\S|\w) +#-----| 0 -> [RegExpAlt] \S|\w + +# 244| [RegExpStar] (\S|\w)* +#-----| 0 -> [RegExpGroup] (\S|\w) + +# 244| [RegExpCharacterClassEscape] \S + +# 244| [RegExpAlt] \S|\w +#-----| 0 -> [RegExpCharacterClassEscape] \S +#-----| 1 -> [RegExpCharacterClassEscape] \w + +# 244| [RegExpCharacterClassEscape] \w + +# 244| [RegExpConstant, RegExpNormalChar] " + +# 248| [RegExpGroup] ((\S|[\w])*) +#-----| 0 -> [RegExpStar] (\S|[\w])* + +# 248| [RegExpSequence] ((\S|[\w])*)" +#-----| 0 -> [RegExpGroup] ((\S|[\w])*) +#-----| 1 -> [RegExpConstant, RegExpNormalChar] " + +# 248| [RegExpGroup] (\S|[\w]) +#-----| 0 -> [RegExpAlt] \S|[\w] + +# 248| [RegExpStar] (\S|[\w])* +#-----| 0 -> [RegExpGroup] (\S|[\w]) + +# 248| [RegExpCharacterClassEscape] \S + +# 248| [RegExpAlt] \S|[\w] +#-----| 0 -> [RegExpCharacterClassEscape] \S +#-----| 1 -> [RegExpCharacterClass] [\w] + +# 248| [RegExpCharacterClass] [\w] +#-----| 0 -> [RegExpCharacterClassEscape] \w + +# 248| [RegExpCharacterClassEscape] \w + +# 248| [RegExpConstant, RegExpNormalChar] " + +# 252| [RegExpGroup] ((1s|[\da-z])*) +#-----| 0 -> [RegExpStar] (1s|[\da-z])* + +# 252| [RegExpSequence] ((1s|[\da-z])*)" +#-----| 0 -> [RegExpGroup] ((1s|[\da-z])*) +#-----| 1 -> [RegExpConstant, RegExpNormalChar] " + +# 252| [RegExpGroup] (1s|[\da-z]) +#-----| 0 -> [RegExpAlt] 1s|[\da-z] + +# 252| [RegExpStar] (1s|[\da-z])* +#-----| 0 -> [RegExpGroup] (1s|[\da-z]) + +# 252| [RegExpConstant, RegExpNormalChar] 1s + +# 252| [RegExpAlt] 1s|[\da-z] +#-----| 0 -> [RegExpConstant, RegExpNormalChar] 1s +#-----| 1 -> [RegExpCharacterClass] [\da-z] + +# 252| [RegExpCharacterClass] [\da-z] +#-----| 0 -> [RegExpCharacterClassEscape] \d +#-----| 1 -> [RegExpCharacterRange] a-z + +# 252| [RegExpCharacterClassEscape] \d + +# 252| [RegExpConstant, RegExpNormalChar] a + +# 252| [RegExpCharacterRange] a-z +#-----| 0 -> [RegExpConstant, RegExpNormalChar] a +#-----| 1 -> [RegExpConstant, RegExpNormalChar] z + +# 252| [RegExpConstant, RegExpNormalChar] z + +# 252| [RegExpConstant, RegExpNormalChar] " + +# 256| [RegExpGroup] ((0|[\d])*) +#-----| 0 -> [RegExpStar] (0|[\d])* + +# 256| [RegExpSequence] ((0|[\d])*)" +#-----| 0 -> [RegExpGroup] ((0|[\d])*) +#-----| 1 -> [RegExpConstant, RegExpNormalChar] " + +# 256| [RegExpGroup] (0|[\d]) +#-----| 0 -> [RegExpAlt] 0|[\d] + +# 256| [RegExpStar] (0|[\d])* +#-----| 0 -> [RegExpGroup] (0|[\d]) + +# 256| [RegExpConstant, RegExpNormalChar] 0 + +# 256| [RegExpAlt] 0|[\d] +#-----| 0 -> [RegExpConstant, RegExpNormalChar] 0 +#-----| 1 -> [RegExpCharacterClass] [\d] + +# 256| [RegExpCharacterClass] [\d] +#-----| 0 -> [RegExpCharacterClassEscape] \d + +# 256| [RegExpCharacterClassEscape] \d + +# 256| [RegExpConstant, RegExpNormalChar] " + +# 260| [RegExpGroup] (([\d]+)*) +#-----| 0 -> [RegExpStar] ([\d]+)* + +# 260| [RegExpSequence] (([\d]+)*)" +#-----| 0 -> [RegExpGroup] (([\d]+)*) +#-----| 1 -> [RegExpConstant, RegExpNormalChar] " + +# 260| [RegExpGroup] ([\d]+) +#-----| 0 -> [RegExpPlus] [\d]+ + +# 260| [RegExpStar] ([\d]+)* +#-----| 0 -> [RegExpGroup] ([\d]+) + +# 260| [RegExpCharacterClass] [\d] +#-----| 0 -> [RegExpCharacterClassEscape] \d + +# 260| [RegExpPlus] [\d]+ +#-----| 0 -> [RegExpCharacterClass] [\d] + +# 260| [RegExpCharacterClassEscape] \d + +# 260| [RegExpConstant, RegExpNormalChar] " + +# 263| [RegExpGroup] (\d+(X\d+)?) +#-----| 0 -> [RegExpSequence] \d+(X\d+)? + +# 263| [RegExpPlus] (\d+(X\d+)?)+ +#-----| 0 -> [RegExpGroup] (\d+(X\d+)?) + +# 263| [RegExpCharacterClassEscape] \d + +# 263| [RegExpPlus] \d+ +#-----| 0 -> [RegExpCharacterClassEscape] \d + +# 263| [RegExpSequence] \d+(X\d+)? +#-----| 0 -> [RegExpPlus] \d+ +#-----| 1 -> [RegExpOpt] (X\d+)? + +# 263| [RegExpGroup] (X\d+) +#-----| 0 -> [RegExpSequence] X\d+ + +# 263| [RegExpOpt] (X\d+)? +#-----| 0 -> [RegExpGroup] (X\d+) + +# 263| [RegExpConstant, RegExpNormalChar] X + +# 263| [RegExpSequence] X\d+ +#-----| 0 -> [RegExpConstant, RegExpNormalChar] X +#-----| 1 -> [RegExpPlus] \d+ + +# 263| [RegExpCharacterClassEscape] \d + +# 263| [RegExpPlus] \d+ +#-----| 0 -> [RegExpCharacterClassEscape] \d + +# 266| [RegExpGroup] (\d+(X\d+)?) +#-----| 0 -> [RegExpSequence] \d+(X\d+)? + +# 266| [RegExpPlus] (\d+(X\d+)?)+ +#-----| 0 -> [RegExpGroup] (\d+(X\d+)?) + +# 266| [RegExpCharacterClassEscape] \d + +# 266| [RegExpPlus] \d+ +#-----| 0 -> [RegExpCharacterClassEscape] \d + +# 266| [RegExpSequence] \d+(X\d+)? +#-----| 0 -> [RegExpPlus] \d+ +#-----| 1 -> [RegExpOpt] (X\d+)? + +# 266| [RegExpGroup] (X\d+) +#-----| 0 -> [RegExpSequence] X\d+ + +# 266| [RegExpOpt] (X\d+)? +#-----| 0 -> [RegExpGroup] (X\d+) + +# 266| [RegExpConstant, RegExpNormalChar] X + +# 266| [RegExpSequence] X\d+ +#-----| 0 -> [RegExpConstant, RegExpNormalChar] X +#-----| 1 -> [RegExpPlus] \d+ + +# 266| [RegExpCharacterClassEscape] \d + +# 266| [RegExpPlus] \d+ +#-----| 0 -> [RegExpCharacterClassEscape] \d + +# 269| [RegExpGroup] ([0-9]+(X[0-9]*)?) +#-----| 0 -> [RegExpSequence] [0-9]+(X[0-9]*)? + +# 269| [RegExpStar] ([0-9]+(X[0-9]*)?)* +#-----| 0 -> [RegExpGroup] ([0-9]+(X[0-9]*)?) + +# 269| [RegExpCharacterClass] [0-9] +#-----| 0 -> [RegExpCharacterRange] 0-9 + +# 269| [RegExpPlus] [0-9]+ +#-----| 0 -> [RegExpCharacterClass] [0-9] + +# 269| [RegExpSequence] [0-9]+(X[0-9]*)? +#-----| 0 -> [RegExpPlus] [0-9]+ +#-----| 1 -> [RegExpOpt] (X[0-9]*)? + +# 269| [RegExpConstant, RegExpNormalChar] 0 + +# 269| [RegExpCharacterRange] 0-9 +#-----| 0 -> [RegExpConstant, RegExpNormalChar] 0 +#-----| 1 -> [RegExpConstant, RegExpNormalChar] 9 + +# 269| [RegExpConstant, RegExpNormalChar] 9 + +# 269| [RegExpGroup] (X[0-9]*) +#-----| 0 -> [RegExpSequence] X[0-9]* + +# 269| [RegExpOpt] (X[0-9]*)? +#-----| 0 -> [RegExpGroup] (X[0-9]*) + +# 269| [RegExpConstant, RegExpNormalChar] X + +# 269| [RegExpSequence] X[0-9]* +#-----| 0 -> [RegExpConstant, RegExpNormalChar] X +#-----| 1 -> [RegExpStar] [0-9]* + +# 269| [RegExpCharacterClass] [0-9] +#-----| 0 -> [RegExpCharacterRange] 0-9 + +# 269| [RegExpStar] [0-9]* +#-----| 0 -> [RegExpCharacterClass] [0-9] + +# 269| [RegExpConstant, RegExpNormalChar] 0 + +# 269| [RegExpCharacterRange] 0-9 +#-----| 0 -> [RegExpConstant, RegExpNormalChar] 0 +#-----| 1 -> [RegExpConstant, RegExpNormalChar] 9 + +# 269| [RegExpConstant, RegExpNormalChar] 9 + +# 272| [RegExpGroup] ([0-9]+(X[0-9]*)?) +#-----| 0 -> [RegExpSequence] [0-9]+(X[0-9]*)? + +# 272| [RegExpStar] ([0-9]+(X[0-9]*)?)* +#-----| 0 -> [RegExpGroup] ([0-9]+(X[0-9]*)?) + +# 272| [RegExpCharacterClass] [0-9] +#-----| 0 -> [RegExpCharacterRange] 0-9 + +# 272| [RegExpPlus] [0-9]+ +#-----| 0 -> [RegExpCharacterClass] [0-9] + +# 272| [RegExpSequence] [0-9]+(X[0-9]*)? +#-----| 0 -> [RegExpPlus] [0-9]+ +#-----| 1 -> [RegExpOpt] (X[0-9]*)? + +# 272| [RegExpConstant, RegExpNormalChar] 0 + +# 272| [RegExpCharacterRange] 0-9 +#-----| 0 -> [RegExpConstant, RegExpNormalChar] 0 +#-----| 1 -> [RegExpConstant, RegExpNormalChar] 9 + +# 272| [RegExpConstant, RegExpNormalChar] 9 + +# 272| [RegExpGroup] (X[0-9]*) +#-----| 0 -> [RegExpSequence] X[0-9]* + +# 272| [RegExpOpt] (X[0-9]*)? +#-----| 0 -> [RegExpGroup] (X[0-9]*) + +# 272| [RegExpConstant, RegExpNormalChar] X + +# 272| [RegExpSequence] X[0-9]* +#-----| 0 -> [RegExpConstant, RegExpNormalChar] X +#-----| 1 -> [RegExpStar] [0-9]* + +# 272| [RegExpCharacterClass] [0-9] +#-----| 0 -> [RegExpCharacterRange] 0-9 + +# 272| [RegExpStar] [0-9]* +#-----| 0 -> [RegExpCharacterClass] [0-9] + +# 272| [RegExpConstant, RegExpNormalChar] 0 + +# 272| [RegExpCharacterRange] 0-9 +#-----| 0 -> [RegExpConstant, RegExpNormalChar] 0 +#-----| 1 -> [RegExpConstant, RegExpNormalChar] 9 + +# 272| [RegExpConstant, RegExpNormalChar] 9 + +# 275| [RegExpCaret] ^ + +# 275| [RegExpSequence] ^([^>]+)*(>|$) +#-----| 0 -> [RegExpCaret] ^ +#-----| 1 -> [RegExpStar] ([^>]+)* +#-----| 2 -> [RegExpGroup] (>|$) + +# 275| [RegExpGroup] ([^>]+) +#-----| 0 -> [RegExpPlus] [^>]+ + +# 275| [RegExpStar] ([^>]+)* +#-----| 0 -> [RegExpGroup] ([^>]+) + +# 275| [RegExpCharacterClass] [^>] +#-----| 0 -> [RegExpConstant, RegExpNormalChar] > + +# 275| [RegExpPlus] [^>]+ +#-----| 0 -> [RegExpCharacterClass] [^>] + +# 275| [RegExpConstant, RegExpNormalChar] > + +# 275| [RegExpGroup] (>|$) +#-----| 0 -> [RegExpAlt] >|$ + +# 275| [RegExpConstant, RegExpNormalChar] > + +# 275| [RegExpAlt] >|$ +#-----| 0 -> [RegExpConstant, RegExpNormalChar] > +#-----| 1 -> [RegExpDollar] $ + +# 275| [RegExpDollar] $ + +# 279| [RegExpCaret] ^ + +# 279| [RegExpSequence] ^([^>a]+)*(>|$) +#-----| 0 -> [RegExpCaret] ^ +#-----| 1 -> [RegExpStar] ([^>a]+)* +#-----| 2 -> [RegExpGroup] (>|$) + +# 279| [RegExpGroup] ([^>a]+) +#-----| 0 -> [RegExpPlus] [^>a]+ + +# 279| [RegExpStar] ([^>a]+)* +#-----| 0 -> [RegExpGroup] ([^>a]+) + +# 279| [RegExpCharacterClass] [^>a] +#-----| 0 -> [RegExpConstant, RegExpNormalChar] > +#-----| 1 -> [RegExpConstant, RegExpNormalChar] a + +# 279| [RegExpPlus] [^>a]+ +#-----| 0 -> [RegExpCharacterClass] [^>a] + +# 279| [RegExpConstant, RegExpNormalChar] > + +# 279| [RegExpConstant, RegExpNormalChar] a + +# 279| [RegExpGroup] (>|$) +#-----| 0 -> [RegExpAlt] >|$ + +# 279| [RegExpConstant, RegExpNormalChar] > + +# 279| [RegExpAlt] >|$ +#-----| 0 -> [RegExpConstant, RegExpNormalChar] > +#-----| 1 -> [RegExpDollar] $ + +# 279| [RegExpDollar] $ + +# 283| [RegExpGroup] (\n\s*) +#-----| 0 -> [RegExpSequence] \n\s* + +# 283| [RegExpPlus] (\n\s*)+ +#-----| 0 -> [RegExpGroup] (\n\s*) + +# 283| [RegExpSequence] (\n\s*)+$ +#-----| 0 -> [RegExpPlus] (\n\s*)+ +#-----| 1 -> [RegExpDollar] $ + +# 283| [RegExpConstant, RegExpEscape] \n + +# 283| [RegExpSequence] \n\s* +#-----| 0 -> [RegExpConstant, RegExpEscape] \n +#-----| 1 -> [RegExpStar] \s* + +# 283| [RegExpCharacterClassEscape] \s + +# 283| [RegExpStar] \s* +#-----| 0 -> [RegExpCharacterClassEscape] \s + +# 283| [RegExpDollar] $ + +# 287| [RegExpCaret] ^ + +# 287| [RegExpSequence] ^(?:\s+|#.*|\(\?#[^)]*\))*(?:[?*+]|\{\d+(?:,\d*)?}) +#-----| 0 -> [RegExpCaret] ^ +#-----| 1 -> [RegExpStar] (?:\s+|#.*|\(\?#[^)]*\))* +#-----| 2 -> [RegExpGroup] (?:[?*+]|\{\d+(?:,\d*)?}) + +# 287| [RegExpGroup] (?:\s+|#.*|\(\?#[^)]*\)) +#-----| 0 -> [RegExpAlt] \s+|#.*|\(\?#[^)]*\) + +# 287| [RegExpStar] (?:\s+|#.*|\(\?#[^)]*\))* +#-----| 0 -> [RegExpGroup] (?:\s+|#.*|\(\?#[^)]*\)) + +# 287| [RegExpCharacterClassEscape] \s + +# 287| [RegExpPlus] \s+ +#-----| 0 -> [RegExpCharacterClassEscape] \s + +# 287| [RegExpAlt] \s+|#.*|\(\?#[^)]*\) +#-----| 0 -> [RegExpPlus] \s+ +#-----| 1 -> [RegExpSequence] #.* +#-----| 2 -> [RegExpSequence] \(\?#[^)]*\) + +# 287| [RegExpConstant, RegExpNormalChar] # + +# 287| [RegExpSequence] #.* +#-----| 0 -> [RegExpConstant, RegExpNormalChar] # +#-----| 1 -> [RegExpStar] .* + +# 287| [RegExpDot] . + +# 287| [RegExpStar] .* +#-----| 0 -> [RegExpDot] . + +# 287| [RegExpConstant, RegExpEscape] \( + +# 287| [RegExpSequence] \(\?#[^)]*\) +#-----| 0 -> [RegExpConstant, RegExpEscape] \( +#-----| 1 -> [RegExpConstant, RegExpEscape] \? +#-----| 2 -> [RegExpConstant, RegExpNormalChar] # +#-----| 3 -> [RegExpStar] [^)]* +#-----| 4 -> [RegExpConstant, RegExpEscape] \) + +# 287| [RegExpConstant, RegExpEscape] \? + +# 287| [RegExpConstant, RegExpNormalChar] # + +# 287| [RegExpCharacterClass] [^)] +#-----| 0 -> [RegExpConstant, RegExpNormalChar] ) + +# 287| [RegExpStar] [^)]* +#-----| 0 -> [RegExpCharacterClass] [^)] + +# 287| [RegExpConstant, RegExpNormalChar] ) + +# 287| [RegExpConstant, RegExpEscape] \) + +# 287| [RegExpGroup] (?:[?*+]|\{\d+(?:,\d*)?}) +#-----| 0 -> [RegExpAlt] [?*+]|\{\d+(?:,\d*)?} + +# 287| [RegExpCharacterClass] [?*+] +#-----| 0 -> [RegExpConstant, RegExpNormalChar] ? +#-----| 1 -> [RegExpConstant, RegExpNormalChar] * +#-----| 2 -> [RegExpConstant, RegExpNormalChar] + + +# 287| [RegExpAlt] [?*+]|\{\d+(?:,\d*)?} +#-----| 0 -> [RegExpCharacterClass] [?*+] +#-----| 1 -> [RegExpSequence] \{\d+(?:,\d*)?} + +# 287| [RegExpConstant, RegExpNormalChar] ? + +# 287| [RegExpConstant, RegExpNormalChar] * + +# 287| [RegExpConstant, RegExpNormalChar] + + +# 287| [RegExpConstant, RegExpEscape] \{ + +# 287| [RegExpSequence] \{\d+(?:,\d*)?} +#-----| 0 -> [RegExpConstant, RegExpEscape] \{ +#-----| 1 -> [RegExpPlus] \d+ +#-----| 2 -> [RegExpOpt] (?:,\d*)? +#-----| 3 -> [RegExpConstant, RegExpNormalChar] } + +# 287| [RegExpCharacterClassEscape] \d + +# 287| [RegExpPlus] \d+ +#-----| 0 -> [RegExpCharacterClassEscape] \d + +# 287| [RegExpGroup] (?:,\d*) +#-----| 0 -> [RegExpSequence] ,\d* + +# 287| [RegExpOpt] (?:,\d*)? +#-----| 0 -> [RegExpGroup] (?:,\d*) + +# 287| [RegExpConstant, RegExpNormalChar] , + +# 287| [RegExpSequence] ,\d* +#-----| 0 -> [RegExpConstant, RegExpNormalChar] , +#-----| 1 -> [RegExpStar] \d* + +# 287| [RegExpCharacterClassEscape] \d + +# 287| [RegExpStar] \d* +#-----| 0 -> [RegExpCharacterClassEscape] \d + +# 287| [RegExpConstant, RegExpNormalChar] } + +# 291| [RegExpConstant, RegExpEscape] \{ + +# 291| [RegExpSequence] \{\[\s*([a-zA-Z]+)\(([a-zA-Z]+)\)((\s*([a-zA-Z]+)\: ?([ a-zA-Z{}]+),?)+)*\s*\]\} +#-----| 0 -> [RegExpConstant, RegExpEscape] \{ +#-----| 1 -> [RegExpConstant, RegExpEscape] \[ +#-----| 2 -> [RegExpStar] \s* +#-----| 3 -> [RegExpGroup] ([a-zA-Z]+) +#-----| 4 -> [RegExpConstant, RegExpEscape] \( +#-----| 5 -> [RegExpGroup] ([a-zA-Z]+) +#-----| 6 -> [RegExpConstant, RegExpEscape] \) +#-----| 7 -> [RegExpStar] ((\s*([a-zA-Z]+)\: ?([ a-zA-Z{}]+),?)+)* +#-----| 8 -> [RegExpStar] \s* +#-----| 9 -> [RegExpConstant, RegExpEscape] \] +#-----| 10 -> [RegExpConstant, RegExpEscape] \} + +# 291| [RegExpConstant, RegExpEscape] \[ + +# 291| [RegExpCharacterClassEscape] \s + +# 291| [RegExpStar] \s* +#-----| 0 -> [RegExpCharacterClassEscape] \s + +# 291| [RegExpGroup] ([a-zA-Z]+) +#-----| 0 -> [RegExpPlus] [a-zA-Z]+ + +# 291| [RegExpCharacterClass] [a-zA-Z] +#-----| 0 -> [RegExpCharacterRange] a-z +#-----| 1 -> [RegExpCharacterRange] A-Z + +# 291| [RegExpPlus] [a-zA-Z]+ +#-----| 0 -> [RegExpCharacterClass] [a-zA-Z] + +# 291| [RegExpConstant, RegExpNormalChar] a + +# 291| [RegExpCharacterRange] a-z +#-----| 0 -> [RegExpConstant, RegExpNormalChar] a +#-----| 1 -> [RegExpConstant, RegExpNormalChar] z + +# 291| [RegExpConstant, RegExpNormalChar] z + +# 291| [RegExpConstant, RegExpNormalChar] A + +# 291| [RegExpCharacterRange] A-Z +#-----| 0 -> [RegExpConstant, RegExpNormalChar] A +#-----| 1 -> [RegExpConstant, RegExpNormalChar] Z + +# 291| [RegExpConstant, RegExpNormalChar] Z + +# 291| [RegExpConstant, RegExpEscape] \( + +# 291| [RegExpGroup] ([a-zA-Z]+) +#-----| 0 -> [RegExpPlus] [a-zA-Z]+ + +# 291| [RegExpCharacterClass] [a-zA-Z] +#-----| 0 -> [RegExpCharacterRange] a-z +#-----| 1 -> [RegExpCharacterRange] A-Z + +# 291| [RegExpPlus] [a-zA-Z]+ +#-----| 0 -> [RegExpCharacterClass] [a-zA-Z] + +# 291| [RegExpConstant, RegExpNormalChar] a + +# 291| [RegExpCharacterRange] a-z +#-----| 0 -> [RegExpConstant, RegExpNormalChar] a +#-----| 1 -> [RegExpConstant, RegExpNormalChar] z + +# 291| [RegExpConstant, RegExpNormalChar] z + +# 291| [RegExpConstant, RegExpNormalChar] A + +# 291| [RegExpCharacterRange] A-Z +#-----| 0 -> [RegExpConstant, RegExpNormalChar] A +#-----| 1 -> [RegExpConstant, RegExpNormalChar] Z + +# 291| [RegExpConstant, RegExpNormalChar] Z + +# 291| [RegExpConstant, RegExpEscape] \) + +# 291| [RegExpGroup] ((\s*([a-zA-Z]+)\: ?([ a-zA-Z{}]+),?)+) +#-----| 0 -> [RegExpPlus] (\s*([a-zA-Z]+)\: ?([ a-zA-Z{}]+),?)+ + +# 291| [RegExpStar] ((\s*([a-zA-Z]+)\: ?([ a-zA-Z{}]+),?)+)* +#-----| 0 -> [RegExpGroup] ((\s*([a-zA-Z]+)\: ?([ a-zA-Z{}]+),?)+) + +# 291| [RegExpGroup] (\s*([a-zA-Z]+)\: ?([ a-zA-Z{}]+),?) +#-----| 0 -> [RegExpSequence] \s*([a-zA-Z]+)\: ?([ a-zA-Z{}]+),? + +# 291| [RegExpPlus] (\s*([a-zA-Z]+)\: ?([ a-zA-Z{}]+),?)+ +#-----| 0 -> [RegExpGroup] (\s*([a-zA-Z]+)\: ?([ a-zA-Z{}]+),?) + +# 291| [RegExpCharacterClassEscape] \s + +# 291| [RegExpStar] \s* +#-----| 0 -> [RegExpCharacterClassEscape] \s + +# 291| [RegExpSequence] \s*([a-zA-Z]+)\: ?([ a-zA-Z{}]+),? +#-----| 0 -> [RegExpStar] \s* +#-----| 1 -> [RegExpGroup] ([a-zA-Z]+) +#-----| 2 -> [RegExpConstant, RegExpEscape] \: +#-----| 3 -> [RegExpOpt] ? +#-----| 4 -> [RegExpGroup] ([ a-zA-Z{}]+) +#-----| 5 -> [RegExpOpt] ,? + +# 291| [RegExpGroup] ([a-zA-Z]+) +#-----| 0 -> [RegExpPlus] [a-zA-Z]+ + +# 291| [RegExpCharacterClass] [a-zA-Z] +#-----| 0 -> [RegExpCharacterRange] a-z +#-----| 1 -> [RegExpCharacterRange] A-Z + +# 291| [RegExpPlus] [a-zA-Z]+ +#-----| 0 -> [RegExpCharacterClass] [a-zA-Z] + +# 291| [RegExpConstant, RegExpNormalChar] a + +# 291| [RegExpCharacterRange] a-z +#-----| 0 -> [RegExpConstant, RegExpNormalChar] a +#-----| 1 -> [RegExpConstant, RegExpNormalChar] z + +# 291| [RegExpConstant, RegExpNormalChar] z + +# 291| [RegExpConstant, RegExpNormalChar] A + +# 291| [RegExpCharacterRange] A-Z +#-----| 0 -> [RegExpConstant, RegExpNormalChar] A +#-----| 1 -> [RegExpConstant, RegExpNormalChar] Z + +# 291| [RegExpConstant, RegExpNormalChar] Z + +# 291| [RegExpConstant, RegExpEscape] \: + +# 291| [RegExpConstant, RegExpNormalChar] + +# 291| [RegExpOpt] ? +#-----| 0 -> [RegExpConstant, RegExpNormalChar] + +# 291| [RegExpGroup] ([ a-zA-Z{}]+) +#-----| 0 -> [RegExpPlus] [ a-zA-Z{}]+ + +# 291| [RegExpCharacterClass] [ a-zA-Z{}] +#-----| 0 -> [RegExpConstant, RegExpNormalChar] +#-----| 1 -> [RegExpCharacterRange] a-z +#-----| 2 -> [RegExpCharacterRange] A-Z +#-----| 3 -> [RegExpConstant, RegExpNormalChar] { +#-----| 4 -> [RegExpConstant, RegExpNormalChar] } + +# 291| [RegExpPlus] [ a-zA-Z{}]+ +#-----| 0 -> [RegExpCharacterClass] [ a-zA-Z{}] + +# 291| [RegExpConstant, RegExpNormalChar] + +# 291| [RegExpConstant, RegExpNormalChar] a + +# 291| [RegExpCharacterRange] a-z +#-----| 0 -> [RegExpConstant, RegExpNormalChar] a +#-----| 1 -> [RegExpConstant, RegExpNormalChar] z + +# 291| [RegExpConstant, RegExpNormalChar] z + +# 291| [RegExpConstant, RegExpNormalChar] A + +# 291| [RegExpCharacterRange] A-Z +#-----| 0 -> [RegExpConstant, RegExpNormalChar] A +#-----| 1 -> [RegExpConstant, RegExpNormalChar] Z + +# 291| [RegExpConstant, RegExpNormalChar] Z + +# 291| [RegExpConstant, RegExpNormalChar] { + +# 291| [RegExpConstant, RegExpNormalChar] } + +# 291| [RegExpConstant, RegExpNormalChar] , + +# 291| [RegExpOpt] ,? +#-----| 0 -> [RegExpConstant, RegExpNormalChar] , + +# 291| [RegExpCharacterClassEscape] \s + +# 291| [RegExpStar] \s* +#-----| 0 -> [RegExpCharacterClassEscape] \s + +# 291| [RegExpConstant, RegExpEscape] \] + +# 291| [RegExpConstant, RegExpEscape] \} + +# 295| [RegExpGroup] (a+|b+|c+) +#-----| 0 -> [RegExpAlt] a+|b+|c+ + +# 295| [RegExpStar] (a+|b+|c+)* +#-----| 0 -> [RegExpGroup] (a+|b+|c+) + +# 295| [RegExpSequence] (a+|b+|c+)*c +#-----| 0 -> [RegExpStar] (a+|b+|c+)* +#-----| 1 -> [RegExpConstant, RegExpNormalChar] c + +# 295| [RegExpConstant, RegExpNormalChar] a + +# 295| [RegExpPlus] a+ +#-----| 0 -> [RegExpConstant, RegExpNormalChar] a + +# 295| [RegExpAlt] a+|b+|c+ +#-----| 0 -> [RegExpPlus] a+ +#-----| 1 -> [RegExpPlus] b+ +#-----| 2 -> [RegExpPlus] c+ + +# 295| [RegExpConstant, RegExpNormalChar] b + +# 295| [RegExpPlus] b+ +#-----| 0 -> [RegExpConstant, RegExpNormalChar] b + +# 295| [RegExpConstant, RegExpNormalChar] c + +# 295| [RegExpPlus] c+ +#-----| 0 -> [RegExpConstant, RegExpNormalChar] c + +# 295| [RegExpConstant, RegExpNormalChar] c + +# 299| [RegExpGroup] (((a+a?)*)+b+) +#-----| 0 -> [RegExpSequence] ((a+a?)*)+b+ + +# 299| [RegExpGroup] ((a+a?)*) +#-----| 0 -> [RegExpStar] (a+a?)* + +# 299| [RegExpPlus] ((a+a?)*)+ +#-----| 0 -> [RegExpGroup] ((a+a?)*) + +# 299| [RegExpSequence] ((a+a?)*)+b+ +#-----| 0 -> [RegExpPlus] ((a+a?)*)+ +#-----| 1 -> [RegExpPlus] b+ + +# 299| [RegExpGroup] (a+a?) +#-----| 0 -> [RegExpSequence] a+a? + +# 299| [RegExpStar] (a+a?)* +#-----| 0 -> [RegExpGroup] (a+a?) + +# 299| [RegExpConstant, RegExpNormalChar] a + +# 299| [RegExpPlus] a+ +#-----| 0 -> [RegExpConstant, RegExpNormalChar] a + +# 299| [RegExpSequence] a+a? +#-----| 0 -> [RegExpPlus] a+ +#-----| 1 -> [RegExpOpt] a? + +# 299| [RegExpConstant, RegExpNormalChar] a + +# 299| [RegExpOpt] a? +#-----| 0 -> [RegExpConstant, RegExpNormalChar] a + +# 299| [RegExpConstant, RegExpNormalChar] b + +# 299| [RegExpPlus] b+ +#-----| 0 -> [RegExpConstant, RegExpNormalChar] b + +# 303| [RegExpGroup] (a+) +#-----| 0 -> [RegExpPlus] a+ + +# 303| [RegExpPlus] (a+)+ +#-----| 0 -> [RegExpGroup] (a+) + +# 303| [RegExpSequence] (a+)+bbbb +#-----| 0 -> [RegExpPlus] (a+)+ +#-----| 1 -> [RegExpConstant, RegExpNormalChar] bbbb + +# 303| [RegExpConstant, RegExpNormalChar] a + +# 303| [RegExpPlus] a+ +#-----| 0 -> [RegExpConstant, RegExpNormalChar] a + +# 303| [RegExpConstant, RegExpNormalChar] bbbb + +# 306| [RegExpGroup] (a+) +#-----| 0 -> [RegExpPlus] a+ + +# 306| [RegExpPlus] (a+)+ +#-----| 0 -> [RegExpGroup] (a+) + +# 306| [RegExpSequence] (a+)+aaaaa*a+ +#-----| 0 -> [RegExpPlus] (a+)+ +#-----| 1 -> [RegExpConstant, RegExpNormalChar] aaaa +#-----| 2 -> [RegExpStar] a* +#-----| 3 -> [RegExpPlus] a+ + +# 306| [RegExpConstant, RegExpNormalChar] a + +# 306| [RegExpPlus] a+ +#-----| 0 -> [RegExpConstant, RegExpNormalChar] a + +# 306| [RegExpConstant, RegExpNormalChar] aaaa + +# 306| [RegExpConstant, RegExpNormalChar] a + +# 306| [RegExpStar] a* +#-----| 0 -> [RegExpConstant, RegExpNormalChar] a + +# 306| [RegExpConstant, RegExpNormalChar] a + +# 306| [RegExpPlus] a+ +#-----| 0 -> [RegExpConstant, RegExpNormalChar] a + +# 309| [RegExpGroup] (a+) +#-----| 0 -> [RegExpPlus] a+ + +# 309| [RegExpPlus] (a+)+ +#-----| 0 -> [RegExpGroup] (a+) + +# 309| [RegExpSequence] (a+)+aaaaa*a+ +#-----| 0 -> [RegExpPlus] (a+)+ +#-----| 1 -> [RegExpConstant, RegExpNormalChar] aaaa +#-----| 2 -> [RegExpStar] a* +#-----| 3 -> [RegExpPlus] a+ + +# 309| [RegExpConstant, RegExpNormalChar] a + +# 309| [RegExpPlus] a+ +#-----| 0 -> [RegExpConstant, RegExpNormalChar] a + +# 309| [RegExpConstant, RegExpNormalChar] aaaa + +# 309| [RegExpConstant, RegExpNormalChar] a + +# 309| [RegExpStar] a* +#-----| 0 -> [RegExpConstant, RegExpNormalChar] a + +# 309| [RegExpConstant, RegExpNormalChar] a + +# 309| [RegExpPlus] a+ +#-----| 0 -> [RegExpConstant, RegExpNormalChar] a + +# 313| [RegExpGroup] (a+) +#-----| 0 -> [RegExpPlus] a+ + +# 313| [RegExpPlus] (a+)+ +#-----| 0 -> [RegExpGroup] (a+) + +# 313| [RegExpSequence] (a+)+aaaaa$ +#-----| 0 -> [RegExpPlus] (a+)+ +#-----| 1 -> [RegExpConstant, RegExpNormalChar] aaaaa +#-----| 2 -> [RegExpDollar] $ + +# 313| [RegExpConstant, RegExpNormalChar] a + +# 313| [RegExpPlus] a+ +#-----| 0 -> [RegExpConstant, RegExpNormalChar] a + +# 313| [RegExpConstant, RegExpNormalChar] aaaaa + +# 313| [RegExpDollar] $ + +# 316| [RegExpGroup] (\n+) +#-----| 0 -> [RegExpPlus] \n+ + +# 316| [RegExpPlus] (\n+)+ +#-----| 0 -> [RegExpGroup] (\n+) + +# 316| [RegExpSequence] (\n+)+\n\n +#-----| 0 -> [RegExpPlus] (\n+)+ +#-----| 1 -> [RegExpConstant, RegExpEscape] \n +#-----| 2 -> [RegExpConstant, RegExpEscape] \n + +# 316| [RegExpConstant, RegExpEscape] \n + +# 316| [RegExpPlus] \n+ +#-----| 0 -> [RegExpConstant, RegExpEscape] \n + +# 316| [RegExpConstant, RegExpEscape] \n + +# 316| [RegExpConstant, RegExpEscape] \n + +# 319| [RegExpGroup] (\n+) +#-----| 0 -> [RegExpPlus] \n+ + +# 319| [RegExpPlus] (\n+)+ +#-----| 0 -> [RegExpGroup] (\n+) + +# 319| [RegExpSequence] (\n+)+\n\n +#-----| 0 -> [RegExpPlus] (\n+)+ +#-----| 1 -> [RegExpConstant, RegExpEscape] \n +#-----| 2 -> [RegExpConstant, RegExpEscape] \n + +# 319| [RegExpConstant, RegExpEscape] \n + +# 319| [RegExpPlus] \n+ +#-----| 0 -> [RegExpConstant, RegExpEscape] \n + +# 319| [RegExpConstant, RegExpEscape] \n + +# 319| [RegExpConstant, RegExpEscape] \n + +# 323| [RegExpGroup] (\n+) +#-----| 0 -> [RegExpPlus] \n+ + +# 323| [RegExpPlus] (\n+)+ +#-----| 0 -> [RegExpGroup] (\n+) + +# 323| [RegExpSequence] (\n+)+\n\n$ +#-----| 0 -> [RegExpPlus] (\n+)+ +#-----| 1 -> [RegExpConstant, RegExpEscape] \n +#-----| 2 -> [RegExpConstant, RegExpEscape] \n +#-----| 3 -> [RegExpDollar] $ + +# 323| [RegExpConstant, RegExpEscape] \n + +# 323| [RegExpPlus] \n+ +#-----| 0 -> [RegExpConstant, RegExpEscape] \n + +# 323| [RegExpConstant, RegExpEscape] \n + +# 323| [RegExpConstant, RegExpEscape] \n + +# 323| [RegExpDollar] $ + +# 327| [RegExpGroup] ([^X]+) +#-----| 0 -> [RegExpPlus] [^X]+ + +# 327| [RegExpStar] ([^X]+)* +#-----| 0 -> [RegExpGroup] ([^X]+) + +# 327| [RegExpSequence] ([^X]+)*$ +#-----| 0 -> [RegExpStar] ([^X]+)* +#-----| 1 -> [RegExpDollar] $ + +# 327| [RegExpCharacterClass] [^X] +#-----| 0 -> [RegExpConstant, RegExpNormalChar] X + +# 327| [RegExpPlus] [^X]+ +#-----| 0 -> [RegExpCharacterClass] [^X] + +# 327| [RegExpConstant, RegExpNormalChar] X + +# 327| [RegExpDollar] $ + +# 331| [RegExpGroup] (([^X]b)+) +#-----| 0 -> [RegExpPlus] ([^X]b)+ + +# 331| [RegExpStar] (([^X]b)+)* +#-----| 0 -> [RegExpGroup] (([^X]b)+) + +# 331| [RegExpSequence] (([^X]b)+)*$ +#-----| 0 -> [RegExpStar] (([^X]b)+)* +#-----| 1 -> [RegExpDollar] $ + +# 331| [RegExpGroup] ([^X]b) +#-----| 0 -> [RegExpSequence] [^X]b + +# 331| [RegExpPlus] ([^X]b)+ +#-----| 0 -> [RegExpGroup] ([^X]b) + +# 331| [RegExpCharacterClass] [^X] +#-----| 0 -> [RegExpConstant, RegExpNormalChar] X + +# 331| [RegExpSequence] [^X]b +#-----| 0 -> [RegExpCharacterClass] [^X] +#-----| 1 -> [RegExpConstant, RegExpNormalChar] b + +# 331| [RegExpConstant, RegExpNormalChar] X + +# 331| [RegExpConstant, RegExpNormalChar] b + +# 331| [RegExpDollar] $ + +# 334| [RegExpGroup] (([^X]b)+) +#-----| 0 -> [RegExpPlus] ([^X]b)+ + +# 334| [RegExpStar] (([^X]b)+)* +#-----| 0 -> [RegExpGroup] (([^X]b)+) + +# 334| [RegExpSequence] (([^X]b)+)*($|[^X]b) +#-----| 0 -> [RegExpStar] (([^X]b)+)* +#-----| 1 -> [RegExpGroup] ($|[^X]b) + +# 334| [RegExpGroup] ([^X]b) +#-----| 0 -> [RegExpSequence] [^X]b + +# 334| [RegExpPlus] ([^X]b)+ +#-----| 0 -> [RegExpGroup] ([^X]b) + +# 334| [RegExpCharacterClass] [^X] +#-----| 0 -> [RegExpConstant, RegExpNormalChar] X + +# 334| [RegExpSequence] [^X]b +#-----| 0 -> [RegExpCharacterClass] [^X] +#-----| 1 -> [RegExpConstant, RegExpNormalChar] b + +# 334| [RegExpConstant, RegExpNormalChar] X + +# 334| [RegExpConstant, RegExpNormalChar] b + +# 334| [RegExpGroup] ($|[^X]b) +#-----| 0 -> [RegExpAlt] $|[^X]b + +# 334| [RegExpDollar] $ + +# 334| [RegExpAlt] $|[^X]b +#-----| 0 -> [RegExpDollar] $ +#-----| 1 -> [RegExpSequence] [^X]b + +# 334| [RegExpCharacterClass] [^X] +#-----| 0 -> [RegExpConstant, RegExpNormalChar] X + +# 334| [RegExpSequence] [^X]b +#-----| 0 -> [RegExpCharacterClass] [^X] +#-----| 1 -> [RegExpConstant, RegExpNormalChar] b + +# 334| [RegExpConstant, RegExpNormalChar] X + +# 334| [RegExpConstant, RegExpNormalChar] b + +# 337| [RegExpGroup] (([^X]b)+) +#-----| 0 -> [RegExpPlus] ([^X]b)+ + +# 337| [RegExpStar] (([^X]b)+)* +#-----| 0 -> [RegExpGroup] (([^X]b)+) + +# 337| [RegExpSequence] (([^X]b)+)*($|[^X]b) +#-----| 0 -> [RegExpStar] (([^X]b)+)* +#-----| 1 -> [RegExpGroup] ($|[^X]b) + +# 337| [RegExpGroup] ([^X]b) +#-----| 0 -> [RegExpSequence] [^X]b + +# 337| [RegExpPlus] ([^X]b)+ +#-----| 0 -> [RegExpGroup] ([^X]b) + +# 337| [RegExpCharacterClass] [^X] +#-----| 0 -> [RegExpConstant, RegExpNormalChar] X + +# 337| [RegExpSequence] [^X]b +#-----| 0 -> [RegExpCharacterClass] [^X] +#-----| 1 -> [RegExpConstant, RegExpNormalChar] b + +# 337| [RegExpConstant, RegExpNormalChar] X + +# 337| [RegExpConstant, RegExpNormalChar] b + +# 337| [RegExpGroup] ($|[^X]b) +#-----| 0 -> [RegExpAlt] $|[^X]b + +# 337| [RegExpDollar] $ + +# 337| [RegExpAlt] $|[^X]b +#-----| 0 -> [RegExpDollar] $ +#-----| 1 -> [RegExpSequence] [^X]b + +# 337| [RegExpCharacterClass] [^X] +#-----| 0 -> [RegExpConstant, RegExpNormalChar] X + +# 337| [RegExpSequence] [^X]b +#-----| 0 -> [RegExpCharacterClass] [^X] +#-----| 1 -> [RegExpConstant, RegExpNormalChar] b + +# 337| [RegExpConstant, RegExpNormalChar] X + +# 337| [RegExpConstant, RegExpNormalChar] b + +# 341| [RegExpGroup] (([^X]b)+) +#-----| 0 -> [RegExpPlus] ([^X]b)+ + +# 341| [RegExpStar] (([^X]b)+)* +#-----| 0 -> [RegExpGroup] (([^X]b)+) + +# 341| [RegExpSequence] (([^X]b)+)*($|[^X]c) +#-----| 0 -> [RegExpStar] (([^X]b)+)* +#-----| 1 -> [RegExpGroup] ($|[^X]c) + +# 341| [RegExpGroup] ([^X]b) +#-----| 0 -> [RegExpSequence] [^X]b + +# 341| [RegExpPlus] ([^X]b)+ +#-----| 0 -> [RegExpGroup] ([^X]b) + +# 341| [RegExpCharacterClass] [^X] +#-----| 0 -> [RegExpConstant, RegExpNormalChar] X + +# 341| [RegExpSequence] [^X]b +#-----| 0 -> [RegExpCharacterClass] [^X] +#-----| 1 -> [RegExpConstant, RegExpNormalChar] b + +# 341| [RegExpConstant, RegExpNormalChar] X + +# 341| [RegExpConstant, RegExpNormalChar] b + +# 341| [RegExpGroup] ($|[^X]c) +#-----| 0 -> [RegExpAlt] $|[^X]c + +# 341| [RegExpDollar] $ + +# 341| [RegExpAlt] $|[^X]c +#-----| 0 -> [RegExpDollar] $ +#-----| 1 -> [RegExpSequence] [^X]c + +# 341| [RegExpCharacterClass] [^X] +#-----| 0 -> [RegExpConstant, RegExpNormalChar] X + +# 341| [RegExpSequence] [^X]c +#-----| 0 -> [RegExpCharacterClass] [^X] +#-----| 1 -> [RegExpConstant, RegExpNormalChar] c + +# 341| [RegExpConstant, RegExpNormalChar] X + +# 341| [RegExpConstant, RegExpNormalChar] c + +# 344| [RegExpGroup] ((ab)+) +#-----| 0 -> [RegExpPlus] (ab)+ + +# 344| [RegExpStar] ((ab)+)* +#-----| 0 -> [RegExpGroup] ((ab)+) + +# 344| [RegExpSequence] ((ab)+)*ababab +#-----| 0 -> [RegExpStar] ((ab)+)* +#-----| 1 -> [RegExpConstant, RegExpNormalChar] ababab + +# 344| [RegExpGroup] (ab) +#-----| 0 -> [RegExpConstant, RegExpNormalChar] ab + +# 344| [RegExpPlus] (ab)+ +#-----| 0 -> [RegExpGroup] (ab) + +# 344| [RegExpConstant, RegExpNormalChar] ab + +# 344| [RegExpConstant, RegExpNormalChar] ababab + +# 347| [RegExpGroup] ((ab)+) +#-----| 0 -> [RegExpPlus] (ab)+ + +# 347| [RegExpStar] ((ab)+)* +#-----| 0 -> [RegExpGroup] ((ab)+) + +# 347| [RegExpSequence] ((ab)+)*ababab +#-----| 0 -> [RegExpStar] ((ab)+)* +#-----| 1 -> [RegExpConstant, RegExpNormalChar] ababab + +# 347| [RegExpGroup] (ab) +#-----| 0 -> [RegExpConstant, RegExpNormalChar] ab + +# 347| [RegExpPlus] (ab)+ +#-----| 0 -> [RegExpGroup] (ab) + +# 347| [RegExpConstant, RegExpNormalChar] ab + +# 347| [RegExpConstant, RegExpNormalChar] ababab + +# 350| [RegExpGroup] ((ab)+) +#-----| 0 -> [RegExpPlus] (ab)+ + +# 350| [RegExpStar] ((ab)+)* +#-----| 0 -> [RegExpGroup] ((ab)+) + +# 350| [RegExpSequence] ((ab)+)*abab(ab)*(ab)+ +#-----| 0 -> [RegExpStar] ((ab)+)* +#-----| 1 -> [RegExpConstant, RegExpNormalChar] abab +#-----| 2 -> [RegExpStar] (ab)* +#-----| 3 -> [RegExpPlus] (ab)+ + +# 350| [RegExpGroup] (ab) +#-----| 0 -> [RegExpConstant, RegExpNormalChar] ab + +# 350| [RegExpPlus] (ab)+ +#-----| 0 -> [RegExpGroup] (ab) + +# 350| [RegExpConstant, RegExpNormalChar] ab + +# 350| [RegExpConstant, RegExpNormalChar] abab + +# 350| [RegExpGroup] (ab) +#-----| 0 -> [RegExpConstant, RegExpNormalChar] ab + +# 350| [RegExpStar] (ab)* +#-----| 0 -> [RegExpGroup] (ab) + +# 350| [RegExpConstant, RegExpNormalChar] ab + +# 350| [RegExpGroup] (ab) +#-----| 0 -> [RegExpConstant, RegExpNormalChar] ab + +# 350| [RegExpPlus] (ab)+ +#-----| 0 -> [RegExpGroup] (ab) + +# 350| [RegExpConstant, RegExpNormalChar] ab + +# 353| [RegExpGroup] ((ab)+) +#-----| 0 -> [RegExpPlus] (ab)+ + +# 353| [RegExpStar] ((ab)+)* +#-----| 0 -> [RegExpGroup] ((ab)+) + +# 353| [RegExpSequence] ((ab)+)*abab(ab)*(ab)+ +#-----| 0 -> [RegExpStar] ((ab)+)* +#-----| 1 -> [RegExpConstant, RegExpNormalChar] abab +#-----| 2 -> [RegExpStar] (ab)* +#-----| 3 -> [RegExpPlus] (ab)+ + +# 353| [RegExpGroup] (ab) +#-----| 0 -> [RegExpConstant, RegExpNormalChar] ab + +# 353| [RegExpPlus] (ab)+ +#-----| 0 -> [RegExpGroup] (ab) + +# 353| [RegExpConstant, RegExpNormalChar] ab + +# 353| [RegExpConstant, RegExpNormalChar] abab + +# 353| [RegExpGroup] (ab) +#-----| 0 -> [RegExpConstant, RegExpNormalChar] ab + +# 353| [RegExpStar] (ab)* +#-----| 0 -> [RegExpGroup] (ab) + +# 353| [RegExpConstant, RegExpNormalChar] ab + +# 353| [RegExpGroup] (ab) +#-----| 0 -> [RegExpConstant, RegExpNormalChar] ab + +# 353| [RegExpPlus] (ab)+ +#-----| 0 -> [RegExpGroup] (ab) + +# 353| [RegExpConstant, RegExpNormalChar] ab + +# 356| [RegExpGroup] ((ab)+) +#-----| 0 -> [RegExpPlus] (ab)+ + +# 356| [RegExpStar] ((ab)+)* +#-----| 0 -> [RegExpGroup] ((ab)+) + +# 356| [RegExpGroup] (ab) +#-----| 0 -> [RegExpConstant, RegExpNormalChar] ab + +# 356| [RegExpPlus] (ab)+ +#-----| 0 -> [RegExpGroup] (ab) + +# 356| [RegExpConstant, RegExpNormalChar] ab + +# 359| [RegExpGroup] ((ab)+) +#-----| 0 -> [RegExpPlus] (ab)+ + +# 359| [RegExpStar] ((ab)+)* +#-----| 0 -> [RegExpGroup] ((ab)+) + +# 359| [RegExpGroup] (ab) +#-----| 0 -> [RegExpConstant, RegExpNormalChar] ab + +# 359| [RegExpPlus] (ab)+ +#-----| 0 -> [RegExpGroup] (ab) + +# 359| [RegExpConstant, RegExpNormalChar] ab + +# 363| [RegExpGroup] ((ab)+) +#-----| 0 -> [RegExpPlus] (ab)+ + +# 363| [RegExpStar] ((ab)+)* +#-----| 0 -> [RegExpGroup] ((ab)+) + +# 363| [RegExpSequence] ((ab)+)*$ +#-----| 0 -> [RegExpStar] ((ab)+)* +#-----| 1 -> [RegExpDollar] $ + +# 363| [RegExpGroup] (ab) +#-----| 0 -> [RegExpConstant, RegExpNormalChar] ab + +# 363| [RegExpPlus] (ab)+ +#-----| 0 -> [RegExpGroup] (ab) + +# 363| [RegExpConstant, RegExpNormalChar] ab + +# 363| [RegExpDollar] $ + +# 366| [RegExpGroup] ((ab)+) +#-----| 0 -> [RegExpPlus] (ab)+ + +# 366| [RegExpStar] ((ab)+)* +#-----| 0 -> [RegExpGroup] ((ab)+) + +# 366| [RegExpSequence] ((ab)+)*[a1][b1][a2][b2][a3][b3] +#-----| 0 -> [RegExpStar] ((ab)+)* +#-----| 1 -> [RegExpCharacterClass] [a1] +#-----| 2 -> [RegExpCharacterClass] [b1] +#-----| 3 -> [RegExpCharacterClass] [a2] +#-----| 4 -> [RegExpCharacterClass] [b2] +#-----| 5 -> [RegExpCharacterClass] [a3] +#-----| 6 -> [RegExpCharacterClass] [b3] + +# 366| [RegExpGroup] (ab) +#-----| 0 -> [RegExpConstant, RegExpNormalChar] ab + +# 366| [RegExpPlus] (ab)+ +#-----| 0 -> [RegExpGroup] (ab) + +# 366| [RegExpConstant, RegExpNormalChar] ab + +# 366| [RegExpCharacterClass] [a1] +#-----| 0 -> [RegExpConstant, RegExpNormalChar] a +#-----| 1 -> [RegExpConstant, RegExpNormalChar] 1 + +# 366| [RegExpConstant, RegExpNormalChar] a + +# 366| [RegExpConstant, RegExpNormalChar] 1 + +# 366| [RegExpCharacterClass] [b1] +#-----| 0 -> [RegExpConstant, RegExpNormalChar] b +#-----| 1 -> [RegExpConstant, RegExpNormalChar] 1 + +# 366| [RegExpConstant, RegExpNormalChar] b + +# 366| [RegExpConstant, RegExpNormalChar] 1 + +# 366| [RegExpCharacterClass] [a2] +#-----| 0 -> [RegExpConstant, RegExpNormalChar] a +#-----| 1 -> [RegExpConstant, RegExpNormalChar] 2 + +# 366| [RegExpConstant, RegExpNormalChar] a + +# 366| [RegExpConstant, RegExpNormalChar] 2 + +# 366| [RegExpCharacterClass] [b2] +#-----| 0 -> [RegExpConstant, RegExpNormalChar] b +#-----| 1 -> [RegExpConstant, RegExpNormalChar] 2 + +# 366| [RegExpConstant, RegExpNormalChar] b + +# 366| [RegExpConstant, RegExpNormalChar] 2 + +# 366| [RegExpCharacterClass] [a3] +#-----| 0 -> [RegExpConstant, RegExpNormalChar] a +#-----| 1 -> [RegExpConstant, RegExpNormalChar] 3 + +# 366| [RegExpConstant, RegExpNormalChar] a + +# 366| [RegExpConstant, RegExpNormalChar] 3 + +# 366| [RegExpCharacterClass] [b3] +#-----| 0 -> [RegExpConstant, RegExpNormalChar] b +#-----| 1 -> [RegExpConstant, RegExpNormalChar] 3 + +# 366| [RegExpConstant, RegExpNormalChar] b + +# 366| [RegExpConstant, RegExpNormalChar] 3 + +# 369| [RegExpGroup] ((ab)+) +#-----| 0 -> [RegExpPlus] (ab)+ + +# 369| [RegExpStar] ((ab)+)* +#-----| 0 -> [RegExpGroup] ((ab)+) + +# 369| [RegExpSequence] ((ab)+)*[a1][b1][a2][b2][a3][b3] +#-----| 0 -> [RegExpStar] ((ab)+)* +#-----| 1 -> [RegExpCharacterClass] [a1] +#-----| 2 -> [RegExpCharacterClass] [b1] +#-----| 3 -> [RegExpCharacterClass] [a2] +#-----| 4 -> [RegExpCharacterClass] [b2] +#-----| 5 -> [RegExpCharacterClass] [a3] +#-----| 6 -> [RegExpCharacterClass] [b3] + +# 369| [RegExpGroup] (ab) +#-----| 0 -> [RegExpConstant, RegExpNormalChar] ab + +# 369| [RegExpPlus] (ab)+ +#-----| 0 -> [RegExpGroup] (ab) + +# 369| [RegExpConstant, RegExpNormalChar] ab + +# 369| [RegExpCharacterClass] [a1] +#-----| 0 -> [RegExpConstant, RegExpNormalChar] a +#-----| 1 -> [RegExpConstant, RegExpNormalChar] 1 + +# 369| [RegExpConstant, RegExpNormalChar] a + +# 369| [RegExpConstant, RegExpNormalChar] 1 + +# 369| [RegExpCharacterClass] [b1] +#-----| 0 -> [RegExpConstant, RegExpNormalChar] b +#-----| 1 -> [RegExpConstant, RegExpNormalChar] 1 + +# 369| [RegExpConstant, RegExpNormalChar] b + +# 369| [RegExpConstant, RegExpNormalChar] 1 + +# 369| [RegExpCharacterClass] [a2] +#-----| 0 -> [RegExpConstant, RegExpNormalChar] a +#-----| 1 -> [RegExpConstant, RegExpNormalChar] 2 + +# 369| [RegExpConstant, RegExpNormalChar] a + +# 369| [RegExpConstant, RegExpNormalChar] 2 + +# 369| [RegExpCharacterClass] [b2] +#-----| 0 -> [RegExpConstant, RegExpNormalChar] b +#-----| 1 -> [RegExpConstant, RegExpNormalChar] 2 + +# 369| [RegExpConstant, RegExpNormalChar] b + +# 369| [RegExpConstant, RegExpNormalChar] 2 + +# 369| [RegExpCharacterClass] [a3] +#-----| 0 -> [RegExpConstant, RegExpNormalChar] a +#-----| 1 -> [RegExpConstant, RegExpNormalChar] 3 + +# 369| [RegExpConstant, RegExpNormalChar] a + +# 369| [RegExpConstant, RegExpNormalChar] 3 + +# 369| [RegExpCharacterClass] [b3] +#-----| 0 -> [RegExpConstant, RegExpNormalChar] b +#-----| 1 -> [RegExpConstant, RegExpNormalChar] 3 + +# 369| [RegExpConstant, RegExpNormalChar] b + +# 369| [RegExpConstant, RegExpNormalChar] 3 + +# 373| [RegExpGroup] ([\n\s]+) +#-----| 0 -> [RegExpPlus] [\n\s]+ + +# 373| [RegExpStar] ([\n\s]+)* +#-----| 0 -> [RegExpGroup] ([\n\s]+) + +# 373| [RegExpSequence] ([\n\s]+)*(.) +#-----| 0 -> [RegExpStar] ([\n\s]+)* +#-----| 1 -> [RegExpGroup] (.) + +# 373| [RegExpCharacterClass] [\n\s] +#-----| 0 -> [RegExpConstant, RegExpEscape] \n +#-----| 1 -> [RegExpCharacterClassEscape] \s + +# 373| [RegExpPlus] [\n\s]+ +#-----| 0 -> [RegExpCharacterClass] [\n\s] + +# 373| [RegExpConstant, RegExpEscape] \n + +# 373| [RegExpCharacterClassEscape] \s + +# 373| [RegExpGroup] (.) +#-----| 0 -> [RegExpDot] . + +# 373| [RegExpDot] . + +# 376| [RegExpGroup] (A*A*X) +#-----| 0 -> [RegExpSequence] A*A*X + +# 376| [RegExpStar] (A*A*X)* +#-----| 0 -> [RegExpGroup] (A*A*X) + +# 376| [RegExpConstant, RegExpNormalChar] A + +# 376| [RegExpStar] A* +#-----| 0 -> [RegExpConstant, RegExpNormalChar] A + +# 376| [RegExpSequence] A*A*X +#-----| 0 -> [RegExpStar] A* +#-----| 1 -> [RegExpStar] A* +#-----| 2 -> [RegExpConstant, RegExpNormalChar] X + +# 376| [RegExpConstant, RegExpNormalChar] A + +# 376| [RegExpStar] A* +#-----| 0 -> [RegExpConstant, RegExpNormalChar] A + +# 376| [RegExpConstant, RegExpNormalChar] X + +# 379| [RegExpGroup] ([^\\\]]+) +#-----| 0 -> [RegExpPlus] [^\\\]]+ + +# 379| [RegExpStar] ([^\\\]]+)* +#-----| 0 -> [RegExpGroup] ([^\\\]]+) + +# 379| [RegExpCharacterClass] [^\\\]] +#-----| 0 -> [RegExpConstant, RegExpEscape] \\ +#-----| 1 -> [RegExpConstant, RegExpEscape] \] + +# 379| [RegExpPlus] [^\\\]]+ +#-----| 0 -> [RegExpCharacterClass] [^\\\]] + +# 379| [RegExpConstant, RegExpEscape] \\ + +# 379| [RegExpConstant, RegExpEscape] \] + +# 382| [RegExpGroup] (\w*foobarbaz\w*foobarbaz\w*foobarbaz\w*foobarbaz\s*foobarbaz\d*foobarbaz\w*) +#-----| 0 -> [RegExpSequence] \w*foobarbaz\w*foobarbaz\w*foobarbaz\w*foobarbaz\s*foobarbaz\d*foobarbaz\w* + +# 382| [RegExpPlus] (\w*foobarbaz\w*foobarbaz\w*foobarbaz\w*foobarbaz\s*foobarbaz\d*foobarbaz\w*)+ +#-----| 0 -> [RegExpGroup] (\w*foobarbaz\w*foobarbaz\w*foobarbaz\w*foobarbaz\s*foobarbaz\d*foobarbaz\w*) + +# 382| [RegExpSequence] (\w*foobarbaz\w*foobarbaz\w*foobarbaz\w*foobarbaz\s*foobarbaz\d*foobarbaz\w*)+- +#-----| 0 -> [RegExpPlus] (\w*foobarbaz\w*foobarbaz\w*foobarbaz\w*foobarbaz\s*foobarbaz\d*foobarbaz\w*)+ +#-----| 1 -> [RegExpConstant, RegExpNormalChar] - + +# 382| [RegExpCharacterClassEscape] \w + +# 382| [RegExpStar] \w* +#-----| 0 -> [RegExpCharacterClassEscape] \w + +# 382| [RegExpSequence] \w*foobarbaz\w*foobarbaz\w*foobarbaz\w*foobarbaz\s*foobarbaz\d*foobarbaz\w* +#-----| 0 -> [RegExpStar] \w* +#-----| 1 -> [RegExpConstant, RegExpNormalChar] foobarbaz +#-----| 2 -> [RegExpStar] \w* +#-----| 3 -> [RegExpConstant, RegExpNormalChar] foobarbaz +#-----| 4 -> [RegExpStar] \w* +#-----| 5 -> [RegExpConstant, RegExpNormalChar] foobarbaz +#-----| 6 -> [RegExpStar] \w* +#-----| 7 -> [RegExpConstant, RegExpNormalChar] foobarbaz +#-----| 8 -> [RegExpStar] \s* +#-----| 9 -> [RegExpConstant, RegExpNormalChar] foobarbaz +#-----| 10 -> [RegExpStar] \d* +#-----| 11 -> [RegExpConstant, RegExpNormalChar] foobarbaz +#-----| 12 -> [RegExpStar] \w* + +# 382| [RegExpConstant, RegExpNormalChar] foobarbaz + +# 382| [RegExpCharacterClassEscape] \w + +# 382| [RegExpStar] \w* +#-----| 0 -> [RegExpCharacterClassEscape] \w + +# 382| [RegExpConstant, RegExpNormalChar] foobarbaz + +# 382| [RegExpCharacterClassEscape] \w + +# 382| [RegExpStar] \w* +#-----| 0 -> [RegExpCharacterClassEscape] \w + +# 382| [RegExpConstant, RegExpNormalChar] foobarbaz + +# 382| [RegExpCharacterClassEscape] \w + +# 382| [RegExpStar] \w* +#-----| 0 -> [RegExpCharacterClassEscape] \w + +# 382| [RegExpConstant, RegExpNormalChar] foobarbaz + +# 382| [RegExpCharacterClassEscape] \s + +# 382| [RegExpStar] \s* +#-----| 0 -> [RegExpCharacterClassEscape] \s + +# 382| [RegExpConstant, RegExpNormalChar] foobarbaz + +# 382| [RegExpCharacterClassEscape] \d + +# 382| [RegExpStar] \d* +#-----| 0 -> [RegExpCharacterClassEscape] \d + +# 382| [RegExpConstant, RegExpNormalChar] foobarbaz + +# 382| [RegExpCharacterClassEscape] \w + +# 382| [RegExpStar] \w* +#-----| 0 -> [RegExpCharacterClassEscape] \w + +# 382| [RegExpConstant, RegExpNormalChar] - + +# 386| [RegExpGroup] (\w*foobarfoobarfoobarfoobarfoobarfoobarfoobarfoobar) +#-----| 0 -> [RegExpSequence] \w*foobarfoobarfoobarfoobarfoobarfoobarfoobarfoobar + +# 386| [RegExpPlus] (\w*foobarfoobarfoobarfoobarfoobarfoobarfoobarfoobar)+ +#-----| 0 -> [RegExpGroup] (\w*foobarfoobarfoobarfoobarfoobarfoobarfoobarfoobar) + +# 386| [RegExpCharacterClassEscape] \w + +# 386| [RegExpStar] \w* +#-----| 0 -> [RegExpCharacterClassEscape] \w + +# 386| [RegExpSequence] \w*foobarfoobarfoobarfoobarfoobarfoobarfoobarfoobar +#-----| 0 -> [RegExpStar] \w* +#-----| 1 -> [RegExpConstant, RegExpNormalChar] foobarfoobarfoobarfoobarfoobarfoobarfoobarfoobar + +# 386| [RegExpConstant, RegExpNormalChar] foobarfoobarfoobarfoobarfoobarfoobarfoobarfoobar + +# 387| [RegExpGroup] (\w*foobarfoobarfoobar) +#-----| 0 -> [RegExpSequence] \w*foobarfoobarfoobar + +# 387| [RegExpPlus] (\w*foobarfoobarfoobar)+ +#-----| 0 -> [RegExpGroup] (\w*foobarfoobarfoobar) + +# 387| [RegExpCharacterClassEscape] \w + +# 387| [RegExpStar] \w* +#-----| 0 -> [RegExpCharacterClassEscape] \w + +# 387| [RegExpSequence] \w*foobarfoobarfoobar +#-----| 0 -> [RegExpStar] \w* +#-----| 1 -> [RegExpConstant, RegExpNormalChar] foobarfoobarfoobar + +# 387| [RegExpConstant, RegExpNormalChar] foobarfoobarfoobar + +# 391| [RegExpConstant, RegExpNormalChar] a + +# 391| [RegExpRange] a{2,3} +#-----| 0 -> [RegExpConstant, RegExpNormalChar] a + +# 391| [RegExpSequence] a{2,3}(b+)+X +#-----| 0 -> [RegExpRange] a{2,3} +#-----| 1 -> [RegExpPlus] (b+)+ +#-----| 2 -> [RegExpConstant, RegExpNormalChar] X + +# 391| [RegExpGroup] (b+) +#-----| 0 -> [RegExpPlus] b+ + +# 391| [RegExpPlus] (b+)+ +#-----| 0 -> [RegExpGroup] (b+) + +# 391| [RegExpConstant, RegExpNormalChar] b + +# 391| [RegExpPlus] b+ +#-----| 0 -> [RegExpConstant, RegExpNormalChar] b + +# 391| [RegExpConstant, RegExpNormalChar] X + +# 395| [RegExpCaret] ^ + +# 395| [RegExpSequence] ^<(\w+)((?:\s+\w+(?:\s*=\s*(?:(?:"[^"]*")|(?:'[^']*')|[^>\s]+))?)*)\s*(\/?)> +#-----| 0 -> [RegExpCaret] ^ +#-----| 1 -> [RegExpConstant, RegExpNormalChar] < +#-----| 2 -> [RegExpGroup] (\w+) +#-----| 3 -> [RegExpGroup] ((?:\s+\w+(?:\s*=\s*(?:(?:"[^"]*")|(?:'[^']*')|[^>\s]+))?)*) +#-----| 4 -> [RegExpStar] \s* +#-----| 5 -> [RegExpGroup] (\/?) +#-----| 6 -> [RegExpConstant, RegExpNormalChar] > + +# 395| [RegExpConstant, RegExpNormalChar] < + +# 395| [RegExpGroup] (\w+) +#-----| 0 -> [RegExpPlus] \w+ + +# 395| [RegExpCharacterClassEscape] \w + +# 395| [RegExpPlus] \w+ +#-----| 0 -> [RegExpCharacterClassEscape] \w + +# 395| [RegExpGroup] ((?:\s+\w+(?:\s*=\s*(?:(?:"[^"]*")|(?:'[^']*')|[^>\s]+))?)*) +#-----| 0 -> [RegExpStar] (?:\s+\w+(?:\s*=\s*(?:(?:"[^"]*")|(?:'[^']*')|[^>\s]+))?)* + +# 395| [RegExpGroup] (?:\s+\w+(?:\s*=\s*(?:(?:"[^"]*")|(?:'[^']*')|[^>\s]+))?) +#-----| 0 -> [RegExpSequence] \s+\w+(?:\s*=\s*(?:(?:"[^"]*")|(?:'[^']*')|[^>\s]+))? + +# 395| [RegExpStar] (?:\s+\w+(?:\s*=\s*(?:(?:"[^"]*")|(?:'[^']*')|[^>\s]+))?)* +#-----| 0 -> [RegExpGroup] (?:\s+\w+(?:\s*=\s*(?:(?:"[^"]*")|(?:'[^']*')|[^>\s]+))?) + +# 395| [RegExpCharacterClassEscape] \s + +# 395| [RegExpPlus] \s+ +#-----| 0 -> [RegExpCharacterClassEscape] \s + +# 395| [RegExpSequence] \s+\w+(?:\s*=\s*(?:(?:"[^"]*")|(?:'[^']*')|[^>\s]+))? +#-----| 0 -> [RegExpPlus] \s+ +#-----| 1 -> [RegExpPlus] \w+ +#-----| 2 -> [RegExpOpt] (?:\s*=\s*(?:(?:"[^"]*")|(?:'[^']*')|[^>\s]+))? + +# 395| [RegExpCharacterClassEscape] \w + +# 395| [RegExpPlus] \w+ +#-----| 0 -> [RegExpCharacterClassEscape] \w + +# 395| [RegExpGroup] (?:\s*=\s*(?:(?:"[^"]*")|(?:'[^']*')|[^>\s]+)) +#-----| 0 -> [RegExpSequence] \s*=\s*(?:(?:"[^"]*")|(?:'[^']*')|[^>\s]+) + +# 395| [RegExpOpt] (?:\s*=\s*(?:(?:"[^"]*")|(?:'[^']*')|[^>\s]+))? +#-----| 0 -> [RegExpGroup] (?:\s*=\s*(?:(?:"[^"]*")|(?:'[^']*')|[^>\s]+)) + +# 395| [RegExpCharacterClassEscape] \s + +# 395| [RegExpStar] \s* +#-----| 0 -> [RegExpCharacterClassEscape] \s + +# 395| [RegExpSequence] \s*=\s*(?:(?:"[^"]*")|(?:'[^']*')|[^>\s]+) +#-----| 0 -> [RegExpStar] \s* +#-----| 1 -> [RegExpConstant, RegExpNormalChar] = +#-----| 2 -> [RegExpStar] \s* +#-----| 3 -> [RegExpGroup] (?:(?:"[^"]*")|(?:'[^']*')|[^>\s]+) + +# 395| [RegExpConstant, RegExpNormalChar] = + +# 395| [RegExpCharacterClassEscape] \s + +# 395| [RegExpStar] \s* +#-----| 0 -> [RegExpCharacterClassEscape] \s + +# 395| [RegExpGroup] (?:(?:"[^"]*")|(?:'[^']*')|[^>\s]+) +#-----| 0 -> [RegExpAlt] (?:"[^"]*")|(?:'[^']*')|[^>\s]+ + +# 395| [RegExpGroup] (?:"[^"]*") +#-----| 0 -> [RegExpSequence] "[^"]*" + +# 395| [RegExpAlt] (?:"[^"]*")|(?:'[^']*')|[^>\s]+ +#-----| 0 -> [RegExpGroup] (?:"[^"]*") +#-----| 1 -> [RegExpGroup] (?:'[^']*') +#-----| 2 -> [RegExpPlus] [^>\s]+ + +# 395| [RegExpConstant, RegExpNormalChar] " + +# 395| [RegExpSequence] "[^"]*" +#-----| 0 -> [RegExpConstant, RegExpNormalChar] " +#-----| 1 -> [RegExpStar] [^"]* +#-----| 2 -> [RegExpConstant, RegExpNormalChar] " + +# 395| [RegExpCharacterClass] [^"] +#-----| 0 -> [RegExpConstant, RegExpNormalChar] " + +# 395| [RegExpStar] [^"]* +#-----| 0 -> [RegExpCharacterClass] [^"] + +# 395| [RegExpConstant, RegExpNormalChar] " + +# 395| [RegExpConstant, RegExpNormalChar] " + +# 395| [RegExpGroup] (?:'[^']*') +#-----| 0 -> [RegExpSequence] '[^']*' + +# 395| [RegExpConstant, RegExpNormalChar] ' + +# 395| [RegExpSequence] '[^']*' +#-----| 0 -> [RegExpConstant, RegExpNormalChar] ' +#-----| 1 -> [RegExpStar] [^']* +#-----| 2 -> [RegExpConstant, RegExpNormalChar] ' + +# 395| [RegExpCharacterClass] [^'] +#-----| 0 -> [RegExpConstant, RegExpNormalChar] ' + +# 395| [RegExpStar] [^']* +#-----| 0 -> [RegExpCharacterClass] [^'] + +# 395| [RegExpConstant, RegExpNormalChar] ' + +# 395| [RegExpConstant, RegExpNormalChar] ' + +# 395| [RegExpCharacterClass] [^>\s] +#-----| 0 -> [RegExpConstant, RegExpNormalChar] > +#-----| 1 -> [RegExpCharacterClassEscape] \s + +# 395| [RegExpPlus] [^>\s]+ +#-----| 0 -> [RegExpCharacterClass] [^>\s] + +# 395| [RegExpConstant, RegExpNormalChar] > + +# 395| [RegExpCharacterClassEscape] \s + +# 395| [RegExpCharacterClassEscape] \s + +# 395| [RegExpStar] \s* +#-----| 0 -> [RegExpCharacterClassEscape] \s + +# 395| [RegExpGroup] (\/?) +#-----| 0 -> [RegExpOpt] \/? + +# 395| [RegExpConstant, RegExpEscape] \/ + +# 395| [RegExpOpt] \/? +#-----| 0 -> [RegExpConstant, RegExpEscape] \/ + +# 395| [RegExpConstant, RegExpNormalChar] > + +# 398| [RegExpGroup] (a+) +#-----| 0 -> [RegExpPlus] a+ + +# 398| [RegExpStar] (a+)* +#-----| 0 -> [RegExpGroup] (a+) + +# 398| [RegExpSequence] (a+)*[\s\S][\s\S][\s\S]? +#-----| 0 -> [RegExpStar] (a+)* +#-----| 1 -> [RegExpCharacterClass] [\s\S] +#-----| 2 -> [RegExpCharacterClass] [\s\S] +#-----| 3 -> [RegExpOpt] [\s\S]? + +# 398| [RegExpConstant, RegExpNormalChar] a + +# 398| [RegExpPlus] a+ +#-----| 0 -> [RegExpConstant, RegExpNormalChar] a + +# 398| [RegExpCharacterClass] [\s\S] +#-----| 0 -> [RegExpCharacterClassEscape] \s +#-----| 1 -> [RegExpCharacterClassEscape] \S + +# 398| [RegExpCharacterClassEscape] \s + +# 398| [RegExpCharacterClassEscape] \S + +# 398| [RegExpCharacterClass] [\s\S] +#-----| 0 -> [RegExpCharacterClassEscape] \s +#-----| 1 -> [RegExpCharacterClassEscape] \S + +# 398| [RegExpCharacterClassEscape] \s + +# 398| [RegExpCharacterClassEscape] \S + +# 398| [RegExpCharacterClass] [\s\S] +#-----| 0 -> [RegExpCharacterClassEscape] \s +#-----| 1 -> [RegExpCharacterClassEscape] \S + +# 398| [RegExpOpt] [\s\S]? +#-----| 0 -> [RegExpCharacterClass] [\s\S] + +# 398| [RegExpCharacterClassEscape] \s + +# 398| [RegExpCharacterClassEscape] \S + +# 401| [RegExpGroup] (a+) +#-----| 0 -> [RegExpPlus] a+ + +# 401| [RegExpStar] (a+)* +#-----| 0 -> [RegExpGroup] (a+) + +# 401| [RegExpSequence] (a+)*[\s\S]{2,3} +#-----| 0 -> [RegExpStar] (a+)* +#-----| 1 -> [RegExpRange] [\s\S]{2,3} + +# 401| [RegExpConstant, RegExpNormalChar] a + +# 401| [RegExpPlus] a+ +#-----| 0 -> [RegExpConstant, RegExpNormalChar] a + +# 401| [RegExpCharacterClass] [\s\S] +#-----| 0 -> [RegExpCharacterClassEscape] \s +#-----| 1 -> [RegExpCharacterClassEscape] \S + +# 401| [RegExpRange] [\s\S]{2,3} +#-----| 0 -> [RegExpCharacterClass] [\s\S] + +# 401| [RegExpCharacterClassEscape] \s + +# 401| [RegExpCharacterClassEscape] \S + +# 404| [RegExpGroup] (a+) +#-----| 0 -> [RegExpPlus] a+ + +# 404| [RegExpStar] (a+)* +#-----| 0 -> [RegExpGroup] (a+) + +# 404| [RegExpSequence] (a+)*([\s\S]{2,}|X)$ +#-----| 0 -> [RegExpStar] (a+)* +#-----| 1 -> [RegExpGroup] ([\s\S]{2,}|X) +#-----| 2 -> [RegExpDollar] $ + +# 404| [RegExpConstant, RegExpNormalChar] a + +# 404| [RegExpPlus] a+ +#-----| 0 -> [RegExpConstant, RegExpNormalChar] a + +# 404| [RegExpGroup] ([\s\S]{2,}|X) +#-----| 0 -> [RegExpAlt] [\s\S]{2,}|X + +# 404| [RegExpCharacterClass] [\s\S] +#-----| 0 -> [RegExpCharacterClassEscape] \s +#-----| 1 -> [RegExpCharacterClassEscape] \S + +# 404| [InfiniteRepetitionQuantifier, RegExpRange] [\s\S]{2,} +#-----| 0 -> [RegExpCharacterClass] [\s\S] + +# 404| [RegExpAlt] [\s\S]{2,}|X +#-----| 0 -> [InfiniteRepetitionQuantifier, RegExpRange] [\s\S]{2,} +#-----| 1 -> [RegExpConstant, RegExpNormalChar] X + +# 404| [RegExpCharacterClassEscape] \s + +# 404| [RegExpCharacterClassEscape] \S + +# 404| [RegExpConstant, RegExpNormalChar] X + +# 404| [RegExpDollar] $ + +# 407| [RegExpGroup] (a+) +#-----| 0 -> [RegExpPlus] a+ + +# 407| [RegExpStar] (a+)* +#-----| 0 -> [RegExpGroup] (a+) + +# 407| [RegExpSequence] (a+)*([\s\S]*|X)$ +#-----| 0 -> [RegExpStar] (a+)* +#-----| 1 -> [RegExpGroup] ([\s\S]*|X) +#-----| 2 -> [RegExpDollar] $ + +# 407| [RegExpConstant, RegExpNormalChar] a + +# 407| [RegExpPlus] a+ +#-----| 0 -> [RegExpConstant, RegExpNormalChar] a + +# 407| [RegExpGroup] ([\s\S]*|X) +#-----| 0 -> [RegExpAlt] [\s\S]*|X + +# 407| [RegExpCharacterClass] [\s\S] +#-----| 0 -> [RegExpCharacterClassEscape] \s +#-----| 1 -> [RegExpCharacterClassEscape] \S + +# 407| [RegExpStar] [\s\S]* +#-----| 0 -> [RegExpCharacterClass] [\s\S] + +# 407| [RegExpAlt] [\s\S]*|X +#-----| 0 -> [RegExpStar] [\s\S]* +#-----| 1 -> [RegExpConstant, RegExpNormalChar] X + +# 407| [RegExpCharacterClassEscape] \s + +# 407| [RegExpCharacterClassEscape] \S + +# 407| [RegExpConstant, RegExpNormalChar] X + +# 407| [RegExpDollar] $ + +# 411| [RegExpGroup] ((a+)*$|[\s\S]+) +#-----| 0 -> [RegExpAlt] (a+)*$|[\s\S]+ + +# 411| [RegExpGroup] (a+) +#-----| 0 -> [RegExpPlus] a+ + +# 411| [RegExpStar] (a+)* +#-----| 0 -> [RegExpGroup] (a+) + +# 411| [RegExpSequence] (a+)*$ +#-----| 0 -> [RegExpStar] (a+)* +#-----| 1 -> [RegExpDollar] $ + +# 411| [RegExpAlt] (a+)*$|[\s\S]+ +#-----| 0 -> [RegExpSequence] (a+)*$ +#-----| 1 -> [RegExpPlus] [\s\S]+ + +# 411| [RegExpConstant, RegExpNormalChar] a + +# 411| [RegExpPlus] a+ +#-----| 0 -> [RegExpConstant, RegExpNormalChar] a + +# 411| [RegExpDollar] $ + +# 411| [RegExpCharacterClass] [\s\S] +#-----| 0 -> [RegExpCharacterClassEscape] \s +#-----| 1 -> [RegExpCharacterClassEscape] \S + +# 411| [RegExpPlus] [\s\S]+ +#-----| 0 -> [RegExpCharacterClass] [\s\S] + +# 411| [RegExpCharacterClassEscape] \s + +# 411| [RegExpCharacterClassEscape] \S + +# 414| [RegExpGroup] ([\s\S]+|(a+)*$) +#-----| 0 -> [RegExpAlt] [\s\S]+|(a+)*$ + +# 414| [RegExpCharacterClass] [\s\S] +#-----| 0 -> [RegExpCharacterClassEscape] \s +#-----| 1 -> [RegExpCharacterClassEscape] \S + +# 414| [RegExpPlus] [\s\S]+ +#-----| 0 -> [RegExpCharacterClass] [\s\S] + +# 414| [RegExpAlt] [\s\S]+|(a+)*$ +#-----| 0 -> [RegExpPlus] [\s\S]+ +#-----| 1 -> [RegExpSequence] (a+)*$ + +# 414| [RegExpCharacterClassEscape] \s + +# 414| [RegExpCharacterClassEscape] \S + +# 414| [RegExpGroup] (a+) +#-----| 0 -> [RegExpPlus] a+ + +# 414| [RegExpStar] (a+)* +#-----| 0 -> [RegExpGroup] (a+) + +# 414| [RegExpSequence] (a+)*$ +#-----| 0 -> [RegExpStar] (a+)* +#-----| 1 -> [RegExpDollar] $ + +# 414| [RegExpConstant, RegExpNormalChar] a + +# 414| [RegExpPlus] a+ +#-----| 0 -> [RegExpConstant, RegExpNormalChar] a + +# 414| [RegExpDollar] $ + +# 417| [RegExpGroup] ((;|^)a+) +#-----| 0 -> [RegExpSequence] (;|^)a+ + +# 417| [RegExpPlus] ((;|^)a+)+ +#-----| 0 -> [RegExpGroup] ((;|^)a+) + +# 417| [RegExpSequence] ((;|^)a+)+$ +#-----| 0 -> [RegExpPlus] ((;|^)a+)+ +#-----| 1 -> [RegExpDollar] $ + +# 417| [RegExpGroup] (;|^) +#-----| 0 -> [RegExpAlt] ;|^ + +# 417| [RegExpSequence] (;|^)a+ +#-----| 0 -> [RegExpGroup] (;|^) +#-----| 1 -> [RegExpPlus] a+ + +# 417| [RegExpConstant, RegExpNormalChar] ; + +# 417| [RegExpAlt] ;|^ +#-----| 0 -> [RegExpConstant, RegExpNormalChar] ; +#-----| 1 -> [RegExpCaret] ^ + +# 417| [RegExpCaret] ^ + +# 417| [RegExpConstant, RegExpNormalChar] a + +# 417| [RegExpPlus] a+ +#-----| 0 -> [RegExpConstant, RegExpNormalChar] a + +# 417| [RegExpDollar] $ + +# 421| [RegExpGroup] (^|;) +#-----| 0 -> [RegExpAlt] ^|; + +# 421| [RegExpSequence] (^|;)(0|1)(0|1)(0|1)(0|1)(0|1)(0|1)(0|1)(0|1)(0|1)(0|1)(0|1)(0|1)(0|1)(0|1)(e+)+f +#-----| 0 -> [RegExpGroup] (^|;) +#-----| 1 -> [RegExpGroup] (0|1) +#-----| 2 -> [RegExpGroup] (0|1) +#-----| 3 -> [RegExpGroup] (0|1) +#-----| 4 -> [RegExpGroup] (0|1) +#-----| 5 -> [RegExpGroup] (0|1) +#-----| 6 -> [RegExpGroup] (0|1) +#-----| 7 -> [RegExpGroup] (0|1) +#-----| 8 -> [RegExpGroup] (0|1) +#-----| 9 -> [RegExpGroup] (0|1) +#-----| 10 -> [RegExpGroup] (0|1) +#-----| 11 -> [RegExpGroup] (0|1) +#-----| 12 -> [RegExpGroup] (0|1) +#-----| 13 -> [RegExpGroup] (0|1) +#-----| 14 -> [RegExpGroup] (0|1) +#-----| 15 -> [RegExpPlus] (e+)+ +#-----| 16 -> [RegExpConstant, RegExpNormalChar] f + +# 421| [RegExpCaret] ^ + +# 421| [RegExpAlt] ^|; +#-----| 0 -> [RegExpCaret] ^ +#-----| 1 -> [RegExpConstant, RegExpNormalChar] ; + +# 421| [RegExpConstant, RegExpNormalChar] ; + +# 421| [RegExpGroup] (0|1) +#-----| 0 -> [RegExpAlt] 0|1 + +# 421| [RegExpConstant, RegExpNormalChar] 0 + +# 421| [RegExpAlt] 0|1 +#-----| 0 -> [RegExpConstant, RegExpNormalChar] 0 +#-----| 1 -> [RegExpConstant, RegExpNormalChar] 1 + +# 421| [RegExpConstant, RegExpNormalChar] 1 + +# 421| [RegExpGroup] (0|1) +#-----| 0 -> [RegExpAlt] 0|1 + +# 421| [RegExpConstant, RegExpNormalChar] 0 + +# 421| [RegExpAlt] 0|1 +#-----| 0 -> [RegExpConstant, RegExpNormalChar] 0 +#-----| 1 -> [RegExpConstant, RegExpNormalChar] 1 + +# 421| [RegExpConstant, RegExpNormalChar] 1 + +# 421| [RegExpGroup] (0|1) +#-----| 0 -> [RegExpAlt] 0|1 + +# 421| [RegExpConstant, RegExpNormalChar] 0 + +# 421| [RegExpAlt] 0|1 +#-----| 0 -> [RegExpConstant, RegExpNormalChar] 0 +#-----| 1 -> [RegExpConstant, RegExpNormalChar] 1 + +# 421| [RegExpConstant, RegExpNormalChar] 1 + +# 421| [RegExpGroup] (0|1) +#-----| 0 -> [RegExpAlt] 0|1 + +# 421| [RegExpConstant, RegExpNormalChar] 0 + +# 421| [RegExpAlt] 0|1 +#-----| 0 -> [RegExpConstant, RegExpNormalChar] 0 +#-----| 1 -> [RegExpConstant, RegExpNormalChar] 1 + +# 421| [RegExpConstant, RegExpNormalChar] 1 + +# 421| [RegExpGroup] (0|1) +#-----| 0 -> [RegExpAlt] 0|1 + +# 421| [RegExpConstant, RegExpNormalChar] 0 + +# 421| [RegExpAlt] 0|1 +#-----| 0 -> [RegExpConstant, RegExpNormalChar] 0 +#-----| 1 -> [RegExpConstant, RegExpNormalChar] 1 + +# 421| [RegExpConstant, RegExpNormalChar] 1 + +# 421| [RegExpGroup] (0|1) +#-----| 0 -> [RegExpAlt] 0|1 + +# 421| [RegExpConstant, RegExpNormalChar] 0 + +# 421| [RegExpAlt] 0|1 +#-----| 0 -> [RegExpConstant, RegExpNormalChar] 0 +#-----| 1 -> [RegExpConstant, RegExpNormalChar] 1 + +# 421| [RegExpConstant, RegExpNormalChar] 1 + +# 421| [RegExpGroup] (0|1) +#-----| 0 -> [RegExpAlt] 0|1 + +# 421| [RegExpConstant, RegExpNormalChar] 0 + +# 421| [RegExpAlt] 0|1 +#-----| 0 -> [RegExpConstant, RegExpNormalChar] 0 +#-----| 1 -> [RegExpConstant, RegExpNormalChar] 1 + +# 421| [RegExpConstant, RegExpNormalChar] 1 + +# 421| [RegExpGroup] (0|1) +#-----| 0 -> [RegExpAlt] 0|1 + +# 421| [RegExpConstant, RegExpNormalChar] 0 + +# 421| [RegExpAlt] 0|1 +#-----| 0 -> [RegExpConstant, RegExpNormalChar] 0 +#-----| 1 -> [RegExpConstant, RegExpNormalChar] 1 + +# 421| [RegExpConstant, RegExpNormalChar] 1 + +# 421| [RegExpGroup] (0|1) +#-----| 0 -> [RegExpAlt] 0|1 + +# 421| [RegExpConstant, RegExpNormalChar] 0 + +# 421| [RegExpAlt] 0|1 +#-----| 0 -> [RegExpConstant, RegExpNormalChar] 0 +#-----| 1 -> [RegExpConstant, RegExpNormalChar] 1 + +# 421| [RegExpConstant, RegExpNormalChar] 1 + +# 421| [RegExpGroup] (0|1) +#-----| 0 -> [RegExpAlt] 0|1 + +# 421| [RegExpConstant, RegExpNormalChar] 0 + +# 421| [RegExpAlt] 0|1 +#-----| 0 -> [RegExpConstant, RegExpNormalChar] 0 +#-----| 1 -> [RegExpConstant, RegExpNormalChar] 1 + +# 421| [RegExpConstant, RegExpNormalChar] 1 + +# 421| [RegExpGroup] (0|1) +#-----| 0 -> [RegExpAlt] 0|1 + +# 421| [RegExpConstant, RegExpNormalChar] 0 + +# 421| [RegExpAlt] 0|1 +#-----| 0 -> [RegExpConstant, RegExpNormalChar] 0 +#-----| 1 -> [RegExpConstant, RegExpNormalChar] 1 + +# 421| [RegExpConstant, RegExpNormalChar] 1 + +# 421| [RegExpGroup] (0|1) +#-----| 0 -> [RegExpAlt] 0|1 + +# 421| [RegExpConstant, RegExpNormalChar] 0 + +# 421| [RegExpAlt] 0|1 +#-----| 0 -> [RegExpConstant, RegExpNormalChar] 0 +#-----| 1 -> [RegExpConstant, RegExpNormalChar] 1 + +# 421| [RegExpConstant, RegExpNormalChar] 1 + +# 421| [RegExpGroup] (0|1) +#-----| 0 -> [RegExpAlt] 0|1 + +# 421| [RegExpConstant, RegExpNormalChar] 0 + +# 421| [RegExpAlt] 0|1 +#-----| 0 -> [RegExpConstant, RegExpNormalChar] 0 +#-----| 1 -> [RegExpConstant, RegExpNormalChar] 1 + +# 421| [RegExpConstant, RegExpNormalChar] 1 + +# 421| [RegExpGroup] (0|1) +#-----| 0 -> [RegExpAlt] 0|1 + +# 421| [RegExpConstant, RegExpNormalChar] 0 + +# 421| [RegExpAlt] 0|1 +#-----| 0 -> [RegExpConstant, RegExpNormalChar] 0 +#-----| 1 -> [RegExpConstant, RegExpNormalChar] 1 + +# 421| [RegExpConstant, RegExpNormalChar] 1 + +# 421| [RegExpGroup] (e+) +#-----| 0 -> [RegExpPlus] e+ + +# 421| [RegExpPlus] (e+)+ +#-----| 0 -> [RegExpGroup] (e+) + +# 421| [RegExpConstant, RegExpNormalChar] e + +# 421| [RegExpPlus] e+ +#-----| 0 -> [RegExpConstant, RegExpNormalChar] e + +# 421| [RegExpConstant, RegExpNormalChar] f + +# 425| [RegExpCaret] ^ + +# 425| [RegExpSequence] ^ab(c+)+$ +#-----| 0 -> [RegExpCaret] ^ +#-----| 1 -> [RegExpConstant, RegExpNormalChar] ab +#-----| 2 -> [RegExpPlus] (c+)+ +#-----| 3 -> [RegExpDollar] $ + +# 425| [RegExpConstant, RegExpNormalChar] ab + +# 425| [RegExpGroup] (c+) +#-----| 0 -> [RegExpPlus] c+ + +# 425| [RegExpPlus] (c+)+ +#-----| 0 -> [RegExpGroup] (c+) + +# 425| [RegExpConstant, RegExpNormalChar] c + +# 425| [RegExpPlus] c+ +#-----| 0 -> [RegExpConstant, RegExpNormalChar] c + +# 425| [RegExpDollar] $ + +# 429| [RegExpGroup] (\d(\s+)*) +#-----| 0 -> [RegExpSequence] \d(\s+)* + +# 429| [RegExpRange] (\d(\s+)*){20} +#-----| 0 -> [RegExpGroup] (\d(\s+)*) + +# 429| [RegExpCharacterClassEscape] \d + +# 429| [RegExpSequence] \d(\s+)* +#-----| 0 -> [RegExpCharacterClassEscape] \d +#-----| 1 -> [RegExpStar] (\s+)* + +# 429| [RegExpGroup] (\s+) +#-----| 0 -> [RegExpPlus] \s+ + +# 429| [RegExpStar] (\s+)* +#-----| 0 -> [RegExpGroup] (\s+) + +# 429| [RegExpCharacterClassEscape] \s + +# 429| [RegExpPlus] \s+ +#-----| 0 -> [RegExpCharacterClassEscape] \s + +# 432| [RegExpGroup] (([^/]|X)+) +#-----| 0 -> [RegExpPlus] ([^/]|X)+ + +# 432| [RegExpSequence] (([^/]|X)+)(\/[\s\S]*)*$ +#-----| 0 -> [RegExpGroup] (([^/]|X)+) +#-----| 1 -> [RegExpStar] (\/[\s\S]*)* +#-----| 2 -> [RegExpDollar] $ + +# 432| [RegExpGroup] ([^/]|X) +#-----| 0 -> [RegExpAlt] [^/]|X + +# 432| [RegExpPlus] ([^/]|X)+ +#-----| 0 -> [RegExpGroup] ([^/]|X) + +# 432| [RegExpCharacterClass] [^/] +#-----| 0 -> [RegExpConstant, RegExpNormalChar] / + +# 432| [RegExpAlt] [^/]|X +#-----| 0 -> [RegExpCharacterClass] [^/] +#-----| 1 -> [RegExpConstant, RegExpNormalChar] X + +# 432| [RegExpConstant, RegExpNormalChar] / + +# 432| [RegExpConstant, RegExpNormalChar] X + +# 432| [RegExpGroup] (\/[\s\S]*) +#-----| 0 -> [RegExpSequence] \/[\s\S]* + +# 432| [RegExpStar] (\/[\s\S]*)* +#-----| 0 -> [RegExpGroup] (\/[\s\S]*) + +# 432| [RegExpConstant, RegExpEscape] \/ + +# 432| [RegExpSequence] \/[\s\S]* +#-----| 0 -> [RegExpConstant, RegExpEscape] \/ +#-----| 1 -> [RegExpStar] [\s\S]* + +# 432| [RegExpCharacterClass] [\s\S] +#-----| 0 -> [RegExpCharacterClassEscape] \s +#-----| 1 -> [RegExpCharacterClassEscape] \S + +# 432| [RegExpStar] [\s\S]* +#-----| 0 -> [RegExpCharacterClass] [\s\S] + +# 432| [RegExpCharacterClassEscape] \s + +# 432| [RegExpCharacterClassEscape] \S + +# 432| [RegExpDollar] $ + +# 435| [RegExpCaret] ^ + +# 435| [RegExpSequence] ^((x([^Y]+)?)*(Y|$)) +#-----| 0 -> [RegExpCaret] ^ +#-----| 1 -> [RegExpGroup] ((x([^Y]+)?)*(Y|$)) + +# 435| [RegExpGroup] ((x([^Y]+)?)*(Y|$)) +#-----| 0 -> [RegExpSequence] (x([^Y]+)?)*(Y|$) + +# 435| [RegExpGroup] (x([^Y]+)?) +#-----| 0 -> [RegExpSequence] x([^Y]+)? + +# 435| [RegExpStar] (x([^Y]+)?)* +#-----| 0 -> [RegExpGroup] (x([^Y]+)?) + +# 435| [RegExpSequence] (x([^Y]+)?)*(Y|$) +#-----| 0 -> [RegExpStar] (x([^Y]+)?)* +#-----| 1 -> [RegExpGroup] (Y|$) + +# 435| [RegExpConstant, RegExpNormalChar] x + +# 435| [RegExpSequence] x([^Y]+)? +#-----| 0 -> [RegExpConstant, RegExpNormalChar] x +#-----| 1 -> [RegExpOpt] ([^Y]+)? + +# 435| [RegExpGroup] ([^Y]+) +#-----| 0 -> [RegExpPlus] [^Y]+ + +# 435| [RegExpOpt] ([^Y]+)? +#-----| 0 -> [RegExpGroup] ([^Y]+) + +# 435| [RegExpCharacterClass] [^Y] +#-----| 0 -> [RegExpConstant, RegExpNormalChar] Y + +# 435| [RegExpPlus] [^Y]+ +#-----| 0 -> [RegExpCharacterClass] [^Y] + +# 435| [RegExpConstant, RegExpNormalChar] Y + +# 435| [RegExpGroup] (Y|$) +#-----| 0 -> [RegExpAlt] Y|$ + +# 435| [RegExpConstant, RegExpNormalChar] Y + +# 435| [RegExpAlt] Y|$ +#-----| 0 -> [RegExpConstant, RegExpNormalChar] Y +#-----| 1 -> [RegExpDollar] $ + +# 435| [RegExpDollar] $ + +# 439| [RegExpConstant, RegExpNormalChar] foo + +# 439| [RegExpSequence] foo([\w-]*)+bar +#-----| 0 -> [RegExpConstant, RegExpNormalChar] foo +#-----| 1 -> [RegExpPlus] ([\w-]*)+ +#-----| 2 -> [RegExpConstant, RegExpNormalChar] bar + +# 439| [RegExpGroup] ([\w-]*) +#-----| 0 -> [RegExpStar] [\w-]* + +# 439| [RegExpPlus] ([\w-]*)+ +#-----| 0 -> [RegExpGroup] ([\w-]*) + +# 439| [RegExpCharacterClass] [\w-] +#-----| 0 -> [RegExpCharacterClassEscape] \w +#-----| 1 -> [RegExpConstant, RegExpNormalChar] - + +# 439| [RegExpStar] [\w-]* +#-----| 0 -> [RegExpCharacterClass] [\w-] + +# 439| [RegExpCharacterClassEscape] \w + +# 439| [RegExpConstant, RegExpNormalChar] - + +# 439| [RegExpConstant, RegExpNormalChar] bar + +# 443| [RegExpGroup] ((ab)*) +#-----| 0 -> [RegExpStar] (ab)* + +# 443| [RegExpPlus] ((ab)*)+ +#-----| 0 -> [RegExpGroup] ((ab)*) + +# 443| [RegExpSequence] ((ab)*)+c +#-----| 0 -> [RegExpPlus] ((ab)*)+ +#-----| 1 -> [RegExpConstant, RegExpNormalChar] c + +# 443| [RegExpGroup] (ab) +#-----| 0 -> [RegExpConstant, RegExpNormalChar] ab + +# 443| [RegExpStar] (ab)* +#-----| 0 -> [RegExpGroup] (ab) + +# 443| [RegExpConstant, RegExpNormalChar] ab + +# 443| [RegExpConstant, RegExpNormalChar] c + +# 447| [RegExpGroup] (a?a?) +#-----| 0 -> [RegExpSequence] a?a? + +# 447| [RegExpStar] (a?a?)* +#-----| 0 -> [RegExpGroup] (a?a?) + +# 447| [RegExpSequence] (a?a?)*b +#-----| 0 -> [RegExpStar] (a?a?)* +#-----| 1 -> [RegExpConstant, RegExpNormalChar] b + +# 447| [RegExpConstant, RegExpNormalChar] a + +# 447| [RegExpOpt] a? +#-----| 0 -> [RegExpConstant, RegExpNormalChar] a + +# 447| [RegExpSequence] a?a? +#-----| 0 -> [RegExpOpt] a? +#-----| 1 -> [RegExpOpt] a? + +# 447| [RegExpConstant, RegExpNormalChar] a + +# 447| [RegExpOpt] a? +#-----| 0 -> [RegExpConstant, RegExpNormalChar] a + +# 447| [RegExpConstant, RegExpNormalChar] b + +# 450| [RegExpGroup] (a?) +#-----| 0 -> [RegExpOpt] a? + +# 450| [RegExpStar] (a?)* +#-----| 0 -> [RegExpGroup] (a?) + +# 450| [RegExpSequence] (a?)*b +#-----| 0 -> [RegExpStar] (a?)* +#-----| 1 -> [RegExpConstant, RegExpNormalChar] b + +# 450| [RegExpConstant, RegExpNormalChar] a + +# 450| [RegExpOpt] a? +#-----| 0 -> [RegExpConstant, RegExpNormalChar] a + +# 450| [RegExpConstant, RegExpNormalChar] b + +# 454| [RegExpGroup] (c?a?) +#-----| 0 -> [RegExpSequence] c?a? + +# 454| [RegExpStar] (c?a?)* +#-----| 0 -> [RegExpGroup] (c?a?) + +# 454| [RegExpSequence] (c?a?)*b +#-----| 0 -> [RegExpStar] (c?a?)* +#-----| 1 -> [RegExpConstant, RegExpNormalChar] b + +# 454| [RegExpConstant, RegExpNormalChar] c + +# 454| [RegExpOpt] c? +#-----| 0 -> [RegExpConstant, RegExpNormalChar] c + +# 454| [RegExpSequence] c?a? +#-----| 0 -> [RegExpOpt] c? +#-----| 1 -> [RegExpOpt] a? + +# 454| [RegExpConstant, RegExpNormalChar] a + +# 454| [RegExpOpt] a? +#-----| 0 -> [RegExpConstant, RegExpNormalChar] a + +# 454| [RegExpConstant, RegExpNormalChar] b + +# 458| [RegExpGroup] (?:a|a?) +#-----| 0 -> [RegExpAlt] a|a? + +# 458| [RegExpPlus] (?:a|a?)+ +#-----| 0 -> [RegExpGroup] (?:a|a?) + +# 458| [RegExpSequence] (?:a|a?)+b +#-----| 0 -> [RegExpPlus] (?:a|a?)+ +#-----| 1 -> [RegExpConstant, RegExpNormalChar] b + +# 458| [RegExpConstant, RegExpNormalChar] a + +# 458| [RegExpAlt] a|a? +#-----| 0 -> [RegExpConstant, RegExpNormalChar] a +#-----| 1 -> [RegExpOpt] a? + +# 458| [RegExpConstant, RegExpNormalChar] a + +# 458| [RegExpOpt] a? +#-----| 0 -> [RegExpConstant, RegExpNormalChar] a + +# 458| [RegExpConstant, RegExpNormalChar] b + +# 462| [RegExpGroup] (a?b?) +#-----| 0 -> [RegExpSequence] a?b? + +# 462| [RegExpStar] (a?b?)* +#-----| 0 -> [RegExpGroup] (a?b?) + +# 462| [RegExpSequence] (a?b?)*$ +#-----| 0 -> [RegExpStar] (a?b?)* +#-----| 1 -> [RegExpDollar] $ + +# 462| [RegExpConstant, RegExpNormalChar] a + +# 462| [RegExpOpt] a? +#-----| 0 -> [RegExpConstant, RegExpNormalChar] a + +# 462| [RegExpSequence] a?b? +#-----| 0 -> [RegExpOpt] a? +#-----| 1 -> [RegExpOpt] b? + +# 462| [RegExpConstant, RegExpNormalChar] b + +# 462| [RegExpOpt] b? +#-----| 0 -> [RegExpConstant, RegExpNormalChar] b + +# 462| [RegExpDollar] $ + +# 466| [RegExpConstant, RegExpNormalChar] PRE + +# 466| [RegExpSequence] PRE(([a-c]|[c-d])T(e?e?e?e?|X))+(cTcT|cTXcTX$) +#-----| 0 -> [RegExpConstant, RegExpNormalChar] PRE +#-----| 1 -> [RegExpPlus] (([a-c]|[c-d])T(e?e?e?e?|X))+ +#-----| 2 -> [RegExpGroup] (cTcT|cTXcTX$) + +# 466| [RegExpGroup] (([a-c]|[c-d])T(e?e?e?e?|X)) +#-----| 0 -> [RegExpSequence] ([a-c]|[c-d])T(e?e?e?e?|X) + +# 466| [RegExpPlus] (([a-c]|[c-d])T(e?e?e?e?|X))+ +#-----| 0 -> [RegExpGroup] (([a-c]|[c-d])T(e?e?e?e?|X)) + +# 466| [RegExpGroup] ([a-c]|[c-d]) +#-----| 0 -> [RegExpAlt] [a-c]|[c-d] + +# 466| [RegExpSequence] ([a-c]|[c-d])T(e?e?e?e?|X) +#-----| 0 -> [RegExpGroup] ([a-c]|[c-d]) +#-----| 1 -> [RegExpConstant, RegExpNormalChar] T +#-----| 2 -> [RegExpGroup] (e?e?e?e?|X) + +# 466| [RegExpCharacterClass] [a-c] +#-----| 0 -> [RegExpCharacterRange] a-c + +# 466| [RegExpAlt] [a-c]|[c-d] +#-----| 0 -> [RegExpCharacterClass] [a-c] +#-----| 1 -> [RegExpCharacterClass] [c-d] + +# 466| [RegExpConstant, RegExpNormalChar] a + +# 466| [RegExpCharacterRange] a-c +#-----| 0 -> [RegExpConstant, RegExpNormalChar] a +#-----| 1 -> [RegExpConstant, RegExpNormalChar] c + +# 466| [RegExpConstant, RegExpNormalChar] c + +# 466| [RegExpCharacterClass] [c-d] +#-----| 0 -> [RegExpCharacterRange] c-d + +# 466| [RegExpConstant, RegExpNormalChar] c + +# 466| [RegExpCharacterRange] c-d +#-----| 0 -> [RegExpConstant, RegExpNormalChar] c +#-----| 1 -> [RegExpConstant, RegExpNormalChar] d + +# 466| [RegExpConstant, RegExpNormalChar] d + +# 466| [RegExpConstant, RegExpNormalChar] T + +# 466| [RegExpGroup] (e?e?e?e?|X) +#-----| 0 -> [RegExpAlt] e?e?e?e?|X + +# 466| [RegExpConstant, RegExpNormalChar] e + +# 466| [RegExpOpt] e? +#-----| 0 -> [RegExpConstant, RegExpNormalChar] e + +# 466| [RegExpSequence] e?e?e?e? +#-----| 0 -> [RegExpOpt] e? +#-----| 1 -> [RegExpOpt] e? +#-----| 2 -> [RegExpOpt] e? +#-----| 3 -> [RegExpOpt] e? + +# 466| [RegExpAlt] e?e?e?e?|X +#-----| 0 -> [RegExpSequence] e?e?e?e? +#-----| 1 -> [RegExpConstant, RegExpNormalChar] X + +# 466| [RegExpConstant, RegExpNormalChar] e + +# 466| [RegExpOpt] e? +#-----| 0 -> [RegExpConstant, RegExpNormalChar] e + +# 466| [RegExpConstant, RegExpNormalChar] e + +# 466| [RegExpOpt] e? +#-----| 0 -> [RegExpConstant, RegExpNormalChar] e + +# 466| [RegExpConstant, RegExpNormalChar] e + +# 466| [RegExpOpt] e? +#-----| 0 -> [RegExpConstant, RegExpNormalChar] e + +# 466| [RegExpConstant, RegExpNormalChar] X + +# 466| [RegExpGroup] (cTcT|cTXcTX$) +#-----| 0 -> [RegExpAlt] cTcT|cTXcTX$ + +# 466| [RegExpConstant, RegExpNormalChar] cTcT + +# 466| [RegExpAlt] cTcT|cTXcTX$ +#-----| 0 -> [RegExpConstant, RegExpNormalChar] cTcT +#-----| 1 -> [RegExpSequence] cTXcTX$ + +# 466| [RegExpConstant, RegExpNormalChar] cTXcTX + +# 466| [RegExpSequence] cTXcTX$ +#-----| 0 -> [RegExpConstant, RegExpNormalChar] cTXcTX +#-----| 1 -> [RegExpDollar] $ + +# 466| [RegExpDollar] $ + +# 470| [RegExpCaret] ^ + +# 470| [RegExpSequence] ^((a)+\w)+$ +#-----| 0 -> [RegExpCaret] ^ +#-----| 1 -> [RegExpPlus] ((a)+\w)+ +#-----| 2 -> [RegExpDollar] $ + +# 470| [RegExpGroup] ((a)+\w) +#-----| 0 -> [RegExpSequence] (a)+\w + +# 470| [RegExpPlus] ((a)+\w)+ +#-----| 0 -> [RegExpGroup] ((a)+\w) + +# 470| [RegExpGroup] (a) +#-----| 0 -> [RegExpConstant, RegExpNormalChar] a + +# 470| [RegExpPlus] (a)+ +#-----| 0 -> [RegExpGroup] (a) + +# 470| [RegExpSequence] (a)+\w +#-----| 0 -> [RegExpPlus] (a)+ +#-----| 1 -> [RegExpCharacterClassEscape] \w + +# 470| [RegExpConstant, RegExpNormalChar] a + +# 470| [RegExpCharacterClassEscape] \w + +# 470| [RegExpDollar] $ + +# 474| [RegExpCaret] ^ + +# 474| [RegExpSequence] ^(b+.)+$ +#-----| 0 -> [RegExpCaret] ^ +#-----| 1 -> [RegExpPlus] (b+.)+ +#-----| 2 -> [RegExpDollar] $ + +# 474| [RegExpGroup] (b+.) +#-----| 0 -> [RegExpSequence] b+. + +# 474| [RegExpPlus] (b+.)+ +#-----| 0 -> [RegExpGroup] (b+.) + +# 474| [RegExpConstant, RegExpNormalChar] b + +# 474| [RegExpPlus] b+ +#-----| 0 -> [RegExpConstant, RegExpNormalChar] b + +# 474| [RegExpSequence] b+. +#-----| 0 -> [RegExpPlus] b+ +#-----| 1 -> [RegExpDot] . + +# 474| [RegExpDot] . + +# 474| [RegExpDollar] $ + +# 478| [RegExpGroup] (a*) +#-----| 0 -> [RegExpStar] a* + +# 478| [RegExpStar] (a*)* +#-----| 0 -> [RegExpGroup] (a*) + +# 478| [RegExpSequence] (a*)*b +#-----| 0 -> [RegExpStar] (a*)* +#-----| 1 -> [RegExpConstant, RegExpNormalChar] b + +# 478| [RegExpConstant, RegExpNormalChar] a + +# 478| [RegExpStar] a* +#-----| 0 -> [RegExpConstant, RegExpNormalChar] a + +# 478| [RegExpConstant, RegExpNormalChar] b + +# 479| [RegExpGroup] (a+) +#-----| 0 -> [RegExpPlus] a+ + +# 479| [RegExpStar] (a+)* +#-----| 0 -> [RegExpGroup] (a+) + +# 479| [RegExpSequence] (a+)*b +#-----| 0 -> [RegExpStar] (a+)* +#-----| 1 -> [RegExpConstant, RegExpNormalChar] b + +# 479| [RegExpConstant, RegExpNormalChar] a + +# 479| [RegExpPlus] a+ +#-----| 0 -> [RegExpConstant, RegExpNormalChar] a + +# 479| [RegExpConstant, RegExpNormalChar] b + +# 480| [RegExpGroup] (a*) +#-----| 0 -> [RegExpStar] a* + +# 480| [RegExpPlus] (a*)+ +#-----| 0 -> [RegExpGroup] (a*) + +# 480| [RegExpSequence] (a*)+b +#-----| 0 -> [RegExpPlus] (a*)+ +#-----| 1 -> [RegExpConstant, RegExpNormalChar] b + +# 480| [RegExpConstant, RegExpNormalChar] a + +# 480| [RegExpStar] a* +#-----| 0 -> [RegExpConstant, RegExpNormalChar] a + +# 480| [RegExpConstant, RegExpNormalChar] b + +# 481| [RegExpGroup] (a+) +#-----| 0 -> [RegExpPlus] a+ + +# 481| [RegExpPlus] (a+)+ +#-----| 0 -> [RegExpGroup] (a+) + +# 481| [RegExpSequence] (a+)+b +#-----| 0 -> [RegExpPlus] (a+)+ +#-----| 1 -> [RegExpConstant, RegExpNormalChar] b + +# 481| [RegExpConstant, RegExpNormalChar] a + +# 481| [RegExpPlus] a+ +#-----| 0 -> [RegExpConstant, RegExpNormalChar] a + +# 481| [RegExpConstant, RegExpNormalChar] b + +# 484| [RegExpGroup] (a|b) +#-----| 0 -> [RegExpAlt] a|b + +# 484| [RegExpPlus] (a|b)+ +#-----| 0 -> [RegExpGroup] (a|b) + +# 484| [RegExpConstant, RegExpNormalChar] a + +# 484| [RegExpAlt] a|b +#-----| 0 -> [RegExpConstant, RegExpNormalChar] a +#-----| 1 -> [RegExpConstant, RegExpNormalChar] b + +# 484| [RegExpConstant, RegExpNormalChar] b + +# 487| [RegExpGroup] (?:[\s;,"'<>(){}|\[\]@=+*]|:(?![/\\])) +#-----| 0 -> [RegExpAlt] [\s;,"'<>(){}|\[\]@=+*]|:(?![/\\]) + +# 487| [RegExpPlus] (?:[\s;,"'<>(){}|\[\]@=+*]|:(?![/\\]))+ +#-----| 0 -> [RegExpGroup] (?:[\s;,"'<>(){}|\[\]@=+*]|:(?![/\\])) + +# 487| [RegExpCharacterClass] [\s;,"'<>(){}|\[\]@=+*] +#-----| 0 -> [RegExpCharacterClassEscape] \s +#-----| 1 -> [RegExpConstant, RegExpNormalChar] ; +#-----| 2 -> [RegExpConstant, RegExpNormalChar] , +#-----| 3 -> [RegExpConstant, RegExpNormalChar] " +#-----| 4 -> [RegExpConstant, RegExpNormalChar] ' +#-----| 5 -> [RegExpConstant, RegExpNormalChar] < +#-----| 6 -> [RegExpConstant, RegExpNormalChar] > +#-----| 7 -> [RegExpConstant, RegExpNormalChar] ( +#-----| 8 -> [RegExpConstant, RegExpNormalChar] ) +#-----| 9 -> [RegExpConstant, RegExpNormalChar] { +#-----| 10 -> [RegExpConstant, RegExpNormalChar] } +#-----| 11 -> [RegExpConstant, RegExpNormalChar] | +#-----| 12 -> [RegExpConstant, RegExpEscape] \[ +#-----| 13 -> [RegExpConstant, RegExpEscape] \] +#-----| 14 -> [RegExpConstant, RegExpNormalChar] @ +#-----| 15 -> [RegExpConstant, RegExpNormalChar] = +#-----| 16 -> [RegExpConstant, RegExpNormalChar] + +#-----| 17 -> [RegExpConstant, RegExpNormalChar] * + +# 487| [RegExpAlt] [\s;,"'<>(){}|\[\]@=+*]|:(?![/\\]) +#-----| 0 -> [RegExpCharacterClass] [\s;,"'<>(){}|\[\]@=+*] +#-----| 1 -> [RegExpSequence] :(?![/\\]) + +# 487| [RegExpCharacterClassEscape] \s + +# 487| [RegExpConstant, RegExpNormalChar] ; + +# 487| [RegExpConstant, RegExpNormalChar] , + +# 487| [RegExpConstant, RegExpNormalChar] " + +# 487| [RegExpConstant, RegExpNormalChar] ' + +# 487| [RegExpConstant, RegExpNormalChar] < + +# 487| [RegExpConstant, RegExpNormalChar] > + +# 487| [RegExpConstant, RegExpNormalChar] ( + +# 487| [RegExpConstant, RegExpNormalChar] ) + +# 487| [RegExpConstant, RegExpNormalChar] { + +# 487| [RegExpConstant, RegExpNormalChar] } + +# 487| [RegExpConstant, RegExpNormalChar] | + +# 487| [RegExpConstant, RegExpEscape] \[ + +# 487| [RegExpConstant, RegExpEscape] \] + +# 487| [RegExpConstant, RegExpNormalChar] @ + +# 487| [RegExpConstant, RegExpNormalChar] = + +# 487| [RegExpConstant, RegExpNormalChar] + + +# 487| [RegExpConstant, RegExpNormalChar] * + +# 487| [RegExpConstant, RegExpNormalChar] : + +# 487| [RegExpSequence] :(?![/\\]) +#-----| 0 -> [RegExpConstant, RegExpNormalChar] : +#-----| 1 -> [RegExpNegativeLookahead] (?![/\\]) + +# 487| [RegExpNegativeLookahead] (?![/\\]) + +# 487| [RegExpCharacterClass] [/\\] +#-----| 0 -> [RegExpConstant, RegExpNormalChar] / +#-----| 1 -> [RegExpConstant, RegExpEscape] \\ + +# 487| [RegExpConstant, RegExpNormalChar] / + +# 487| [RegExpConstant, RegExpEscape] \\ + +# 491| [RegExpCaret] ^ + +# 491| [RegExpSequence] ^((?:a{|-)|\w\{)+X$ +#-----| 0 -> [RegExpCaret] ^ +#-----| 1 -> [RegExpPlus] ((?:a{|-)|\w\{)+ +#-----| 2 -> [RegExpConstant, RegExpNormalChar] X +#-----| 3 -> [RegExpDollar] $ + +# 491| [RegExpGroup] ((?:a{|-)|\w\{) +#-----| 0 -> [RegExpAlt] (?:a{|-)|\w\{ + +# 491| [RegExpPlus] ((?:a{|-)|\w\{)+ +#-----| 0 -> [RegExpGroup] ((?:a{|-)|\w\{) + +# 491| [RegExpGroup] (?:a{|-) +#-----| 0 -> [RegExpAlt] a{|- + +# 491| [RegExpAlt] (?:a{|-)|\w\{ +#-----| 0 -> [RegExpGroup] (?:a{|-) +#-----| 1 -> [RegExpSequence] \w\{ + +# 491| [RegExpConstant, RegExpNormalChar] a{ + +# 491| [RegExpAlt] a{|- +#-----| 0 -> [RegExpConstant, RegExpNormalChar] a{ +#-----| 1 -> [RegExpConstant, RegExpNormalChar] - + +# 491| [RegExpConstant, RegExpNormalChar] - + +# 491| [RegExpCharacterClassEscape] \w + +# 491| [RegExpSequence] \w\{ +#-----| 0 -> [RegExpCharacterClassEscape] \w +#-----| 1 -> [RegExpConstant, RegExpEscape] \{ + +# 491| [RegExpConstant, RegExpEscape] \{ + +# 491| [RegExpConstant, RegExpNormalChar] X + +# 491| [RegExpDollar] $ + +# 492| [RegExpCaret] ^ + +# 492| [RegExpSequence] ^((?:a{0|-)|\w\{\d)+X$ +#-----| 0 -> [RegExpCaret] ^ +#-----| 1 -> [RegExpPlus] ((?:a{0|-)|\w\{\d)+ +#-----| 2 -> [RegExpConstant, RegExpNormalChar] X +#-----| 3 -> [RegExpDollar] $ + +# 492| [RegExpGroup] ((?:a{0|-)|\w\{\d) +#-----| 0 -> [RegExpAlt] (?:a{0|-)|\w\{\d + +# 492| [RegExpPlus] ((?:a{0|-)|\w\{\d)+ +#-----| 0 -> [RegExpGroup] ((?:a{0|-)|\w\{\d) + +# 492| [RegExpGroup] (?:a{0|-) +#-----| 0 -> [RegExpAlt] a{0|- + +# 492| [RegExpAlt] (?:a{0|-)|\w\{\d +#-----| 0 -> [RegExpGroup] (?:a{0|-) +#-----| 1 -> [RegExpSequence] \w\{\d + +# 492| [RegExpConstant, RegExpNormalChar] a{0 + +# 492| [RegExpAlt] a{0|- +#-----| 0 -> [RegExpConstant, RegExpNormalChar] a{0 +#-----| 1 -> [RegExpConstant, RegExpNormalChar] - + +# 492| [RegExpConstant, RegExpNormalChar] - + +# 492| [RegExpCharacterClassEscape] \w + +# 492| [RegExpSequence] \w\{\d +#-----| 0 -> [RegExpCharacterClassEscape] \w +#-----| 1 -> [RegExpConstant, RegExpEscape] \{ +#-----| 2 -> [RegExpCharacterClassEscape] \d + +# 492| [RegExpConstant, RegExpEscape] \{ + +# 492| [RegExpCharacterClassEscape] \d + +# 492| [RegExpConstant, RegExpNormalChar] X + +# 492| [RegExpDollar] $ + +# 493| [RegExpCaret] ^ + +# 493| [RegExpSequence] ^((?:a{0,|-)|\w\{\d,)+X$ +#-----| 0 -> [RegExpCaret] ^ +#-----| 1 -> [RegExpPlus] ((?:a{0,|-)|\w\{\d,)+ +#-----| 2 -> [RegExpConstant, RegExpNormalChar] X +#-----| 3 -> [RegExpDollar] $ + +# 493| [RegExpGroup] ((?:a{0,|-)|\w\{\d,) +#-----| 0 -> [RegExpAlt] (?:a{0,|-)|\w\{\d, + +# 493| [RegExpPlus] ((?:a{0,|-)|\w\{\d,)+ +#-----| 0 -> [RegExpGroup] ((?:a{0,|-)|\w\{\d,) + +# 493| [RegExpGroup] (?:a{0,|-) +#-----| 0 -> [RegExpAlt] a{0,|- + +# 493| [RegExpAlt] (?:a{0,|-)|\w\{\d, +#-----| 0 -> [RegExpGroup] (?:a{0,|-) +#-----| 1 -> [RegExpSequence] \w\{\d, + +# 493| [RegExpConstant, RegExpNormalChar] a{0, + +# 493| [RegExpAlt] a{0,|- +#-----| 0 -> [RegExpConstant, RegExpNormalChar] a{0, +#-----| 1 -> [RegExpConstant, RegExpNormalChar] - + +# 493| [RegExpConstant, RegExpNormalChar] - + +# 493| [RegExpCharacterClassEscape] \w + +# 493| [RegExpSequence] \w\{\d, +#-----| 0 -> [RegExpCharacterClassEscape] \w +#-----| 1 -> [RegExpConstant, RegExpEscape] \{ +#-----| 2 -> [RegExpCharacterClassEscape] \d +#-----| 3 -> [RegExpConstant, RegExpNormalChar] , + +# 493| [RegExpConstant, RegExpEscape] \{ + +# 493| [RegExpCharacterClassEscape] \d + +# 493| [RegExpConstant, RegExpNormalChar] , + +# 493| [RegExpConstant, RegExpNormalChar] X + +# 493| [RegExpDollar] $ + +# 494| [RegExpCaret] ^ + +# 494| [RegExpSequence] ^((?:a{0,2|-)|\w\{\d,\d)+X$ +#-----| 0 -> [RegExpCaret] ^ +#-----| 1 -> [RegExpPlus] ((?:a{0,2|-)|\w\{\d,\d)+ +#-----| 2 -> [RegExpConstant, RegExpNormalChar] X +#-----| 3 -> [RegExpDollar] $ + +# 494| [RegExpGroup] ((?:a{0,2|-)|\w\{\d,\d) +#-----| 0 -> [RegExpAlt] (?:a{0,2|-)|\w\{\d,\d + +# 494| [RegExpPlus] ((?:a{0,2|-)|\w\{\d,\d)+ +#-----| 0 -> [RegExpGroup] ((?:a{0,2|-)|\w\{\d,\d) + +# 494| [RegExpGroup] (?:a{0,2|-) +#-----| 0 -> [RegExpAlt] a{0,2|- + +# 494| [RegExpAlt] (?:a{0,2|-)|\w\{\d,\d +#-----| 0 -> [RegExpGroup] (?:a{0,2|-) +#-----| 1 -> [RegExpSequence] \w\{\d,\d + +# 494| [RegExpConstant, RegExpNormalChar] a{0,2 + +# 494| [RegExpAlt] a{0,2|- +#-----| 0 -> [RegExpConstant, RegExpNormalChar] a{0,2 +#-----| 1 -> [RegExpConstant, RegExpNormalChar] - + +# 494| [RegExpConstant, RegExpNormalChar] - + +# 494| [RegExpCharacterClassEscape] \w + +# 494| [RegExpSequence] \w\{\d,\d +#-----| 0 -> [RegExpCharacterClassEscape] \w +#-----| 1 -> [RegExpConstant, RegExpEscape] \{ +#-----| 2 -> [RegExpCharacterClassEscape] \d +#-----| 3 -> [RegExpConstant, RegExpNormalChar] , +#-----| 4 -> [RegExpCharacterClassEscape] \d + +# 494| [RegExpConstant, RegExpEscape] \{ + +# 494| [RegExpCharacterClassEscape] \d + +# 494| [RegExpConstant, RegExpNormalChar] , + +# 494| [RegExpCharacterClassEscape] \d + +# 494| [RegExpConstant, RegExpNormalChar] X + +# 494| [RegExpDollar] $ + +# 497| [RegExpCaret] ^ + +# 497| [RegExpSequence] ^((?:a{0,2}|-)|\w\{\d,\d\})+X$ +#-----| 0 -> [RegExpCaret] ^ +#-----| 1 -> [RegExpPlus] ((?:a{0,2}|-)|\w\{\d,\d\})+ +#-----| 2 -> [RegExpConstant, RegExpNormalChar] X +#-----| 3 -> [RegExpDollar] $ + +# 497| [RegExpGroup] ((?:a{0,2}|-)|\w\{\d,\d\}) +#-----| 0 -> [RegExpAlt] (?:a{0,2}|-)|\w\{\d,\d\} + +# 497| [RegExpPlus] ((?:a{0,2}|-)|\w\{\d,\d\})+ +#-----| 0 -> [RegExpGroup] ((?:a{0,2}|-)|\w\{\d,\d\}) + +# 497| [RegExpGroup] (?:a{0,2}|-) +#-----| 0 -> [RegExpAlt] a{0,2}|- + +# 497| [RegExpAlt] (?:a{0,2}|-)|\w\{\d,\d\} +#-----| 0 -> [RegExpGroup] (?:a{0,2}|-) +#-----| 1 -> [RegExpSequence] \w\{\d,\d\} + +# 497| [RegExpConstant, RegExpNormalChar] a + +# 497| [RegExpRange] a{0,2} +#-----| 0 -> [RegExpConstant, RegExpNormalChar] a + +# 497| [RegExpAlt] a{0,2}|- +#-----| 0 -> [RegExpRange] a{0,2} +#-----| 1 -> [RegExpConstant, RegExpNormalChar] - + +# 497| [RegExpConstant, RegExpNormalChar] - + +# 497| [RegExpCharacterClassEscape] \w + +# 497| [RegExpSequence] \w\{\d,\d\} +#-----| 0 -> [RegExpCharacterClassEscape] \w +#-----| 1 -> [RegExpConstant, RegExpEscape] \{ +#-----| 2 -> [RegExpCharacterClassEscape] \d +#-----| 3 -> [RegExpConstant, RegExpNormalChar] , +#-----| 4 -> [RegExpCharacterClassEscape] \d +#-----| 5 -> [RegExpConstant, RegExpEscape] \} + +# 497| [RegExpConstant, RegExpEscape] \{ + +# 497| [RegExpCharacterClassEscape] \d + +# 497| [RegExpConstant, RegExpNormalChar] , + +# 497| [RegExpCharacterClassEscape] \d + +# 497| [RegExpConstant, RegExpEscape] \} + +# 497| [RegExpConstant, RegExpNormalChar] X + +# 497| [RegExpDollar] $ + +# 501| [RegExpConstant, RegExpNormalChar] X + +# 501| [RegExpSequence] X(\u0061|a)*Y +#-----| 0 -> [RegExpConstant, RegExpNormalChar] X +#-----| 1 -> [RegExpStar] (\u0061|a)* +#-----| 2 -> [RegExpConstant, RegExpNormalChar] Y + +# 501| [RegExpGroup] (\u0061|a) +#-----| 0 -> [RegExpAlt] \u0061|a + +# 501| [RegExpStar] (\u0061|a)* +#-----| 0 -> [RegExpGroup] (\u0061|a) + +# 501| [RegExpConstant, RegExpEscape] \u0061 + +# 501| [RegExpAlt] \u0061|a +#-----| 0 -> [RegExpConstant, RegExpEscape] \u0061 +#-----| 1 -> [RegExpConstant, RegExpNormalChar] a + +# 501| [RegExpConstant, RegExpNormalChar] a + +# 501| [RegExpConstant, RegExpNormalChar] Y + +# 504| [RegExpConstant, RegExpNormalChar] X + +# 504| [RegExpSequence] X(\u0061|b)+Y +#-----| 0 -> [RegExpConstant, RegExpNormalChar] X +#-----| 1 -> [RegExpPlus] (\u0061|b)+ +#-----| 2 -> [RegExpConstant, RegExpNormalChar] Y + +# 504| [RegExpGroup] (\u0061|b) +#-----| 0 -> [RegExpAlt] \u0061|b + +# 504| [RegExpPlus] (\u0061|b)+ +#-----| 0 -> [RegExpGroup] (\u0061|b) + +# 504| [RegExpConstant, RegExpEscape] \u0061 + +# 504| [RegExpAlt] \u0061|b +#-----| 0 -> [RegExpConstant, RegExpEscape] \u0061 +#-----| 1 -> [RegExpConstant, RegExpNormalChar] b + +# 504| [RegExpConstant, RegExpNormalChar] b + +# 504| [RegExpConstant, RegExpNormalChar] Y + +# 508| [RegExpConstant, RegExpNormalChar] X + +# 508| [RegExpSequence] X(\U00000061|a)*Y +#-----| 0 -> [RegExpConstant, RegExpNormalChar] X +#-----| 1 -> [RegExpStar] (\U00000061|a)* +#-----| 2 -> [RegExpConstant, RegExpNormalChar] Y + +# 508| [RegExpGroup] (\U00000061|a) +#-----| 0 -> [RegExpAlt] \U00000061|a + +# 508| [RegExpStar] (\U00000061|a)* +#-----| 0 -> [RegExpGroup] (\U00000061|a) + +# 508| [RegExpConstant, RegExpEscape] \U00000061 + +# 508| [RegExpAlt] \U00000061|a +#-----| 0 -> [RegExpConstant, RegExpEscape] \U00000061 +#-----| 1 -> [RegExpConstant, RegExpNormalChar] a + +# 508| [RegExpConstant, RegExpNormalChar] a + +# 508| [RegExpConstant, RegExpNormalChar] Y + +# 511| [RegExpConstant, RegExpNormalChar] X + +# 511| [RegExpSequence] X(\U00000061|b)+Y +#-----| 0 -> [RegExpConstant, RegExpNormalChar] X +#-----| 1 -> [RegExpPlus] (\U00000061|b)+ +#-----| 2 -> [RegExpConstant, RegExpNormalChar] Y + +# 511| [RegExpGroup] (\U00000061|b) +#-----| 0 -> [RegExpAlt] \U00000061|b + +# 511| [RegExpPlus] (\U00000061|b)+ +#-----| 0 -> [RegExpGroup] (\U00000061|b) + +# 511| [RegExpConstant, RegExpEscape] \U00000061 + +# 511| [RegExpAlt] \U00000061|b +#-----| 0 -> [RegExpConstant, RegExpEscape] \U00000061 +#-----| 1 -> [RegExpConstant, RegExpNormalChar] b + +# 511| [RegExpConstant, RegExpNormalChar] b + +# 511| [RegExpConstant, RegExpNormalChar] Y + +# 515| [RegExpConstant, RegExpNormalChar] X + +# 515| [RegExpSequence] X(\x61|a)*Y +#-----| 0 -> [RegExpConstant, RegExpNormalChar] X +#-----| 1 -> [RegExpStar] (\x61|a)* +#-----| 2 -> [RegExpConstant, RegExpNormalChar] Y + +# 515| [RegExpGroup] (\x61|a) +#-----| 0 -> [RegExpAlt] \x61|a + +# 515| [RegExpStar] (\x61|a)* +#-----| 0 -> [RegExpGroup] (\x61|a) + +# 515| [RegExpConstant, RegExpEscape] \x61 + +# 515| [RegExpAlt] \x61|a +#-----| 0 -> [RegExpConstant, RegExpEscape] \x61 +#-----| 1 -> [RegExpConstant, RegExpNormalChar] a + +# 515| [RegExpConstant, RegExpNormalChar] a + +# 515| [RegExpConstant, RegExpNormalChar] Y + +# 518| [RegExpConstant, RegExpNormalChar] X + +# 518| [RegExpSequence] X(\x61|b)+Y +#-----| 0 -> [RegExpConstant, RegExpNormalChar] X +#-----| 1 -> [RegExpPlus] (\x61|b)+ +#-----| 2 -> [RegExpConstant, RegExpNormalChar] Y + +# 518| [RegExpGroup] (\x61|b) +#-----| 0 -> [RegExpAlt] \x61|b + +# 518| [RegExpPlus] (\x61|b)+ +#-----| 0 -> [RegExpGroup] (\x61|b) + +# 518| [RegExpConstant, RegExpEscape] \x61 + +# 518| [RegExpAlt] \x61|b +#-----| 0 -> [RegExpConstant, RegExpEscape] \x61 +#-----| 1 -> [RegExpConstant, RegExpNormalChar] b + +# 518| [RegExpConstant, RegExpNormalChar] b + +# 518| [RegExpConstant, RegExpNormalChar] Y + +# 522| [RegExpConstant, RegExpNormalChar] X + +# 522| [RegExpSequence] X(\x{061}|a)*Y +#-----| 0 -> [RegExpConstant, RegExpNormalChar] X +#-----| 1 -> [RegExpStar] (\x{061}|a)* +#-----| 2 -> [RegExpConstant, RegExpNormalChar] Y + +# 522| [RegExpGroup] (\x{061}|a) +#-----| 0 -> [RegExpAlt] \x{061}|a + +# 522| [RegExpStar] (\x{061}|a)* +#-----| 0 -> [RegExpGroup] (\x{061}|a) + +# 522| [RegExpConstant, RegExpEscape] \x{0 + +# 522| [RegExpConstant, RegExpEscape] \x{061} + +# 522| [RegExpAlt] \x{061}|a +#-----| 0 -> [RegExpConstant, RegExpEscape] \x{0 +#-----| 0 -> [RegExpConstant, RegExpEscape] \x{061} +#-----| 1 -> [RegExpConstant, RegExpNormalChar] a + +# 522| [RegExpConstant, RegExpNormalChar] a + +# 522| [RegExpConstant, RegExpNormalChar] Y + +# 525| [RegExpConstant, RegExpNormalChar] X + +# 525| [RegExpSequence] X(\x{061}|b)+Y +#-----| 0 -> [RegExpConstant, RegExpNormalChar] X +#-----| 1 -> [RegExpPlus] (\x{061}|b)+ +#-----| 2 -> [RegExpConstant, RegExpNormalChar] Y + +# 525| [RegExpGroup] (\x{061}|b) +#-----| 0 -> [RegExpAlt] \x{061}|b + +# 525| [RegExpPlus] (\x{061}|b)+ +#-----| 0 -> [RegExpGroup] (\x{061}|b) + +# 525| [RegExpConstant, RegExpEscape] \x{0 + +# 525| [RegExpConstant, RegExpEscape] \x{061} + +# 525| [RegExpAlt] \x{061}|b +#-----| 0 -> [RegExpConstant, RegExpEscape] \x{0 +#-----| 0 -> [RegExpConstant, RegExpEscape] \x{061} +#-----| 1 -> [RegExpConstant, RegExpNormalChar] b + +# 525| [RegExpConstant, RegExpNormalChar] b + +# 525| [RegExpConstant, RegExpNormalChar] Y + +# 529| [RegExpConstant, RegExpNormalChar] X + +# 529| [RegExpSequence] X(\p{Digit}|7)*Y +#-----| 0 -> [RegExpConstant, RegExpNormalChar] X +#-----| 1 -> [RegExpStar] (\p{Digit}|7)* +#-----| 2 -> [RegExpConstant, RegExpNormalChar] Y + +# 529| [RegExpGroup] (\p{Digit}|7) +#-----| 0 -> [RegExpAlt] \p{Digit}|7 + +# 529| [RegExpStar] (\p{Digit}|7)* +#-----| 0 -> [RegExpGroup] (\p{Digit}|7) + +# 529| [RegExpNamedCharacterProperty] \p{Digit} + +# 529| [RegExpAlt] \p{Digit}|7 +#-----| 0 -> [RegExpNamedCharacterProperty] \p{Digit} +#-----| 1 -> [RegExpConstant, RegExpNormalChar] 7 + +# 529| [RegExpAlt] |7 + +# 529| [RegExpConstant, RegExpNormalChar] 7 + +# 529| [RegExpConstant, RegExpNormalChar] Y + +# 532| [RegExpConstant, RegExpNormalChar] X + +# 532| [RegExpSequence] X(\p{Digit}|b)+Y +#-----| 0 -> [RegExpConstant, RegExpNormalChar] X +#-----| 1 -> [RegExpPlus] (\p{Digit}|b)+ +#-----| 2 -> [RegExpConstant, RegExpNormalChar] Y + +# 532| [RegExpGroup] (\p{Digit}|b) +#-----| 0 -> [RegExpAlt] \p{Digit}|b + +# 532| [RegExpPlus] (\p{Digit}|b)+ +#-----| 0 -> [RegExpGroup] (\p{Digit}|b) + +# 532| [RegExpNamedCharacterProperty] \p{Digit} + +# 532| [RegExpAlt] \p{Digit}|b +#-----| 0 -> [RegExpNamedCharacterProperty] \p{Digit} +#-----| 1 -> [RegExpConstant, RegExpNormalChar] b + +# 532| [RegExpAlt] |b + +# 532| [RegExpConstant, RegExpNormalChar] b + +# 532| [RegExpConstant, RegExpNormalChar] Y + +# 536| [RegExpConstant, RegExpNormalChar] X + +# 536| [RegExpSequence] X(\P{Digit}|b)*Y +#-----| 0 -> [RegExpConstant, RegExpNormalChar] X +#-----| 1 -> [RegExpStar] (\P{Digit}|b)* +#-----| 2 -> [RegExpConstant, RegExpNormalChar] Y + +# 536| [RegExpGroup] (\P{Digit}|b) +#-----| 0 -> [RegExpAlt] \P{Digit}|b + +# 536| [RegExpStar] (\P{Digit}|b)* +#-----| 0 -> [RegExpGroup] (\P{Digit}|b) + +# 536| [RegExpNamedCharacterProperty] \P{Digit} + +# 536| [RegExpAlt] \P{Digit}|b +#-----| 0 -> [RegExpNamedCharacterProperty] \P{Digit} +#-----| 1 -> [RegExpConstant, RegExpNormalChar] b + +# 536| [RegExpAlt] |b + +# 536| [RegExpConstant, RegExpNormalChar] b + +# 536| [RegExpConstant, RegExpNormalChar] Y + +# 539| [RegExpConstant, RegExpNormalChar] X + +# 539| [RegExpSequence] X(\P{Digit}|7)+Y +#-----| 0 -> [RegExpConstant, RegExpNormalChar] X +#-----| 1 -> [RegExpPlus] (\P{Digit}|7)+ +#-----| 2 -> [RegExpConstant, RegExpNormalChar] Y + +# 539| [RegExpGroup] (\P{Digit}|7) +#-----| 0 -> [RegExpAlt] \P{Digit}|7 + +# 539| [RegExpPlus] (\P{Digit}|7)+ +#-----| 0 -> [RegExpGroup] (\P{Digit}|7) + +# 539| [RegExpNamedCharacterProperty] \P{Digit} + +# 539| [RegExpAlt] \P{Digit}|7 +#-----| 0 -> [RegExpNamedCharacterProperty] \P{Digit} +#-----| 1 -> [RegExpConstant, RegExpNormalChar] 7 + +# 539| [RegExpAlt] |7 + +# 539| [RegExpConstant, RegExpNormalChar] 7 + +# 539| [RegExpConstant, RegExpNormalChar] Y + +# 543| [RegExpConstant, RegExpNormalChar] X + +# 543| [RegExpSequence] X(\p{IsDigit}|7)*Y +#-----| 0 -> [RegExpConstant, RegExpNormalChar] X +#-----| 1 -> [RegExpStar] (\p{IsDigit}|7)* +#-----| 2 -> [RegExpConstant, RegExpNormalChar] Y + +# 543| [RegExpGroup] (\p{IsDigit}|7) +#-----| 0 -> [RegExpAlt] \p{IsDigit}|7 + +# 543| [RegExpStar] (\p{IsDigit}|7)* +#-----| 0 -> [RegExpGroup] (\p{IsDigit}|7) + +# 543| [RegExpNamedCharacterProperty] \p{IsDigit} + +# 543| [RegExpAlt] \p{IsDigit}|7 +#-----| 0 -> [RegExpNamedCharacterProperty] \p{IsDigit} +#-----| 1 -> [RegExpConstant, RegExpNormalChar] 7 + +# 543| [RegExpAlt] |7 + +# 543| [RegExpConstant, RegExpNormalChar] 7 + +# 543| [RegExpConstant, RegExpNormalChar] Y + +# 546| [RegExpConstant, RegExpNormalChar] X + +# 546| [RegExpSequence] X(\p{IsDigit}|b)+Y +#-----| 0 -> [RegExpConstant, RegExpNormalChar] X +#-----| 1 -> [RegExpPlus] (\p{IsDigit}|b)+ +#-----| 2 -> [RegExpConstant, RegExpNormalChar] Y + +# 546| [RegExpGroup] (\p{IsDigit}|b) +#-----| 0 -> [RegExpAlt] \p{IsDigit}|b + +# 546| [RegExpPlus] (\p{IsDigit}|b)+ +#-----| 0 -> [RegExpGroup] (\p{IsDigit}|b) + +# 546| [RegExpNamedCharacterProperty] \p{IsDigit} + +# 546| [RegExpAlt] \p{IsDigit}|b +#-----| 0 -> [RegExpNamedCharacterProperty] \p{IsDigit} +#-----| 1 -> [RegExpConstant, RegExpNormalChar] b + +# 546| [RegExpAlt] |b + +# 546| [RegExpConstant, RegExpNormalChar] b + +# 546| [RegExpConstant, RegExpNormalChar] Y + +# 550| [RegExpConstant, RegExpNormalChar] X + +# 550| [RegExpSequence] X(\p{Alpha}|a)*Y +#-----| 0 -> [RegExpConstant, RegExpNormalChar] X +#-----| 1 -> [RegExpStar] (\p{Alpha}|a)* +#-----| 2 -> [RegExpConstant, RegExpNormalChar] Y + +# 550| [RegExpGroup] (\p{Alpha}|a) +#-----| 0 -> [RegExpAlt] \p{Alpha}|a + +# 550| [RegExpStar] (\p{Alpha}|a)* +#-----| 0 -> [RegExpGroup] (\p{Alpha}|a) + +# 550| [RegExpNamedCharacterProperty] \p{Alpha} + +# 550| [RegExpAlt] \p{Alpha}|a +#-----| 0 -> [RegExpNamedCharacterProperty] \p{Alpha} +#-----| 1 -> [RegExpConstant, RegExpNormalChar] a + +# 550| [RegExpAlt] |a + +# 550| [RegExpConstant, RegExpNormalChar] a + +# 550| [RegExpConstant, RegExpNormalChar] Y + +# 553| [RegExpConstant, RegExpNormalChar] X + +# 553| [RegExpSequence] X(\p{Alpha}|7)+Y +#-----| 0 -> [RegExpConstant, RegExpNormalChar] X +#-----| 1 -> [RegExpPlus] (\p{Alpha}|7)+ +#-----| 2 -> [RegExpConstant, RegExpNormalChar] Y + +# 553| [RegExpGroup] (\p{Alpha}|7) +#-----| 0 -> [RegExpAlt] \p{Alpha}|7 + +# 553| [RegExpPlus] (\p{Alpha}|7)+ +#-----| 0 -> [RegExpGroup] (\p{Alpha}|7) + +# 553| [RegExpNamedCharacterProperty] \p{Alpha} + +# 553| [RegExpAlt] \p{Alpha}|7 +#-----| 0 -> [RegExpNamedCharacterProperty] \p{Alpha} +#-----| 1 -> [RegExpConstant, RegExpNormalChar] 7 + +# 553| [RegExpAlt] |7 + +# 553| [RegExpConstant, RegExpNormalChar] 7 + +# 553| [RegExpConstant, RegExpNormalChar] Y + +# 556| [RegExpGroup] ("[^"]*?"|[^"\s]+) +#-----| 0 -> [RegExpAlt] "[^"]*?"|[^"\s]+ + +# 556| [RegExpPlus] ("[^"]*?"|[^"\s]+)+ +#-----| 0 -> [RegExpGroup] ("[^"]*?"|[^"\s]+) + +# 556| [RegExpSequence] ("[^"]*?"|[^"\s]+)+(?=\s*|\s*$) +#-----| 0 -> [RegExpPlus] ("[^"]*?"|[^"\s]+)+ +#-----| 1 -> [RegExpPositiveLookahead] (?=\s*|\s*$) + +# 556| [RegExpConstant, RegExpNormalChar] " + +# 556| [RegExpSequence] "[^"]*?" +#-----| 0 -> [RegExpConstant, RegExpNormalChar] " +#-----| 1 -> [RegExpStar] [^"]*? +#-----| 2 -> [RegExpConstant, RegExpNormalChar] " + +# 556| [RegExpAlt] "[^"]*?"|[^"\s]+ +#-----| 0 -> [RegExpSequence] "[^"]*?" +#-----| 1 -> [RegExpPlus] [^"\s]+ + +# 556| [RegExpCharacterClass] [^"] +#-----| 0 -> [RegExpConstant, RegExpNormalChar] " + +# 556| [RegExpStar] [^"]*? +#-----| 0 -> [RegExpCharacterClass] [^"] + +# 556| [RegExpConstant, RegExpNormalChar] " + +# 556| [RegExpConstant, RegExpNormalChar] " + +# 556| [RegExpCharacterClass] [^"\s] +#-----| 0 -> [RegExpConstant, RegExpNormalChar] " +#-----| 1 -> [RegExpCharacterClassEscape] \s + +# 556| [RegExpPlus] [^"\s]+ +#-----| 0 -> [RegExpCharacterClass] [^"\s] + +# 556| [RegExpConstant, RegExpNormalChar] " + +# 556| [RegExpCharacterClassEscape] \s + +# 556| [RegExpPositiveLookahead] (?=\s*|\s*$) + +# 556| [RegExpCharacterClassEscape] \s + +# 556| [RegExpStar] \s* +#-----| 0 -> [RegExpCharacterClassEscape] \s + +# 556| [RegExpAlt] \s*|\s*$ +#-----| 0 -> [RegExpStar] \s* +#-----| 1 -> [RegExpSequence] \s*$ + +# 556| [RegExpCharacterClassEscape] \s + +# 556| [RegExpStar] \s* +#-----| 0 -> [RegExpCharacterClassEscape] \s + +# 556| [RegExpSequence] \s*$ +#-----| 0 -> [RegExpStar] \s* +#-----| 1 -> [RegExpDollar] $ + +# 556| [RegExpDollar] $ + +# 559| [RegExpGroup] ("[^"]*?"|[^"\s]+) +#-----| 0 -> [RegExpAlt] "[^"]*?"|[^"\s]+ + +# 559| [RegExpPlus] ("[^"]*?"|[^"\s]+)+ +#-----| 0 -> [RegExpGroup] ("[^"]*?"|[^"\s]+) + +# 559| [RegExpSequence] ("[^"]*?"|[^"\s]+)+(?=\s*|\s*$) +#-----| 0 -> [RegExpPlus] ("[^"]*?"|[^"\s]+)+ +#-----| 1 -> [RegExpPositiveLookahead] (?=\s*|\s*$) + +# 559| [RegExpConstant, RegExpNormalChar] " + +# 559| [RegExpSequence] "[^"]*?" +#-----| 0 -> [RegExpConstant, RegExpNormalChar] " +#-----| 1 -> [RegExpStar] [^"]*? +#-----| 2 -> [RegExpConstant, RegExpNormalChar] " + +# 559| [RegExpAlt] "[^"]*?"|[^"\s]+ +#-----| 0 -> [RegExpSequence] "[^"]*?" +#-----| 1 -> [RegExpPlus] [^"\s]+ + +# 559| [RegExpCharacterClass] [^"] +#-----| 0 -> [RegExpConstant, RegExpNormalChar] " + +# 559| [RegExpStar] [^"]*? +#-----| 0 -> [RegExpCharacterClass] [^"] + +# 559| [RegExpConstant, RegExpNormalChar] " + +# 559| [RegExpConstant, RegExpNormalChar] " + +# 559| [RegExpCharacterClass] [^"\s] +#-----| 0 -> [RegExpConstant, RegExpNormalChar] " +#-----| 1 -> [RegExpCharacterClassEscape] \s + +# 559| [RegExpPlus] [^"\s]+ +#-----| 0 -> [RegExpCharacterClass] [^"\s] + +# 559| [RegExpConstant, RegExpNormalChar] " + +# 559| [RegExpCharacterClassEscape] \s + +# 559| [RegExpPositiveLookahead] (?=\s*|\s*$) + +# 559| [RegExpCharacterClassEscape] \s + +# 559| [RegExpStar] \s* +#-----| 0 -> [RegExpCharacterClassEscape] \s + +# 559| [RegExpAlt] \s*|\s*$ +#-----| 0 -> [RegExpStar] \s* +#-----| 1 -> [RegExpSequence] \s*$ + +# 559| [RegExpCharacterClassEscape] \s + +# 559| [RegExpStar] \s* +#-----| 0 -> [RegExpCharacterClassEscape] \s + +# 559| [RegExpSequence] \s*$ +#-----| 0 -> [RegExpStar] \s* +#-----| 1 -> [RegExpDollar] $ + +# 559| [RegExpDollar] $ + +# 563| [RegExpConstant, RegExpNormalChar] / + +# 563| [RegExpSequence] /("[^"]*?"|[^"\s]+)+(?=\s*|\s*$)X +#-----| 0 -> [RegExpConstant, RegExpNormalChar] / +#-----| 1 -> [RegExpPlus] ("[^"]*?"|[^"\s]+)+ +#-----| 2 -> [RegExpPositiveLookahead] (?=\s*|\s*$) +#-----| 3 -> [RegExpConstant, RegExpNormalChar] X + +# 563| [RegExpGroup] ("[^"]*?"|[^"\s]+) +#-----| 0 -> [RegExpAlt] "[^"]*?"|[^"\s]+ + +# 563| [RegExpPlus] ("[^"]*?"|[^"\s]+)+ +#-----| 0 -> [RegExpGroup] ("[^"]*?"|[^"\s]+) + +# 563| [RegExpConstant, RegExpNormalChar] " + +# 563| [RegExpSequence] "[^"]*?" +#-----| 0 -> [RegExpConstant, RegExpNormalChar] " +#-----| 1 -> [RegExpStar] [^"]*? +#-----| 2 -> [RegExpConstant, RegExpNormalChar] " + +# 563| [RegExpAlt] "[^"]*?"|[^"\s]+ +#-----| 0 -> [RegExpSequence] "[^"]*?" +#-----| 1 -> [RegExpPlus] [^"\s]+ + +# 563| [RegExpCharacterClass] [^"] +#-----| 0 -> [RegExpConstant, RegExpNormalChar] " + +# 563| [RegExpStar] [^"]*? +#-----| 0 -> [RegExpCharacterClass] [^"] + +# 563| [RegExpConstant, RegExpNormalChar] " + +# 563| [RegExpConstant, RegExpNormalChar] " + +# 563| [RegExpCharacterClass] [^"\s] +#-----| 0 -> [RegExpConstant, RegExpNormalChar] " +#-----| 1 -> [RegExpCharacterClassEscape] \s + +# 563| [RegExpPlus] [^"\s]+ +#-----| 0 -> [RegExpCharacterClass] [^"\s] + +# 563| [RegExpConstant, RegExpNormalChar] " + +# 563| [RegExpCharacterClassEscape] \s + +# 563| [RegExpPositiveLookahead] (?=\s*|\s*$) + +# 563| [RegExpCharacterClassEscape] \s + +# 563| [RegExpStar] \s* +#-----| 0 -> [RegExpCharacterClassEscape] \s + +# 563| [RegExpAlt] \s*|\s*$ +#-----| 0 -> [RegExpStar] \s* +#-----| 1 -> [RegExpSequence] \s*$ + +# 563| [RegExpCharacterClassEscape] \s + +# 563| [RegExpStar] \s* +#-----| 0 -> [RegExpCharacterClassEscape] \s + +# 563| [RegExpSequence] \s*$ +#-----| 0 -> [RegExpStar] \s* +#-----| 1 -> [RegExpDollar] $ + +# 563| [RegExpDollar] $ + +# 563| [RegExpConstant, RegExpNormalChar] X + +# 564| [RegExpConstant, RegExpNormalChar] / + +# 564| [RegExpSequence] /("[^"]*?"|[^"\s]+)+(?=X) +#-----| 0 -> [RegExpConstant, RegExpNormalChar] / +#-----| 1 -> [RegExpPlus] ("[^"]*?"|[^"\s]+)+ +#-----| 2 -> [RegExpPositiveLookahead] (?=X) + +# 564| [RegExpGroup] ("[^"]*?"|[^"\s]+) +#-----| 0 -> [RegExpAlt] "[^"]*?"|[^"\s]+ + +# 564| [RegExpPlus] ("[^"]*?"|[^"\s]+)+ +#-----| 0 -> [RegExpGroup] ("[^"]*?"|[^"\s]+) + +# 564| [RegExpConstant, RegExpNormalChar] " + +# 564| [RegExpSequence] "[^"]*?" +#-----| 0 -> [RegExpConstant, RegExpNormalChar] " +#-----| 1 -> [RegExpStar] [^"]*? +#-----| 2 -> [RegExpConstant, RegExpNormalChar] " + +# 564| [RegExpAlt] "[^"]*?"|[^"\s]+ +#-----| 0 -> [RegExpSequence] "[^"]*?" +#-----| 1 -> [RegExpPlus] [^"\s]+ + +# 564| [RegExpCharacterClass] [^"] +#-----| 0 -> [RegExpConstant, RegExpNormalChar] " + +# 564| [RegExpStar] [^"]*? +#-----| 0 -> [RegExpCharacterClass] [^"] + +# 564| [RegExpConstant, RegExpNormalChar] " + +# 564| [RegExpConstant, RegExpNormalChar] " + +# 564| [RegExpCharacterClass] [^"\s] +#-----| 0 -> [RegExpConstant, RegExpNormalChar] " +#-----| 1 -> [RegExpCharacterClassEscape] \s + +# 564| [RegExpPlus] [^"\s]+ +#-----| 0 -> [RegExpCharacterClass] [^"\s] + +# 564| [RegExpConstant, RegExpNormalChar] " + +# 564| [RegExpCharacterClassEscape] \s + +# 564| [RegExpPositiveLookahead] (?=X) + +# 564| [RegExpConstant, RegExpNormalChar] X + +# 568| [RegExpCaret] \A + +# 568| [RegExpSequence] \A(\d|0)*x +#-----| 0 -> [RegExpCaret] \A +#-----| 1 -> [RegExpStar] (\d|0)* +#-----| 2 -> [RegExpConstant, RegExpNormalChar] x + +# 568| [RegExpGroup] (\d|0) +#-----| 0 -> [RegExpAlt] \d|0 + +# 568| [RegExpStar] (\d|0)* +#-----| 0 -> [RegExpGroup] (\d|0) + +# 568| [RegExpCharacterClassEscape] \d + +# 568| [RegExpAlt] \d|0 +#-----| 0 -> [RegExpCharacterClassEscape] \d +#-----| 1 -> [RegExpConstant, RegExpNormalChar] 0 + +# 568| [RegExpConstant, RegExpNormalChar] 0 + +# 568| [RegExpConstant, RegExpNormalChar] x + +# 569| [RegExpGroup] (\d|0) +#-----| 0 -> [RegExpAlt] \d|0 + +# 569| [RegExpStar] (\d|0)* +#-----| 0 -> [RegExpGroup] (\d|0) + +# 569| [RegExpSequence] (\d|0)*\Z +#-----| 0 -> [RegExpStar] (\d|0)* +#-----| 1 -> [RegExpDollar] \Z + +# 569| [RegExpCharacterClassEscape] \d + +# 569| [RegExpAlt] \d|0 +#-----| 0 -> [RegExpCharacterClassEscape] \d +#-----| 1 -> [RegExpConstant, RegExpNormalChar] 0 + +# 569| [RegExpConstant, RegExpNormalChar] 0 + +# 569| [RegExpDollar] \Z + +# 570| [RegExpSpecialChar] \b + +# 570| [RegExpSequence] \b(\d|0)*x +#-----| 0 -> [RegExpSpecialChar] \b +#-----| 1 -> [RegExpStar] (\d|0)* +#-----| 2 -> [RegExpConstant, RegExpNormalChar] x + +# 570| [RegExpGroup] (\d|0) +#-----| 0 -> [RegExpAlt] \d|0 + +# 570| [RegExpStar] (\d|0)* +#-----| 0 -> [RegExpGroup] (\d|0) + +# 570| [RegExpCharacterClassEscape] \d + +# 570| [RegExpAlt] \d|0 +#-----| 0 -> [RegExpCharacterClassEscape] \d +#-----| 1 -> [RegExpConstant, RegExpNormalChar] 0 + +# 570| [RegExpConstant, RegExpNormalChar] 0 + +# 570| [RegExpConstant, RegExpNormalChar] x + +# 573| [RegExpConstant, RegExpNormalChar] a + +# 573| [RegExpStar] a* +#-----| 0 -> [RegExpConstant, RegExpNormalChar] a + +# 573| [RegExpConstant, RegExpNormalChar] b + +# 574| [RegExpGroup] (a*) +#-----| 0 -> [RegExpStar] a* + +# 574| [RegExpStar] (a*)* +#-----| 0 -> [RegExpGroup] (a*) + +# 574| [RegExpConstant, RegExpNormalChar] a + +# 574| [RegExpStar] a* +#-----| 0 -> [RegExpConstant, RegExpNormalChar] a + +# 574| [RegExpConstant, RegExpNormalChar] b + +# 575| [RegExpConstant, RegExpNormalChar] a + +# 575| [RegExpStar] a* +#-----| 0 -> [RegExpConstant, RegExpNormalChar] a + +# 575| [RegExpConstant, RegExpNormalChar] b + +# 579| [RegExpConstant, RegExpNormalChar] aa + +# 579| [RegExpAlt] aa|a* +#-----| 0 -> [RegExpConstant, RegExpNormalChar] aa +#-----| 1 -> [RegExpStar] a* + +# 579| [RegExpConstant, RegExpNormalChar] a + +# 579| [RegExpStar] a* +#-----| 0 -> [RegExpConstant, RegExpNormalChar] a + +# 579| [RegExpConstant, RegExpNormalChar] b + +# 579| [RegExpConstant, RegExpNormalChar] c + +regex.swift: +# 103| [RegExpDot] . + +# 103| [RegExpStar] .* +#-----| 0 -> [RegExpDot] . + +# 125| [RegExpDot] . + +# 125| [RegExpStar] .* +#-----| 0 -> [RegExpDot] . + +# 142| [RegExpDot] . + +# 142| [RegExpStar] .* +#-----| 0 -> [RegExpDot] . + +# 142| [RegExpDot] . + +# 142| [RegExpPlus] .+ +#-----| 0 -> [RegExpDot] . + +# 149| [RegExpGroup] ([\w.]+) +#-----| 0 -> [RegExpPlus] [\w.]+ + +# 149| [RegExpStar] ([\w.]+)* +#-----| 0 -> [RegExpGroup] ([\w.]+) + +# 149| [RegExpCharacterClass] [\w.] +#-----| 0 -> [RegExpCharacterClassEscape] \w +#-----| 1 -> [RegExpConstant, RegExpNormalChar] . + +# 149| [RegExpPlus] [\w.]+ +#-----| 0 -> [RegExpCharacterClass] [\w.] + +# 149| [RegExpCharacterClassEscape] \w + +# 149| [RegExpConstant, RegExpNormalChar] . + +# 156| [RegExpConstant, RegExpNormalChar] +# 156| + +# 157| [RegExpConstant, RegExpEscape] \n + +# 158| [RegExpConstant, RegExpEscape] \n + +# 168| [RegExpConstant, RegExpNormalChar] aa + +# 168| [RegExpAlt] aa|bb +#-----| 0 -> [RegExpConstant, RegExpNormalChar] aa +#-----| 1 -> [RegExpConstant, RegExpNormalChar] bb + +# 168| [RegExpConstant, RegExpNormalChar] bb + +# 172| [RegExpConstant, RegExpNormalChar] aa + +# 172| [RegExpAlt] aa| +# 172| bb +#-----| 0 -> [RegExpConstant, RegExpNormalChar] aa +#-----| 1 -> [RegExpConstant, RegExpNormalChar] +#-----| bb + +# 172| [RegExpConstant, RegExpNormalChar] +# 172| bb + +# 180| [RegExpCharacterClass] [a-z] +#-----| 0 -> [RegExpCharacterRange] a-z + +# 180| [RegExpConstant, RegExpNormalChar] a + +# 180| [RegExpCharacterRange] a-z +#-----| 0 -> [RegExpConstant, RegExpNormalChar] a +#-----| 1 -> [RegExpConstant, RegExpNormalChar] z + +# 180| [RegExpConstant, RegExpNormalChar] z + +# 181| [RegExpCharacterClass] [a-zA-Z] +#-----| 0 -> [RegExpCharacterRange] a-z +#-----| 1 -> [RegExpCharacterRange] A-Z + +# 181| [RegExpConstant, RegExpNormalChar] a + +# 181| [RegExpCharacterRange] a-z +#-----| 0 -> [RegExpConstant, RegExpNormalChar] a +#-----| 1 -> [RegExpConstant, RegExpNormalChar] z + +# 181| [RegExpConstant, RegExpNormalChar] z + +# 181| [RegExpConstant, RegExpNormalChar] A + +# 181| [RegExpCharacterRange] A-Z +#-----| 0 -> [RegExpConstant, RegExpNormalChar] A +#-----| 1 -> [RegExpConstant, RegExpNormalChar] Z + +# 181| [RegExpConstant, RegExpNormalChar] Z + +# 184| [RegExpCharacterClass] [a-] +#-----| 0 -> [RegExpConstant, RegExpNormalChar] a +#-----| 1 -> [RegExpConstant, RegExpNormalChar] - + +# 184| [RegExpConstant, RegExpNormalChar] a + +# 184| [RegExpConstant, RegExpNormalChar] - + +# 185| [RegExpCharacterClass] [-a] +#-----| 0 -> [RegExpConstant, RegExpNormalChar] - +#-----| 1 -> [RegExpConstant, RegExpNormalChar] a + +# 185| [RegExpConstant, RegExpNormalChar] - + +# 185| [RegExpConstant, RegExpNormalChar] a + +# 186| [RegExpCharacterClass] [-] +#-----| 0 -> [RegExpConstant, RegExpNormalChar] - + +# 186| [RegExpConstant, RegExpNormalChar] - + +# 187| [RegExpCharacterClass] [*] +#-----| 0 -> [RegExpConstant, RegExpNormalChar] * + +# 187| [RegExpConstant, RegExpNormalChar] * + +# 188| [RegExpCharacterClass] [^a] +#-----| 0 -> [RegExpConstant, RegExpNormalChar] a + +# 188| [RegExpConstant, RegExpNormalChar] a + +# 189| [RegExpCharacterClass] [a^] +#-----| 0 -> [RegExpConstant, RegExpNormalChar] a +#-----| 1 -> [RegExpConstant, RegExpNormalChar] ^ + +# 189| [RegExpConstant, RegExpNormalChar] a + +# 189| [RegExpConstant, RegExpNormalChar] ^ + +# 190| [RegExpCharacterClass] [\\] +#-----| 0 -> [RegExpConstant, RegExpEscape] \\ + +# 190| [RegExpConstant, RegExpEscape] \\ + +# 191| [RegExpCharacterClass] [\\\]] +#-----| 0 -> [RegExpConstant, RegExpEscape] \\ +#-----| 1 -> [RegExpConstant, RegExpEscape] \] + +# 191| [RegExpConstant, RegExpEscape] \\ + +# 191| [RegExpConstant, RegExpEscape] \] + +# 192| [RegExpCharacterClass] [:] +#-----| 0 -> [RegExpConstant, RegExpNormalChar] : + +# 192| [RegExpConstant, RegExpNormalChar] : + +# 193| [RegExpNamedCharacterProperty] [:digit:] + +# 194| [RegExpNamedCharacterProperty] [:alnum:] + +# 197| [RegExpCharacterClass] []a] +#-----| 0 -> [RegExpConstant, RegExpNormalChar] ] +#-----| 1 -> [RegExpConstant, RegExpNormalChar] a + +# 197| [RegExpConstant, RegExpNormalChar] ] + +# 197| [RegExpConstant, RegExpNormalChar] a + +# 198| [RegExpNamedCharacterProperty] [:aaaaa:] diff --git a/swift/ql/test/library-tests/regex/parse.ql b/swift/ql/test/library-tests/regex/parse.ql new file mode 100644 index 00000000000..52f9b67d9d2 --- /dev/null +++ b/swift/ql/test/library-tests/regex/parse.ql @@ -0,0 +1,27 @@ +/** + * @kind graph + */ + +import swift +import codeql.swift.regex.Regex as RE + +query predicate nodes(RE::RegExpTerm n, string attr, string val) { + attr = "semmle.label" and + val = "[" + concat(n.getAPrimaryQlClass(), ", ") + "] " + n.toString() + or + attr = "semmle.order" and + val = + any(int i | + n = + rank[i](RE::RegExpTerm t, string fp, int sl, int sc, int el, int ec | + t.hasLocationInfo(fp, sl, sc, el, ec) + | + t order by fp, sl, sc, el, ec, t.toString() + ) + ).toString() +} + +query predicate edges(RE::RegExpTerm pred, RE::RegExpTerm succ, string attr, string val) { + attr in ["semmle.label", "semmle.order"] and + val = any(int i | succ = pred.getChild(i)).toString() +} diff --git a/swift/ql/test/library-tests/regex/redos_variants.swift b/swift/ql/test/library-tests/regex/redos_variants.swift new file mode 100644 index 00000000000..d09a512b042 --- /dev/null +++ b/swift/ql/test/library-tests/regex/redos_variants.swift @@ -0,0 +1,580 @@ + +// --- stubs --- + +struct URL { + init?(string: String) {} +} + +struct AnyRegexOutput { +} + +protocol RegexComponent { +} + +struct Regex : RegexComponent { + struct Match { + } + + init(_ pattern: String) throws where Output == AnyRegexOutput { } + + func firstMatch(in string: String) throws -> Regex.Match? { return nil} + func prefixMatch(in string: String) throws -> Regex.Match? { return nil} + func wholeMatch(in string: String) throws -> Regex.Match? { return nil} + + typealias RegexOutput = Output +} + +extension String { + init(contentsOf: URL) { + let data = "" + self.init(data) + } +} + +// --- tests --- +// +// the focus for these tests is different vulnerable and non-vulnerable regexp strings. + +func myRegexpVariantsTests(myUrl: URL) throws { + let tainted = String(contentsOf: myUrl) // tainted + + // basic cases: + // attack string: "a" x lots + "!" + + _ = try Regex(".*").firstMatch(in: tainted) // $ regex=.* input=tainted + + _ = try Regex("a*b").firstMatch(in: tainted) // $ regex=a*b input=tainted + _ = try Regex("(a*)b").firstMatch(in: tainted) // $ regex=(a*)b input=tainted + _ = try Regex("(a)*b").firstMatch(in: tainted) // $ regex=(a)*b input=tainted + _ = try Regex("(a*)*b").firstMatch(in: tainted) // $ regex=(a*)*b input=tainted redos-vulnerable= + _ = try Regex("((a*)*b)").firstMatch(in: tainted) // $ regex=((a*)*b) input=tainted redos-vulnerable= + + _ = try Regex("(a|aa?)b").firstMatch(in: tainted) // $ regex=(a|aa?)b input=tainted + _ = try Regex("(a|aa?)*b").firstMatch(in: tainted) // $ regex=(a|aa?)*b input=tainted redos-vulnerable= + + // from the qhelp: + // attack string: "_" x lots + "!" + + _ = try Regex("^_(__|.)+_$").firstMatch(in: tainted) // $ regex=^_(__|.)+_$ input=tainted redos-vulnerable= + _ = try Regex("^_(__|[^_])+_$").firstMatch(in: tainted) // $ regex=^_(__|[^_])+_$ input=tainted + + // real world cases: + + // Adapted from marked (https://github.com/markedjs/marked), which is licensed + // under the MIT license; see file licenses/marked-LICENSE. + // GOOD + _ = try Regex(#"^\b_((?:__|[\s\S])+?)_\b|^\*((?:\*\*|[\s\S])+?)\*(?!\*)"#).firstMatch(in: tainted) // $ SPURIOUS: redos-vulnerable= + // BAD + // attack string: "_" + "__".repeat(100) + _ = try Regex(#"^\b_((?:__|[\s\S])+?)_\b|^\*((?:\*\*|[\s\S])+?)\*(?!\*)"#).wholeMatch(in: tainted) // $ redos-vulnerable= + + // GOOD + // Adapted from marked (https://github.com/markedjs/marked), which is licensed + // under the MIT license; see file licenses/marked-LICENSE. + _ = try Regex(#"^\b_((?:__|[^_])+?)_\b|^\*((?:\*\*|[^*])+?)\*(?!\*)"#).firstMatch(in: tainted) + + // GOOD - there is no witness in the end that could cause the regexp to not match + // Adapted from brace-expansion (https://github.com/juliangruber/brace-expansion), + // which is licensed under the MIT license; see file licenses/brace-expansion-LICENSE. + _ = try Regex("(.*,)+.+").firstMatch(in: tainted) + + // BAD + // attack string: " '" + "\\\\".repeat(100) + // Adapted from CodeMirror (https://github.com/codemirror/codemirror), + // which is licensed under the MIT license; see file licenses/CodeMirror-LICENSE. + _ = try Regex(#"^(?:\s+(?:"(?:[^"\\]|\\\\|\\.)+"|'(?:[^'\\]|\\\\|\\.)+'|\((?:[^)\\]|\\\\|\\.)+\)))?"#).firstMatch(in: tainted) // $ redos-vulnerable= + + // GOOD + // Adapted from jest (https://github.com/facebook/jest), which is licensed + // under the MIT license; see file licenses/jest-LICENSE. + _ = try Regex(#"^ *(\S.*\|.*)\n *([-:]+ *\|[-| :]*)\n((?:.*\|.*(?:\n|$))*)\n*"#).firstMatch(in: tainted) + + // BAD + // attack string: "/" + "\\/a".repeat(100) + // Adapted from ANodeBlog (https://github.com/gefangshuai/ANodeBlog), + // which is licensed under the Apache License 2.0; see file licenses/ANodeBlog-LICENSE. + _ = try Regex(#"\/(?![ *])(\\\/|.)*?\/[gim]*(?=\W|$)"#).firstMatch(in: tainted) // $ redos-vulnerable= + + // BAD + // attack string: "##".repeat(100) + "\na" + // Adapted from CodeMirror (https://github.com/codemirror/codemirror), + // which is licensed under the MIT license; see file licenses/CodeMirror-LICENSE. + _ = try Regex(#"^([\s\[\{\(]|#.*)*$"#).firstMatch(in: tainted) // $ redos-vulnerable= + + // BAD + // attack string: "a" + "[]".repeat(100) + ".b\n" + // Adapted from Knockout (https://github.com/knockout/knockout), which is + // licensed under the MIT license; see file licenses/knockout-LICENSE + _ = try Regex(#"^[\_$a-z][\_$a-z0-9]*(\[.*?\])*(\.[\_$a-z][\_$a-z0-9]*(\[.*?\])*)*$"#).firstMatch(in: tainted) // $ redos-vulnerable= + + // BAD + // attack string: "[" + "][".repeat(100) + "]!" + // Adapted from Prototype.js (https://github.com/prototypejs/prototype), which + // is licensed under the MIT license; see file licenses/Prototype.js-LICENSE. + _ = try Regex(#"(([\w#:.~>+()\s-]+|\*|\[.*?\])+)\s*(,|$)"#).firstMatch(in: tainted) // $ redos-vulnerable= + + // BAD + // attack string: "'" + "\\a".repeat(100) + '"' + // Adapted from Prism (https://github.com/PrismJS/prism), which is licensed + // under the MIT license; see file licenses/Prism-LICENSE. + _ = try Regex(#"("|')(\\?.)*?\1"#).firstMatch(in: tainted) // $ redos-vulnerable= + + // more cases: + + // GOOD + _ = try Regex(#"(\r\n|\r|\n)+"#).firstMatch(in: tainted) + + // GOOD + _ = try Regex("(a|.)*").firstMatch(in: tainted) + + // BAD - testing the NFA + // attack string: "a" x lots + "!" + _ = try Regex("^([a-z]+)+$").firstMatch(in: tainted) // $ redos-vulnerable= + _ = try Regex("^([a-z]*)*$").firstMatch(in: tainted) // $ redos-vulnerable= + _ = try Regex(#"^([a-zA-Z0-9])(([\\.-]|[_]+)?([a-zA-Z0-9]+))*(@){1}[a-z0-9]+[.]{1}(([a-z]{2,3})|([a-z]{2,3}[.]{1}[a-z]{2,3}))$"#).firstMatch(in: tainted) // $ redos-vulnerable= + _ = try Regex("^(([a-z])+.)+[A-Z]([a-z])+$").firstMatch(in: tainted) // $ redos-vulnerable= + + // BAD + // attack string: "b" x lots + "!" + _ = try Regex("(b|a?b)*c").firstMatch(in: tainted) // $ redos-vulnerable= + + // GOOD + _ = try Regex(#"(.|\n)*!"#).firstMatch(in: tainted) + + // BAD + // attack string: "\n".repeat(100) + "." + _ = try Regex(#"(?s)(.|\n)*!"#).firstMatch(in: tainted) // $ hasParseFailure MISSING: redos-vulnerable= + + // GOOD + _ = try Regex(#"([\w.]+)*"#).firstMatch(in: tainted) + // BAD + // attack string: "a" x lots + "!" + _ = try Regex(#"([\w.]+)*"#).wholeMatch(in: tainted) // $ MISSING: redos-vulnerable= + + // BAD + // attack string: "b" x lots + "!" + _ = try Regex(#"(([\s\S]|[^a])*)""#).firstMatch(in: tainted) // $ redos-vulnerable= + + // GOOD - there is no witness in the end that could cause the regexp to not match + _ = try Regex(#"([^"']+)*"#).firstMatch(in: tainted) + + // BAD + // attack string: "b" x lots + "!" + _ = try Regex(#"((.|[^a])*)""#).firstMatch(in: tainted) // $ redos-vulnerable= + + // GOOD + _ = try Regex(#"((a|[^a])*)""#).firstMatch(in: tainted) + + // BAD + // attack string: "b" x lots + "!" + _ = try Regex(#"((b|[^a])*)""#).firstMatch(in: tainted) // $ redos-vulnerable= + + // BAD + // attack string: "G" x lots + "!" + _ = try Regex(#"((G|[^a])*)""#).firstMatch(in: tainted) // $ redos-vulnerable= + + // BAD + // attack string: "0" x lots + "!" + _ = try Regex(#"(([0-9]|[^a])*)""#).firstMatch(in: tainted) // $ redos-vulnerable= + + // BAD [NOT DETECTED] + // (no confirmed attack string) + _ = try Regex(#"(?:=(?:([!#\$%&'\*\+\-\.\^_`\|~0-9A-Za-z]+)|"((?:\\[\x00-\x7f]|[^\x00-\x08\x0a-\x1f\x7f"])*)"))?"#).firstMatch(in: tainted) // $ MISSING: redos-vulnerable= + + // BAD [NOT DETECTED] + // (no confirmed attack string) + _ = try Regex(#""((?:\\[\x00-\x7f]|[^\x00-\x08\x0a-\x1f\x7f"])*)""#).firstMatch(in: tainted) // $ MISSING: redos-vulnerable= + + // GOOD + _ = try Regex(#""((?:\\[\x00-\x7f]|[^\x00-\x08\x0a-\x1f\x7f"\\])*)""#).firstMatch(in: tainted) + + // BAD + // attack string: "d" x lots + "!" + _ = try Regex(#"(([a-z]|[d-h])*)""#).firstMatch(in: tainted) // $ redos-vulnerable= + + // BAD + // attack string: "_" x lots + _ = try Regex(#"(([^a-z]|[^0-9])*)""#).firstMatch(in: tainted) // $ redos-vulnerable= + + // BAD + // attack string: "0" x lots + "!" + _ = try Regex(#"((\d|[0-9])*)""#).firstMatch(in: tainted) // $ redos-vulnerable= + + // BAD + // attack string: "\n" x lots + "." + _ = try Regex(#"((\s|\s)*)""#).firstMatch(in: tainted) // $ redos-vulnerable= + + // BAD + // attack string: "G" x lots + "!" + _ = try Regex(#"((\w|G)*)""#).firstMatch(in: tainted) // $ redos-vulnerable= + + // GOOD + _ = try Regex(#"((\s|\d)*)""#).firstMatch(in: tainted) + + // BAD + // attack string: "5" x lots + "!" + _ = try Regex(#"((\d|\d)*)""#).firstMatch(in: tainted) // $ redos-vulnerable= + + // BAD + // attack string: "0" x lots + "!" + _ = try Regex(#"((\d|\w)*)""#).firstMatch(in: tainted) // $ redos-vulnerable= + + // BAD + // attack string: "5" x lots + "!" + _ = try Regex(#"((\d|5)*)""#).firstMatch(in: tainted) // $ redos-vulnerable= + + // BAD + // attack string: "\u{000C}" x lots + "!", + _ = try Regex(#"((\s|[\f])*)""#).firstMatch(in: tainted) // $ redos-vulnerable= + + // BAD + // attack string: "\n" x lots + "." + _ = try Regex(#"((\s|[\v]|\\v)*)""#).firstMatch(in: tainted) // $ redos-vulnerable= + + // BAD + // attack string: "\u{000C}" x lots + "!", + _ = try Regex(#"((\f|[\f])*)""#).firstMatch(in: tainted) // $ redos-vulnerable= + + // BAD + // attack string: "\n" x lots + "." + _ = try Regex(#"((\W|\D)*)""#).firstMatch(in: tainted) // $ redos-vulnerable= + + // BAD + // attack string: "a" x lots + "!" + _ = try Regex(#"((\S|\w)*)""#).firstMatch(in: tainted) // $ redos-vulnerable= + + // BAD + // attack string: "a" x lots + "!" + _ = try Regex(#"((\S|[\w])*)""#).firstMatch(in: tainted) // $ redos-vulnerable= + + // BAD + // attack string: "1s" x lots + "!" + _ = try Regex(#"((1s|[\da-z])*)""#).firstMatch(in: tainted) // $ redos-vulnerable= + + // BAD + // attack string: "0" x lots + "!" + _ = try Regex(#"((0|[\d])*)""#).firstMatch(in: tainted) // $ redos-vulnerable= + + // BAD + // attack string: "0" x lots + "!" + _ = try Regex(#"(([\d]+)*)""#).firstMatch(in: tainted) // $ redos-vulnerable= + + // GOOD - there is no witness in the end that could cause the regexp to not match + _ = try Regex(#"(\d+(X\d+)?)+"#).firstMatch(in: tainted) + // BAD + // attack string: "0" x lots + "!" + _ = try Regex(#"(\d+(X\d+)?)+"#).wholeMatch(in: tainted) // $ MISSING: redos-vulnerable= + + // GOOD - there is no witness in the end that could cause the regexp to not match + _ = try Regex("([0-9]+(X[0-9]*)?)*").firstMatch(in: tainted) + // BAD + // attack string: "0" x lots + "!" + _ = try Regex("([0-9]+(X[0-9]*)?)*").wholeMatch(in: tainted) // $ MISSING: redos-vulnerable= + + // GOOD + _ = try Regex("^([^>]+)*(>|$)").firstMatch(in: tainted) + + // BAD + // attack string: "##".repeat(100) + "\na" + _ = try Regex("^([^>a]+)*(>|$)").firstMatch(in: tainted) // $ redos-vulnerable= + + // BAD + // attack string: "\n" x lots + "." + _ = try Regex(#"(\n\s*)+$"#).firstMatch(in: tainted) // $ redos-vulnerable= + + // BAD + // attack string: "\n" x lots + "." + _ = try Regex(#"^(?:\s+|#.*|\(\?#[^)]*\))*(?:[?*+]|\{\d+(?:,\d*)?})"#).firstMatch(in: tainted) // $ redos-vulnerable= + + // BAD + // (no confirmed attack string) + _ = try Regex(#"\{\[\s*([a-zA-Z]+)\(([a-zA-Z]+)\)((\s*([a-zA-Z]+)\: ?([ a-zA-Z{}]+),?)+)*\s*\]\}"#).firstMatch(in: tainted) // $ redos-vulnerable= + + // BAD + // attack string: "a" x lots + "!" + _ = try Regex("(a+|b+|c+)*c").firstMatch(in: tainted) // $ redos-vulnerable= + + // BAD + // attack string: "a" x lots + "!" + _ = try Regex("(((a+a?)*)+b+)").firstMatch(in: tainted) // $ redos-vulnerable= + + // BAD + // attack string: "a" x lots + "!" + _ = try Regex("(a+)+bbbb").firstMatch(in: tainted) // $ redos-vulnerable= + + // GOOD + _ = try Regex("(a+)+aaaaa*a+").firstMatch(in: tainted) + // BAD + // attack string: "a" x lots + "!" + _ = try Regex("(a+)+aaaaa*a+").wholeMatch(in: tainted) // $ MISSING: redos-vulnerable= + + // BAD + // attack string: "a" x lots + "!" + _ = try Regex("(a+)+aaaaa$").firstMatch(in: tainted) // $ redos-vulnerable= + + // GOOD + _ = try Regex(#"(\n+)+\n\n"#).firstMatch(in: tainted) + // BAD + // attack string: "\n" x lots + "." + _ = try Regex(#"(\n+)+\n\n"#).wholeMatch(in: tainted) // $ MISSING: redos-vulnerable= + + // BAD + // attack string: "\n" x lots + "." + _ = try Regex(#"(\n+)+\n\n$"#).firstMatch(in: tainted) // $ redos-vulnerable= + + // BAD + // attack string: " " x lots + "X" + _ = try Regex("([^X]+)*$").firstMatch(in: tainted) // $ redos-vulnerable= + + // BAD + // attack string: "b" x lots + "!" + _ = try Regex("(([^X]b)+)*$").firstMatch(in: tainted) // $ redos-vulnerable= + + // GOOD + _ = try Regex("(([^X]b)+)*($|[^X]b)").firstMatch(in: tainted) + // BAD + // attack string: "b" x lots + "!" + _ = try Regex("(([^X]b)+)*($|[^X]b)").wholeMatch(in: tainted) // $ MISSING: redos-vulnerable= + + // BAD + // attack string: "b" x lots + "!" + _ = try Regex("(([^X]b)+)*($|[^X]c)").firstMatch(in: tainted) // $ redos-vulnerable= + + // GOOD + _ = try Regex("((ab)+)*ababab").firstMatch(in: tainted) + // BAD + // attack string: "ab" x lots + "!" + _ = try Regex("((ab)+)*ababab").wholeMatch(in: tainted) // $ MISSING: redos-vulnerable= + + // GOOD + _ = try Regex("((ab)+)*abab(ab)*(ab)+").firstMatch(in: tainted) + // BAD + // attack string: "ab" x lots + "!" + _ = try Regex("((ab)+)*abab(ab)*(ab)+").wholeMatch(in: tainted) // $ MISSING: redos-vulnerable= + + // GOOD + _ = try Regex("((ab)+)*").firstMatch(in: tainted) + // BAD + // attack string: "ab" x lots + "!" + _ = try Regex("((ab)+)*").wholeMatch(in: tainted) // $ MISSING: redos-vulnerable= + + // BAD + // attack string: "ab" x lots + "!" + _ = try Regex("((ab)+)*$").firstMatch(in: tainted) // $ redos-vulnerable= + + // GOOD + _ = try Regex("((ab)+)*[a1][b1][a2][b2][a3][b3]").firstMatch(in: tainted) + // BAD + // attack string: "ab" x lots + "!" + _ = try Regex("((ab)+)*[a1][b1][a2][b2][a3][b3]").wholeMatch(in: tainted) // $ MISSING: redos-vulnerable= + + // BAD + // (no confirmed attack string) + _ = try Regex(#"([\n\s]+)*(.)"#).firstMatch(in: tainted) // $ redos-vulnerable= + + // GOOD - any witness passes through the accept state. + _ = try Regex("(A*A*X)*").firstMatch(in: tainted) + + // GOOD + _ = try Regex(#"([^\\\]]+)*"#).firstMatch(in: tainted) + + // BAD + _ = try Regex(#"(\w*foobarbaz\w*foobarbaz\w*foobarbaz\w*foobarbaz\s*foobarbaz\d*foobarbaz\w*)+-"#).firstMatch(in: tainted) // $ redos-vulnerable= + + // GOOD + // (these regexs explore a query performance issue we had at one point) + _ = try Regex(#"(\w*foobarfoobarfoobarfoobarfoobarfoobarfoobarfoobar)+"#).firstMatch(in: tainted) + _ = try Regex(#"(\w*foobarfoobarfoobar)+"#).firstMatch(in: tainted) + + // BAD (but cannot currently construct a prefix) + // attack string: "aa" + "b" x lots + "!" + _ = try Regex("a{2,3}(b+)+X").firstMatch(in: tainted) // $ redos-vulnerable= + + // BAD (and a good prefix test) + // (no confirmed attack string) + _ = try Regex(#"^<(\w+)((?:\s+\w+(?:\s*=\s*(?:(?:"[^"]*")|(?:'[^']*')|[^>\s]+))?)*)\s*(\/?)>"#).firstMatch(in: tainted) // $ redos-vulnerable= + + // GOOD + _ = try Regex(#"(a+)*[\s\S][\s\S][\s\S]?"#).firstMatch(in: tainted) + + // GOOD - but we fail to see that repeating the attack string ends in the "accept any" state (due to not parsing the range `[\s\S]{2,3}`). + _ = try Regex(#"(a+)*[\s\S]{2,3}"#).firstMatch(in: tainted) // $ SPURIOUS: redos-vulnerable= + + // GOOD - but we spuriously conclude that a rejecting suffix exists (due to not parsing the range `[\s\S]{2,}` when constructing the NFA). + _ = try Regex(#"(a+)*([\s\S]{2,}|X)$"#).firstMatch(in: tainted) // $ SPURIOUS: redos-vulnerable= + + // GOOD + _ = try Regex(#"(a+)*([\s\S]*|X)$"#).firstMatch(in: tainted) + + // BAD + // attack string: "a" x lots + "!" + _ = try Regex(#"((a+)*$|[\s\S]+)"#).firstMatch(in: tainted) // $ redos-vulnerable= + + // GOOD - but still flagged. The only change compared to the above is the order of alternatives, which we don't model. + _ = try Regex(#"([\s\S]+|(a+)*$)"#).firstMatch(in: tainted) // $ SPURIOUS: redos-vulnerable= + + // GOOD + _ = try Regex("((;|^)a+)+$").firstMatch(in: tainted) + + // BAD (a good prefix test) + // attack string: "00000000000000" + "e" x lots + "!" + _ = try Regex("(^|;)(0|1)(0|1)(0|1)(0|1)(0|1)(0|1)(0|1)(0|1)(0|1)(0|1)(0|1)(0|1)(0|1)(0|1)(e+)+f").firstMatch(in: tainted) // $ redos-vulnerable= + + // BAD + // atack string: "ab" + "c" x lots + "!" + _ = try Regex("^ab(c+)+$").firstMatch(in: tainted) // $ redos-vulnerable= + + // BAD + // (no confirmed attack string) + _ = try Regex(#"(\d(\s+)*){20}"#).firstMatch(in: tainted) // $ redos-vulnerable= + + // GOOD - but we spuriously conclude that a rejecting suffix exists. + _ = try Regex(#"(([^/]|X)+)(\/[\s\S]*)*$"#).firstMatch(in: tainted) // $ SPURIOUS: redos-vulnerable= + + // GOOD - but we spuriously conclude that a rejecting suffix exists. + _ = try Regex("^((x([^Y]+)?)*(Y|$))").firstMatch(in: tainted) // $ SPURIOUS: redos-vulnerable= + + // BAD + // (no confirmed attack string) + _ = try Regex(#"foo([\w-]*)+bar"#).firstMatch(in: tainted) // $ redos-vulnerable= + + // BAD + // attack string: "ab" x lots + "!" + _ = try Regex("((ab)*)+c").firstMatch(in: tainted) // $ redos-vulnerable= + + // BAD + // attack string: "a" x lots + "!" + _ = try Regex("(a?a?)*b").firstMatch(in: tainted) // $ redos-vulnerable= + + // GOOD + _ = try Regex("(a?)*b").firstMatch(in: tainted) + + // BAD - but not detected + // (no confirmed attack string) + _ = try Regex("(c?a?)*b").firstMatch(in: tainted) // $ MISSING: redos-vulnerable= + + // BAD + // attack string: "a" x lots + "!" + _ = try Regex("(?:a|a?)+b").firstMatch(in: tainted) // $ redos-vulnerable= + + // BAD - but not detected. + // attack string: "ab" x lots + "!" + _ = try Regex("(a?b?)*$").firstMatch(in: tainted) // $ MISSING: redos-vulnerable= + + // BAD + // (no confirmed attack string) + _ = try Regex("PRE(([a-c]|[c-d])T(e?e?e?e?|X))+(cTcT|cTXcTX$)").firstMatch(in: tainted) // $ redos-vulnerable= + + // BAD + // attack string: "a" x lots + "!" + _ = try Regex(#"^((a)+\w)+$"#).firstMatch(in: tainted) // $ redos-vulnerable= + + // BAD + // attack string: "bbbbbbbbbb." x lots + "!" + _ = try Regex("^(b+.)+$").firstMatch(in: tainted) // $ redos-vulnerable= + + // BAD - all 4 bad combinations of nested * and + + // attack string: "a" x lots + "!" + _ = try Regex("(a*)*b").firstMatch(in: tainted) // $ redos-vulnerable= + _ = try Regex("(a+)*b").firstMatch(in: tainted) // $ redos-vulnerable= + _ = try Regex("(a*)+b").firstMatch(in: tainted) // $ redos-vulnerable= + _ = try Regex("(a+)+b").firstMatch(in: tainted) // $ redos-vulnerable= + + // GOOD + _ = try Regex("(a|b)+").firstMatch(in: tainted) + + // GOOD + _ = try Regex(#"(?:[\s;,"'<>(){}|\[\]@=+*]|:(?![/\\]))+"#).firstMatch(in: tainted) + + // BAD? + // (no confirmed attack string) + _ = try Regex(#"^((?:a{|-)|\w\{)+X$"#).firstMatch(in: tainted) // $ redos-vulnerable= + _ = try Regex(#"^((?:a{0|-)|\w\{\d)+X$"#).firstMatch(in: tainted) // $ redos-vulnerable= + _ = try Regex(#"^((?:a{0,|-)|\w\{\d,)+X$"#).firstMatch(in: tainted) // $ redos-vulnerable= + _ = try Regex(#"^((?:a{0,2|-)|\w\{\d,\d)+X$"#).firstMatch(in: tainted) // $ redos-vulnerable= + + // GOOD + _ = try Regex(#"^((?:a{0,2}|-)|\w\{\d,\d\})+X$"#).firstMatch(in: tainted) + + // BAD + // attack string: "X" + "a" x lots + _ = try Regex(#"X(\u0061|a)*Y"#).firstMatch(in: tainted) // $ redos-vulnerable= + + // GOOD + _ = try Regex(#"X(\u0061|b)+Y"#).firstMatch(in: tainted) + + // BAD + // attack string: "X" + "a" x lots + _ = try Regex(#"X(\U00000061|a)*Y"#).firstMatch(in: tainted) // $ redos-vulnerable= + + // GOOD + _ = try Regex(#"X(\U00000061|b)+Y"#).firstMatch(in: tainted) + + // BAD + // attack string: "X" + "a" x lots + _ = try Regex(#"X(\x61|a)*Y"#).firstMatch(in: tainted) // $ redos-vulnerable= + + // GOOD + _ = try Regex(#"X(\x61|b)+Y"#).firstMatch(in: tainted) + + // BAD + // attack string: "X" + "a" x lots + _ = try Regex(#"X(\x{061}|a)*Y"#).firstMatch(in: tainted) // $ redos-vulnerable= + + // GOOD + _ = try Regex(#"X(\x{061}|b)+Y"#).firstMatch(in: tainted) + + // BAD + // attack string: "X" + "7" x lots + _ = try Regex(#"X(\p{Digit}|7)*Y"#).firstMatch(in: tainted) // $ redos-vulnerable= + + // GOOD + _ = try Regex(#"X(\p{Digit}|b)+Y"#).firstMatch(in: tainted) + + // BAD + // attack string: "X" + "b" x lots + _ = try Regex(#"X(\P{Digit}|b)*Y"#).firstMatch(in: tainted) // $ redos-vulnerable= + + // GOOD + _ = try Regex(#"X(\P{Digit}|7)+Y"#).firstMatch(in: tainted) + + // BAD + // attack string: "X" + "7" x lots + _ = try Regex(#"X(\p{IsDigit}|7)*Y"#).firstMatch(in: tainted) // $ redos-vulnerable= + + // GOOD + _ = try Regex(#"X(\p{IsDigit}|b)+Y"#).firstMatch(in: tainted) + + // BAD - but not detected + // attack string: "X" + "a" x lots + _ = try Regex(#"X(\p{Alpha}|a)*Y"#).firstMatch(in: tainted) // $ MISSING: redos-vulnerable= + + // GOOD + _ = try Regex(#"X(\p{Alpha}|7)+Y"#).firstMatch(in: tainted) + + // GOOD + _ = try Regex(#"("[^"]*?"|[^"\s]+)+(?=\s*|\s*$)"#).firstMatch(in: tainted) + // BAD + // attack string: "##" x lots + "\na" + _ = try Regex(#"("[^"]*?"|[^"\s]+)+(?=\s*|\s*$)"#).wholeMatch(in: tainted) // $ MISSING: redos-vulnerable= + + // BAD + // attack string: "/" + "\\/a" x lots + _ = try Regex(#"/("[^"]*?"|[^"\s]+)+(?=\s*|\s*$)X"#).firstMatch(in: tainted) // $ redos-vulnerable= + _ = try Regex(#"/("[^"]*?"|[^"\s]+)+(?=X)"#).firstMatch(in: tainted) // $ redos-vulnerable= + + // BAD + // attack string: "0" x lots + "!" + _ = try Regex(#"\A(\d|0)*x"#).firstMatch(in: tainted) // $ redos-vulnerable= + _ = try Regex(#"(\d|0)*\Z"#).firstMatch(in: tainted) // $ redos-vulnerable= + _ = try Regex(#"\b(\d|0)*x"#).firstMatch(in: tainted) // $ redos-vulnerable= + + // GOOD - possessive quantifiers don't backtrack + _ = try Regex("(a*+)*+b").firstMatch(in: tainted) // $ hasParseFailure + _ = try Regex("(a*)*+b").firstMatch(in: tainted) // $ hasParseFailure + _ = try Regex("(a*+)*b").firstMatch(in: tainted) // $ hasParseFailure + + // BAD - but not detected due to the way possessive quantifiers are approximated + // attack string: "aab" x lots + "!" + _ = try Regex("((aa|a*+)b)*c").firstMatch(in: tainted) // $ hasParseFailure MISSING: redos-vulnerable= +} diff --git a/swift/ql/test/library-tests/regex/regex.expected b/swift/ql/test/library-tests/regex/regex.expected new file mode 100644 index 00000000000..48de9172b36 --- /dev/null +++ b/swift/ql/test/library-tests/regex/regex.expected @@ -0,0 +1,2 @@ +failures +testFailures diff --git a/swift/ql/test/library-tests/regex/regex.ql b/swift/ql/test/library-tests/regex/regex.ql new file mode 100644 index 00000000000..f3d0d67f120 --- /dev/null +++ b/swift/ql/test/library-tests/regex/regex.ql @@ -0,0 +1,52 @@ +import swift +import codeql.swift.regex.Regex +private import codeql.swift.regex.internal.ParseRegex +private import codeql.swift.regex.RegexTreeView::RegexTreeView as TreeView +import codeql.regex.nfa.ExponentialBackTracking::Make +import TestUtilities.InlineExpectationsTest + +bindingset[s] +string quote(string s) { if s.matches("% %") then result = "\"" + s + "\"" else result = s } + +module RegexTest implements TestSig { + string getARelevantTag() { result = ["regex", "input", "redos-vulnerable", "hasParseFailure"] } + + predicate hasActualResult(Location location, string element, string tag, string value) { + exists(TreeView::RegExpTerm t | + hasReDoSResult(t, _, _, _) and + location = t.getLocation() and + element = t.toString() and + tag = "redos-vulnerable" and + value = "" + ) + or + exists(RegexEval eval, RegExp regex | + eval.getARegex() = regex and + regex.failedToParse(_) and + location = eval.getLocation() and + element = eval.toString() and + tag = "hasParseFailure" and + value = "" + ) + } + + predicate hasOptionalResult(Location location, string element, string tag, string value) { + exists(RegexEval eval, Expr input | + eval.getStringInput() = input and + location = input.getLocation() and + element = input.toString() and + tag = "input" and + value = quote(input.toString()) + ) + or + exists(RegexEval eval, RegExp regex | + eval.getARegex() = regex and + location = eval.getLocation() and + element = eval.toString() and + tag = "regex" and + value = quote(regex.toString().replaceAll("\n", "NEWLINE")) + ) + } +} + +import MakeTest diff --git a/swift/ql/test/library-tests/regex/regex.swift b/swift/ql/test/library-tests/regex/regex.swift new file mode 100644 index 00000000000..f48e8e0dc7d --- /dev/null +++ b/swift/ql/test/library-tests/regex/regex.swift @@ -0,0 +1,199 @@ + +// --- stubs --- + +struct Locale { +} + +struct AnyRegexOutput { +} + +protocol RegexComponent { + associatedtype RegexOutput +} + +struct Regex : RegexComponent { + struct Match { + } + + init(_ pattern: String) throws where Output == AnyRegexOutput { } + + func firstMatch(in string: String) throws -> Regex.Match? { return nil} + func prefixMatch(in string: String) throws -> Regex.Match? { return nil} + func wholeMatch(in string: String) throws -> Regex.Match? { return nil} + + typealias RegexOutput = Output +} + +extension RangeReplaceableCollection { + mutating func replace(_ regex: some RegexComponent, with replacement: Replacement, maxReplacements: Int = .max) where Replacement : Collection, Replacement.Element == Character { } + func replacing(_ regex: some RegexComponent, with replacement: Replacement, maxReplacements: Int = .max) -> Self where Replacement: Collection, Replacement.Element == Character { return self } + mutating func trimPrefix(_ regex: some RegexComponent) { } +} + +extension StringProtocol { + func range(of aString: T, options mask:String.CompareOptions = [], range searchRange: Range? = nil, locale: Locale? = nil) -> Range? where T : StringProtocol { return nil } + func replacingOccurrences(of target: Target, with replacement: Replacement, options: String.CompareOptions = [], range searchRange: Range? = nil) -> String where Target : StringProtocol, Replacement : StringProtocol { return "" } +} + +extension String : RegexComponent { + typealias CompareOptions = NSString.CompareOptions + typealias Output = Substring + typealias RegexOutput = String.Output +} + +class NSObject { +} + +class NSString : NSObject { + struct CompareOptions : OptionSet { + var rawValue: UInt + + static var regularExpression: NSString.CompareOptions { get { return CompareOptions(rawValue: 1) } } + } + + convenience init(string aString: String) { self.init() } + + func range(of searchString: String, options mask: NSString.CompareOptions = []) -> NSRange { return NSRange(location: 0, length: 0) } + func replacingOccurrences(of target: String, with replacement: String, options: NSString.CompareOptions = [], range searchRange: NSRange) -> String { return "" } + + var length: Int { get { return 0 } } +} + +class NSMutableString : NSString { +} + +struct _NSRange { + init(location: Int, length: Int) { } +} + +typealias NSRange = _NSRange + +func NSMakeRange(_ loc: Int, _ len: Int) -> NSRange { return NSRange(location: loc, length: len) } + +class NSTextCheckingResult : NSObject { +} + +class NSRegularExpression : NSObject { + struct Options : OptionSet { + var rawValue: UInt + } + + struct MatchingOptions : OptionSet { + var rawValue: UInt + } + + init(pattern: String, options: NSRegularExpression.Options = []) throws { } + + // some types have been simplified a little here + func numberOfMatches(in string: String, options: NSRegularExpression.MatchingOptions = [], range: NSRange) -> Int { return 0 } + func enumerateMatches(in string: String, options: NSRegularExpression.MatchingOptions = [], range: NSRange, using block: (Int, Int, Int) -> Void) { } + func matches(in string: String, options: NSRegularExpression.MatchingOptions = [], range: NSRange) -> [NSTextCheckingResult] { return [] } + func firstMatch(in string: String, options: NSRegularExpression.MatchingOptions = [], range: NSRange) -> NSTextCheckingResult? { return nil } + func rangeOfFirstMatch(in string: String, options: NSRegularExpression.MatchingOptions = [], range: NSRange) -> NSRange { return NSRange(location: 0, length: 0) } + func replaceMatches(in string: NSMutableString, options: NSRegularExpression.MatchingOptions = [], range: NSRange, withTemplate templ: String) -> Int { return 0 } + func stringByReplacingMatches(in string: String, options: NSRegularExpression.MatchingOptions = [], range: NSRange, withTemplate templ: String) -> String { return "" } +} + +// --- tests --- +// +// the focus for these tests is different ways of evaluating regexps. + +func myRegexpMethodsTests(b: Bool, str_unknown: String) throws { + let input = "abcdef" + let regex = try Regex(".*") + + // --- Regex --- + + _ = try regex.firstMatch(in: input) // $ regex=.* input=input + _ = try regex.prefixMatch(in: input) // $ regex=.* input=input + _ = try regex.wholeMatch(in: input) // $ regex=.* input=input + + // --- RangeReplaceableCollection --- + + var inputVar = input + inputVar.replace(regex, with: "") // $ regex=.* input=&... + _ = input.replacing(regex, with: "") // $ regex=.* input=input + inputVar.trimPrefix(regex) // $ regex=.* input=&... + + // --- StringProtocol --- + + _ = input.range(of: ".*", options: .regularExpression, range: nil, locale: nil) // $ MISSING: regex=.* input=input + _ = input.replacingOccurrences(of: ".*", with: "", options: .regularExpression) // $ MISSING: regex=.* input=input + + // --- NSRegularExpression --- + + let nsregex = try NSRegularExpression(pattern: ".*") + _ = nsregex.numberOfMatches(in: input, options: [], range: NSRange(location: 0, length: input.utf16.count)) // $ regex=.* input=input + nsregex.enumerateMatches(in: input, range: NSMakeRange(0, input.utf16.count), using: {a, b, c in } ) // $ regex=.* input=input + _ = nsregex.matches(in: input, range: NSMakeRange(0, input.utf16.count)) // $ regex=.* input=input + _ = nsregex.firstMatch(in: input, range: NSMakeRange(0, input.utf16.count)) // $ regex=.* input=input + _ = nsregex.rangeOfFirstMatch(in: input, range: NSMakeRange(0, input.utf16.count)) // $ regex=.* input=input + _ = nsregex.replaceMatches(in: NSMutableString(string: input), range: NSMakeRange(0, input.utf16.count), withTemplate: "") // $ regex=.* input="call to NSString.init(string:)" + _ = nsregex.stringByReplacingMatches(in: input, range: NSMakeRange(0, input.utf16.count), withTemplate: "") // $ regex=.* input=input + + // --- NSString --- + + let inputNS = NSString(string: "abcdef") + _ = inputNS.range(of: "*", options: .regularExpression) // $ MISSING: regex=.* input=inputNS + _ = inputNS.replacingOccurrences(of: ".*", with: "", options: .regularExpression, range: NSMakeRange(0, inputNS.length)) // $ MISSING: regex=.* input=inputNS + + // --- flow --- + + let either_regex = try Regex(b ? ".*" : ".+") + _ = try either_regex.firstMatch(in: input) // $ regex=.* regex=.+ input=input + + let base_str = "a" + let appended_regex = try Regex(base_str + "b") + _ = try appended_regex.firstMatch(in: input) // $ input=input MISSING: regex=ab + + let multiple_evaluated_regex = try Regex(#"([\w.]+)*"#) + try _ = multiple_evaluated_regex.firstMatch(in: input) // $ input=input regex=([\w.]+)* + try _ = multiple_evaluated_regex.prefixMatch(in: input) // $ input=input regex=([\w.]+)* + try _ = multiple_evaluated_regex.wholeMatch(in: input) // $ input=input regex=([\w.]+)* MISSING: redos-vulnerable= + + // --- escape sequences --- + + _ = try Regex("\n").firstMatch(in: input) // $ regex=NEWLINE input=input + _ = try Regex("\\n").firstMatch(in: input) // $ regex=\n input=input + _ = try Regex(#"\n"#).firstMatch(in: input) // $ regex=\n input=input + + // --- interpolated values --- + + let str_constant = "aa" + _ = try Regex("\(str_constant))|bb").firstMatch(in: input) // $ input=input MISSING: regex=aa|bb + _ = try Regex("\(str_unknown))|bb").firstMatch(in: input) // $ input=input + + // --- multi-line --- + + _ = try Regex(""" + aa|bb + """).firstMatch(in: input) // $ input=input regex=aa|bb + + _ = try Regex(""" + aa| + bb + """).firstMatch(in: input) // $ input=input regex=aa|NEWLINEbb + + // --- exploring parser correctness --- + + // ranges + _ = try Regex("[a-z]").firstMatch(in: input) // $ input=input regex=[a-z] + _ = try Regex("[a-zA-Z]").firstMatch(in: input) // $ input=input regex=[a-zA-Z] + + // character classes + _ = try Regex("[a-]").firstMatch(in: input) // $ input=input regex=[a-] + _ = try Regex("[-a]").firstMatch(in: input) // $ input=input regex=[-a] + _ = try Regex("[-]").firstMatch(in: input) // $ input=input regex=[-] + _ = try Regex("[*]").firstMatch(in: input) // $ input=input regex=[*] + _ = try Regex("[^a]").firstMatch(in: input) // $ input=input regex=[^a] + _ = try Regex("[a^]").firstMatch(in: input) // $ input=input regex=[a^] + _ = try Regex(#"[\\]"#).firstMatch(in: input) // $ input=input regex=[\\] + _ = try Regex(#"[\\\]]"#).firstMatch(in: input) // $ input=input regex=[\\\]] + _ = try Regex("[:]").firstMatch(in: input) // $ input=input regex=[:] + _ = try Regex("[:digit:]").firstMatch(in: input) // $ input=input regex=[:digit:] SPURIOUS: $hasParseFailure + _ = try Regex("[:alnum:]").firstMatch(in: input) // $ input=input regex=[:alnum:] SPURIOUS: $hasParseFailure + + // invalid (Swift doesn't like these regexs) + _ = try Regex("[]a]").firstMatch(in: input) // this is valid in other regex implementations, and is likely harmless to accept + _ = try Regex("[:aaaaa:]").firstMatch(in: input) // $ hasParseFailure +} diff --git a/swift/ql/test/library-tests/regex/regex_swift57.swift.disabled b/swift/ql/test/library-tests/regex/regex_swift57.swift.disabled new file mode 100644 index 00000000000..2d510950c26 --- /dev/null +++ b/swift/ql/test/library-tests/regex/regex_swift57.swift.disabled @@ -0,0 +1,72 @@ +// these tests require Swift 5.7 or so, and currently require the +// `-enable-bare-slash-regex` compiler flag. + +// --- stubs --- + +struct AnyRegexOutput { +} + +protocol RegexComponent { + associatedtype RegexOutput +} + +struct Regex : RegexComponent { + struct Match { + } + + init(_ pattern: String) throws where Output == AnyRegexOutput { } + + func firstMatch(in string: String) throws -> Regex.Match? { return nil } + + typealias RegexOutput = Output +} + +extension BidirectionalCollection { + func contains(_ regex: some RegexComponent) -> Bool { return false } + func firstMatch(of r: some RegexComponent) -> Regex.Match? { return nil } // slightly simplified + func firstMatch(of r: some RegexComponent) -> Regex.Match? where SubSequence == Substring { return nil } + func firstRange(of regex: some RegexComponent) -> Range? { return nil } + func matches(of r: some RegexComponent) -> [Regex.Match] { return [] } // slightly simplified + func prefixMatch(of regex: R) -> Regex.Match? where R: RegexComponent { return nil } + func ranges(of regex: some RegexComponent) -> [Range] { return [] } + func starts(with regex: some RegexComponent) -> Bool { return false } + func trimmingPrefix(_ regex: some RegexComponent) -> Self.SubSequence { return self.suffix(0) } + func split(separator: some RegexComponent, maxSplits: Int = .max, omittingEmptySubsequences: Bool = true) -> [Self.SubSequence] { return [] } +} + +extension String : RegexComponent { + typealias Output = Substring + typealias RegexOutput = String.Output +} + +// --- tests --- + +func myRegexpMethodsTests() throws { + let input = "abcdef" + let regex = try Regex(".*") + + // --- BidirectionalCollection --- + + _ = input.contains(regex) + _ = input.firstMatch(of: regex) + _ = input.firstRange(of: regex) + _ = input.matches(of: regex) + _ = input.prefixMatch(of: regex) + _ = input.ranges(of: regex) + _ = input.starts(with: regex) + _ = input.trimmingPrefix(regex) + _ = input.split(separator: regex) + + // --- compile time regexps --- + + let regex2 = /a*b*/ + _ = try regex2.firstMatch(in: input) + + let regex3 = /(?a*)*b/ + _ = try regex3.firstMatch(in: input) + + let regex4 = #/a*b*/# + _ = try regex4.firstMatch(in: input) + + _ = input.contains(/a*b*/) +} diff --git a/swift/ql/test/library-tests/regex/test_fragment_licenses/ANodeBlog-LICENSE b/swift/ql/test/library-tests/regex/test_fragment_licenses/ANodeBlog-LICENSE new file mode 100644 index 00000000000..8dada3edaf5 --- /dev/null +++ b/swift/ql/test/library-tests/regex/test_fragment_licenses/ANodeBlog-LICENSE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "{}" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright {yyyy} {name of copyright owner} + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/swift/ql/test/library-tests/regex/test_fragment_licenses/CodeMirror-LICENSE b/swift/ql/test/library-tests/regex/test_fragment_licenses/CodeMirror-LICENSE new file mode 100644 index 00000000000..ff7db4b99f5 --- /dev/null +++ b/swift/ql/test/library-tests/regex/test_fragment_licenses/CodeMirror-LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (C) 2017 by Marijn Haverbeke and others + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/swift/ql/test/library-tests/regex/test_fragment_licenses/Prism-LICENSE b/swift/ql/test/library-tests/regex/test_fragment_licenses/Prism-LICENSE new file mode 100644 index 00000000000..528949f42d9 --- /dev/null +++ b/swift/ql/test/library-tests/regex/test_fragment_licenses/Prism-LICENSE @@ -0,0 +1,21 @@ +MIT LICENSE + +Copyright (c) 2012 Lea Verou + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/swift/ql/test/library-tests/regex/test_fragment_licenses/Prototype.js-LICENSE b/swift/ql/test/library-tests/regex/test_fragment_licenses/Prototype.js-LICENSE new file mode 100644 index 00000000000..05789cf0190 --- /dev/null +++ b/swift/ql/test/library-tests/regex/test_fragment_licenses/Prototype.js-LICENSE @@ -0,0 +1,16 @@ +Copyright (c) 2005-2010 Sam Stephenson + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/swift/ql/test/library-tests/regex/test_fragment_licenses/brace-expansion-LICENSE b/swift/ql/test/library-tests/regex/test_fragment_licenses/brace-expansion-LICENSE new file mode 100644 index 00000000000..de3226673c3 --- /dev/null +++ b/swift/ql/test/library-tests/regex/test_fragment_licenses/brace-expansion-LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2013 Julian Gruber + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/swift/ql/test/library-tests/regex/test_fragment_licenses/jest-LICENSE b/swift/ql/test/library-tests/regex/test_fragment_licenses/jest-LICENSE new file mode 100644 index 00000000000..10e779c44ac --- /dev/null +++ b/swift/ql/test/library-tests/regex/test_fragment_licenses/jest-LICENSE @@ -0,0 +1,23 @@ +MIT License + +For Jest software + +Copyright (c) 2014-present, Facebook, Inc. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/swift/ql/test/library-tests/regex/test_fragment_licenses/knockout-LICENSE b/swift/ql/test/library-tests/regex/test_fragment_licenses/knockout-LICENSE new file mode 100644 index 00000000000..08a07b1ae8d --- /dev/null +++ b/swift/ql/test/library-tests/regex/test_fragment_licenses/knockout-LICENSE @@ -0,0 +1,22 @@ +The MIT License (MIT) - http://www.opensource.org/licenses/mit-license.php + +Copyright (c) Steven Sanderson, the Knockout.js team, and other contributors +http://knockoutjs.com/ + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/swift/ql/test/library-tests/regex/test_fragment_licenses/marked-LICENSE b/swift/ql/test/library-tests/regex/test_fragment_licenses/marked-LICENSE new file mode 100644 index 00000000000..64b41a0e463 --- /dev/null +++ b/swift/ql/test/library-tests/regex/test_fragment_licenses/marked-LICENSE @@ -0,0 +1,43 @@ +# License information + +## Contribution License Agreement + +If you contribute code to this project, you are implicitly allowing your code +to be distributed under the MIT license. You are also implicitly verifying that +all code is your original work. `` + +## Marked + +Copyright (c) 2011-2018, Christopher Jeffrey (https://github.com/chjj/) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +## Markdown + +Copyright © 2004, John Gruber +http://daringfireball.net/ +All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: + +* Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. +* Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. +* Neither the name “Markdown” nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. + +This software is provided by the copyright holders and contributors “as is” and any express or implied warranties, including, but not limited to, the implied warranties of merchantability and fitness for a particular purpose are disclaimed. In no event shall the copyright owner or contributors be liable for any direct, indirect, incidental, special, exemplary, or consequential damages (including, but not limited to, procurement of substitute goods or services; loss of use, data, or profits; or business interruption) however caused and on any theory of liability, whether in contract, strict liability, or tort (including negligence or otherwise) arising in any way out of the use of this software, even if advised of the possibility of such damage. diff --git a/swift/ql/test/library-tests/regex/test_the_tests.swift.disabled b/swift/ql/test/library-tests/regex/test_the_tests.swift.disabled new file mode 100644 index 00000000000..d7ffbc6ef05 --- /dev/null +++ b/swift/ql/test/library-tests/regex/test_the_tests.swift.disabled @@ -0,0 +1,148 @@ +// This program tests the regular expressions from the tests to figure out which ones are really +// vulnerable to various attack strings with the Swift `Regex` implementation. We also confirm +// all really are valid Swift regular expressions. +// +// The program works by searching the test source file (or part of it) for regular expressions in +// the form `Regex("...")` or `Regex(#"..."#)`. A drawback of this approach is that it doesn't +// process escape characters in the way Swift does, so it's best not to use them in non-trivial +// test cases (use Swift #""# strings to avoid needing escape sequences). + +import Foundation + +class TestCase: Thread { + var regex: Regex + var regexStr: String + var targetString: String + var wholeMatch: Bool + var matched: Bool + var done: Bool + + init(regex: Regex, regexStr: String, targetString: String, wholeMatch: Bool) { + self.regex = regex + self.regexStr = regexStr + self.targetString = targetString + self.wholeMatch = wholeMatch + self.matched = false + self.done = false + } + + override func main() { + // run the regex on the target string + do + { + if (wholeMatch) { + if let _ = try regex.wholeMatch(in: targetString) { + matched = true + //print(" wholeMatch \(regexStr) on \(targetString)") + } + } else { + if let _ = try regex.firstMatch(in: targetString) { + matched = true + //print(" firstMatch \(regexStr) on \(targetString)") + } + } + } catch { + print("WEIRD FAILURE") + } + done = true + } +} + +let attackStrings = [ + String(repeating: "a", count: 100) + "!", + String(repeating: "b", count: 100) + "!", + String(repeating: "d", count: 100) + "!", + String(repeating: "G", count: 100) + "!", + String(repeating: "0", count: 100) + "!", + String(repeating: "5", count: 100) + "!", + String(repeating: "ab", count: 100) + "!", + String(repeating: "aab", count: 100) + "!", + String(repeating: "1s", count: 100) + "!", + String(repeating: "\n", count: 100) + ".", + + "_" + String(repeating: "__", count: 100) + "!", + " '" + String(repeating: #"\\\\"#, count: 100), + "/" + String(repeating: "\\/a", count:100), + "/" + String(repeating: "\\\\/a", count:100), + String(repeating: "##", count: 100) + "\na", + "a" + String(repeating: "[]", count: 100) + ".b\n", + "[" + String(repeating: "][", count: 100) + "]!", + "'" + String(repeating: "\\a", count: 100) + "\"", + "'" + String(repeating: "\\\\a", count: 100) + "\"", + String(repeating: "\u{000C}", count: 100) + "!", + "00000000000000" + String(repeating: "e", count: 100) + "!", + "aa" + String(repeating: "b", count: 100) + "!", + "ab" + String(repeating: "c", count: 100) + "!", + String(repeating: " X", count: 100) + "!", + String(repeating: "bbbbbbbbbb.", count: 100) + "!", + + "X" + String(repeating: "a", count: 100), + "X" + String(repeating: "b", count: 100), + "X" + String(repeating: "7", count: 100), +] + +print("testing regular expressions...") +print() + +var tests: [TestCase] = [] + +let lineRegex = try Regex(".*Regex\\(#*\"(.*)\"#*\\).*") // matches `Regex("...")`, `Regex(#"..."#)` etc. + +// read source file, process it line by line... +let wholeFile = try String(contentsOfFile: "redos_variants.swift") +var lines = wholeFile.components(separatedBy: .newlines) + +// filter lines (running more than a few thousand test cases at once is not recommended) +lines = Array(lines[1 ..< 120]) + +var regexpCount = 0 +for line in lines { + + // check if the line matches the line regex... + if let match = try lineRegex.wholeMatch(in: line) { + if let regexSubstr = match.output[1].substring { + let regexStr = String(regexSubstr) + //print("regex: \(regexStr)") + + // create the regex + if let regex = try? Regex(regexStr) { + // create test cases + for attackString in attackStrings { + tests.append(TestCase(regex: regex, regexStr: regexStr, targetString: attackString, wholeMatch: true)) + tests.append(TestCase(regex: regex, regexStr: regexStr, targetString: attackString, wholeMatch: false)) + } + regexpCount += 1 + } else { + print("FAILED TO PARSE \(regexStr)") + } + } + } +} + +// run the tests... +// (in parallel, because each test doesn't necessarily terminate and we have no easy way to force +// them to terminate short of ending the process as a whole) +print("\(regexpCount) regular expression(s)") +print("\(tests.count) test case(s)") +for test in tests { + test.start() +} + +// wait... +Thread.sleep(forTimeInterval: 20.0) + +// report those cases that are still running +print("incomplete after 20 seconds:") +for test in tests { + if test.done == false { + var str = test.targetString + str = str.replacingOccurrences(of: "\n", with: "\\n") + if str.count > 35 { + str = str.prefix(15) + " ... " + str.suffix(15) + } + let match = test.wholeMatch ? "wholeMatch" : "firstMatch" + print(" \(test.regexStr) on \(str) (\(match))") + } +} + +print("end.") diff --git a/swift/ql/test/query-tests/Security/CWE-079/UnsafeWebViewFetch.expected b/swift/ql/test/query-tests/Security/CWE-079/UnsafeWebViewFetch.expected index b21ed3b030c..e7c8443bfa2 100644 --- a/swift/ql/test/query-tests/Security/CWE-079/UnsafeWebViewFetch.expected +++ b/swift/ql/test/query-tests/Security/CWE-079/UnsafeWebViewFetch.expected @@ -1,7 +1,4 @@ edges -| UnsafeWebViewFetch.swift:10:2:10:25 | [summary param] 0 in URL.init(string:) | UnsafeWebViewFetch.swift:10:2:10:25 | [summary] to write: return (return) in URL.init(string:) | -| UnsafeWebViewFetch.swift:11:2:11:43 | [summary param] 1 in URL.init(string:relativeTo:) | UnsafeWebViewFetch.swift:11:2:11:43 | [summary] to write: return (return) in URL.init(string:relativeTo:) | -| UnsafeWebViewFetch.swift:43:5:43:29 | [summary param] 0 in Data.init(_:) | UnsafeWebViewFetch.swift:43:5:43:29 | [summary] to write: return (return) in Data.init(_:) | | UnsafeWebViewFetch.swift:94:10:94:37 | try ... | UnsafeWebViewFetch.swift:117:21:117:35 | call to getRemoteData() | | UnsafeWebViewFetch.swift:94:10:94:37 | try ... | UnsafeWebViewFetch.swift:120:25:120:39 | call to getRemoteData() | | UnsafeWebViewFetch.swift:94:10:94:37 | try ... | UnsafeWebViewFetch.swift:164:21:164:35 | call to getRemoteData() | @@ -25,15 +22,12 @@ edges | UnsafeWebViewFetch.swift:131:18:131:42 | call to URL.init(string:) | UnsafeWebViewFetch.swift:139:48:139:57 | ...! | | UnsafeWebViewFetch.swift:131:18:131:42 | call to URL.init(string:) | UnsafeWebViewFetch.swift:153:85:153:94 | ...! | | UnsafeWebViewFetch.swift:131:18:131:42 | call to URL.init(string:) | UnsafeWebViewFetch.swift:154:86:154:95 | ...! | -| UnsafeWebViewFetch.swift:131:30:131:30 | remoteString | UnsafeWebViewFetch.swift:10:2:10:25 | [summary param] 0 in URL.init(string:) | | UnsafeWebViewFetch.swift:131:30:131:30 | remoteString | UnsafeWebViewFetch.swift:131:18:131:42 | call to URL.init(string:) | | UnsafeWebViewFetch.swift:132:19:132:61 | call to URL.init(string:relativeTo:) | UnsafeWebViewFetch.swift:140:47:140:57 | ...! | | UnsafeWebViewFetch.swift:132:19:132:61 | call to URL.init(string:relativeTo:) | UnsafeWebViewFetch.swift:141:48:141:58 | ...! | -| UnsafeWebViewFetch.swift:132:52:132:52 | remoteURL | UnsafeWebViewFetch.swift:11:2:11:43 | [summary param] 1 in URL.init(string:relativeTo:) | | UnsafeWebViewFetch.swift:132:52:132:52 | remoteURL | UnsafeWebViewFetch.swift:132:19:132:61 | call to URL.init(string:relativeTo:) | | UnsafeWebViewFetch.swift:150:19:150:41 | call to Data.init(_:) | UnsafeWebViewFetch.swift:152:15:152:15 | remoteData | | UnsafeWebViewFetch.swift:150:19:150:41 | call to Data.init(_:) | UnsafeWebViewFetch.swift:154:15:154:15 | remoteData | -| UnsafeWebViewFetch.swift:150:24:150:37 | .utf8 | UnsafeWebViewFetch.swift:43:5:43:29 | [summary param] 0 in Data.init(_:) | | UnsafeWebViewFetch.swift:150:24:150:37 | .utf8 | UnsafeWebViewFetch.swift:150:19:150:41 | call to Data.init(_:) | | UnsafeWebViewFetch.swift:164:21:164:35 | call to getRemoteData() | UnsafeWebViewFetch.swift:168:25:168:25 | remoteString | | UnsafeWebViewFetch.swift:164:21:164:35 | call to getRemoteData() | UnsafeWebViewFetch.swift:171:25:171:51 | ... .+(_:_:) ... | @@ -49,25 +43,16 @@ edges | UnsafeWebViewFetch.swift:178:18:178:42 | call to URL.init(string:) | UnsafeWebViewFetch.swift:186:48:186:57 | ...! | | UnsafeWebViewFetch.swift:178:18:178:42 | call to URL.init(string:) | UnsafeWebViewFetch.swift:200:90:200:99 | ...! | | UnsafeWebViewFetch.swift:178:18:178:42 | call to URL.init(string:) | UnsafeWebViewFetch.swift:201:91:201:100 | ...! | -| UnsafeWebViewFetch.swift:178:30:178:30 | remoteString | UnsafeWebViewFetch.swift:10:2:10:25 | [summary param] 0 in URL.init(string:) | | UnsafeWebViewFetch.swift:178:30:178:30 | remoteString | UnsafeWebViewFetch.swift:178:18:178:42 | call to URL.init(string:) | | UnsafeWebViewFetch.swift:179:19:179:61 | call to URL.init(string:relativeTo:) | UnsafeWebViewFetch.swift:187:47:187:57 | ...! | | UnsafeWebViewFetch.swift:179:19:179:61 | call to URL.init(string:relativeTo:) | UnsafeWebViewFetch.swift:188:48:188:58 | ...! | -| UnsafeWebViewFetch.swift:179:52:179:52 | remoteURL | UnsafeWebViewFetch.swift:11:2:11:43 | [summary param] 1 in URL.init(string:relativeTo:) | | UnsafeWebViewFetch.swift:179:52:179:52 | remoteURL | UnsafeWebViewFetch.swift:179:19:179:61 | call to URL.init(string:relativeTo:) | | UnsafeWebViewFetch.swift:197:19:197:41 | call to Data.init(_:) | UnsafeWebViewFetch.swift:199:15:199:15 | remoteData | | UnsafeWebViewFetch.swift:197:19:197:41 | call to Data.init(_:) | UnsafeWebViewFetch.swift:201:15:201:15 | remoteData | -| UnsafeWebViewFetch.swift:197:24:197:37 | .utf8 | UnsafeWebViewFetch.swift:43:5:43:29 | [summary param] 0 in Data.init(_:) | | UnsafeWebViewFetch.swift:197:24:197:37 | .utf8 | UnsafeWebViewFetch.swift:197:19:197:41 | call to Data.init(_:) | | UnsafeWebViewFetch.swift:206:17:206:31 | call to getRemoteData() | UnsafeWebViewFetch.swift:210:25:210:25 | htmlData | | UnsafeWebViewFetch.swift:206:17:206:31 | call to getRemoteData() | UnsafeWebViewFetch.swift:211:25:211:25 | htmlData | nodes -| UnsafeWebViewFetch.swift:10:2:10:25 | [summary param] 0 in URL.init(string:) | semmle.label | [summary param] 0 in URL.init(string:) | -| UnsafeWebViewFetch.swift:10:2:10:25 | [summary] to write: return (return) in URL.init(string:) | semmle.label | [summary] to write: return (return) in URL.init(string:) | -| UnsafeWebViewFetch.swift:11:2:11:43 | [summary param] 1 in URL.init(string:relativeTo:) | semmle.label | [summary param] 1 in URL.init(string:relativeTo:) | -| UnsafeWebViewFetch.swift:11:2:11:43 | [summary] to write: return (return) in URL.init(string:relativeTo:) | semmle.label | [summary] to write: return (return) in URL.init(string:relativeTo:) | -| UnsafeWebViewFetch.swift:43:5:43:29 | [summary param] 0 in Data.init(_:) | semmle.label | [summary param] 0 in Data.init(_:) | -| UnsafeWebViewFetch.swift:43:5:43:29 | [summary] to write: return (return) in Data.init(_:) | semmle.label | [summary] to write: return (return) in Data.init(_:) | | UnsafeWebViewFetch.swift:94:10:94:37 | try ... | semmle.label | try ... | | UnsafeWebViewFetch.swift:94:14:94:37 | call to String.init(contentsOf:) | semmle.label | call to String.init(contentsOf:) | | UnsafeWebViewFetch.swift:103:25:103:84 | try! ... | semmle.label | try! ... | @@ -126,12 +111,6 @@ nodes | UnsafeWebViewFetch.swift:210:25:210:25 | htmlData | semmle.label | htmlData | | UnsafeWebViewFetch.swift:211:25:211:25 | htmlData | semmle.label | htmlData | subpaths -| UnsafeWebViewFetch.swift:131:30:131:30 | remoteString | UnsafeWebViewFetch.swift:10:2:10:25 | [summary param] 0 in URL.init(string:) | UnsafeWebViewFetch.swift:10:2:10:25 | [summary] to write: return (return) in URL.init(string:) | UnsafeWebViewFetch.swift:131:18:131:42 | call to URL.init(string:) | -| UnsafeWebViewFetch.swift:132:52:132:52 | remoteURL | UnsafeWebViewFetch.swift:11:2:11:43 | [summary param] 1 in URL.init(string:relativeTo:) | UnsafeWebViewFetch.swift:11:2:11:43 | [summary] to write: return (return) in URL.init(string:relativeTo:) | UnsafeWebViewFetch.swift:132:19:132:61 | call to URL.init(string:relativeTo:) | -| UnsafeWebViewFetch.swift:150:24:150:37 | .utf8 | UnsafeWebViewFetch.swift:43:5:43:29 | [summary param] 0 in Data.init(_:) | UnsafeWebViewFetch.swift:43:5:43:29 | [summary] to write: return (return) in Data.init(_:) | UnsafeWebViewFetch.swift:150:19:150:41 | call to Data.init(_:) | -| UnsafeWebViewFetch.swift:178:30:178:30 | remoteString | UnsafeWebViewFetch.swift:10:2:10:25 | [summary param] 0 in URL.init(string:) | UnsafeWebViewFetch.swift:10:2:10:25 | [summary] to write: return (return) in URL.init(string:) | UnsafeWebViewFetch.swift:178:18:178:42 | call to URL.init(string:) | -| UnsafeWebViewFetch.swift:179:52:179:52 | remoteURL | UnsafeWebViewFetch.swift:11:2:11:43 | [summary param] 1 in URL.init(string:relativeTo:) | UnsafeWebViewFetch.swift:11:2:11:43 | [summary] to write: return (return) in URL.init(string:relativeTo:) | UnsafeWebViewFetch.swift:179:19:179:61 | call to URL.init(string:relativeTo:) | -| UnsafeWebViewFetch.swift:197:24:197:37 | .utf8 | UnsafeWebViewFetch.swift:43:5:43:29 | [summary param] 0 in Data.init(_:) | UnsafeWebViewFetch.swift:43:5:43:29 | [summary] to write: return (return) in Data.init(_:) | UnsafeWebViewFetch.swift:197:19:197:41 | call to Data.init(_:) | #select | UnsafeWebViewFetch.swift:103:25:103:84 | try! ... | UnsafeWebViewFetch.swift:103:30:103:84 | call to String.init(contentsOf:) | UnsafeWebViewFetch.swift:103:25:103:84 | try! ... | Tainted data is used in a WebView fetch without restricting the base URL. | | UnsafeWebViewFetch.swift:106:25:106:25 | data | UnsafeWebViewFetch.swift:105:18:105:72 | call to String.init(contentsOf:) | UnsafeWebViewFetch.swift:106:25:106:25 | data | Tainted data is used in a WebView fetch without restricting the base URL. | diff --git a/swift/ql/test/query-tests/Security/CWE-089/CONSISTENCY/CfgConsistency.expected b/swift/ql/test/query-tests/Security/CWE-089/CONSISTENCY/CfgConsistency.expected deleted file mode 100644 index 069a6ecbc6a..00000000000 --- a/swift/ql/test/query-tests/Security/CWE-089/CONSISTENCY/CfgConsistency.expected +++ /dev/null @@ -1,2 +0,0 @@ -deadEnd -| file://:0:0:0:0 | ... = ... | diff --git a/swift/ql/test/query-tests/Security/CWE-089/SqlInjection.expected b/swift/ql/test/query-tests/Security/CWE-089/SqlInjection.expected index 7c5466939f2..a4617e9b811 100644 --- a/swift/ql/test/query-tests/Security/CWE-089/SqlInjection.expected +++ b/swift/ql/test/query-tests/Security/CWE-089/SqlInjection.expected @@ -97,8 +97,6 @@ edges | SQLite.swift:62:25:62:79 | call to String.init(contentsOf:) | SQLite.swift:117:16:117:16 | unsafeQuery1 | | SQLite.swift:62:25:62:79 | call to String.init(contentsOf:) | SQLite.swift:119:16:119:16 | unsafeQuery1 | | SQLite.swift:62:25:62:79 | call to String.init(contentsOf:) | SQLite.swift:132:20:132:20 | remoteString | -| sqlite3_c_api.swift:15:2:15:71 | [summary param] this in copyBytes(to:count:) | sqlite3_c_api.swift:15:2:15:71 | [summary] to write: argument 0 in copyBytes(to:count:) | -| sqlite3_c_api.swift:37:2:37:103 | [summary param] this in data(using:allowLossyConversion:) | sqlite3_c_api.swift:37:2:37:103 | [summary] to write: return (return) in data(using:allowLossyConversion:) | | sqlite3_c_api.swift:122:26:122:80 | call to String.init(contentsOf:) | sqlite3_c_api.swift:133:33:133:33 | unsafeQuery1 | | sqlite3_c_api.swift:122:26:122:80 | call to String.init(contentsOf:) | sqlite3_c_api.swift:134:33:134:33 | unsafeQuery2 | | sqlite3_c_api.swift:122:26:122:80 | call to String.init(contentsOf:) | sqlite3_c_api.swift:135:33:135:33 | unsafeQuery3 | @@ -106,10 +104,8 @@ edges | sqlite3_c_api.swift:122:26:122:80 | call to String.init(contentsOf:) | sqlite3_c_api.swift:175:29:175:29 | unsafeQuery3 | | sqlite3_c_api.swift:122:26:122:80 | call to String.init(contentsOf:) | sqlite3_c_api.swift:183:29:183:29 | unsafeQuery3 | | sqlite3_c_api.swift:122:26:122:80 | call to String.init(contentsOf:) | sqlite3_c_api.swift:189:13:189:13 | unsafeQuery3 | -| sqlite3_c_api.swift:189:13:189:13 | unsafeQuery3 | sqlite3_c_api.swift:37:2:37:103 | [summary param] this in data(using:allowLossyConversion:) | | sqlite3_c_api.swift:189:13:189:13 | unsafeQuery3 | sqlite3_c_api.swift:189:13:189:58 | call to data(using:allowLossyConversion:) | | sqlite3_c_api.swift:189:13:189:58 | call to data(using:allowLossyConversion:) | sqlite3_c_api.swift:190:2:190:2 | data | -| sqlite3_c_api.swift:190:2:190:2 | data | sqlite3_c_api.swift:15:2:15:71 | [summary param] this in copyBytes(to:count:) | | sqlite3_c_api.swift:190:2:190:2 | data | sqlite3_c_api.swift:190:21:190:21 | [post] buffer | | sqlite3_c_api.swift:190:21:190:21 | [post] buffer | sqlite3_c_api.swift:194:28:194:28 | buffer | | sqlite3_c_api.swift:190:21:190:21 | [post] buffer | sqlite3_c_api.swift:202:31:202:31 | buffer | @@ -226,10 +222,6 @@ nodes | SQLite.swift:117:16:117:16 | unsafeQuery1 | semmle.label | unsafeQuery1 | | SQLite.swift:119:16:119:16 | unsafeQuery1 | semmle.label | unsafeQuery1 | | SQLite.swift:132:20:132:20 | remoteString | semmle.label | remoteString | -| sqlite3_c_api.swift:15:2:15:71 | [summary param] this in copyBytes(to:count:) | semmle.label | [summary param] this in copyBytes(to:count:) | -| sqlite3_c_api.swift:15:2:15:71 | [summary] to write: argument 0 in copyBytes(to:count:) | semmle.label | [summary] to write: argument 0 in copyBytes(to:count:) | -| sqlite3_c_api.swift:37:2:37:103 | [summary param] this in data(using:allowLossyConversion:) | semmle.label | [summary param] this in data(using:allowLossyConversion:) | -| sqlite3_c_api.swift:37:2:37:103 | [summary] to write: return (return) in data(using:allowLossyConversion:) | semmle.label | [summary] to write: return (return) in data(using:allowLossyConversion:) | | sqlite3_c_api.swift:122:26:122:80 | call to String.init(contentsOf:) | semmle.label | call to String.init(contentsOf:) | | sqlite3_c_api.swift:133:33:133:33 | unsafeQuery1 | semmle.label | unsafeQuery1 | | sqlite3_c_api.swift:134:33:134:33 | unsafeQuery2 | semmle.label | unsafeQuery2 | @@ -245,8 +237,6 @@ nodes | sqlite3_c_api.swift:202:31:202:31 | buffer | semmle.label | buffer | | sqlite3_c_api.swift:210:31:210:31 | buffer | semmle.label | buffer | subpaths -| sqlite3_c_api.swift:189:13:189:13 | unsafeQuery3 | sqlite3_c_api.swift:37:2:37:103 | [summary param] this in data(using:allowLossyConversion:) | sqlite3_c_api.swift:37:2:37:103 | [summary] to write: return (return) in data(using:allowLossyConversion:) | sqlite3_c_api.swift:189:13:189:58 | call to data(using:allowLossyConversion:) | -| sqlite3_c_api.swift:190:2:190:2 | data | sqlite3_c_api.swift:15:2:15:71 | [summary param] this in copyBytes(to:count:) | sqlite3_c_api.swift:15:2:15:71 | [summary] to write: argument 0 in copyBytes(to:count:) | sqlite3_c_api.swift:190:21:190:21 | [post] buffer | #select | GRDB.swift:106:41:106:41 | remoteString | GRDB.swift:104:25:104:79 | call to String.init(contentsOf:) | GRDB.swift:106:41:106:41 | remoteString | This query depends on a $@. | GRDB.swift:104:25:104:79 | call to String.init(contentsOf:) | user-provided value | | GRDB.swift:108:41:108:41 | remoteString | GRDB.swift:104:25:104:79 | call to String.init(contentsOf:) | GRDB.swift:108:41:108:41 | remoteString | This query depends on a $@. | GRDB.swift:104:25:104:79 | call to String.init(contentsOf:) | user-provided value | diff --git a/swift/ql/test/query-tests/Security/CWE-094/UnsafeJsEval.expected b/swift/ql/test/query-tests/Security/CWE-094/UnsafeJsEval.expected index de1c23c52a8..3e810179bcf 100644 --- a/swift/ql/test/query-tests/Security/CWE-094/UnsafeJsEval.expected +++ b/swift/ql/test/query-tests/Security/CWE-094/UnsafeJsEval.expected @@ -1,8 +1,5 @@ edges -| UnsafeJsEval.swift:69:2:73:5 | [summary param] 0 in WKUserScript.init(source:injectionTime:forMainFrameOnly:) | UnsafeJsEval.swift:69:2:73:5 | [summary] to write: return (return) in WKUserScript.init(source:injectionTime:forMainFrameOnly:) | -| UnsafeJsEval.swift:75:2:80:5 | [summary param] 0 in WKUserScript.init(source:injectionTime:forMainFrameOnly:in:) | UnsafeJsEval.swift:75:2:80:5 | [summary] to write: return (return) in WKUserScript.init(source:injectionTime:forMainFrameOnly:in:) | | UnsafeJsEval.swift:124:21:124:42 | string | UnsafeJsEval.swift:124:70:124:70 | string | -| UnsafeJsEval.swift:144:5:144:29 | [summary param] 0 in Data.init(_:) | UnsafeJsEval.swift:144:5:144:29 | [summary] to write: return (return) in Data.init(_:) | | UnsafeJsEval.swift:165:10:165:37 | try ... | UnsafeJsEval.swift:201:21:201:35 | call to getRemoteData() | | UnsafeJsEval.swift:165:14:165:37 | call to String.init(contentsOf:) | UnsafeJsEval.swift:165:10:165:37 | try ... | | UnsafeJsEval.swift:201:21:201:35 | call to getRemoteData() | UnsafeJsEval.swift:205:7:205:7 | remoteString | @@ -28,7 +25,6 @@ edges | UnsafeJsEval.swift:208:7:208:39 | ... .+(_:_:) ... | UnsafeJsEval.swift:285:13:285:13 | string | | UnsafeJsEval.swift:208:7:208:39 | ... .+(_:_:) ... | UnsafeJsEval.swift:299:13:299:13 | string | | UnsafeJsEval.swift:211:19:211:41 | call to Data.init(_:) | UnsafeJsEval.swift:214:24:214:24 | remoteData | -| UnsafeJsEval.swift:211:24:211:37 | .utf8 | UnsafeJsEval.swift:144:5:144:29 | [summary param] 0 in Data.init(_:) | | UnsafeJsEval.swift:211:24:211:37 | .utf8 | UnsafeJsEval.swift:211:19:211:41 | call to Data.init(_:) | | UnsafeJsEval.swift:214:7:214:49 | call to String.init(decoding:as:) | UnsafeJsEval.swift:265:13:265:13 | string | | UnsafeJsEval.swift:214:7:214:49 | call to String.init(decoding:as:) | UnsafeJsEval.swift:268:13:268:13 | string | @@ -37,12 +33,9 @@ edges | UnsafeJsEval.swift:214:7:214:49 | call to String.init(decoding:as:) | UnsafeJsEval.swift:285:13:285:13 | string | | UnsafeJsEval.swift:214:7:214:49 | call to String.init(decoding:as:) | UnsafeJsEval.swift:299:13:299:13 | string | | UnsafeJsEval.swift:214:24:214:24 | remoteData | UnsafeJsEval.swift:214:7:214:49 | call to String.init(decoding:as:) | -| UnsafeJsEval.swift:214:24:214:24 | remoteData | file://:0:0:0:0 | [summary param] 0 in String.init(decoding:as:) | | UnsafeJsEval.swift:265:13:265:13 | string | UnsafeJsEval.swift:266:43:266:43 | string | -| UnsafeJsEval.swift:266:43:266:43 | string | UnsafeJsEval.swift:69:2:73:5 | [summary param] 0 in WKUserScript.init(source:injectionTime:forMainFrameOnly:) | | UnsafeJsEval.swift:266:43:266:43 | string | UnsafeJsEval.swift:266:22:266:107 | call to WKUserScript.init(source:injectionTime:forMainFrameOnly:) | | UnsafeJsEval.swift:268:13:268:13 | string | UnsafeJsEval.swift:269:43:269:43 | string | -| UnsafeJsEval.swift:269:43:269:43 | string | UnsafeJsEval.swift:75:2:80:5 | [summary param] 0 in WKUserScript.init(source:injectionTime:forMainFrameOnly:in:) | | UnsafeJsEval.swift:269:43:269:43 | string | UnsafeJsEval.swift:269:22:269:124 | call to WKUserScript.init(source:injectionTime:forMainFrameOnly:in:) | | UnsafeJsEval.swift:276:13:276:13 | string | UnsafeJsEval.swift:277:26:277:26 | string | | UnsafeJsEval.swift:279:13:279:13 | string | UnsafeJsEval.swift:280:26:280:26 | string | @@ -63,16 +56,9 @@ edges | UnsafeJsEval.swift:301:31:301:84 | call to JSStringCreateWithUTF8CString(_:) | UnsafeJsEval.swift:301:16:301:85 | call to JSStringRetain(_:) | | UnsafeJsEval.swift:301:31:301:84 | call to JSStringCreateWithUTF8CString(_:) | UnsafeJsEval.swift:305:17:305:17 | jsstr | | UnsafeJsEval.swift:318:24:318:87 | call to String.init(contentsOf:) | UnsafeJsEval.swift:320:44:320:74 | ... .+(_:_:) ... | -| file://:0:0:0:0 | [summary param] 0 in String.init(decoding:as:) | file://:0:0:0:0 | [summary] to write: return (return) in String.init(decoding:as:) | nodes -| UnsafeJsEval.swift:69:2:73:5 | [summary param] 0 in WKUserScript.init(source:injectionTime:forMainFrameOnly:) | semmle.label | [summary param] 0 in WKUserScript.init(source:injectionTime:forMainFrameOnly:) | -| UnsafeJsEval.swift:69:2:73:5 | [summary] to write: return (return) in WKUserScript.init(source:injectionTime:forMainFrameOnly:) | semmle.label | [summary] to write: return (return) in WKUserScript.init(source:injectionTime:forMainFrameOnly:) | -| UnsafeJsEval.swift:75:2:80:5 | [summary param] 0 in WKUserScript.init(source:injectionTime:forMainFrameOnly:in:) | semmle.label | [summary param] 0 in WKUserScript.init(source:injectionTime:forMainFrameOnly:in:) | -| UnsafeJsEval.swift:75:2:80:5 | [summary] to write: return (return) in WKUserScript.init(source:injectionTime:forMainFrameOnly:in:) | semmle.label | [summary] to write: return (return) in WKUserScript.init(source:injectionTime:forMainFrameOnly:in:) | | UnsafeJsEval.swift:124:21:124:42 | string | semmle.label | string | | UnsafeJsEval.swift:124:70:124:70 | string | semmle.label | string | -| UnsafeJsEval.swift:144:5:144:29 | [summary param] 0 in Data.init(_:) | semmle.label | [summary param] 0 in Data.init(_:) | -| UnsafeJsEval.swift:144:5:144:29 | [summary] to write: return (return) in Data.init(_:) | semmle.label | [summary] to write: return (return) in Data.init(_:) | | UnsafeJsEval.swift:165:10:165:37 | try ... | semmle.label | try ... | | UnsafeJsEval.swift:165:14:165:37 | call to String.init(contentsOf:) | semmle.label | call to String.init(contentsOf:) | | UnsafeJsEval.swift:201:21:201:35 | call to getRemoteData() | semmle.label | call to getRemoteData() | @@ -108,13 +94,7 @@ nodes | UnsafeJsEval.swift:305:17:305:17 | jsstr | semmle.label | jsstr | | UnsafeJsEval.swift:318:24:318:87 | call to String.init(contentsOf:) | semmle.label | call to String.init(contentsOf:) | | UnsafeJsEval.swift:320:44:320:74 | ... .+(_:_:) ... | semmle.label | ... .+(_:_:) ... | -| file://:0:0:0:0 | [summary param] 0 in String.init(decoding:as:) | semmle.label | [summary param] 0 in String.init(decoding:as:) | -| file://:0:0:0:0 | [summary] to write: return (return) in String.init(decoding:as:) | semmle.label | [summary] to write: return (return) in String.init(decoding:as:) | subpaths -| UnsafeJsEval.swift:211:24:211:37 | .utf8 | UnsafeJsEval.swift:144:5:144:29 | [summary param] 0 in Data.init(_:) | UnsafeJsEval.swift:144:5:144:29 | [summary] to write: return (return) in Data.init(_:) | UnsafeJsEval.swift:211:19:211:41 | call to Data.init(_:) | -| UnsafeJsEval.swift:214:24:214:24 | remoteData | file://:0:0:0:0 | [summary param] 0 in String.init(decoding:as:) | file://:0:0:0:0 | [summary] to write: return (return) in String.init(decoding:as:) | UnsafeJsEval.swift:214:7:214:49 | call to String.init(decoding:as:) | -| UnsafeJsEval.swift:266:43:266:43 | string | UnsafeJsEval.swift:69:2:73:5 | [summary param] 0 in WKUserScript.init(source:injectionTime:forMainFrameOnly:) | UnsafeJsEval.swift:69:2:73:5 | [summary] to write: return (return) in WKUserScript.init(source:injectionTime:forMainFrameOnly:) | UnsafeJsEval.swift:266:22:266:107 | call to WKUserScript.init(source:injectionTime:forMainFrameOnly:) | -| UnsafeJsEval.swift:269:43:269:43 | string | UnsafeJsEval.swift:75:2:80:5 | [summary param] 0 in WKUserScript.init(source:injectionTime:forMainFrameOnly:in:) | UnsafeJsEval.swift:75:2:80:5 | [summary] to write: return (return) in WKUserScript.init(source:injectionTime:forMainFrameOnly:in:) | UnsafeJsEval.swift:269:22:269:124 | call to WKUserScript.init(source:injectionTime:forMainFrameOnly:in:) | | UnsafeJsEval.swift:287:31:287:97 | call to JSStringCreateWithCharacters(_:_:) | UnsafeJsEval.swift:124:21:124:42 | string | UnsafeJsEval.swift:124:70:124:70 | string | UnsafeJsEval.swift:287:16:287:98 | call to JSStringRetain(_:) | | UnsafeJsEval.swift:301:31:301:84 | call to JSStringCreateWithUTF8CString(_:) | UnsafeJsEval.swift:124:21:124:42 | string | UnsafeJsEval.swift:124:70:124:70 | string | UnsafeJsEval.swift:301:16:301:85 | call to JSStringRetain(_:) | #select diff --git a/swift/ql/test/query-tests/Security/CWE-1204/CONSISTENCY/CfgConsistency.expected b/swift/ql/test/query-tests/Security/CWE-1204/CONSISTENCY/CfgConsistency.expected deleted file mode 100644 index 90f66c1830d..00000000000 --- a/swift/ql/test/query-tests/Security/CWE-1204/CONSISTENCY/CfgConsistency.expected +++ /dev/null @@ -1,11 +0,0 @@ -deadEnd -| file://:0:0:0:0 | ... = ... | -| file://:0:0:0:0 | ... = ... | -| file://:0:0:0:0 | ... = ... | -| file://:0:0:0:0 | ... = ... | -| file://:0:0:0:0 | ... = ... | -| file://:0:0:0:0 | ... = ... | -| file://:0:0:0:0 | ... = ... | -| file://:0:0:0:0 | ... = ... | -| file://:0:0:0:0 | ... = ... | -| file://:0:0:0:0 | ... = ... | diff --git a/swift/ql/test/query-tests/Security/CWE-1204/StaticInitializationVector.expected b/swift/ql/test/query-tests/Security/CWE-1204/StaticInitializationVector.expected index aa6cf49411c..848ce4937ae 100644 --- a/swift/ql/test/query-tests/Security/CWE-1204/StaticInitializationVector.expected +++ b/swift/ql/test/query-tests/Security/CWE-1204/StaticInitializationVector.expected @@ -1,20 +1,15 @@ edges -| rncryptor.swift:5:5:5:29 | [summary param] 0 in Data.init(_:) | rncryptor.swift:5:5:5:29 | [summary] to write: return (return) in Data.init(_:) | | rncryptor.swift:60:19:60:25 | call to Data.init(_:) | rncryptor.swift:68:104:68:104 | myConstIV1 | | rncryptor.swift:60:19:60:25 | call to Data.init(_:) | rncryptor.swift:77:125:77:125 | myConstIV1 | -| rncryptor.swift:60:24:60:24 | 0 | rncryptor.swift:5:5:5:29 | [summary param] 0 in Data.init(_:) | | rncryptor.swift:60:24:60:24 | 0 | rncryptor.swift:60:19:60:25 | call to Data.init(_:) | | rncryptor.swift:61:19:61:27 | call to Data.init(_:) | rncryptor.swift:70:104:70:104 | myConstIV2 | | rncryptor.swift:61:19:61:27 | call to Data.init(_:) | rncryptor.swift:79:133:79:133 | myConstIV2 | -| rncryptor.swift:61:24:61:24 | 123 | rncryptor.swift:5:5:5:29 | [summary param] 0 in Data.init(_:) | | rncryptor.swift:61:24:61:24 | 123 | rncryptor.swift:61:19:61:27 | call to Data.init(_:) | | rncryptor.swift:62:19:62:35 | call to Data.init(_:) | rncryptor.swift:72:84:72:84 | myConstIV3 | | rncryptor.swift:62:19:62:35 | call to Data.init(_:) | rncryptor.swift:81:105:81:105 | myConstIV3 | -| rncryptor.swift:62:24:62:34 | [...] | rncryptor.swift:5:5:5:29 | [summary param] 0 in Data.init(_:) | | rncryptor.swift:62:24:62:34 | [...] | rncryptor.swift:62:19:62:35 | call to Data.init(_:) | | rncryptor.swift:63:19:63:28 | call to Data.init(_:) | rncryptor.swift:74:84:74:84 | myConstIV4 | | rncryptor.swift:63:19:63:28 | call to Data.init(_:) | rncryptor.swift:83:113:83:113 | myConstIV4 | -| rncryptor.swift:63:24:63:24 | iv | rncryptor.swift:5:5:5:29 | [summary param] 0 in Data.init(_:) | | rncryptor.swift:63:24:63:24 | iv | rncryptor.swift:63:19:63:28 | call to Data.init(_:) | | test.swift:53:19:53:34 | iv | test.swift:54:17:54:17 | iv | | test.swift:85:3:85:3 | this string is constant | test.swift:101:17:101:35 | call to getConstantString() | @@ -40,8 +35,6 @@ edges | test.swift:101:17:101:35 | call to getConstantString() | test.swift:130:39:130:39 | ivString | | test.swift:147:22:147:22 | iv | test.swift:53:19:53:34 | iv | nodes -| rncryptor.swift:5:5:5:29 | [summary param] 0 in Data.init(_:) | semmle.label | [summary param] 0 in Data.init(_:) | -| rncryptor.swift:5:5:5:29 | [summary] to write: return (return) in Data.init(_:) | semmle.label | [summary] to write: return (return) in Data.init(_:) | | rncryptor.swift:60:19:60:25 | call to Data.init(_:) | semmle.label | call to Data.init(_:) | | rncryptor.swift:60:24:60:24 | 0 | semmle.label | 0 | | rncryptor.swift:61:19:61:27 | call to Data.init(_:) | semmle.label | call to Data.init(_:) | @@ -84,10 +77,6 @@ nodes | test.swift:167:22:167:22 | iv | semmle.label | iv | | test.swift:168:22:168:22 | iv | semmle.label | iv | subpaths -| rncryptor.swift:60:24:60:24 | 0 | rncryptor.swift:5:5:5:29 | [summary param] 0 in Data.init(_:) | rncryptor.swift:5:5:5:29 | [summary] to write: return (return) in Data.init(_:) | rncryptor.swift:60:19:60:25 | call to Data.init(_:) | -| rncryptor.swift:61:24:61:24 | 123 | rncryptor.swift:5:5:5:29 | [summary param] 0 in Data.init(_:) | rncryptor.swift:5:5:5:29 | [summary] to write: return (return) in Data.init(_:) | rncryptor.swift:61:19:61:27 | call to Data.init(_:) | -| rncryptor.swift:62:24:62:34 | [...] | rncryptor.swift:5:5:5:29 | [summary param] 0 in Data.init(_:) | rncryptor.swift:5:5:5:29 | [summary] to write: return (return) in Data.init(_:) | rncryptor.swift:62:19:62:35 | call to Data.init(_:) | -| rncryptor.swift:63:24:63:24 | iv | rncryptor.swift:5:5:5:29 | [summary param] 0 in Data.init(_:) | rncryptor.swift:5:5:5:29 | [summary] to write: return (return) in Data.init(_:) | rncryptor.swift:63:19:63:28 | call to Data.init(_:) | #select | rncryptor.swift:68:104:68:104 | myConstIV1 | rncryptor.swift:60:24:60:24 | 0 | rncryptor.swift:68:104:68:104 | myConstIV1 | The static value '0' is used as an initialization vector for encryption. | | rncryptor.swift:70:104:70:104 | myConstIV2 | rncryptor.swift:61:24:61:24 | 123 | rncryptor.swift:70:104:70:104 | myConstIV2 | The static value '123' is used as an initialization vector for encryption. | diff --git a/swift/ql/test/query-tests/Security/CWE-134/UncontrolledFormatString.expected b/swift/ql/test/query-tests/Security/CWE-134/UncontrolledFormatString.expected index dd2c4f66f74..3a2d4eb80c6 100644 --- a/swift/ql/test/query-tests/Security/CWE-134/UncontrolledFormatString.expected +++ b/swift/ql/test/query-tests/Security/CWE-134/UncontrolledFormatString.expected @@ -1,5 +1,4 @@ edges -| UncontrolledFormatString.swift:30:5:30:35 | [summary param] 0 in NSString.init(string:) | UncontrolledFormatString.swift:30:5:30:35 | [summary] to write: return (return) in NSString.init(string:) | | UncontrolledFormatString.swift:64:24:64:77 | call to String.init(contentsOf:) | UncontrolledFormatString.swift:70:28:70:28 | tainted | | UncontrolledFormatString.swift:64:24:64:77 | call to String.init(contentsOf:) | UncontrolledFormatString.swift:73:28:73:28 | tainted | | UncontrolledFormatString.swift:64:24:64:77 | call to String.init(contentsOf:) | UncontrolledFormatString.swift:74:28:74:28 | tainted | @@ -13,17 +12,11 @@ edges | UncontrolledFormatString.swift:64:24:64:77 | call to String.init(contentsOf:) | UncontrolledFormatString.swift:85:72:85:72 | tainted | | UncontrolledFormatString.swift:64:24:64:77 | call to String.init(contentsOf:) | UncontrolledFormatString.swift:88:11:88:11 | tainted | | UncontrolledFormatString.swift:64:24:64:77 | call to String.init(contentsOf:) | UncontrolledFormatString.swift:91:61:91:61 | tainted | -| UncontrolledFormatString.swift:81:47:81:47 | tainted | UncontrolledFormatString.swift:30:5:30:35 | [summary param] 0 in NSString.init(string:) | | UncontrolledFormatString.swift:81:47:81:47 | tainted | UncontrolledFormatString.swift:81:30:81:54 | call to NSString.init(string:) | -| UncontrolledFormatString.swift:82:65:82:65 | tainted | UncontrolledFormatString.swift:30:5:30:35 | [summary param] 0 in NSString.init(string:) | | UncontrolledFormatString.swift:82:65:82:65 | tainted | UncontrolledFormatString.swift:82:48:82:72 | call to NSString.init(string:) | -| UncontrolledFormatString.swift:84:54:84:54 | tainted | UncontrolledFormatString.swift:30:5:30:35 | [summary param] 0 in NSString.init(string:) | | UncontrolledFormatString.swift:84:54:84:54 | tainted | UncontrolledFormatString.swift:84:37:84:61 | call to NSString.init(string:) | -| UncontrolledFormatString.swift:85:72:85:72 | tainted | UncontrolledFormatString.swift:30:5:30:35 | [summary param] 0 in NSString.init(string:) | | UncontrolledFormatString.swift:85:72:85:72 | tainted | UncontrolledFormatString.swift:85:55:85:79 | call to NSString.init(string:) | nodes -| UncontrolledFormatString.swift:30:5:30:35 | [summary param] 0 in NSString.init(string:) | semmle.label | [summary param] 0 in NSString.init(string:) | -| UncontrolledFormatString.swift:30:5:30:35 | [summary] to write: return (return) in NSString.init(string:) | semmle.label | [summary] to write: return (return) in NSString.init(string:) | | UncontrolledFormatString.swift:64:24:64:77 | call to String.init(contentsOf:) | semmle.label | call to String.init(contentsOf:) | | UncontrolledFormatString.swift:70:28:70:28 | tainted | semmle.label | tainted | | UncontrolledFormatString.swift:73:28:73:28 | tainted | semmle.label | tainted | @@ -43,10 +36,6 @@ nodes | UncontrolledFormatString.swift:88:11:88:11 | tainted | semmle.label | tainted | | UncontrolledFormatString.swift:91:61:91:61 | tainted | semmle.label | tainted | subpaths -| UncontrolledFormatString.swift:81:47:81:47 | tainted | UncontrolledFormatString.swift:30:5:30:35 | [summary param] 0 in NSString.init(string:) | UncontrolledFormatString.swift:30:5:30:35 | [summary] to write: return (return) in NSString.init(string:) | UncontrolledFormatString.swift:81:30:81:54 | call to NSString.init(string:) | -| UncontrolledFormatString.swift:82:65:82:65 | tainted | UncontrolledFormatString.swift:30:5:30:35 | [summary param] 0 in NSString.init(string:) | UncontrolledFormatString.swift:30:5:30:35 | [summary] to write: return (return) in NSString.init(string:) | UncontrolledFormatString.swift:82:48:82:72 | call to NSString.init(string:) | -| UncontrolledFormatString.swift:84:54:84:54 | tainted | UncontrolledFormatString.swift:30:5:30:35 | [summary param] 0 in NSString.init(string:) | UncontrolledFormatString.swift:30:5:30:35 | [summary] to write: return (return) in NSString.init(string:) | UncontrolledFormatString.swift:84:37:84:61 | call to NSString.init(string:) | -| UncontrolledFormatString.swift:85:72:85:72 | tainted | UncontrolledFormatString.swift:30:5:30:35 | [summary param] 0 in NSString.init(string:) | UncontrolledFormatString.swift:30:5:30:35 | [summary] to write: return (return) in NSString.init(string:) | UncontrolledFormatString.swift:85:55:85:79 | call to NSString.init(string:) | #select | UncontrolledFormatString.swift:70:28:70:28 | tainted | UncontrolledFormatString.swift:64:24:64:77 | call to String.init(contentsOf:) | UncontrolledFormatString.swift:70:28:70:28 | tainted | This format string depends on $@. | UncontrolledFormatString.swift:64:24:64:77 | call to String.init(contentsOf:) | this user-provided value | | UncontrolledFormatString.swift:73:28:73:28 | tainted | UncontrolledFormatString.swift:64:24:64:77 | call to String.init(contentsOf:) | UncontrolledFormatString.swift:73:28:73:28 | tainted | This format string depends on $@. | UncontrolledFormatString.swift:64:24:64:77 | call to String.init(contentsOf:) | this user-provided value | diff --git a/swift/ql/test/query-tests/Security/CWE-259/CONSISTENCY/CfgConsistency.expected b/swift/ql/test/query-tests/Security/CWE-259/CONSISTENCY/CfgConsistency.expected deleted file mode 100644 index 853828d4f77..00000000000 --- a/swift/ql/test/query-tests/Security/CWE-259/CONSISTENCY/CfgConsistency.expected +++ /dev/null @@ -1,5 +0,0 @@ -deadEnd -| file://:0:0:0:0 | ... = ... | -| file://:0:0:0:0 | ... = ... | -| file://:0:0:0:0 | ... = ... | -| file://:0:0:0:0 | ... = ... | diff --git a/swift/ql/test/query-tests/Security/CWE-311/CONSISTENCY/CfgConsistency.expected b/swift/ql/test/query-tests/Security/CWE-311/CONSISTENCY/CfgConsistency.expected deleted file mode 100644 index 069a6ecbc6a..00000000000 --- a/swift/ql/test/query-tests/Security/CWE-311/CONSISTENCY/CfgConsistency.expected +++ /dev/null @@ -1,2 +0,0 @@ -deadEnd -| file://:0:0:0:0 | ... = ... | diff --git a/swift/ql/test/query-tests/Security/CWE-311/CleartextTransmission.expected b/swift/ql/test/query-tests/Security/CWE-311/CleartextTransmission.expected index 36c206ed9fd..78ddf30855b 100644 --- a/swift/ql/test/query-tests/Security/CWE-311/CleartextTransmission.expected +++ b/swift/ql/test/query-tests/Security/CWE-311/CleartextTransmission.expected @@ -2,9 +2,7 @@ edges | testAlamofire.swift:150:45:150:45 | password | testAlamofire.swift:150:13:150:45 | ... .+(_:_:) ... | | testAlamofire.swift:152:51:152:51 | password | testAlamofire.swift:152:19:152:51 | ... .+(_:_:) ... | | testAlamofire.swift:154:38:154:38 | email | testAlamofire.swift:154:14:154:46 | ... .+(_:_:) ... | -| testSend.swift:5:5:5:29 | [summary param] 0 in Data.init(_:) | testSend.swift:5:5:5:29 | [summary] to write: return (return) in Data.init(_:) | | testSend.swift:33:14:33:32 | call to Data.init(_:) | testSend.swift:37:19:37:19 | data2 | -| testSend.swift:33:19:33:19 | passwordPlain | testSend.swift:5:5:5:29 | [summary param] 0 in Data.init(_:) | | testSend.swift:33:19:33:19 | passwordPlain | testSend.swift:33:14:33:32 | call to Data.init(_:) | | testSend.swift:41:10:41:18 | data | testSend.swift:41:45:41:45 | data | | testSend.swift:52:13:52:13 | password | testSend.swift:59:27:59:27 | str1 | @@ -22,8 +20,6 @@ nodes | testAlamofire.swift:152:51:152:51 | password | semmle.label | password | | testAlamofire.swift:154:14:154:46 | ... .+(_:_:) ... | semmle.label | ... .+(_:_:) ... | | testAlamofire.swift:154:38:154:38 | email | semmle.label | email | -| testSend.swift:5:5:5:29 | [summary param] 0 in Data.init(_:) | semmle.label | [summary param] 0 in Data.init(_:) | -| testSend.swift:5:5:5:29 | [summary] to write: return (return) in Data.init(_:) | semmle.label | [summary] to write: return (return) in Data.init(_:) | | testSend.swift:29:19:29:19 | passwordPlain | semmle.label | passwordPlain | | testSend.swift:33:14:33:32 | call to Data.init(_:) | semmle.label | call to Data.init(_:) | | testSend.swift:33:19:33:19 | passwordPlain | semmle.label | passwordPlain | @@ -47,7 +43,6 @@ nodes | testURL.swift:16:55:16:55 | credit_card_no | semmle.label | credit_card_no | | testURL.swift:20:22:20:22 | passwd | semmle.label | passwd | subpaths -| testSend.swift:33:19:33:19 | passwordPlain | testSend.swift:5:5:5:29 | [summary param] 0 in Data.init(_:) | testSend.swift:5:5:5:29 | [summary] to write: return (return) in Data.init(_:) | testSend.swift:33:14:33:32 | call to Data.init(_:) | | testSend.swift:54:17:54:17 | password | testSend.swift:41:10:41:18 | data | testSend.swift:41:45:41:45 | data | testSend.swift:54:13:54:25 | call to pad(_:) | #select | testAlamofire.swift:150:13:150:45 | ... .+(_:_:) ... | testAlamofire.swift:150:45:150:45 | password | testAlamofire.swift:150:13:150:45 | ... .+(_:_:) ... | This operation transmits '... .+(_:_:) ...', which may contain unencrypted sensitive data from $@. | testAlamofire.swift:150:45:150:45 | password | password | diff --git a/swift/ql/test/query-tests/Security/CWE-312/CONSISTENCY/CfgConsistency.expected b/swift/ql/test/query-tests/Security/CWE-312/CONSISTENCY/CfgConsistency.expected deleted file mode 100644 index 9d02611e7a7..00000000000 --- a/swift/ql/test/query-tests/Security/CWE-312/CONSISTENCY/CfgConsistency.expected +++ /dev/null @@ -1,6 +0,0 @@ -deadEnd -| file://:0:0:0:0 | ... = ... | -| file://:0:0:0:0 | ... = ... | -| file://:0:0:0:0 | ... = ... | -| file://:0:0:0:0 | ... = ... | -| file://:0:0:0:0 | ... = ... | diff --git a/swift/ql/test/query-tests/Security/CWE-321/CONSISTENCY/CfgConsistency.expected b/swift/ql/test/query-tests/Security/CWE-321/CONSISTENCY/CfgConsistency.expected deleted file mode 100644 index 65cf24d02a7..00000000000 --- a/swift/ql/test/query-tests/Security/CWE-321/CONSISTENCY/CfgConsistency.expected +++ /dev/null @@ -1,13 +0,0 @@ -deadEnd -| file://:0:0:0:0 | ... = ... | -| file://:0:0:0:0 | ... = ... | -| file://:0:0:0:0 | ... = ... | -| file://:0:0:0:0 | ... = ... | -| file://:0:0:0:0 | ... = ... | -| file://:0:0:0:0 | ... = ... | -| file://:0:0:0:0 | ... = ... | -| file://:0:0:0:0 | ... = ... | -| file://:0:0:0:0 | ... = ... | -| file://:0:0:0:0 | ... = ... | -| file://:0:0:0:0 | ... = ... | -| file://:0:0:0:0 | ... = ... | diff --git a/swift/ql/test/query-tests/Security/CWE-321/HardcodedEncryptionKey.expected b/swift/ql/test/query-tests/Security/CWE-321/HardcodedEncryptionKey.expected index 380823043c0..f61deac228d 100644 --- a/swift/ql/test/query-tests/Security/CWE-321/HardcodedEncryptionKey.expected +++ b/swift/ql/test/query-tests/Security/CWE-321/HardcodedEncryptionKey.expected @@ -22,12 +22,10 @@ edges | file://:0:0:0:0 | [post] self [encryptionKey] | file://:0:0:0:0 | [post] self | | file://:0:0:0:0 | [post] self [encryptionKey] | file://:0:0:0:0 | [post] self | | file://:0:0:0:0 | value | file://:0:0:0:0 | [post] self [encryptionKey] | -| misc.swift:5:5:5:29 | [summary param] 0 in Data.init(_:) | misc.swift:5:5:5:29 | [summary] to write: return (return) in Data.init(_:) | | misc.swift:30:7:30:7 | value | file://:0:0:0:0 | value | | misc.swift:46:19:46:38 | call to Data.init(_:) | misc.swift:49:41:49:41 | myConstKey | | misc.swift:46:19:46:38 | call to Data.init(_:) | misc.swift:53:25:53:25 | myConstKey | | misc.swift:46:19:46:38 | call to Data.init(_:) | misc.swift:57:41:57:41 | myConstKey | -| misc.swift:46:24:46:24 | abcdef123456 | misc.swift:5:5:5:29 | [summary param] 0 in Data.init(_:) | | misc.swift:46:24:46:24 | abcdef123456 | misc.swift:46:19:46:38 | call to Data.init(_:) | | misc.swift:53:2:53:2 | [post] config [encryptionKey] | misc.swift:53:2:53:2 | [post] config | | misc.swift:53:25:53:25 | myConstKey | misc.swift:30:7:30:7 | value | @@ -37,7 +35,6 @@ edges | misc.swift:57:41:57:41 | myConstKey | misc.swift:30:7:30:7 | value | | misc.swift:57:41:57:41 | myConstKey | misc.swift:57:2:57:18 | [post] getter for .config | | misc.swift:57:41:57:41 | myConstKey | misc.swift:57:2:57:18 | [post] getter for .config [encryptionKey] | -| rncryptor.swift:5:5:5:29 | [summary param] 0 in Data.init(_:) | rncryptor.swift:5:5:5:29 | [summary] to write: return (return) in Data.init(_:) | | rncryptor.swift:60:19:60:38 | call to Data.init(_:) | rncryptor.swift:65:73:65:73 | myConstKey | | rncryptor.swift:60:19:60:38 | call to Data.init(_:) | rncryptor.swift:66:73:66:73 | myConstKey | | rncryptor.swift:60:19:60:38 | call to Data.init(_:) | rncryptor.swift:67:73:67:73 | myConstKey | @@ -53,7 +50,6 @@ edges | rncryptor.swift:60:19:60:38 | call to Data.init(_:) | rncryptor.swift:80:94:80:94 | myConstKey | | rncryptor.swift:60:19:60:38 | call to Data.init(_:) | rncryptor.swift:81:102:81:102 | myConstKey | | rncryptor.swift:60:19:60:38 | call to Data.init(_:) | rncryptor.swift:83:92:83:92 | myConstKey | -| rncryptor.swift:60:24:60:24 | abcdef123456 | rncryptor.swift:5:5:5:29 | [summary param] 0 in Data.init(_:) | | rncryptor.swift:60:24:60:24 | abcdef123456 | rncryptor.swift:60:19:60:38 | call to Data.init(_:) | nodes | cryptoswift.swift:76:3:76:3 | this string is constant | semmle.label | this string is constant | @@ -82,8 +78,6 @@ nodes | file://:0:0:0:0 | [post] self | semmle.label | [post] self | | file://:0:0:0:0 | [post] self [encryptionKey] | semmle.label | [post] self [encryptionKey] | | file://:0:0:0:0 | value | semmle.label | value | -| misc.swift:5:5:5:29 | [summary param] 0 in Data.init(_:) | semmle.label | [summary param] 0 in Data.init(_:) | -| misc.swift:5:5:5:29 | [summary] to write: return (return) in Data.init(_:) | semmle.label | [summary] to write: return (return) in Data.init(_:) | | misc.swift:30:7:30:7 | value | semmle.label | value | | misc.swift:46:19:46:38 | call to Data.init(_:) | semmle.label | call to Data.init(_:) | | misc.swift:46:24:46:24 | abcdef123456 | semmle.label | abcdef123456 | @@ -94,8 +88,6 @@ nodes | misc.swift:57:2:57:18 | [post] getter for .config | semmle.label | [post] getter for .config | | misc.swift:57:2:57:18 | [post] getter for .config [encryptionKey] | semmle.label | [post] getter for .config [encryptionKey] | | misc.swift:57:41:57:41 | myConstKey | semmle.label | myConstKey | -| rncryptor.swift:5:5:5:29 | [summary param] 0 in Data.init(_:) | semmle.label | [summary param] 0 in Data.init(_:) | -| rncryptor.swift:5:5:5:29 | [summary] to write: return (return) in Data.init(_:) | semmle.label | [summary] to write: return (return) in Data.init(_:) | | rncryptor.swift:60:19:60:38 | call to Data.init(_:) | semmle.label | call to Data.init(_:) | | rncryptor.swift:60:24:60:24 | abcdef123456 | semmle.label | abcdef123456 | | rncryptor.swift:65:73:65:73 | myConstKey | semmle.label | myConstKey | @@ -114,12 +106,10 @@ nodes | rncryptor.swift:81:102:81:102 | myConstKey | semmle.label | myConstKey | | rncryptor.swift:83:92:83:92 | myConstKey | semmle.label | myConstKey | subpaths -| misc.swift:46:24:46:24 | abcdef123456 | misc.swift:5:5:5:29 | [summary param] 0 in Data.init(_:) | misc.swift:5:5:5:29 | [summary] to write: return (return) in Data.init(_:) | misc.swift:46:19:46:38 | call to Data.init(_:) | | misc.swift:53:25:53:25 | myConstKey | misc.swift:30:7:30:7 | value | file://:0:0:0:0 | [post] self | misc.swift:53:2:53:2 | [post] config | | misc.swift:53:25:53:25 | myConstKey | misc.swift:30:7:30:7 | value | file://:0:0:0:0 | [post] self [encryptionKey] | misc.swift:53:2:53:2 | [post] config [encryptionKey] | | misc.swift:57:41:57:41 | myConstKey | misc.swift:30:7:30:7 | value | file://:0:0:0:0 | [post] self | misc.swift:57:2:57:18 | [post] getter for .config | | misc.swift:57:41:57:41 | myConstKey | misc.swift:30:7:30:7 | value | file://:0:0:0:0 | [post] self [encryptionKey] | misc.swift:57:2:57:18 | [post] getter for .config [encryptionKey] | -| rncryptor.swift:60:24:60:24 | abcdef123456 | rncryptor.swift:5:5:5:29 | [summary param] 0 in Data.init(_:) | rncryptor.swift:5:5:5:29 | [summary] to write: return (return) in Data.init(_:) | rncryptor.swift:60:19:60:38 | call to Data.init(_:) | #select | cryptoswift.swift:108:21:108:21 | keyString | cryptoswift.swift:76:3:76:3 | this string is constant | cryptoswift.swift:108:21:108:21 | keyString | The key 'keyString' has been initialized with hard-coded values from $@. | cryptoswift.swift:76:3:76:3 | this string is constant | this string is constant | | cryptoswift.swift:109:21:109:21 | keyString | cryptoswift.swift:76:3:76:3 | this string is constant | cryptoswift.swift:109:21:109:21 | keyString | The key 'keyString' has been initialized with hard-coded values from $@. | cryptoswift.swift:76:3:76:3 | this string is constant | this string is constant | diff --git a/swift/ql/test/query-tests/Security/CWE-327/CONSISTENCY/CfgConsistency.expected b/swift/ql/test/query-tests/Security/CWE-327/CONSISTENCY/CfgConsistency.expected deleted file mode 100644 index fc0518030f4..00000000000 --- a/swift/ql/test/query-tests/Security/CWE-327/CONSISTENCY/CfgConsistency.expected +++ /dev/null @@ -1,9 +0,0 @@ -deadEnd -| file://:0:0:0:0 | ... = ... | -| file://:0:0:0:0 | ... = ... | -| file://:0:0:0:0 | ... = ... | -| file://:0:0:0:0 | ... = ... | -| file://:0:0:0:0 | ... = ... | -| file://:0:0:0:0 | ... = ... | -| file://:0:0:0:0 | ... = ... | -| file://:0:0:0:0 | ... = ... | diff --git a/swift/ql/test/query-tests/Security/CWE-328/CONSISTENCY/CfgConsistency.expected b/swift/ql/test/query-tests/Security/CWE-328/CONSISTENCY/CfgConsistency.expected deleted file mode 100644 index 069a6ecbc6a..00000000000 --- a/swift/ql/test/query-tests/Security/CWE-328/CONSISTENCY/CfgConsistency.expected +++ /dev/null @@ -1,2 +0,0 @@ -deadEnd -| file://:0:0:0:0 | ... = ... | diff --git a/swift/ql/test/query-tests/Security/CWE-611/CONSISTENCY/CfgConsistency.expected b/swift/ql/test/query-tests/Security/CWE-611/CONSISTENCY/CfgConsistency.expected deleted file mode 100644 index 107533d785d..00000000000 --- a/swift/ql/test/query-tests/Security/CWE-611/CONSISTENCY/CfgConsistency.expected +++ /dev/null @@ -1,5 +0,0 @@ -deadEnd -| file://:0:0:0:0 | StmtCondition | -| file://:0:0:0:0 | StmtCondition | -| file://:0:0:0:0 | hasher | -| file://:0:0:0:0 | hasher | diff --git a/swift/ql/test/query-tests/Security/CWE-757/CONSISTENCY/CfgConsistency.expected b/swift/ql/test/query-tests/Security/CWE-757/CONSISTENCY/CfgConsistency.expected deleted file mode 100644 index 853828d4f77..00000000000 --- a/swift/ql/test/query-tests/Security/CWE-757/CONSISTENCY/CfgConsistency.expected +++ /dev/null @@ -1,5 +0,0 @@ -deadEnd -| file://:0:0:0:0 | ... = ... | -| file://:0:0:0:0 | ... = ... | -| file://:0:0:0:0 | ... = ... | -| file://:0:0:0:0 | ... = ... | diff --git a/swift/ql/test/query-tests/Security/CWE-760/CONSISTENCY/CfgConsistency.expected b/swift/ql/test/query-tests/Security/CWE-760/CONSISTENCY/CfgConsistency.expected deleted file mode 100644 index 853828d4f77..00000000000 --- a/swift/ql/test/query-tests/Security/CWE-760/CONSISTENCY/CfgConsistency.expected +++ /dev/null @@ -1,5 +0,0 @@ -deadEnd -| file://:0:0:0:0 | ... = ... | -| file://:0:0:0:0 | ... = ... | -| file://:0:0:0:0 | ... = ... | -| file://:0:0:0:0 | ... = ... | diff --git a/swift/ql/test/query-tests/Security/CWE-760/ConstantSalt.expected b/swift/ql/test/query-tests/Security/CWE-760/ConstantSalt.expected index 6dd93198451..cd9fda87e19 100644 --- a/swift/ql/test/query-tests/Security/CWE-760/ConstantSalt.expected +++ b/swift/ql/test/query-tests/Security/CWE-760/ConstantSalt.expected @@ -1,26 +1,21 @@ edges -| rncryptor.swift:5:5:5:29 | [summary param] 0 in Data.init(_:) | rncryptor.swift:5:5:5:29 | [summary] to write: return (return) in Data.init(_:) | | rncryptor.swift:59:24:59:43 | call to Data.init(_:) | rncryptor.swift:63:57:63:57 | myConstantSalt1 | | rncryptor.swift:59:24:59:43 | call to Data.init(_:) | rncryptor.swift:68:106:68:106 | myConstantSalt1 | | rncryptor.swift:59:24:59:43 | call to Data.init(_:) | rncryptor.swift:71:106:71:106 | myConstantSalt1 | | rncryptor.swift:59:24:59:43 | call to Data.init(_:) | rncryptor.swift:75:127:75:127 | myConstantSalt1 | | rncryptor.swift:59:24:59:43 | call to Data.init(_:) | rncryptor.swift:78:135:78:135 | myConstantSalt1 | -| rncryptor.swift:59:29:59:29 | abcdef123456 | rncryptor.swift:5:5:5:29 | [summary param] 0 in Data.init(_:) | | rncryptor.swift:59:29:59:29 | abcdef123456 | rncryptor.swift:59:24:59:43 | call to Data.init(_:) | | rncryptor.swift:60:24:60:30 | call to Data.init(_:) | rncryptor.swift:65:55:65:55 | myConstantSalt2 | | rncryptor.swift:60:24:60:30 | call to Data.init(_:) | rncryptor.swift:69:131:69:131 | myConstantSalt2 | | rncryptor.swift:60:24:60:30 | call to Data.init(_:) | rncryptor.swift:72:131:72:131 | myConstantSalt2 | | rncryptor.swift:60:24:60:30 | call to Data.init(_:) | rncryptor.swift:76:152:76:152 | myConstantSalt2 | | rncryptor.swift:60:24:60:30 | call to Data.init(_:) | rncryptor.swift:79:160:79:160 | myConstantSalt2 | -| rncryptor.swift:60:29:60:29 | 0 | rncryptor.swift:5:5:5:29 | [summary param] 0 in Data.init(_:) | | rncryptor.swift:60:29:60:29 | 0 | rncryptor.swift:60:24:60:30 | call to Data.init(_:) | | test.swift:43:35:43:130 | [...] | test.swift:51:49:51:49 | constantSalt | | test.swift:43:35:43:130 | [...] | test.swift:56:59:56:59 | constantSalt | | test.swift:43:35:43:130 | [...] | test.swift:62:59:62:59 | constantSalt | | test.swift:43:35:43:130 | [...] | test.swift:67:53:67:53 | constantSalt | nodes -| rncryptor.swift:5:5:5:29 | [summary param] 0 in Data.init(_:) | semmle.label | [summary param] 0 in Data.init(_:) | -| rncryptor.swift:5:5:5:29 | [summary] to write: return (return) in Data.init(_:) | semmle.label | [summary] to write: return (return) in Data.init(_:) | | rncryptor.swift:59:24:59:43 | call to Data.init(_:) | semmle.label | call to Data.init(_:) | | rncryptor.swift:59:29:59:29 | abcdef123456 | semmle.label | abcdef123456 | | rncryptor.swift:60:24:60:30 | call to Data.init(_:) | semmle.label | call to Data.init(_:) | @@ -41,8 +36,6 @@ nodes | test.swift:62:59:62:59 | constantSalt | semmle.label | constantSalt | | test.swift:67:53:67:53 | constantSalt | semmle.label | constantSalt | subpaths -| rncryptor.swift:59:29:59:29 | abcdef123456 | rncryptor.swift:5:5:5:29 | [summary param] 0 in Data.init(_:) | rncryptor.swift:5:5:5:29 | [summary] to write: return (return) in Data.init(_:) | rncryptor.swift:59:24:59:43 | call to Data.init(_:) | -| rncryptor.swift:60:29:60:29 | 0 | rncryptor.swift:5:5:5:29 | [summary param] 0 in Data.init(_:) | rncryptor.swift:5:5:5:29 | [summary] to write: return (return) in Data.init(_:) | rncryptor.swift:60:24:60:30 | call to Data.init(_:) | #select | rncryptor.swift:63:57:63:57 | myConstantSalt1 | rncryptor.swift:59:29:59:29 | abcdef123456 | rncryptor.swift:63:57:63:57 | myConstantSalt1 | The value 'abcdef123456' is used as a constant salt, which is insecure for hashing passwords. | | rncryptor.swift:65:55:65:55 | myConstantSalt2 | rncryptor.swift:60:29:60:29 | 0 | rncryptor.swift:65:55:65:55 | myConstantSalt2 | The value '0' is used as a constant salt, which is insecure for hashing passwords. | diff --git a/swift/ql/test/query-tests/Security/CWE-916/CONSISTENCY/CfgConsistency.expected b/swift/ql/test/query-tests/Security/CWE-916/CONSISTENCY/CfgConsistency.expected deleted file mode 100644 index 853828d4f77..00000000000 --- a/swift/ql/test/query-tests/Security/CWE-916/CONSISTENCY/CfgConsistency.expected +++ /dev/null @@ -1,5 +0,0 @@ -deadEnd -| file://:0:0:0:0 | ... = ... | -| file://:0:0:0:0 | ... = ... | -| file://:0:0:0:0 | ... = ... | -| file://:0:0:0:0 | ... = ... | diff --git a/swift/rules.bzl b/swift/rules.bzl index ba9e4b0e8bf..6ed085de63c 100644 --- a/swift/rules.bzl +++ b/swift/rules.bzl @@ -10,11 +10,13 @@ def _wrap_cc(rule, kwargs): # temporary, before we do universal merging "-universal_binaries", ]) - _add_args(kwargs, "target_compatible_with", select({ - "@platforms//os:linux": [], - "@platforms//os:macos": [], - "//conditions:default": ["@platforms//:incompatible"], - })) + if "target_compatible_with" not in kwargs: + # Restrict to Linux or macOS by default, but allow overriding + _add_args(kwargs, "target_compatible_with", select({ + "@platforms//os:linux": [], + "@platforms//os:macos": [], + "//conditions:default": ["@platforms//:incompatible"], + })) rule(**kwargs) def swift_cc_binary(**kwargs): diff --git a/swift/third_party/load.bzl b/swift/third_party/load.bzl index df21ba1a894..9cefc77948d 100644 --- a/swift/third_party/load.bzl +++ b/swift/third_party/load.bzl @@ -1,11 +1,14 @@ load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive") load("@bazel_tools//tools/build_defs/repo:utils.bzl", "maybe") -_swift_prebuilt_version = "swift-5.8.1-RELEASE.208" +# TODO: remove `remove-result-of.patch` once we update to a Swift version containing +# https://github.com/apple/swift/commit/2ed2cea2 +# (probably when updating to 5.9) +_swift_prebuilt_version = "swift-5.8.1-RELEASE.212" _swift_sha_map = { - "Linux-X64": "1d93286d6219e5c5746938ab9287d90efea98039f022cb1433296ccbc1684bc0", - "macOS-ARM64": "a29ce5143cb2c68190e337a35ebb163e961a58b9d8826fe7f8daf4d8381ee75d", - "macOS-X64": "a7e63ea732750c783142083df20a34c8d337b9b9ba210fa6a9e5ada7b7880189", + "Linux-X64": "3e902cc9dbf02129f6bcac84902524a235df0e36d30f3ac54e642b64d3f95a2b", + "macOS-ARM64": "4d93f326bd8a41c89bcf593676407fab2dd84b665f6bfb7ab667a9673084bcda", + "macOS-X64": "988cd193a0590abd282d8d8f3ec2489583d3d2b34162a4e91208fb91e5fb5981", } _swift_arch_map = { @@ -39,6 +42,15 @@ def load_dependencies(workspace_name): ), build_file = _build(workspace_name, "swift-llvm-support"), sha256 = sha256, + patch_args = ["-p1"], + patches = [ + "@%s//swift/third_party/swift-llvm-support:patches/%s.patch" % (workspace_name, patch_name) + for patch_name in ( + "remove-result-of", + "remove-redundant-operators", + "add-constructor-to-Compilation", + ) + ], ) _github_archive( diff --git a/swift/third_party/swift-llvm-support/patches/add-constructor-to-Compilation.patch b/swift/third_party/swift-llvm-support/patches/add-constructor-to-Compilation.patch new file mode 100644 index 00000000000..a663e6db42a --- /dev/null +++ b/swift/third_party/swift-llvm-support/patches/add-constructor-to-Compilation.patch @@ -0,0 +1,17 @@ +In C++20 aggregate initialization on classes with user-declared constructor is not +available, while in C++11-C++17 it was available if they were defaulted or deleted. + +diff --git a/include/swift/Driver/Compilation.h b/include/swift/Driver/Compilation.h +index ee76f92e0de..bd987157593 100644 +--- a/include/swift/Driver/Compilation.h ++++ b/include/swift/Driver/Compilation.h +@@ -89,6 +89,9 @@ public: + /// This data is used for cross-module module dependencies. + fine_grained_dependencies::ModuleDepGraph depGraph; + ++ Result(bool hadAbnormalExit = false, int exitCode = 0, fine_grained_dependencies::ModuleDepGraph depGraph = {}) ++ : hadAbnormalExit{hadAbnormalExit}, exitCode{exitCode}, depGraph{std::move(depGraph)} {} ++ + Result(const Result &) = delete; + Result &operator=(const Result &) = delete; + diff --git a/swift/third_party/swift-llvm-support/patches/remove-redundant-operators.patch b/swift/third_party/swift-llvm-support/patches/remove-redundant-operators.patch new file mode 100644 index 00000000000..03f1c0ff08c --- /dev/null +++ b/swift/third_party/swift-llvm-support/patches/remove-redundant-operators.patch @@ -0,0 +1,24 @@ +In C++20 the removed operators are available via operator rewriting and +implicit constructors, providing them leads to ambiguity. + +diff --git a/include/swift/SIL/SILValue.h b/include/swift/SIL/SILValue.h +index 378ca039c7e..37c119c50c1 100644 +--- a/include/swift/SIL/SILValue.h ++++ b/include/swift/SIL/SILValue.h +@@ -271,16 +271,6 @@ struct ValueOwnershipKind { + + explicit operator bool() const { return value != OwnershipKind::Any; } + +- bool operator==(ValueOwnershipKind other) const { +- return value == other.value; +- } +- bool operator!=(ValueOwnershipKind other) const { +- return !(value == other.value); +- } +- +- bool operator==(innerty other) const { return value == other; } +- bool operator!=(innerty other) const { return !(value == other); } +- + /// We merge by moving down the lattice. + ValueOwnershipKind merge(ValueOwnershipKind rhs) const { + return value.meet(rhs.value); diff --git a/swift/third_party/swift-llvm-support/patches/remove-result-of.patch b/swift/third_party/swift-llvm-support/patches/remove-result-of.patch new file mode 100644 index 00000000000..ab3f2155b67 --- /dev/null +++ b/swift/third_party/swift-llvm-support/patches/remove-result-of.patch @@ -0,0 +1,30 @@ +`std::result_of` was removed in C++20, but is still used in the Swift headers. We can't +remove it from there before prebuilding, as that is still done with C++14, but we can +replace it with `std::invoke_result` for compiling the extractor. + +diff --git a/include/swift/Basic/RelativePointer.h b/include/swift/Basic/RelativePointer.h +index 73f91262afa..bdaa304c804 100644 +--- a/include/swift/Basic/RelativePointer.h ++++ b/include/swift/Basic/RelativePointer.h +@@ -551,7 +551,7 @@ public: + } + + template +- typename std::result_of::type operator()(ArgTy... arg) const { ++ typename std::invoke_result::type operator()(ArgTy... arg) const { + #if SWIFT_PTRAUTH + void *ptr = this->super::getWithoutCast(); + return reinterpret_cast(ptrauth_sign_unauthenticated( +diff --git a/include/swift/Basic/STLExtras.h b/include/swift/Basic/STLExtras.h +index 7fa3d0c8890..6bc891a9b63 100644 +--- a/include/swift/Basic/STLExtras.h ++++ b/include/swift/Basic/STLExtras.h +@@ -405,7 +405,7 @@ class OptionalTransformIterator { + typename std::iterator_traits::reference; + + using ResultReference = +- typename std::result_of::type; ++ typename std::invoke_result::type; + + public: + /// Used to indicate when the current iterator has already been diff --git a/swift/tools/BUILD.bazel b/swift/tools/BUILD.bazel index 8a3496a2700..ee834e543cc 100644 --- a/swift/tools/BUILD.bazel +++ b/swift/tools/BUILD.bazel @@ -19,8 +19,9 @@ sh_binary( pkg_files( name = "scripts", srcs = [ - ":identify-environment", + "autobuild.cmd", ":autobuild", + ":identify-environment", ":qltest", ], attributes = pkg_attributes(mode = "0755"), diff --git a/swift/tools/autobuild.cmd b/swift/tools/autobuild.cmd new file mode 100644 index 00000000000..fc90f9f2f05 --- /dev/null +++ b/swift/tools/autobuild.cmd @@ -0,0 +1 @@ +"%CODEQL_EXTRACTOR_SWIFT_ROOT%/tools/%CODEQL_PLATFORM%/autobuilder-incompatible-os.exe" diff --git a/swift/tools/autobuild.sh b/swift/tools/autobuild.sh index 9be42363be6..3e25e800ced 100755 --- a/swift/tools/autobuild.sh +++ b/swift/tools/autobuild.sh @@ -3,5 +3,5 @@ if [[ "$OSTYPE" == "darwin"* ]]; then exec "${CODEQL_EXTRACTOR_SWIFT_ROOT}/tools/${CODEQL_PLATFORM}/xcode-autobuilder" else - exec "${CODEQL_EXTRACTOR_SWIFT_ROOT}/tools/${CODEQL_PLATFORM}/incompatible-os" + exec "${CODEQL_EXTRACTOR_SWIFT_ROOT}/tools/${CODEQL_PLATFORM}/autobuilder-incompatible-os" fi diff --git a/swift/tools/autobuilder-diagnostics/BUILD.bazel b/swift/tools/autobuilder-diagnostics/BUILD.bazel deleted file mode 100644 index 32d6ce703e9..00000000000 --- a/swift/tools/autobuilder-diagnostics/BUILD.bazel +++ /dev/null @@ -1,10 +0,0 @@ -load("//swift:rules.bzl", "swift_cc_binary") - -swift_cc_binary( - name = "incompatible-os", - srcs = ["IncompatibleOs.cpp"], - visibility = ["//swift:__subpackages__"], - deps = [ - "//swift/logging", - ], -) diff --git a/swift/tools/autobuilder-diagnostics/IncompatibleOs.cpp b/swift/tools/diagnostics/AutobuilderIncompatibleOs.cpp similarity index 100% rename from swift/tools/autobuilder-diagnostics/IncompatibleOs.cpp rename to swift/tools/diagnostics/AutobuilderIncompatibleOs.cpp diff --git a/swift/tools/diagnostics/BUILD.bazel b/swift/tools/diagnostics/BUILD.bazel new file mode 100644 index 00000000000..e02d6aed13b --- /dev/null +++ b/swift/tools/diagnostics/BUILD.bazel @@ -0,0 +1,12 @@ +load("//swift:rules.bzl", "swift_cc_binary") + +swift_cc_binary( + name = "autobuilder-incompatible-os", + srcs = ["AutobuilderIncompatibleOs.cpp"], + # No restrictions (Windows allowed) + target_compatible_with = [], + visibility = ["//swift:__subpackages__"], + deps = [ + "//swift/logging", + ], +)