diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index c97b2d96286..5324ac8f301 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -14,14 +14,16 @@ If you have an idea for a query that you would like to share with other CodeQL u 1. **Directory structure** - There are six language-specific query directories in this repository: + There are eight language-specific query directories in this repository: * C/C++: `cpp/ql/src` * C#: `csharp/ql/src` - * Java: `java/ql/src` + * Go: `go/ql/src` + * Java/Kotlin: `java/ql/src` * JavaScript: `javascript/ql/src` * Python: `python/ql/src` * Ruby: `ruby/ql/src` + * Swift: `swift/ql/src` Each language-specific directory contains further subdirectories that group queries based on their `@tags` or purpose. - Experimental queries and libraries are stored in the `experimental` subdirectory within each language-specific directory in the [CodeQL repository](https://github.com/github/codeql). For example, experimental Java queries and libraries are stored in `java/ql/src/experimental` and any corresponding tests in `java/ql/test/experimental`. diff --git a/cpp/ql/lib/CHANGELOG.md b/cpp/ql/lib/CHANGELOG.md index e990e830005..8f6d36edc61 100644 --- a/cpp/ql/lib/CHANGELOG.md +++ b/cpp/ql/lib/CHANGELOG.md @@ -1,3 +1,17 @@ +## 0.8.0 + +### New Features + +* The `ProductFlow::StateConfigSig` signature now includes default predicates for `isBarrier1`, `isBarrier2`, `isAdditionalFlowStep1`, and `isAdditionalFlowStep1`. Hence, it is no longer needed to provide `none()` implementations of these predicates if they are not needed. + +### Minor Analysis Improvements + +* Deleted the deprecated `getURL` predicate from the `Container`, `Folder`, and `File` classes. Use the `getLocation` predicate instead. + +## 0.7.4 + +No user-facing changes. + ## 0.7.3 ### Minor Analysis Improvements diff --git a/cpp/ql/lib/change-notes/2023-06-19-delete-container-url.md b/cpp/ql/lib/change-notes/2023-06-19-delete-container-url.md deleted file mode 100644 index 9fef359a1e8..00000000000 --- a/cpp/ql/lib/change-notes/2023-06-19-delete-container-url.md +++ /dev/null @@ -1,4 +0,0 @@ ---- -category: minorAnalysis ---- -* Deleted the deprecated `getURL` predicate from the `Container`, `Folder`, and `File` classes. Use the `getLocation` predicate instead. \ No newline at end of file diff --git a/cpp/ql/lib/change-notes/2023-07-07-irguards-compares-pointers.md b/cpp/ql/lib/change-notes/2023-07-07-irguards-compares-pointers.md new file mode 100644 index 00000000000..92ea844d853 --- /dev/null +++ b/cpp/ql/lib/change-notes/2023-07-07-irguards-compares-pointers.md @@ -0,0 +1,4 @@ +--- +category: minorAnalysis +--- +* The `IRGuards` library has improved handling of pointer addition and subtraction operations. diff --git a/cpp/ql/lib/change-notes/2023-07-12-default-stateconfigsig-predicates.md b/cpp/ql/lib/change-notes/2023-07-12-default-stateconfigsig-predicates.md new file mode 100644 index 00000000000..c9aba58603a --- /dev/null +++ b/cpp/ql/lib/change-notes/2023-07-12-default-stateconfigsig-predicates.md @@ -0,0 +1,6 @@ +--- +category: feature +--- +* The `DataFlow::StateConfigSig` signature module has gained default implementations for `isBarrier/2` and `isAdditionalFlowStep/4`. + Hence it is no longer needed to provide `none()` implementations of these predicates if they are not needed. + diff --git a/cpp/ql/lib/change-notes/released/0.7.4.md b/cpp/ql/lib/change-notes/released/0.7.4.md new file mode 100644 index 00000000000..1b33df9cb1e --- /dev/null +++ b/cpp/ql/lib/change-notes/released/0.7.4.md @@ -0,0 +1,3 @@ +## 0.7.4 + +No user-facing changes. diff --git a/cpp/ql/lib/change-notes/released/0.8.0.md b/cpp/ql/lib/change-notes/released/0.8.0.md new file mode 100644 index 00000000000..573e0cb34b6 --- /dev/null +++ b/cpp/ql/lib/change-notes/released/0.8.0.md @@ -0,0 +1,9 @@ +## 0.8.0 + +### New Features + +* The `ProductFlow::StateConfigSig` signature now includes default predicates for `isBarrier1`, `isBarrier2`, `isAdditionalFlowStep1`, and `isAdditionalFlowStep1`. Hence, it is no longer needed to provide `none()` implementations of these predicates if they are not needed. + +### Minor Analysis Improvements + +* Deleted the deprecated `getURL` predicate from the `Container`, `Folder`, and `File` classes. Use the `getLocation` predicate instead. diff --git a/cpp/ql/lib/codeql-pack.release.yml b/cpp/ql/lib/codeql-pack.release.yml index a4ea9c8de17..37eab3197dc 100644 --- a/cpp/ql/lib/codeql-pack.release.yml +++ b/cpp/ql/lib/codeql-pack.release.yml @@ -1,2 +1,2 @@ --- -lastReleaseVersion: 0.7.3 +lastReleaseVersion: 0.8.0 diff --git a/cpp/ql/lib/qlpack.yml b/cpp/ql/lib/qlpack.yml index 0065372f811..a5a413b7b8c 100644 --- a/cpp/ql/lib/qlpack.yml +++ b/cpp/ql/lib/qlpack.yml @@ -1,5 +1,5 @@ name: codeql/cpp-all -version: 0.7.4-dev +version: 0.8.1-dev groups: cpp dbscheme: semmlecode.cpp.dbscheme extractor: cpp diff --git a/cpp/ql/lib/semmle/code/cpp/controlflow/IRGuards.qll b/cpp/ql/lib/semmle/code/cpp/controlflow/IRGuards.qll index d12eaa4e238..49b7e0c8b6d 100644 --- a/cpp/ql/lib/semmle/code/cpp/controlflow/IRGuards.qll +++ b/cpp/ql/lib/semmle/code/cpp/controlflow/IRGuards.qll @@ -627,6 +627,20 @@ private predicate sub_lt( x = int_value(rhs.getRight()) and k = c - x ) + or + exists(PointerSubInstruction lhs, int c, int x | + compares_lt(cmp, lhs.getAUse(), right, c, isLt, testIsTrue) and + left = lhs.getLeftOperand() and + x = int_value(lhs.getRight()) and + k = c + x + ) + or + exists(PointerSubInstruction rhs, int c, int x | + compares_lt(cmp, left, rhs.getAUse(), c, isLt, testIsTrue) and + right = rhs.getLeftOperand() and + x = int_value(rhs.getRight()) and + k = c - x + ) } // left + x < right + c => left < right + (c-x) @@ -653,6 +667,26 @@ private predicate add_lt( ) and k = c + x ) + or + exists(PointerAddInstruction lhs, int c, int x | + compares_lt(cmp, lhs.getAUse(), right, c, isLt, testIsTrue) and + ( + left = lhs.getLeftOperand() and x = int_value(lhs.getRight()) + or + left = lhs.getRightOperand() and x = int_value(lhs.getLeft()) + ) and + k = c - x + ) + or + exists(PointerAddInstruction rhs, int c, int x | + compares_lt(cmp, left, rhs.getAUse(), c, isLt, testIsTrue) and + ( + right = rhs.getLeftOperand() and x = int_value(rhs.getRight()) + or + right = rhs.getRightOperand() and x = int_value(rhs.getLeft()) + ) and + k = c + x + ) } // left - x == right + c => left == right + (c+x) @@ -673,6 +707,20 @@ private predicate sub_eq( x = int_value(rhs.getRight()) and k = c - x ) + or + exists(PointerSubInstruction lhs, int c, int x | + compares_eq(cmp, lhs.getAUse(), right, c, areEqual, testIsTrue) and + left = lhs.getLeftOperand() and + x = int_value(lhs.getRight()) and + k = c + x + ) + or + exists(PointerSubInstruction rhs, int c, int x | + compares_eq(cmp, left, rhs.getAUse(), c, areEqual, testIsTrue) and + right = rhs.getLeftOperand() and + x = int_value(rhs.getRight()) and + k = c - x + ) } // left + x == right + c => left == right + (c-x) @@ -699,6 +747,26 @@ private predicate add_eq( ) and k = c + x ) + or + exists(PointerAddInstruction lhs, int c, int x | + compares_eq(cmp, lhs.getAUse(), right, c, areEqual, testIsTrue) and + ( + left = lhs.getLeftOperand() and x = int_value(lhs.getRight()) + or + left = lhs.getRightOperand() and x = int_value(lhs.getLeft()) + ) and + k = c - x + ) + or + exists(PointerAddInstruction rhs, int c, int x | + compares_eq(cmp, left, rhs.getAUse(), c, areEqual, testIsTrue) and + ( + right = rhs.getLeftOperand() and x = int_value(rhs.getRight()) + or + right = rhs.getRightOperand() and x = int_value(rhs.getLeft()) + ) and + k = c + x + ) } /** The int value of integer constant expression. */ diff --git a/cpp/ql/lib/semmle/code/cpp/dataflow/internal/DataFlow.qll b/cpp/ql/lib/semmle/code/cpp/dataflow/internal/DataFlow.qll index f34554d6eea..47329d133a4 100644 --- a/cpp/ql/lib/semmle/code/cpp/dataflow/internal/DataFlow.qll +++ b/cpp/ql/lib/semmle/code/cpp/dataflow/internal/DataFlow.qll @@ -114,7 +114,7 @@ signature module StateConfigSig { * Holds if data flow through `node` is prohibited when the flow state is * `state`. */ - predicate isBarrier(Node node, FlowState state); + default predicate isBarrier(Node node, FlowState state) { none() } /** Holds if data flow into `node` is prohibited. */ default predicate isBarrierIn(Node node) { none() } @@ -131,7 +131,9 @@ signature module StateConfigSig { * Holds if data may flow from `node1` to `node2` in addition to the normal data-flow steps. * This step is only applicable in `state1` and updates the flow state to `state2`. */ - predicate isAdditionalFlowStep(Node node1, FlowState state1, Node node2, FlowState state2); + default predicate isAdditionalFlowStep(Node node1, FlowState state1, Node node2, FlowState state2) { + none() + } /** * Holds if an arbitrary number of implicit read steps of content `c` may be diff --git a/cpp/ql/lib/semmle/code/cpp/dataflow/internal/DataFlowImpl.qll b/cpp/ql/lib/semmle/code/cpp/dataflow/internal/DataFlowImpl.qll index 284fff191ae..fe8633e9218 100644 --- a/cpp/ql/lib/semmle/code/cpp/dataflow/internal/DataFlowImpl.qll +++ b/cpp/ql/lib/semmle/code/cpp/dataflow/internal/DataFlowImpl.qll @@ -254,6 +254,11 @@ module Impl { not fullBarrier(node2) } + pragma[nomagic] + private predicate isUnreachableInCall1(NodeEx n, LocalCallContextSpecificCall cc) { + isUnreachableInCallCached(n.asNode(), cc.getCall()) + } + /** * Holds if data can flow in one local step from `node1` to `node2`. */ @@ -460,7 +465,6 @@ module Impl { * The Boolean `cc` records whether the node is reached through an * argument in a call. */ - pragma[assume_small_delta] private predicate fwdFlow(NodeEx node, Cc cc) { sourceNode(node, _) and if hasSourceCallCtx() then cc = true else cc = false @@ -570,7 +574,6 @@ module Impl { /** * Holds if `c` is the target of a store in the flow covered by `fwdFlow`. */ - pragma[assume_small_delta] pragma[nomagic] private predicate fwdFlowConsCand(Content c) { exists(NodeEx mid, NodeEx node | @@ -1216,7 +1219,6 @@ module Impl { fwdFlow1(_, _, _, _, _, _, t0, t, ap, _) and t0 != t } - pragma[assume_small_delta] pragma[nomagic] private predicate fwdFlow0( NodeEx node, FlowState state, Cc cc, ParamNodeOption summaryCtx, TypOption argT, @@ -2111,7 +2113,7 @@ module Impl { NodeEx node1, FlowState state, NodeEx node2, boolean preservesValue, DataFlowType t, LocalCallContext cc ) { - not isUnreachableInCallCached(node2.asNode(), cc.(LocalCallContextSpecificCall).getCall()) and + not isUnreachableInCall1(node2, cc) and ( localFlowEntry(node1, pragma[only_bind_into](state)) and ( @@ -2126,7 +2128,7 @@ module Impl { ) and node1 != node2 and cc.relevantFor(node1.getEnclosingCallable()) and - not isUnreachableInCallCached(node1.asNode(), cc.(LocalCallContextSpecificCall).getCall()) + not isUnreachableInCall1(node1, cc) or exists(NodeEx mid | localFlowStepPlus(node1, pragma[only_bind_into](state), mid, preservesValue, t, cc) and @@ -2163,10 +2165,8 @@ module Impl { preservesValue = false and t = node2.getDataFlowType() and callContext.relevantFor(node1.getEnclosingCallable()) and - not exists(DataFlowCall call | call = callContext.(LocalCallContextSpecificCall).getCall() | - isUnreachableInCallCached(node1.asNode(), call) or - isUnreachableInCallCached(node2.asNode(), call) - ) + not isUnreachableInCall1(node1, callContext) and + not isUnreachableInCall1(node2, callContext) } } @@ -2706,7 +2706,7 @@ module Impl { ParamNodeEx getParamNode() { result = p } - override string toString() { result = p + ": " + ap } + override string toString() { result = p + concat(" : " + ppReprType(t)) + " " + ap } predicate hasLocationInfo( string filepath, int startline, int startcolumn, int endline, int endcolumn @@ -2758,12 +2758,21 @@ module Impl { ) } + private predicate forceUnfold(AccessPathApprox apa) { + forceHighPrecision(apa.getHead()) + or + exists(Content c2 | + apa = TConsCons(_, _, c2, _) and + forceHighPrecision(c2) + ) + } + /** * Holds with `unfold = false` if a precise head-tail representation of `apa` is * expected to be expensive. Holds with `unfold = true` otherwise. */ private predicate evalUnfold(AccessPathApprox apa, boolean unfold) { - if forceHighPrecision(apa.getHead()) + if forceUnfold(apa) then unfold = true else exists(int aps, int nodes, int apLimit, int tupleLimit | @@ -2777,7 +2786,6 @@ module Impl { /** * Gets the number of `AccessPath`s that correspond to `apa`. */ - pragma[assume_small_delta] private int countAps(AccessPathApprox apa) { evalUnfold(apa, false) and result = 1 and @@ -2796,7 +2804,6 @@ module Impl { * that it is expanded to a precise head-tail representation. */ language[monotonicAggregates] - pragma[assume_small_delta] private int countPotentialAps(AccessPathApprox apa) { apa instanceof AccessPathApproxNil and result = 1 or @@ -2833,7 +2840,6 @@ module Impl { } private newtype TPathNode = - pragma[assume_small_delta] TPathNodeMid( NodeEx node, FlowState state, CallContext cc, SummaryCtx sc, DataFlowType t, AccessPath ap ) { @@ -2918,7 +2924,6 @@ module Impl { override AccessPathFrontHead getFront() { result = TFrontHead(head_) } - pragma[assume_small_delta] override AccessPathApproxCons getApprox() { result = TConsNil(head_, t) and tail_ = TAccessPathNil() or @@ -2927,7 +2932,6 @@ module Impl { result = TCons1(head_, this.length()) } - pragma[assume_small_delta] override int length() { result = 1 + tail_.length() } private string toStringImpl(boolean needsSuffix) { @@ -3097,6 +3101,12 @@ module Impl { result = " <" + this.(PathNodeMid).getCallContext().toString() + ">" } + private string ppSummaryCtx() { + this instanceof PathNodeSink and result = "" + or + result = " <" + this.(PathNodeMid).getSummaryCtx().toString() + ">" + } + /** Gets a textual representation of this element. */ string toString() { result = this.getNodeEx().toString() + this.ppType() + this.ppAp() } @@ -3105,7 +3115,9 @@ module Impl { * representation of the call context. */ string toStringWithContext() { - result = this.getNodeEx().toString() + this.ppType() + this.ppAp() + this.ppCtx() + result = + this.getNodeEx().toString() + this.ppType() + this.ppAp() + this.ppCtx() + + this.ppSummaryCtx() } /** @@ -3379,7 +3391,6 @@ module Impl { * Holds if data may flow from `mid` to `node`. The last step in or out of * a callable is recorded by `cc`. */ - pragma[assume_small_delta] pragma[nomagic] private predicate pathStep0( PathNodeMid mid, NodeEx node, FlowState state, CallContext cc, SummaryCtx sc, DataFlowType t, @@ -3592,7 +3603,6 @@ module Impl { ) } - pragma[assume_small_delta] pragma[nomagic] private predicate pathThroughCallable0( DataFlowCall call, PathNodeMid mid, ReturnKindExt kind, FlowState state, CallContext cc, diff --git a/cpp/ql/lib/semmle/code/cpp/dataflow/internal/DataFlowImplCommon.qll b/cpp/ql/lib/semmle/code/cpp/dataflow/internal/DataFlowImplCommon.qll index 0d4c033c95d..aff14e7b44d 100644 --- a/cpp/ql/lib/semmle/code/cpp/dataflow/internal/DataFlowImplCommon.qll +++ b/cpp/ql/lib/semmle/code/cpp/dataflow/internal/DataFlowImplCommon.qll @@ -187,7 +187,6 @@ private module LambdaFlow { else any() } - pragma[assume_small_delta] pragma[nomagic] predicate revLambdaFlow0( DataFlowCall lambdaCall, LambdaCallKind kind, Node node, DataFlowType t, boolean toReturn, @@ -274,7 +273,6 @@ private module LambdaFlow { ) } - pragma[assume_small_delta] pragma[nomagic] predicate revLambdaFlowOut( DataFlowCall lambdaCall, LambdaCallKind kind, TReturnPositionSimple pos, DataFlowType t, diff --git a/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/DataFlow.qll b/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/DataFlow.qll index f34554d6eea..47329d133a4 100644 --- a/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/DataFlow.qll +++ b/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/DataFlow.qll @@ -114,7 +114,7 @@ signature module StateConfigSig { * Holds if data flow through `node` is prohibited when the flow state is * `state`. */ - predicate isBarrier(Node node, FlowState state); + default predicate isBarrier(Node node, FlowState state) { none() } /** Holds if data flow into `node` is prohibited. */ default predicate isBarrierIn(Node node) { none() } @@ -131,7 +131,9 @@ signature module StateConfigSig { * Holds if data may flow from `node1` to `node2` in addition to the normal data-flow steps. * This step is only applicable in `state1` and updates the flow state to `state2`. */ - predicate isAdditionalFlowStep(Node node1, FlowState state1, Node node2, FlowState state2); + default predicate isAdditionalFlowStep(Node node1, FlowState state1, Node node2, FlowState state2) { + none() + } /** * Holds if an arbitrary number of implicit read steps of content `c` may be 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 284fff191ae..fe8633e9218 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 @@ -254,6 +254,11 @@ module Impl { not fullBarrier(node2) } + pragma[nomagic] + private predicate isUnreachableInCall1(NodeEx n, LocalCallContextSpecificCall cc) { + isUnreachableInCallCached(n.asNode(), cc.getCall()) + } + /** * Holds if data can flow in one local step from `node1` to `node2`. */ @@ -460,7 +465,6 @@ module Impl { * The Boolean `cc` records whether the node is reached through an * argument in a call. */ - pragma[assume_small_delta] private predicate fwdFlow(NodeEx node, Cc cc) { sourceNode(node, _) and if hasSourceCallCtx() then cc = true else cc = false @@ -570,7 +574,6 @@ module Impl { /** * Holds if `c` is the target of a store in the flow covered by `fwdFlow`. */ - pragma[assume_small_delta] pragma[nomagic] private predicate fwdFlowConsCand(Content c) { exists(NodeEx mid, NodeEx node | @@ -1216,7 +1219,6 @@ module Impl { fwdFlow1(_, _, _, _, _, _, t0, t, ap, _) and t0 != t } - pragma[assume_small_delta] pragma[nomagic] private predicate fwdFlow0( NodeEx node, FlowState state, Cc cc, ParamNodeOption summaryCtx, TypOption argT, @@ -2111,7 +2113,7 @@ module Impl { NodeEx node1, FlowState state, NodeEx node2, boolean preservesValue, DataFlowType t, LocalCallContext cc ) { - not isUnreachableInCallCached(node2.asNode(), cc.(LocalCallContextSpecificCall).getCall()) and + not isUnreachableInCall1(node2, cc) and ( localFlowEntry(node1, pragma[only_bind_into](state)) and ( @@ -2126,7 +2128,7 @@ module Impl { ) and node1 != node2 and cc.relevantFor(node1.getEnclosingCallable()) and - not isUnreachableInCallCached(node1.asNode(), cc.(LocalCallContextSpecificCall).getCall()) + not isUnreachableInCall1(node1, cc) or exists(NodeEx mid | localFlowStepPlus(node1, pragma[only_bind_into](state), mid, preservesValue, t, cc) and @@ -2163,10 +2165,8 @@ module Impl { preservesValue = false and t = node2.getDataFlowType() and callContext.relevantFor(node1.getEnclosingCallable()) and - not exists(DataFlowCall call | call = callContext.(LocalCallContextSpecificCall).getCall() | - isUnreachableInCallCached(node1.asNode(), call) or - isUnreachableInCallCached(node2.asNode(), call) - ) + not isUnreachableInCall1(node1, callContext) and + not isUnreachableInCall1(node2, callContext) } } @@ -2706,7 +2706,7 @@ module Impl { ParamNodeEx getParamNode() { result = p } - override string toString() { result = p + ": " + ap } + override string toString() { result = p + concat(" : " + ppReprType(t)) + " " + ap } predicate hasLocationInfo( string filepath, int startline, int startcolumn, int endline, int endcolumn @@ -2758,12 +2758,21 @@ module Impl { ) } + private predicate forceUnfold(AccessPathApprox apa) { + forceHighPrecision(apa.getHead()) + or + exists(Content c2 | + apa = TConsCons(_, _, c2, _) and + forceHighPrecision(c2) + ) + } + /** * Holds with `unfold = false` if a precise head-tail representation of `apa` is * expected to be expensive. Holds with `unfold = true` otherwise. */ private predicate evalUnfold(AccessPathApprox apa, boolean unfold) { - if forceHighPrecision(apa.getHead()) + if forceUnfold(apa) then unfold = true else exists(int aps, int nodes, int apLimit, int tupleLimit | @@ -2777,7 +2786,6 @@ module Impl { /** * Gets the number of `AccessPath`s that correspond to `apa`. */ - pragma[assume_small_delta] private int countAps(AccessPathApprox apa) { evalUnfold(apa, false) and result = 1 and @@ -2796,7 +2804,6 @@ module Impl { * that it is expanded to a precise head-tail representation. */ language[monotonicAggregates] - pragma[assume_small_delta] private int countPotentialAps(AccessPathApprox apa) { apa instanceof AccessPathApproxNil and result = 1 or @@ -2833,7 +2840,6 @@ module Impl { } private newtype TPathNode = - pragma[assume_small_delta] TPathNodeMid( NodeEx node, FlowState state, CallContext cc, SummaryCtx sc, DataFlowType t, AccessPath ap ) { @@ -2918,7 +2924,6 @@ module Impl { override AccessPathFrontHead getFront() { result = TFrontHead(head_) } - pragma[assume_small_delta] override AccessPathApproxCons getApprox() { result = TConsNil(head_, t) and tail_ = TAccessPathNil() or @@ -2927,7 +2932,6 @@ module Impl { result = TCons1(head_, this.length()) } - pragma[assume_small_delta] override int length() { result = 1 + tail_.length() } private string toStringImpl(boolean needsSuffix) { @@ -3097,6 +3101,12 @@ module Impl { result = " <" + this.(PathNodeMid).getCallContext().toString() + ">" } + private string ppSummaryCtx() { + this instanceof PathNodeSink and result = "" + or + result = " <" + this.(PathNodeMid).getSummaryCtx().toString() + ">" + } + /** Gets a textual representation of this element. */ string toString() { result = this.getNodeEx().toString() + this.ppType() + this.ppAp() } @@ -3105,7 +3115,9 @@ module Impl { * representation of the call context. */ string toStringWithContext() { - result = this.getNodeEx().toString() + this.ppType() + this.ppAp() + this.ppCtx() + result = + this.getNodeEx().toString() + this.ppType() + this.ppAp() + this.ppCtx() + + this.ppSummaryCtx() } /** @@ -3379,7 +3391,6 @@ module Impl { * Holds if data may flow from `mid` to `node`. The last step in or out of * a callable is recorded by `cc`. */ - pragma[assume_small_delta] pragma[nomagic] private predicate pathStep0( PathNodeMid mid, NodeEx node, FlowState state, CallContext cc, SummaryCtx sc, DataFlowType t, @@ -3592,7 +3603,6 @@ module Impl { ) } - pragma[assume_small_delta] pragma[nomagic] private predicate pathThroughCallable0( DataFlowCall call, PathNodeMid mid, ReturnKindExt kind, FlowState state, CallContext cc, diff --git a/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/DataFlowImplCommon.qll b/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/DataFlowImplCommon.qll index 0d4c033c95d..aff14e7b44d 100644 --- a/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/DataFlowImplCommon.qll +++ b/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/DataFlowImplCommon.qll @@ -187,7 +187,6 @@ private module LambdaFlow { else any() } - pragma[assume_small_delta] pragma[nomagic] predicate revLambdaFlow0( DataFlowCall lambdaCall, LambdaCallKind kind, Node node, DataFlowType t, boolean toReturn, @@ -274,7 +273,6 @@ private module LambdaFlow { ) } - pragma[assume_small_delta] pragma[nomagic] predicate revLambdaFlowOut( DataFlowCall lambdaCall, LambdaCallKind kind, TReturnPositionSimple pos, DataFlowType t, diff --git a/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/ProductFlow.qll b/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/ProductFlow.qll index cb06245c568..ee34b9932b4 100644 --- a/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/ProductFlow.qll +++ b/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/ProductFlow.qll @@ -192,13 +192,13 @@ module ProductFlow { * Holds if data flow through `node` is prohibited through the first projection of the product * dataflow graph when the flow state is `state`. */ - predicate isBarrier1(DataFlow::Node node, FlowState1 state); + default predicate isBarrier1(DataFlow::Node node, FlowState1 state) { none() } /** * Holds if data flow through `node` is prohibited through the second projection of the product * dataflow graph when the flow state is `state`. */ - predicate isBarrier2(DataFlow::Node node, FlowState2 state); + default predicate isBarrier2(DataFlow::Node node, FlowState2 state) { none() } /** * Holds if data flow through `node` is prohibited through the first projection of the product @@ -237,9 +237,11 @@ module ProductFlow { * * This step is only applicable in `state1` and updates the flow state to `state2`. */ - predicate isAdditionalFlowStep1( + default predicate isAdditionalFlowStep1( DataFlow::Node node1, FlowState1 state1, DataFlow::Node node2, FlowState1 state2 - ); + ) { + none() + } /** * Holds if data may flow from `node1` to `node2` in addition to the normal data-flow steps in @@ -253,9 +255,11 @@ module ProductFlow { * * This step is only applicable in `state1` and updates the flow state to `state2`. */ - predicate isAdditionalFlowStep2( + default predicate isAdditionalFlowStep2( DataFlow::Node node1, FlowState2 state1, DataFlow::Node node2, FlowState2 state2 - ); + ) { + none() + } /** * Holds if data flow into `node` is prohibited in the first projection of the product @@ -359,7 +363,6 @@ module ProductFlow { Config::isSinkPair(node1.getNode(), node1.getState(), node2.getNode(), node2.getState()) } - pragma[assume_small_delta] pragma[nomagic] private predicate fwdReachableInterprocEntry(Flow1::PathNode node1, Flow2::PathNode node2) { isSourcePair(node1, node2) @@ -396,7 +399,6 @@ module ProductFlow { fwdIsSuccessorExit(pragma[only_bind_into](mid1), pragma[only_bind_into](mid2), succ1, succ2) } - pragma[assume_small_delta] private predicate fwdIsSuccessor( Flow1::PathNode pred1, Flow2::PathNode pred2, Flow1::PathNode succ1, Flow2::PathNode succ2 ) { @@ -406,7 +408,6 @@ module ProductFlow { ) } - pragma[assume_small_delta] pragma[nomagic] private predicate revReachableInterprocEntry(Flow1::PathNode node1, Flow2::PathNode node2) { fwdReachableInterprocEntry(node1, node2) 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 56702bd79a9..33b33113d43 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 @@ -588,7 +588,6 @@ private module Cached { ) } - pragma[assume_small_delta] private predicate convertsIntoArgumentRev(Instruction instr) { convertsIntoArgumentFwd(instr) and ( diff --git a/cpp/ql/lib/semmle/code/cpp/ir/implementation/aliased_ssa/gvn/internal/ValueNumberingInternal.qll b/cpp/ql/lib/semmle/code/cpp/ir/implementation/aliased_ssa/gvn/internal/ValueNumberingInternal.qll index 85a28fbc677..ec003891774 100644 --- a/cpp/ql/lib/semmle/code/cpp/ir/implementation/aliased_ssa/gvn/internal/ValueNumberingInternal.qll +++ b/cpp/ql/lib/semmle/code/cpp/ir/implementation/aliased_ssa/gvn/internal/ValueNumberingInternal.qll @@ -176,7 +176,6 @@ private predicate binaryValueNumber0( ) } -pragma[assume_small_delta] private predicate binaryValueNumber( BinaryInstruction instr, IRFunction irFunc, Opcode opcode, TValueNumber leftOperand, TValueNumber rightOperand @@ -202,7 +201,6 @@ private predicate pointerArithmeticValueNumber0( ) } -pragma[assume_small_delta] private predicate pointerArithmeticValueNumber( PointerArithmeticInstruction instr, IRFunction irFunc, Opcode opcode, int elementSize, TValueNumber leftOperand, TValueNumber rightOperand @@ -249,7 +247,6 @@ private predicate loadTotalOverlapValueNumber0( ) } -pragma[assume_small_delta] private predicate loadTotalOverlapValueNumber( LoadTotalOverlapInstruction instr, IRFunction irFunc, IRType type, TValueNumber memOperand, TValueNumber operand diff --git a/cpp/ql/lib/semmle/code/cpp/ir/implementation/raw/gvn/internal/ValueNumberingInternal.qll b/cpp/ql/lib/semmle/code/cpp/ir/implementation/raw/gvn/internal/ValueNumberingInternal.qll index 85a28fbc677..ec003891774 100644 --- a/cpp/ql/lib/semmle/code/cpp/ir/implementation/raw/gvn/internal/ValueNumberingInternal.qll +++ b/cpp/ql/lib/semmle/code/cpp/ir/implementation/raw/gvn/internal/ValueNumberingInternal.qll @@ -176,7 +176,6 @@ private predicate binaryValueNumber0( ) } -pragma[assume_small_delta] private predicate binaryValueNumber( BinaryInstruction instr, IRFunction irFunc, Opcode opcode, TValueNumber leftOperand, TValueNumber rightOperand @@ -202,7 +201,6 @@ private predicate pointerArithmeticValueNumber0( ) } -pragma[assume_small_delta] private predicate pointerArithmeticValueNumber( PointerArithmeticInstruction instr, IRFunction irFunc, Opcode opcode, int elementSize, TValueNumber leftOperand, TValueNumber rightOperand @@ -249,7 +247,6 @@ private predicate loadTotalOverlapValueNumber0( ) } -pragma[assume_small_delta] private predicate loadTotalOverlapValueNumber( LoadTotalOverlapInstruction instr, IRFunction irFunc, IRType type, TValueNumber memOperand, TValueNumber operand diff --git a/cpp/ql/lib/semmle/code/cpp/ir/implementation/unaliased_ssa/gvn/internal/ValueNumberingInternal.qll b/cpp/ql/lib/semmle/code/cpp/ir/implementation/unaliased_ssa/gvn/internal/ValueNumberingInternal.qll index 85a28fbc677..ec003891774 100644 --- a/cpp/ql/lib/semmle/code/cpp/ir/implementation/unaliased_ssa/gvn/internal/ValueNumberingInternal.qll +++ b/cpp/ql/lib/semmle/code/cpp/ir/implementation/unaliased_ssa/gvn/internal/ValueNumberingInternal.qll @@ -176,7 +176,6 @@ private predicate binaryValueNumber0( ) } -pragma[assume_small_delta] private predicate binaryValueNumber( BinaryInstruction instr, IRFunction irFunc, Opcode opcode, TValueNumber leftOperand, TValueNumber rightOperand @@ -202,7 +201,6 @@ private predicate pointerArithmeticValueNumber0( ) } -pragma[assume_small_delta] private predicate pointerArithmeticValueNumber( PointerArithmeticInstruction instr, IRFunction irFunc, Opcode opcode, int elementSize, TValueNumber leftOperand, TValueNumber rightOperand @@ -249,7 +247,6 @@ private predicate loadTotalOverlapValueNumber0( ) } -pragma[assume_small_delta] private predicate loadTotalOverlapValueNumber( LoadTotalOverlapInstruction instr, IRFunction irFunc, IRType type, TValueNumber memOperand, TValueNumber operand diff --git a/cpp/ql/lib/semmle/code/cpp/rangeanalysis/new/internal/semantic/analysis/RangeAnalysisStage.qll b/cpp/ql/lib/semmle/code/cpp/rangeanalysis/new/internal/semantic/analysis/RangeAnalysisStage.qll index c07a3ea55a0..0576f5ff373 100644 --- a/cpp/ql/lib/semmle/code/cpp/rangeanalysis/new/internal/semantic/analysis/RangeAnalysisStage.qll +++ b/cpp/ql/lib/semmle/code/cpp/rangeanalysis/new/internal/semantic/analysis/RangeAnalysisStage.qll @@ -877,7 +877,6 @@ module RangeStage< ) } - pragma[assume_small_delta] pragma[nomagic] private predicate boundedPhiRankStep( SemSsaPhiNode phi, SemBound b, D::Delta delta, boolean upper, boolean fromBackEdge, diff --git a/cpp/ql/src/CHANGELOG.md b/cpp/ql/src/CHANGELOG.md index ca314dcd6d7..5b7104d2331 100644 --- a/cpp/ql/src/CHANGELOG.md +++ b/cpp/ql/src/CHANGELOG.md @@ -1,3 +1,13 @@ +## 0.7.0 + +### Minor Analysis Improvements + +* The `cpp/comparison-with-wider-type` query now correctly handles relational operations on signed operators. As a result the query may find more results. + +## 0.6.4 + +No user-facing changes. + ## 0.6.3 ### New Queries diff --git a/cpp/ql/src/Critical/FlowAfterFree.qll b/cpp/ql/src/Critical/FlowAfterFree.qll index 0e04b294d70..7705e8841d4 100644 --- a/cpp/ql/src/Critical/FlowAfterFree.qll +++ b/cpp/ql/src/Critical/FlowAfterFree.qll @@ -88,14 +88,6 @@ module FlowFromFree { e = any(StoreInstruction store).getDestinationAddress().getUnconvertedResultExpression() ) } - - predicate isBarrier(DataFlow::Node n, FlowState state) { none() } - - predicate isAdditionalFlowStep( - DataFlow::Node n1, FlowState state1, DataFlow::Node n2, FlowState state2 - ) { - none() - } } import DataFlow::GlobalWithState diff --git a/cpp/ql/src/Critical/UseAfterFree.ql b/cpp/ql/src/Critical/UseAfterFree.ql index 17e83780b71..a4d1ee7be2f 100644 --- a/cpp/ql/src/Critical/UseAfterFree.ql +++ b/cpp/ql/src/Critical/UseAfterFree.ql @@ -135,18 +135,24 @@ module ParameterSinks { } } -predicate isUse(DataFlow::Node n, Expr e) { - isUse0(n, e) - or - exists(CallInstruction call, int i, InitializeParameterInstruction init | - n.asOperand().getDef().getUnconvertedResultExpression() = e and - init = ParameterSinks::getAnAlwaysDereferencedParameter() and - call.getArgumentOperand(i) = n.asOperand() and - init.hasIndex(i) and - init.getEnclosingFunction() = call.getStaticCallTarget() - ) +module IsUse { + private import semmle.code.cpp.ir.dataflow.internal.DataFlowImplCommon + + predicate isUse(DataFlow::Node n, Expr e) { + isUse0(n, e) + or + exists(CallInstruction call, InitializeParameterInstruction init | + n.asOperand().getDef().getUnconvertedResultExpression() = e and + pragma[only_bind_into](init) = ParameterSinks::getAnAlwaysDereferencedParameter() and + viableParamArg(call, DataFlow::instructionNode(init), n) and + pragma[only_bind_out](init.getEnclosingFunction()) = + pragma[only_bind_out](call.getStaticCallTarget()) + ) + } } +import IsUse + /** * `dealloc1` is a deallocation expression, `e` is an expression that dereferences a * pointer, and the `(dealloc1, e)` pair should be excluded by the `FlowFromFree` library. diff --git a/cpp/ql/src/Likely Bugs/Conversion/CastArrayPointerArithmetic.ql b/cpp/ql/src/Likely Bugs/Conversion/CastArrayPointerArithmetic.ql index 79a5d530c21..7dda356353e 100644 --- a/cpp/ql/src/Likely Bugs/Conversion/CastArrayPointerArithmetic.ql +++ b/cpp/ql/src/Likely Bugs/Conversion/CastArrayPointerArithmetic.ql @@ -44,14 +44,6 @@ module CastToPointerArithFlowConfig implements DataFlow::StateConfigSig { ) and getFullyConvertedType(node) = state } - - predicate isBarrier(DataFlow::Node node, FlowState state) { none() } - - predicate isAdditionalFlowStep( - DataFlow::Node node1, FlowState state1, DataFlow::Node node2, FlowState state2 - ) { - none() - } } /** diff --git a/cpp/ql/src/Likely Bugs/Memory Management/UninitializedLocal.ql b/cpp/ql/src/Likely Bugs/Memory Management/UninitializedLocal.ql index baa98bdfb2f..0fbd1707db0 100644 --- a/cpp/ql/src/Likely Bugs/Memory Management/UninitializedLocal.ql +++ b/cpp/ql/src/Likely Bugs/Memory Management/UninitializedLocal.ql @@ -72,6 +72,11 @@ VariableAccess commonException() { or result.getParent() instanceof BuiltInOperation or + // Ignore any uninitialized use that is explicitly cast to void and + // is an expression statement. + result.getActualType() instanceof VoidType and + result.getParent() instanceof ExprStmt + or // Finally, exclude functions that contain assembly blocks. It's // anyone's guess what happens in those. containsInlineAssembly(result.getEnclosingFunction()) diff --git a/cpp/ql/src/Security/CWE/CWE-078/ExecTainted.ql b/cpp/ql/src/Security/CWE/CWE-078/ExecTainted.ql index cc4b748e5dc..0686c4a707c 100644 --- a/cpp/ql/src/Security/CWE/CWE-078/ExecTainted.ql +++ b/cpp/ql/src/Security/CWE/CWE-078/ExecTainted.ql @@ -134,8 +134,6 @@ module ExecTaintConfig implements DataFlow::StateConfigSig { predicate isBarrier(DataFlow::Node node) { isBarrierImpl(node) } - predicate isBarrier(DataFlow::Node node, FlowState state) { none() } - predicate isBarrierOut(DataFlow::Node node) { isSink(node, _) // Prevent duplicates along a call chain, since `shellCommand` will include wrappers } diff --git a/cpp/ql/src/Security/CWE/CWE-119/OverrunWriteProductFlow.ql b/cpp/ql/src/Security/CWE/CWE-119/OverrunWriteProductFlow.ql index 1aa7551f63e..510b7e8b6c4 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 low + * @precision medium * @id cpp/overrun-write * @tags reliability * security @@ -118,12 +118,6 @@ module ValidState { state = [false, true] } - predicate isBarrier(DataFlow::Node node, FlowState state) { none() } - - predicate isBarrierOut(DataFlow::Node node) { - node = any(DataFlow::SsaPhiNode phi).getAnInput(true) - } - predicate isAdditionalFlowStep( DataFlow::Node node1, FlowState state1, DataFlow::Node node2, FlowState state2 ) { @@ -252,20 +246,10 @@ module StringSizeConfig implements ProductFlow::StateConfigSig { ) } - predicate isBarrier1(DataFlow::Node node, FlowState1 state) { none() } - - predicate isBarrier2(DataFlow::Node node, FlowState2 state) { none() } - predicate isBarrierOut2(DataFlow::Node node) { node = any(DataFlow::SsaPhiNode phi).getAnInput(true) } - predicate isAdditionalFlowStep1( - DataFlow::Node node1, FlowState1 state1, DataFlow::Node node2, FlowState1 state2 - ) { - none() - } - predicate isAdditionalFlowStep2( DataFlow::Node node1, FlowState2 state1, DataFlow::Node node2, FlowState2 state2 ) { diff --git a/cpp/ql/src/Security/CWE/CWE-190/ComparisonWithWiderType.ql b/cpp/ql/src/Security/CWE/CWE-190/ComparisonWithWiderType.ql index 6636d100746..7d9ef88adea 100644 --- a/cpp/ql/src/Security/CWE/CWE-190/ComparisonWithWiderType.ql +++ b/cpp/ql/src/Security/CWE/CWE-190/ComparisonWithWiderType.ql @@ -45,13 +45,20 @@ Element friendlyLoc(Expr e) { not e instanceof Access and not e instanceof Call and result = e } +int getComparisonSizeAdjustment(Expr e) { + if e.getType().(IntegralType).isSigned() then result = 1 else result = 0 +} + from Loop l, RelationalOperation rel, VariableAccess small, Expr large where small = rel.getLesserOperand() and large = rel.getGreaterOperand() and rel = l.getCondition().getAChild*() and forall(Expr conv | conv = large.getConversion*() | - upperBound(conv).log2() > getComparisonSize(small) * 8 + // We adjust the comparison size in the case of a signed integer type. + // This is to exclude the sign bit from the comparison that determines if the small type's size is sufficient to hold + // the value of the larger type determined with range analysis. + upperBound(conv).log2() > (getComparisonSize(small) * 8 - getComparisonSizeAdjustment(small)) ) and // Ignore cases where the smaller type is int or larger // These are still bugs, but you should need a very large string or array to diff --git a/cpp/ql/src/change-notes/2023-07-03-improve-uninitialized-local.md b/cpp/ql/src/change-notes/2023-07-03-improve-uninitialized-local.md new file mode 100644 index 00000000000..c8c1a0bd6ba --- /dev/null +++ b/cpp/ql/src/change-notes/2023-07-03-improve-uninitialized-local.md @@ -0,0 +1,4 @@ +--- +category: minorAnalysis +--- +* The `cpp/uninitialized-local` query now excludes uninitialized uses that are explicitly cast to void and are expression statements. As a result, the query will report less false positives. \ No newline at end of file diff --git a/cpp/ql/src/change-notes/released/0.6.4.md b/cpp/ql/src/change-notes/released/0.6.4.md new file mode 100644 index 00000000000..7e98b0159fc --- /dev/null +++ b/cpp/ql/src/change-notes/released/0.6.4.md @@ -0,0 +1,3 @@ +## 0.6.4 + +No user-facing changes. diff --git a/cpp/ql/src/change-notes/released/0.7.0.md b/cpp/ql/src/change-notes/released/0.7.0.md new file mode 100644 index 00000000000..7fac3980802 --- /dev/null +++ b/cpp/ql/src/change-notes/released/0.7.0.md @@ -0,0 +1,5 @@ +## 0.7.0 + +### Minor Analysis Improvements + +* The `cpp/comparison-with-wider-type` query now correctly handles relational operations on signed operators. As a result the query may find more results. diff --git a/cpp/ql/src/codeql-pack.release.yml b/cpp/ql/src/codeql-pack.release.yml index b7dafe32c5d..c761f3e7ab4 100644 --- a/cpp/ql/src/codeql-pack.release.yml +++ b/cpp/ql/src/codeql-pack.release.yml @@ -1,2 +1,2 @@ --- -lastReleaseVersion: 0.6.3 +lastReleaseVersion: 0.7.0 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 42623d37328..c38a012b27b 100644 --- a/cpp/ql/src/experimental/Security/CWE/CWE-193/ConstantSizeArrayOffByOne.ql +++ b/cpp/ql/src/experimental/Security/CWE/CWE-193/ConstantSizeArrayOffByOne.ql @@ -168,8 +168,6 @@ module ArrayAddressToDerefConfig implements DataFlow::StateConfigSig { ) } - predicate isBarrier(DataFlow::Node node, FlowState state) { none() } - predicate isBarrierIn(DataFlow::Node node) { isSource(node, _) } predicate isBarrierOut(DataFlow::Node node) { isSink(node, _) } 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 c1f7e735636..dbbbc5e787f 100644 --- a/cpp/ql/src/experimental/Security/CWE/CWE-193/InvalidPointerDeref.ql +++ b/cpp/ql/src/experimental/Security/CWE/CWE-193/InvalidPointerDeref.ql @@ -196,8 +196,6 @@ module AllocToInvalidPointerConfig implements ProductFlow::StateConfigSig { isSinkImpl(_, sink1, sink2, state2) } - predicate isBarrier1(DataFlow::Node node, FlowState1 state) { none() } - predicate isBarrier2(DataFlow::Node node, FlowState2 state) { node = Barrier2::getABarrierNode(state) } @@ -207,18 +205,6 @@ module AllocToInvalidPointerConfig implements ProductFlow::StateConfigSig { predicate isBarrierOut2(DataFlow::Node node) { node = any(DataFlow::SsaPhiNode phi).getAnInput(true) } - - predicate isAdditionalFlowStep1( - DataFlow::Node node1, FlowState1 state1, DataFlow::Node node2, FlowState1 state2 - ) { - none() - } - - predicate isAdditionalFlowStep2( - DataFlow::Node node1, FlowState2 state1, DataFlow::Node node2, FlowState2 state2 - ) { - none() - } } module AllocToInvalidPointerFlow = ProductFlow::GlobalWithState; diff --git a/cpp/ql/src/qlpack.yml b/cpp/ql/src/qlpack.yml index 077b34194fb..10d6bf9f61d 100644 --- a/cpp/ql/src/qlpack.yml +++ b/cpp/ql/src/qlpack.yml @@ -1,5 +1,5 @@ name: codeql/cpp-queries -version: 0.6.4-dev +version: 0.7.1-dev groups: - cpp - queries 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 c4c821456f0..f8b8bab0e4f 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 @@ -35,22 +35,46 @@ edges | 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 | -| test.cpp:175:30:175:30 | p | test.cpp:191:27:191:30 | access to array | -| test.cpp:175:30:175:30 | p | test.cpp:191:27:191:30 | access to array | -| test.cpp:204:14:204:20 | buffer3 | test.cpp:175:30:175:30 | p | -| test.cpp:204:14:204:20 | buffer3 | test.cpp:204:14:204:20 | buffer3 | -| test.cpp:207:35:207:35 | p | test.cpp:208:14:208:14 | p | -| test.cpp:208:14:208:14 | p | test.cpp:175:30:175:30 | p | -| test.cpp:213:19:213:25 | buffer1 | test.cpp:207:35:207:35 | p | -| test.cpp:213:19:213:25 | buffer1 | test.cpp:213:19:213:25 | buffer1 | -| test.cpp:216:19:216:25 | buffer2 | test.cpp:207:35:207:35 | p | -| test.cpp:216:19:216:25 | buffer2 | test.cpp:216:19:216:25 | buffer2 | -| test.cpp:219:19:219:25 | buffer3 | test.cpp:207:35:207:35 | p | -| test.cpp:219:19:219:25 | buffer3 | test.cpp:219:19:219:25 | buffer3 | +| test.cpp:146:26:146:26 | p indirection | test.cpp:148:6:148:9 | * ... | +| test.cpp:156:12:156:14 | buf | test.cpp:156:12:156:18 | ... + ... | +| test.cpp:156:12:156:18 | ... + ... | test.cpp:158:17:158:18 | & ... indirection | +| test.cpp:158:17:158:18 | & ... indirection | test.cpp:146:26:146:26 | p indirection | +| test.cpp:218:23:218:28 | buffer | test.cpp:220:5:220:11 | access to array | +| test.cpp:218:23:218:28 | buffer | test.cpp:221:5:221:11 | access to array | +| test.cpp:229:25:229:29 | array | test.cpp:231:5:231:10 | access to array | +| test.cpp:229:25:229:29 | array | test.cpp:232:5:232:10 | access to array | +| test.cpp:245:30:245:30 | p | test.cpp:261:27:261:30 | access to array | +| test.cpp:245:30:245:30 | p | test.cpp:261:27:261:30 | access to array | +| test.cpp:274:14:274:20 | buffer3 | test.cpp:245:30:245:30 | p | +| test.cpp:274:14:274:20 | buffer3 | test.cpp:274:14:274:20 | buffer3 | +| test.cpp:277:35:277:35 | p | test.cpp:278:14:278:14 | p | +| test.cpp:278:14:278:14 | p | test.cpp:245:30:245:30 | p | +| test.cpp:283:19:283:25 | buffer1 | test.cpp:277:35:277:35 | p | +| test.cpp:283:19:283:25 | buffer1 | test.cpp:283:19:283:25 | buffer1 | +| test.cpp:286:19:286:25 | buffer2 | test.cpp:277:35:277:35 | p | +| test.cpp:286:19:286:25 | buffer2 | test.cpp:286:19:286:25 | buffer2 | +| test.cpp:289:19:289:25 | buffer3 | test.cpp:277:35:277:35 | p | +| test.cpp:289:19:289:25 | buffer3 | test.cpp:289:19:289:25 | buffer3 | +| test.cpp:292:25:292:27 | arr | test.cpp:299:16:299:21 | access to array | +| test.cpp:292:25:292:27 | arr | test.cpp:299:16:299:21 | access to array | +| test.cpp:306:20:306:23 | arr1 | test.cpp:292:25:292:27 | arr | +| test.cpp:306:20:306:23 | arr1 | test.cpp:306:20:306:23 | arr1 | +| test.cpp:309:20:309:23 | arr2 | test.cpp:292:25:292:27 | arr | +| test.cpp:309:20:309:23 | arr2 | test.cpp:309:20:309:23 | arr2 | +| test.cpp:319:19:319:22 | temp | test.cpp:319:19:319:27 | ... + ... | +| test.cpp:319:19:319:22 | temp | test.cpp:324:23:324:32 | ... + ... | +| test.cpp:319:19:319:27 | ... + ... | test.cpp:325:24:325:26 | end | +| test.cpp:322:19:322:22 | temp | test.cpp:322:19:322:27 | ... + ... | +| test.cpp:322:19:322:22 | temp | test.cpp:324:23:324:32 | ... + ... | +| test.cpp:322:19:322:27 | ... + ... | test.cpp:325:24:325:26 | end | +| test.cpp:324:23:324:26 | temp | test.cpp:324:23:324:32 | ... + ... | +| test.cpp:324:23:324:32 | ... + ... | test.cpp:325:15:325:19 | temp2 | +| test.cpp:351:9:351:11 | arr | test.cpp:351:9:351:14 | access to array | +| test.cpp:351:9:351:11 | arr | test.cpp:351:18:351:25 | access to array | +| test.cpp:351:18:351:20 | arr | test.cpp:351:9:351:14 | access to array | +| test.cpp:351:18:351:20 | arr | test.cpp:351:18:351:25 | access to array | +| test.cpp:351:29:351:31 | arr | test.cpp:351:9:351:14 | access to array | +| test.cpp:351:29:351:31 | arr | test.cpp:351:18:351:25 | 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 | @@ -103,25 +127,51 @@ nodes | 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 | -| test.cpp:175:30:175:30 | p | semmle.label | p | -| test.cpp:175:30:175:30 | p | semmle.label | p | -| test.cpp:191:27:191:30 | access to array | semmle.label | access to array | -| test.cpp:204:14:204:20 | buffer3 | semmle.label | buffer3 | -| test.cpp:204:14:204:20 | buffer3 | semmle.label | buffer3 | -| test.cpp:207:35:207:35 | p | semmle.label | p | -| test.cpp:208:14:208:14 | p | semmle.label | p | -| test.cpp:213:19:213:25 | buffer1 | semmle.label | buffer1 | -| test.cpp:213:19:213:25 | buffer1 | semmle.label | buffer1 | -| test.cpp:216:19:216:25 | buffer2 | semmle.label | buffer2 | -| test.cpp:216:19:216:25 | buffer2 | semmle.label | buffer2 | -| test.cpp:219:19:219:25 | buffer3 | semmle.label | buffer3 | -| test.cpp:219:19:219:25 | buffer3 | semmle.label | buffer3 | +| test.cpp:146:26:146:26 | p indirection | semmle.label | p indirection | +| test.cpp:148:6:148:9 | * ... | semmle.label | * ... | +| test.cpp:156:12:156:14 | buf | semmle.label | buf | +| test.cpp:156:12:156:18 | ... + ... | semmle.label | ... + ... | +| test.cpp:158:17:158:18 | & ... indirection | semmle.label | & ... indirection | +| test.cpp:218:23:218:28 | buffer | semmle.label | buffer | +| test.cpp:220:5:220:11 | access to array | semmle.label | access to array | +| test.cpp:221:5:221:11 | access to array | semmle.label | access to array | +| test.cpp:229:25:229:29 | array | semmle.label | array | +| test.cpp:231:5:231:10 | access to array | semmle.label | access to array | +| test.cpp:232:5:232:10 | access to array | semmle.label | access to array | +| test.cpp:245:30:245:30 | p | semmle.label | p | +| test.cpp:245:30:245:30 | p | semmle.label | p | +| test.cpp:261:27:261:30 | access to array | semmle.label | access to array | +| test.cpp:274:14:274:20 | buffer3 | semmle.label | buffer3 | +| test.cpp:274:14:274:20 | buffer3 | semmle.label | buffer3 | +| test.cpp:277:35:277:35 | p | semmle.label | p | +| test.cpp:278:14:278:14 | p | semmle.label | p | +| test.cpp:283:19:283:25 | buffer1 | semmle.label | buffer1 | +| test.cpp:283:19:283:25 | buffer1 | semmle.label | buffer1 | +| test.cpp:286:19:286:25 | buffer2 | semmle.label | buffer2 | +| test.cpp:286:19:286:25 | buffer2 | semmle.label | buffer2 | +| test.cpp:289:19:289:25 | buffer3 | semmle.label | buffer3 | +| test.cpp:289:19:289:25 | buffer3 | semmle.label | buffer3 | +| test.cpp:292:25:292:27 | arr | semmle.label | arr | +| test.cpp:292:25:292:27 | arr | semmle.label | arr | +| test.cpp:299:16:299:21 | access to array | semmle.label | access to array | +| test.cpp:306:20:306:23 | arr1 | semmle.label | arr1 | +| test.cpp:306:20:306:23 | arr1 | semmle.label | arr1 | +| test.cpp:309:20:309:23 | arr2 | semmle.label | arr2 | +| test.cpp:309:20:309:23 | arr2 | semmle.label | arr2 | +| test.cpp:319:19:319:22 | temp | semmle.label | temp | +| test.cpp:319:19:319:27 | ... + ... | semmle.label | ... + ... | +| test.cpp:322:19:322:22 | temp | semmle.label | temp | +| test.cpp:322:19:322:27 | ... + ... | semmle.label | ... + ... | +| test.cpp:324:23:324:26 | temp | semmle.label | temp | +| test.cpp:324:23:324:32 | ... + ... | semmle.label | ... + ... | +| test.cpp:325:15:325:19 | temp2 | semmle.label | temp2 | +| test.cpp:325:24:325:26 | end | semmle.label | end | +| test.cpp:325:24:325:26 | end | semmle.label | end | +| test.cpp:351:9:351:11 | arr | semmle.label | arr | +| test.cpp:351:9:351:14 | access to array | semmle.label | access to array | +| test.cpp:351:18:351:20 | arr | semmle.label | arr | +| test.cpp:351:18:351:25 | access to array | semmle.label | access to array | +| test.cpp:351:29:351:31 | arr | semmle.label | arr | 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 | @@ -136,6 +186,14 @@ subpaths | 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 | -| test.cpp:191:27:191:30 | PointerAdd: access to array | test.cpp:216:19:216:25 | buffer2 | test.cpp:191:27:191:30 | access to array | This pointer arithmetic may have an off-by-1 error allowing it to overrun $@ at this $@. | test.cpp:215:19:215:25 | buffer2 | buffer2 | test.cpp:191:27:191:30 | Load: access to array | read | +| test.cpp:156:12:156:18 | PointerAdd: ... + ... | test.cpp:156:12:156:14 | buf | test.cpp:148:6:148:9 | * ... | This pointer arithmetic may have an off-by-1 error allowing it to overrun $@ at this $@. | test.cpp:154:7:154:9 | buf | buf | test.cpp:147:3:147:13 | Store: ... = ... | write | +| test.cpp:221:5:221:11 | PointerAdd: access to array | test.cpp:218:23:218:28 | buffer | test.cpp:221:5:221:11 | access to array | This pointer arithmetic may have an off-by-1 error allowing it to overrun $@ at this $@. | test.cpp:217:19:217:24 | buffer | buffer | test.cpp:221:5:221:15 | Store: ... = ... | write | +| test.cpp:232:5:232:10 | PointerAdd: access to array | test.cpp:229:25:229:29 | array | test.cpp:232:5:232:10 | access to array | This pointer arithmetic may have an off-by-1 error allowing it to overrun $@ at this $@. | test.cpp:228:10:228:14 | array | array | test.cpp:232:5:232:19 | Store: ... = ... | write | +| test.cpp:261:27:261:30 | PointerAdd: access to array | test.cpp:286:19:286:25 | buffer2 | test.cpp:261:27:261:30 | access to array | This pointer arithmetic may have an off-by-1 error allowing it to overrun $@ at this $@. | test.cpp:285:19:285:25 | buffer2 | buffer2 | test.cpp:261:27:261:30 | Load: access to array | read | +| test.cpp:299:16:299:21 | PointerAdd: access to array | test.cpp:309:20:309:23 | arr2 | test.cpp:299:16:299:21 | access to array | This pointer arithmetic may have an off-by-1014 error allowing it to overrun $@ at this $@. | test.cpp:308:9:308:12 | arr2 | arr2 | test.cpp:299:16:299:21 | Load: access to array | read | +| test.cpp:322:19:322:27 | PointerAdd: ... + ... | test.cpp:322:19:322:22 | temp | test.cpp:325:24:325:26 | end | This pointer arithmetic may have an off-by-1 error allowing it to overrun $@ at this $@. | test.cpp:314:10:314:13 | temp | temp | test.cpp:330:13:330:24 | Store: ... = ... | write | +| test.cpp:322:19:322:27 | PointerAdd: ... + ... | test.cpp:322:19:322:22 | temp | test.cpp:325:24:325:26 | end | This pointer arithmetic may have an off-by-1 error allowing it to overrun $@ at this $@. | test.cpp:314:10:314:13 | temp | temp | test.cpp:331:13:331:24 | Store: ... = ... | write | +| test.cpp:322:19:322:27 | PointerAdd: ... + ... | test.cpp:322:19:322:22 | temp | test.cpp:325:24:325:26 | end | This pointer arithmetic may have an off-by-1 error allowing it to overrun $@ at this $@. | test.cpp:314:10:314:13 | temp | temp | test.cpp:333:13:333:24 | Store: ... = ... | write | +| test.cpp:351:18:351:25 | PointerAdd: access to array | test.cpp:351:9:351:11 | arr | test.cpp:351:18:351:25 | access to array | This pointer arithmetic may have an off-by-1 error allowing it to overrun $@ at this $@. | test.cpp:348:9:348:11 | arr | arr | test.cpp:351:18:351:25 | Load: access to array | read | +| test.cpp:351:18:351:25 | PointerAdd: access to array | test.cpp:351:18:351:20 | arr | test.cpp:351:18:351:25 | access to array | This pointer arithmetic may have an off-by-1 error allowing it to overrun $@ at this $@. | test.cpp:348:9:348:11 | arr | arr | test.cpp:351:18:351:25 | Load: access to array | read | +| test.cpp:351:18:351:25 | PointerAdd: access to array | test.cpp:351:29:351:31 | arr | test.cpp:351:18:351:25 | access to array | This pointer arithmetic may have an off-by-1 error allowing it to overrun $@ at this $@. | test.cpp:348:9:348:11 | arr | arr | test.cpp:351:18:351:25 | Load: access to array | read | 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 5a618d1c0b2..2d3945e48db 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 @@ -143,6 +143,76 @@ void testStrncmp1() { testStrncmp2(asdf); } +void countdownBuf1(int **p) { + *--(*p) = 1; // GOOD [FALSE POSITIVE] + *--(*p) = 2; // GOOD + *--(*p) = 3; // GOOD + *--(*p) = 4; // GOOD +} + +void countdownBuf2() { + int buf[4]; + + int *x = buf + 4; + + countdownBuf1(&x); +} + +int access(int *p) { + return p[0]; +} + + +// unrolled loop style seen in crypto code. +int countdownLength1(int *p, int len) { + while(len > 0) { + access(p); + p[1] = 1; + p[2] = 2; + p[3] = 3; + p[4] = 4; + p[5] = 5; + p[6] = 6; // BAD [FALSE NEGATIVE] + p[7] = 7; // BAD [FALSE NEGATIVE] + p += 8; + len -= 8; + } + + return p[5]; +} + +int callCountdownLength() { + + int buf[6]; + + return countdownLength1(buf, 6); +} + +int countdownLength2() { + int buf[6]; + int len = 6; + int *p = buf; + + if(len % 8) { + return -1; + } + + while(len > 0) { + p[0] = 0; + p[1] = 1; + p[2] = 2; + p[3] = 3; + p[4] = 4; + p[5] = 5; + p[6] = 6; // GOOD + p[7] = 7; // GOOD + p += 8; + len -= 8; + } + + return p[5]; +} + 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 @@ -218,3 +288,67 @@ void test_call_use2() { unsigned char buffer3[3]; call_call_use(buffer3,3); } + +int guardingCallee(int *arr, int size) { + if (size > MAX_SIZE) { + return -1; + } + + int sum; + for (int i = 0; i < size; i++) { + sum += arr[i]; // GOOD [FALSE POSITIVE] - guarded by size + } + return sum; +} + +int guardingCaller() { + int arr1[MAX_SIZE]; + guardingCallee(arr1, MAX_SIZE); + + int arr2[10]; + guardingCallee(arr2, 10); +} + +// simplified md5 padding +void correlatedCondition(int num) { + char temp[64]; + + char *end; + if(num < 64) { + if (num < 56) { + end = temp + 56; + } + else if (num < 64) { + end = temp + 64; // GOOD [FALSE POSITVE] + } + char *temp2 = temp + num; + while(temp2 != end) { + *temp2 = 0; + temp2++; + } + if(num < 56) { + temp2[0] = 0; + temp2[1] = 0; + // ... + temp2[7] = 0; + } + } +} + +int positiveRange(int x) { + if (x < 40) { + return -1; + } + if (x > 1024) { + return -1; + } + + int offset = (unsigned char)(x + 7)/8; + + int arr[128]; + + for(int i=127-offset; i>= 0; i--) { + arr[i] = arr[i+1] + arr[i+offset]; // GOOD [FALSE POSITIVE] + } + return arr[0]; +} diff --git a/cpp/ql/test/library-tests/controlflow/guards-ir/test.c b/cpp/ql/test/library-tests/controlflow/guards-ir/test.c index fd2d7f08f93..9bb5e91805d 100644 --- a/cpp/ql/test/library-tests/controlflow/guards-ir/test.c +++ b/cpp/ql/test/library-tests/controlflow/guards-ir/test.c @@ -151,3 +151,19 @@ void test5(int x) { void test6(int x, int y) { return x && y; } + +int ptr_test(int *x, int *y) { + if (x == y + 42) { + } + + if (x == y - 42) { + } + + if (x < y + 42) { + } + + if (x < y - 42) { + } + + return 0; +} diff --git a/cpp/ql/test/library-tests/controlflow/guards-ir/tests.expected b/cpp/ql/test/library-tests/controlflow/guards-ir/tests.expected index 524a74155c0..01b46187b69 100644 --- a/cpp/ql/test/library-tests/controlflow/guards-ir/tests.expected +++ b/cpp/ql/test/library-tests/controlflow/guards-ir/tests.expected @@ -30,6 +30,10 @@ astGuards | test.c:152:10:152:10 | x | | test.c:152:10:152:15 | ... && ... | | test.c:152:15:152:15 | y | +| test.c:156:9:156:19 | ... == ... | +| test.c:159:9:159:19 | ... == ... | +| test.c:162:9:162:18 | ... < ... | +| test.c:165:9:165:18 | ... < ... | | test.cpp:18:8:18:10 | call to get | | test.cpp:31:7:31:13 | ... == ... | | test.cpp:42:13:42:20 | call to getABool | @@ -122,6 +126,38 @@ astGuardsCompare | 109 | y < 0+0 when ... < ... is true | | 109 | y >= 0+0 when ... < ... is false | | 109 | y >= 0+0 when ... \|\| ... is false | +| 156 | ... + ... != x+0 when ... == ... is false | +| 156 | ... + ... == x+0 when ... == ... is true | +| 156 | x != ... + ...+0 when ... == ... is false | +| 156 | x != y+42 when ... == ... is false | +| 156 | x == ... + ...+0 when ... == ... is true | +| 156 | x == y+42 when ... == ... is true | +| 156 | y != x+-42 when ... == ... is false | +| 156 | y == x+-42 when ... == ... is true | +| 159 | ... - ... != x+0 when ... == ... is false | +| 159 | ... - ... == x+0 when ... == ... is true | +| 159 | x != ... - ...+0 when ... == ... is false | +| 159 | x != y+-42 when ... == ... is false | +| 159 | x == ... - ...+0 when ... == ... is true | +| 159 | x == y+-42 when ... == ... is true | +| 159 | y != x+42 when ... == ... is false | +| 159 | y == x+42 when ... == ... is true | +| 162 | ... + ... < x+1 when ... < ... is false | +| 162 | ... + ... >= x+1 when ... < ... is true | +| 162 | x < ... + ...+0 when ... < ... is true | +| 162 | x < y+42 when ... < ... is true | +| 162 | x >= ... + ...+0 when ... < ... is false | +| 162 | x >= y+42 when ... < ... is false | +| 162 | y < x+-41 when ... < ... is false | +| 162 | y >= x+-41 when ... < ... is true | +| 165 | ... - ... < x+1 when ... < ... is false | +| 165 | ... - ... >= x+1 when ... < ... is true | +| 165 | x < ... - ...+0 when ... < ... is true | +| 165 | x < y+-42 when ... < ... is true | +| 165 | x >= ... - ...+0 when ... < ... is false | +| 165 | x >= y+-42 when ... < ... is false | +| 165 | y < x+43 when ... < ... is false | +| 165 | y >= x+43 when ... < ... is true | astGuardsControl | test.c:7:9:7:13 | ... > ... | false | 10 | 11 | | test.c:7:9:7:13 | ... > ... | true | 7 | 9 | @@ -208,6 +244,10 @@ astGuardsControl | test.c:152:10:152:10 | x | true | 152 | 152 | | test.c:152:10:152:15 | ... && ... | true | 151 | 152 | | test.c:152:15:152:15 | y | true | 151 | 152 | +| test.c:156:9:156:19 | ... == ... | true | 156 | 157 | +| test.c:159:9:159:19 | ... == ... | true | 159 | 160 | +| test.c:162:9:162:18 | ... < ... | true | 162 | 163 | +| test.c:165:9:165:18 | ... < ... | true | 165 | 166 | | test.cpp:18:8:18:10 | call to get | true | 19 | 19 | | test.cpp:31:7:31:13 | ... == ... | false | 30 | 30 | | test.cpp:31:7:31:13 | ... == ... | false | 34 | 34 | @@ -364,6 +404,22 @@ astGuardsEnsure | test.c:109:9:109:23 | ... \|\| ... | test.c:109:23:109:23 | 0 | < | test.c:109:19:109:19 | y | 1 | 113 | 113 | | test.c:109:19:109:23 | ... < ... | test.c:109:19:109:19 | y | >= | test.c:109:23:109:23 | 0 | 0 | 113 | 113 | | test.c:109:19:109:23 | ... < ... | test.c:109:23:109:23 | 0 | < | test.c:109:19:109:19 | y | 1 | 113 | 113 | +| test.c:156:9:156:19 | ... == ... | test.c:156:9:156:9 | x | == | test.c:156:14:156:14 | y | 42 | 156 | 157 | +| test.c:156:9:156:19 | ... == ... | test.c:156:9:156:9 | x | == | test.c:156:14:156:19 | ... + ... | 0 | 156 | 157 | +| test.c:156:9:156:19 | ... == ... | test.c:156:14:156:14 | y | == | test.c:156:9:156:9 | x | -42 | 156 | 157 | +| test.c:156:9:156:19 | ... == ... | test.c:156:14:156:19 | ... + ... | == | test.c:156:9:156:9 | x | 0 | 156 | 157 | +| test.c:159:9:159:19 | ... == ... | test.c:159:9:159:9 | x | == | test.c:159:14:159:14 | y | -42 | 159 | 160 | +| test.c:159:9:159:19 | ... == ... | test.c:159:9:159:9 | x | == | test.c:159:14:159:19 | ... - ... | 0 | 159 | 160 | +| test.c:159:9:159:19 | ... == ... | test.c:159:14:159:14 | y | == | test.c:159:9:159:9 | x | 42 | 159 | 160 | +| test.c:159:9:159:19 | ... == ... | test.c:159:14:159:19 | ... - ... | == | test.c:159:9:159:9 | x | 0 | 159 | 160 | +| test.c:162:9:162:18 | ... < ... | test.c:162:9:162:9 | x | < | test.c:162:13:162:13 | y | 42 | 162 | 163 | +| test.c:162:9:162:18 | ... < ... | test.c:162:9:162:9 | x | < | test.c:162:13:162:18 | ... + ... | 0 | 162 | 163 | +| test.c:162:9:162:18 | ... < ... | test.c:162:13:162:13 | y | >= | test.c:162:9:162:9 | x | -41 | 162 | 163 | +| test.c:162:9:162:18 | ... < ... | test.c:162:13:162:18 | ... + ... | >= | test.c:162:9:162:9 | x | 1 | 162 | 163 | +| test.c:165:9:165:18 | ... < ... | test.c:165:9:165:9 | x | < | test.c:165:13:165:13 | y | -42 | 165 | 166 | +| test.c:165:9:165:18 | ... < ... | test.c:165:9:165:9 | x | < | test.c:165:13:165:18 | ... - ... | 0 | 165 | 166 | +| test.c:165:9:165:18 | ... < ... | test.c:165:13:165:13 | y | >= | test.c:165:9:165:9 | x | 43 | 165 | 166 | +| test.c:165:9:165:18 | ... < ... | test.c:165:13:165:18 | ... - ... | >= | test.c:165:9:165:9 | x | 1 | 165 | 166 | | test.cpp:31:7:31:13 | ... == ... | test.cpp:31:7:31:7 | x | != | test.cpp:31:12:31:13 | - ... | 0 | 30 | 30 | | test.cpp:31:7:31:13 | ... == ... | test.cpp:31:7:31:7 | x | != | test.cpp:31:12:31:13 | - ... | 0 | 34 | 34 | | test.cpp:31:7:31:13 | ... == ... | test.cpp:31:7:31:7 | x | == | test.cpp:31:12:31:13 | - ... | 0 | 30 | 30 | @@ -397,6 +453,10 @@ irGuards | test.c:146:8:146:8 | Load: x | | test.c:152:10:152:10 | Load: x | | test.c:152:15:152:15 | Load: y | +| test.c:156:9:156:19 | CompareEQ: ... == ... | +| test.c:159:9:159:19 | CompareEQ: ... == ... | +| test.c:162:9:162:18 | CompareLT: ... < ... | +| test.c:165:9:165:18 | CompareLT: ... < ... | | test.cpp:18:8:18:12 | CompareNE: (bool)... | | test.cpp:31:7:31:13 | CompareEQ: ... == ... | | test.cpp:42:13:42:20 | Call: call to getABool | @@ -473,6 +533,38 @@ irGuardsCompare | 109 | x == 0+0 when CompareEQ: ... == ... is true | | 109 | y < 0+0 when CompareLT: ... < ... is true | | 109 | y >= 0+0 when CompareLT: ... < ... is false | +| 156 | ... + ... != x+0 when CompareEQ: ... == ... is false | +| 156 | ... + ... == x+0 when CompareEQ: ... == ... is true | +| 156 | x != ... + ...+0 when CompareEQ: ... == ... is false | +| 156 | x != y+42 when CompareEQ: ... == ... is false | +| 156 | x == ... + ...+0 when CompareEQ: ... == ... is true | +| 156 | x == y+42 when CompareEQ: ... == ... is true | +| 156 | y != x+-42 when CompareEQ: ... == ... is false | +| 156 | y == x+-42 when CompareEQ: ... == ... is true | +| 159 | ... - ... != x+0 when CompareEQ: ... == ... is false | +| 159 | ... - ... == x+0 when CompareEQ: ... == ... is true | +| 159 | x != ... - ...+0 when CompareEQ: ... == ... is false | +| 159 | x != y+-42 when CompareEQ: ... == ... is false | +| 159 | x == ... - ...+0 when CompareEQ: ... == ... is true | +| 159 | x == y+-42 when CompareEQ: ... == ... is true | +| 159 | y != x+42 when CompareEQ: ... == ... is false | +| 159 | y == x+42 when CompareEQ: ... == ... is true | +| 162 | ... + ... < x+1 when CompareLT: ... < ... is false | +| 162 | ... + ... >= x+1 when CompareLT: ... < ... is true | +| 162 | x < ... + ...+0 when CompareLT: ... < ... is true | +| 162 | x < y+42 when CompareLT: ... < ... is true | +| 162 | x >= ... + ...+0 when CompareLT: ... < ... is false | +| 162 | x >= y+42 when CompareLT: ... < ... is false | +| 162 | y < x+-41 when CompareLT: ... < ... is false | +| 162 | y >= x+-41 when CompareLT: ... < ... is true | +| 165 | ... - ... < x+1 when CompareLT: ... < ... is false | +| 165 | ... - ... >= x+1 when CompareLT: ... < ... is true | +| 165 | x < ... - ...+0 when CompareLT: ... < ... is true | +| 165 | x < y+-42 when CompareLT: ... < ... is true | +| 165 | x >= ... - ...+0 when CompareLT: ... < ... is false | +| 165 | x >= y+-42 when CompareLT: ... < ... is false | +| 165 | y < x+43 when CompareLT: ... < ... is false | +| 165 | y >= x+43 when CompareLT: ... < ... is true | irGuardsControl | test.c:7:9:7:13 | CompareGT: ... > ... | false | 11 | 11 | | test.c:7:9:7:13 | CompareGT: ... > ... | true | 8 | 8 | @@ -551,6 +643,10 @@ irGuardsControl | test.c:146:8:146:8 | Load: x | false | 147 | 147 | | test.c:152:10:152:10 | Load: x | true | 152 | 152 | | test.c:152:15:152:15 | Load: y | true | 152 | 152 | +| test.c:156:9:156:19 | CompareEQ: ... == ... | true | 156 | 157 | +| test.c:159:9:159:19 | CompareEQ: ... == ... | true | 159 | 160 | +| test.c:162:9:162:18 | CompareLT: ... < ... | true | 162 | 163 | +| test.c:165:9:165:18 | CompareLT: ... < ... | true | 165 | 166 | | test.cpp:18:8:18:12 | CompareNE: (bool)... | true | 19 | 19 | | test.cpp:31:7:31:13 | CompareEQ: ... == ... | false | 34 | 34 | | test.cpp:31:7:31:13 | CompareEQ: ... == ... | true | 30 | 30 | @@ -690,6 +786,22 @@ irGuardsEnsure | test.c:109:9:109:14 | CompareEQ: ... == ... | test.c:109:14:109:14 | Constant: 0 | != | test.c:109:9:109:9 | Load: x | 0 | 113 | 113 | | test.c:109:19:109:23 | CompareLT: ... < ... | test.c:109:19:109:19 | Load: y | >= | test.c:109:23:109:23 | Constant: (long)... | 0 | 113 | 113 | | test.c:109:19:109:23 | CompareLT: ... < ... | test.c:109:23:109:23 | Constant: (long)... | < | test.c:109:19:109:19 | Load: y | 1 | 113 | 113 | +| test.c:156:9:156:19 | CompareEQ: ... == ... | test.c:156:9:156:9 | Load: x | == | test.c:156:14:156:14 | Load: y | 42 | 156 | 157 | +| test.c:156:9:156:19 | CompareEQ: ... == ... | test.c:156:9:156:9 | Load: x | == | test.c:156:14:156:19 | PointerAdd: ... + ... | 0 | 156 | 157 | +| test.c:156:9:156:19 | CompareEQ: ... == ... | test.c:156:14:156:14 | Load: y | == | test.c:156:9:156:9 | Load: x | -42 | 156 | 157 | +| test.c:156:9:156:19 | CompareEQ: ... == ... | test.c:156:14:156:19 | PointerAdd: ... + ... | == | test.c:156:9:156:9 | Load: x | 0 | 156 | 157 | +| test.c:159:9:159:19 | CompareEQ: ... == ... | test.c:159:9:159:9 | Load: x | == | test.c:159:14:159:14 | Load: y | -42 | 159 | 160 | +| test.c:159:9:159:19 | CompareEQ: ... == ... | test.c:159:9:159:9 | Load: x | == | test.c:159:14:159:19 | PointerSub: ... - ... | 0 | 159 | 160 | +| test.c:159:9:159:19 | CompareEQ: ... == ... | test.c:159:14:159:14 | Load: y | == | test.c:159:9:159:9 | Load: x | 42 | 159 | 160 | +| test.c:159:9:159:19 | CompareEQ: ... == ... | test.c:159:14:159:19 | PointerSub: ... - ... | == | test.c:159:9:159:9 | Load: x | 0 | 159 | 160 | +| test.c:162:9:162:18 | CompareLT: ... < ... | test.c:162:9:162:9 | Load: x | < | test.c:162:13:162:13 | Load: y | 42 | 162 | 163 | +| test.c:162:9:162:18 | CompareLT: ... < ... | test.c:162:9:162:9 | Load: x | < | test.c:162:13:162:18 | PointerAdd: ... + ... | 0 | 162 | 163 | +| test.c:162:9:162:18 | CompareLT: ... < ... | test.c:162:13:162:13 | Load: y | >= | test.c:162:9:162:9 | Load: x | -41 | 162 | 163 | +| test.c:162:9:162:18 | CompareLT: ... < ... | test.c:162:13:162:18 | PointerAdd: ... + ... | >= | test.c:162:9:162:9 | Load: x | 1 | 162 | 163 | +| test.c:165:9:165:18 | CompareLT: ... < ... | test.c:165:9:165:9 | Load: x | < | test.c:165:13:165:13 | Load: y | -42 | 165 | 166 | +| test.c:165:9:165:18 | CompareLT: ... < ... | test.c:165:9:165:9 | Load: x | < | test.c:165:13:165:18 | PointerSub: ... - ... | 0 | 165 | 166 | +| test.c:165:9:165:18 | CompareLT: ... < ... | test.c:165:13:165:13 | Load: y | >= | test.c:165:9:165:9 | Load: x | 43 | 165 | 166 | +| test.c:165:9:165:18 | CompareLT: ... < ... | test.c:165:13:165:18 | PointerSub: ... - ... | >= | test.c:165:9:165:9 | Load: x | 1 | 165 | 166 | | test.cpp:18:8:18:12 | CompareNE: (bool)... | test.cpp:18:8:18:10 | Call: call to get | != | test.cpp:18:8:18:12 | Constant: (bool)... | 0 | 19 | 19 | | test.cpp:18:8:18:12 | CompareNE: (bool)... | test.cpp:18:8:18:12 | Constant: (bool)... | != | test.cpp:18:8:18:10 | Call: call to get | 0 | 19 | 19 | | test.cpp:31:7:31:13 | CompareEQ: ... == ... | test.cpp:31:7:31:7 | Load: x | != | test.cpp:31:12:31:13 | Constant: - ... | 0 | 34 | 34 | diff --git a/cpp/ql/test/library-tests/ir/ir/PrintAST.expected b/cpp/ql/test/library-tests/ir/ir/PrintAST.expected index d714e8912c2..5d83a93e7ef 100644 --- a/cpp/ql/test/library-tests/ir/ir/PrintAST.expected +++ b/cpp/ql/test/library-tests/ir/ir/PrintAST.expected @@ -14462,6 +14462,140 @@ ir.cpp: # 1911| getExpr(): [VariableAccess] x # 1911| Type = [IntType] int # 1911| ValueCategory = prvalue(load) +# 1914| [TopLevelFunction] int static_function(int) +# 1914| : +# 1914| getParameter(0): [Parameter] x +# 1914| Type = [IntType] int +# 1914| getEntryPoint(): [BlockStmt] { ... } +# 1915| getStmt(0): [ReturnStmt] return ... +# 1915| getExpr(): [VariableAccess] x +# 1915| Type = [IntType] int +# 1915| ValueCategory = prvalue(load) +# 1918| [TopLevelFunction] void test_static_functions_with_assignments() +# 1918| : +# 1918| getEntryPoint(): [BlockStmt] { ... } +# 1919| getStmt(0): [DeclStmt] declaration +# 1919| getDeclarationEntry(0): [VariableDeclarationEntry] definition of c +# 1919| Type = [Class] C +# 1919| getVariable().getInitializer(): [Initializer] initializer for c +# 1919| getExpr(): [ConstructorCall] call to C +# 1919| Type = [VoidType] void +# 1919| ValueCategory = prvalue +# 1920| getStmt(1): [DeclStmt] declaration +# 1920| getDeclarationEntry(0): [VariableDeclarationEntry] definition of x +# 1920| Type = [IntType] int +# 1921| getStmt(2): [ExprStmt] ExprStmt +# 1921| getExpr(): [AssignExpr] ... = ... +# 1921| Type = [IntType] int +# 1921| ValueCategory = lvalue +# 1921| getLValue(): [VariableAccess] x +# 1921| Type = [IntType] int +# 1921| ValueCategory = lvalue +# 1921| getRValue(): [FunctionCall] call to StaticMemberFunction +# 1921| Type = [IntType] int +# 1921| ValueCategory = prvalue +# 1921| getQualifier(): [VariableAccess] c +# 1921| Type = [Class] C +# 1921| ValueCategory = lvalue +# 1921| getArgument(0): [Literal] 10 +# 1921| Type = [IntType] int +# 1921| Value = [Literal] 10 +# 1921| ValueCategory = prvalue +# 1922| getStmt(3): [DeclStmt] declaration +# 1922| getDeclarationEntry(0): [VariableDeclarationEntry] definition of y +# 1922| Type = [IntType] int +# 1923| getStmt(4): [ExprStmt] ExprStmt +# 1923| getExpr(): [AssignExpr] ... = ... +# 1923| Type = [IntType] int +# 1923| ValueCategory = lvalue +# 1923| getLValue(): [VariableAccess] y +# 1923| Type = [IntType] int +# 1923| ValueCategory = lvalue +# 1923| getRValue(): [FunctionCall] call to StaticMemberFunction +# 1923| Type = [IntType] int +# 1923| ValueCategory = prvalue +# 1923| getArgument(0): [Literal] 10 +# 1923| Type = [IntType] int +# 1923| Value = [Literal] 10 +# 1923| ValueCategory = prvalue +# 1924| getStmt(5): [DeclStmt] declaration +# 1924| getDeclarationEntry(0): [VariableDeclarationEntry] definition of z +# 1924| Type = [IntType] int +# 1925| getStmt(6): [ExprStmt] ExprStmt +# 1925| getExpr(): [AssignExpr] ... = ... +# 1925| Type = [IntType] int +# 1925| ValueCategory = lvalue +# 1925| getLValue(): [VariableAccess] z +# 1925| Type = [IntType] int +# 1925| ValueCategory = lvalue +# 1925| getRValue(): [FunctionCall] call to static_function +# 1925| Type = [IntType] int +# 1925| ValueCategory = prvalue +# 1925| getArgument(0): [Literal] 10 +# 1925| Type = [IntType] int +# 1925| Value = [Literal] 10 +# 1925| ValueCategory = prvalue +# 1926| getStmt(7): [ReturnStmt] return ... +# 1928| [TopLevelFunction] void test_double_assign() +# 1928| : +# 1928| getEntryPoint(): [BlockStmt] { ... } +# 1929| getStmt(0): [DeclStmt] declaration +# 1929| getDeclarationEntry(0): [VariableDeclarationEntry] definition of i +# 1929| Type = [IntType] int +# 1929| getDeclarationEntry(1): [VariableDeclarationEntry] definition of j +# 1929| Type = [IntType] int +# 1930| getStmt(1): [ExprStmt] ExprStmt +# 1930| getExpr(): [AssignExpr] ... = ... +# 1930| Type = [IntType] int +# 1930| ValueCategory = lvalue +# 1930| getLValue(): [VariableAccess] i +# 1930| Type = [IntType] int +# 1930| ValueCategory = lvalue +# 1930| getRValue(): [AssignExpr] ... = ... +# 1930| Type = [IntType] int +# 1930| ValueCategory = prvalue +# 1930| getLValue(): [VariableAccess] j +# 1930| Type = [IntType] int +# 1930| ValueCategory = lvalue +# 1930| getRValue(): [Literal] 40 +# 1930| Type = [IntType] int +# 1930| Value = [Literal] 40 +# 1930| ValueCategory = prvalue +# 1931| getStmt(2): [ReturnStmt] return ... +# 1933| [TopLevelFunction] void test_assign_with_assign_operation() +# 1933| : +# 1933| getEntryPoint(): [BlockStmt] { ... } +# 1934| getStmt(0): [DeclStmt] declaration +# 1934| getDeclarationEntry(0): [VariableDeclarationEntry] definition of i +# 1934| Type = [IntType] int +# 1934| getDeclarationEntry(1): [VariableDeclarationEntry] definition of j +# 1934| Type = [IntType] int +# 1934| getVariable().getInitializer(): [Initializer] initializer for j +# 1934| getExpr(): [Literal] 0 +# 1934| Type = [IntType] int +# 1934| Value = [Literal] 0 +# 1934| ValueCategory = prvalue +# 1935| getStmt(1): [ExprStmt] ExprStmt +# 1935| getExpr(): [AssignExpr] ... = ... +# 1935| Type = [IntType] int +# 1935| ValueCategory = lvalue +# 1935| getLValue(): [VariableAccess] i +# 1935| Type = [IntType] int +# 1935| ValueCategory = lvalue +# 1935| getRValue(): [AssignAddExpr] ... += ... +# 1935| Type = [IntType] int +# 1935| ValueCategory = prvalue +# 1935| getLValue(): [VariableAccess] j +# 1935| Type = [IntType] int +# 1935| ValueCategory = lvalue +# 1935| getRValue(): [Literal] 40 +# 1935| Type = [IntType] int +# 1935| Value = [Literal] 40 +# 1935| ValueCategory = prvalue +# 1935| getRValue().getFullyConverted(): [ParenthesisExpr] (...) +# 1935| Type = [IntType] int +# 1935| ValueCategory = prvalue +# 1936| getStmt(2): [ReturnStmt] return ... perf-regression.cpp: # 4| [CopyAssignmentOperator] Big& Big::operator=(Big const&) # 4| : diff --git a/cpp/ql/test/library-tests/ir/ir/ir.cpp b/cpp/ql/test/library-tests/ir/ir/ir.cpp index 6475af5fcc1..653a9d41772 100644 --- a/cpp/ql/test/library-tests/ir/ir/ir.cpp +++ b/cpp/ql/test/library-tests/ir/ir/ir.cpp @@ -1911,4 +1911,28 @@ int noreturnTest2(int x) { return x; } +int static_function(int x) { + return x; +} + +void test_static_functions_with_assignments() { + C c; + int x; + x = c.StaticMemberFunction(10); + int y; + y = C::StaticMemberFunction(10); + int z; + z = static_function(10); +} + +void test_double_assign() { + int i, j; + i = j = 40; +} + +void test_assign_with_assign_operation() { + int i, j = 0; + i = (j += 40); +} + // semmle-extractor-options: -std=c++17 --clang diff --git a/cpp/ql/test/library-tests/ir/ir/operand_locations.expected b/cpp/ql/test/library-tests/ir/ir/operand_locations.expected index e6f0bcbfe8d..94ac689208f 100644 --- a/cpp/ql/test/library-tests/ir/ir/operand_locations.expected +++ b/cpp/ql/test/library-tests/ir/ir/operand_locations.expected @@ -8821,6 +8821,76 @@ | ir.cpp:1911:12:1911:12 | Address | &:r1911_2 | | ir.cpp:1911:12:1911:12 | Load | m1907_6 | | ir.cpp:1911:12:1911:12 | StoreValue | r1911_3 | +| ir.cpp:1914:5:1914:19 | Address | &:r1914_7 | +| ir.cpp:1914:5:1914:19 | ChiPartial | partial:m1914_3 | +| ir.cpp:1914:5:1914:19 | ChiTotal | total:m1914_2 | +| ir.cpp:1914:5:1914:19 | Load | m1915_4 | +| ir.cpp:1914:5:1914:19 | SideEffect | m1914_3 | +| ir.cpp:1914:25:1914:25 | Address | &:r1914_5 | +| ir.cpp:1915:5:1915:13 | Address | &:r1915_1 | +| ir.cpp:1915:12:1915:12 | Address | &:r1915_2 | +| ir.cpp:1915:12:1915:12 | Load | m1914_6 | +| ir.cpp:1915:12:1915:12 | StoreValue | r1915_3 | +| ir.cpp:1918:6:1918:43 | ChiPartial | partial:m1918_3 | +| ir.cpp:1918:6:1918:43 | ChiTotal | total:m1918_2 | +| ir.cpp:1918:6:1918:43 | SideEffect | ~m1925_5 | +| ir.cpp:1919:7:1919:7 | Address | &:r1919_1 | +| ir.cpp:1919:7:1919:7 | Address | &:r1919_1 | +| ir.cpp:1919:7:1919:7 | Arg(this) | this:r1919_1 | +| ir.cpp:1919:7:1919:7 | CallTarget | func:r1919_3 | +| ir.cpp:1919:7:1919:7 | ChiPartial | partial:m1919_5 | +| ir.cpp:1919:7:1919:7 | ChiPartial | partial:m1919_7 | +| ir.cpp:1919:7:1919:7 | ChiTotal | total:m1918_4 | +| ir.cpp:1919:7:1919:7 | ChiTotal | total:m1919_2 | +| ir.cpp:1919:7:1919:7 | SideEffect | ~m1918_4 | +| ir.cpp:1920:9:1920:9 | Address | &:r1920_1 | +| ir.cpp:1921:5:1921:5 | Address | &:r1921_7 | +| ir.cpp:1921:11:1921:30 | CallTarget | func:r1921_2 | +| ir.cpp:1921:11:1921:30 | ChiPartial | partial:m1921_5 | +| ir.cpp:1921:11:1921:30 | ChiTotal | total:m1919_6 | +| ir.cpp:1921:11:1921:30 | SideEffect | ~m1919_6 | +| ir.cpp:1921:11:1921:30 | StoreValue | r1921_4 | +| ir.cpp:1921:32:1921:33 | Arg(0) | 0:r1921_3 | +| ir.cpp:1922:9:1922:9 | Address | &:r1922_1 | +| ir.cpp:1923:5:1923:5 | Address | &:r1923_6 | +| ir.cpp:1923:9:1923:31 | CallTarget | func:r1923_1 | +| ir.cpp:1923:9:1923:31 | ChiPartial | partial:m1923_4 | +| ir.cpp:1923:9:1923:31 | ChiTotal | total:m1921_6 | +| ir.cpp:1923:9:1923:31 | SideEffect | ~m1921_6 | +| ir.cpp:1923:9:1923:31 | StoreValue | r1923_3 | +| ir.cpp:1923:33:1923:34 | Arg(0) | 0:r1923_2 | +| ir.cpp:1924:9:1924:9 | Address | &:r1924_1 | +| ir.cpp:1925:5:1925:5 | Address | &:r1925_6 | +| ir.cpp:1925:9:1925:23 | CallTarget | func:r1925_1 | +| ir.cpp:1925:9:1925:23 | ChiPartial | partial:m1925_4 | +| ir.cpp:1925:9:1925:23 | ChiTotal | total:m1923_5 | +| ir.cpp:1925:9:1925:23 | SideEffect | ~m1923_5 | +| ir.cpp:1925:9:1925:23 | StoreValue | r1925_3 | +| ir.cpp:1925:25:1925:26 | Arg(0) | 0:r1925_2 | +| ir.cpp:1928:6:1928:23 | ChiPartial | partial:m1928_3 | +| ir.cpp:1928:6:1928:23 | ChiTotal | total:m1928_2 | +| ir.cpp:1928:6:1928:23 | SideEffect | m1928_3 | +| ir.cpp:1929:7:1929:7 | Address | &:r1929_1 | +| ir.cpp:1929:10:1929:10 | Address | &:r1929_3 | +| ir.cpp:1930:3:1930:3 | Address | &:r1930_5 | +| ir.cpp:1930:7:1930:7 | Address | &:r1930_2 | +| ir.cpp:1930:7:1930:12 | StoreValue | r1930_4 | +| ir.cpp:1930:11:1930:12 | StoreValue | r1930_1 | +| ir.cpp:1930:11:1930:12 | Unary | r1930_1 | +| ir.cpp:1933:6:1933:38 | ChiPartial | partial:m1933_3 | +| ir.cpp:1933:6:1933:38 | ChiTotal | total:m1933_2 | +| ir.cpp:1933:6:1933:38 | SideEffect | m1933_3 | +| ir.cpp:1934:7:1934:7 | Address | &:r1934_1 | +| ir.cpp:1934:10:1934:10 | Address | &:r1934_3 | +| ir.cpp:1934:13:1934:14 | StoreValue | r1934_4 | +| ir.cpp:1935:3:1935:3 | Address | &:r1935_6 | +| ir.cpp:1935:8:1935:8 | Address | &:r1935_2 | +| ir.cpp:1935:8:1935:8 | Address | &:r1935_2 | +| ir.cpp:1935:8:1935:8 | Left | r1935_3 | +| ir.cpp:1935:8:1935:8 | Load | m1934_5 | +| ir.cpp:1935:8:1935:14 | StoreValue | r1935_4 | +| ir.cpp:1935:8:1935:14 | StoreValue | r1935_4 | +| ir.cpp:1935:13:1935:14 | Right | r1935_1 | | perf-regression.cpp:6:3:6:5 | Address | &:r6_5 | | perf-regression.cpp:6:3:6:5 | Address | &:r6_5 | | perf-regression.cpp:6:3:6:5 | Address | &:r6_7 | diff --git a/cpp/ql/test/library-tests/ir/ir/raw_ir.expected b/cpp/ql/test/library-tests/ir/ir/raw_ir.expected index acc0cf6f4c0..01f050b7a3b 100644 --- a/cpp/ql/test/library-tests/ir/ir/raw_ir.expected +++ b/cpp/ql/test/library-tests/ir/ir/raw_ir.expected @@ -10167,6 +10167,105 @@ ir.cpp: # 1907| v1907_9(void) = AliasedUse : ~m? # 1907| v1907_10(void) = ExitFunction : +# 1914| int static_function(int) +# 1914| Block 0 +# 1914| v1914_1(void) = EnterFunction : +# 1914| mu1914_2(unknown) = AliasedDefinition : +# 1914| mu1914_3(unknown) = InitializeNonLocal : +# 1914| r1914_4(glval) = VariableAddress[x] : +# 1914| mu1914_5(int) = InitializeParameter[x] : &:r1914_4 +# 1915| r1915_1(glval) = VariableAddress[#return] : +# 1915| r1915_2(glval) = VariableAddress[x] : +# 1915| r1915_3(int) = Load[x] : &:r1915_2, ~m? +# 1915| mu1915_4(int) = Store[#return] : &:r1915_1, r1915_3 +# 1914| r1914_6(glval) = VariableAddress[#return] : +# 1914| v1914_7(void) = ReturnValue : &:r1914_6, ~m? +# 1914| v1914_8(void) = AliasedUse : ~m? +# 1914| v1914_9(void) = ExitFunction : + +# 1918| void test_static_functions_with_assignments() +# 1918| Block 0 +# 1918| v1918_1(void) = EnterFunction : +# 1918| mu1918_2(unknown) = AliasedDefinition : +# 1918| mu1918_3(unknown) = InitializeNonLocal : +# 1919| r1919_1(glval) = VariableAddress[c] : +# 1919| mu1919_2(C) = Uninitialized[c] : &:r1919_1 +# 1919| r1919_3(glval) = FunctionAddress[C] : +# 1919| v1919_4(void) = Call[C] : func:r1919_3, this:r1919_1 +# 1919| mu1919_5(unknown) = ^CallSideEffect : ~m? +# 1919| mu1919_6(C) = ^IndirectMayWriteSideEffect[-1] : &:r1919_1 +# 1920| r1920_1(glval) = VariableAddress[x] : +# 1920| mu1920_2(int) = Uninitialized[x] : &:r1920_1 +# 1921| r1921_1(glval) = VariableAddress[c] : +# 1921| r1921_2(glval) = FunctionAddress[StaticMemberFunction] : +# 1921| r1921_3(int) = Constant[10] : +# 1921| r1921_4(int) = Call[StaticMemberFunction] : func:r1921_2, 0:r1921_3 +# 1921| mu1921_5(unknown) = ^CallSideEffect : ~m? +# 1921| r1921_6(glval) = VariableAddress[x] : +# 1921| mu1921_7(int) = Store[x] : &:r1921_6, r1921_4 +# 1922| r1922_1(glval) = VariableAddress[y] : +# 1922| mu1922_2(int) = Uninitialized[y] : &:r1922_1 +# 1923| r1923_1(glval) = FunctionAddress[StaticMemberFunction] : +# 1923| r1923_2(int) = Constant[10] : +# 1923| r1923_3(int) = Call[StaticMemberFunction] : func:r1923_1, 0:r1923_2 +# 1923| mu1923_4(unknown) = ^CallSideEffect : ~m? +# 1923| r1923_5(glval) = VariableAddress[y] : +# 1923| mu1923_6(int) = Store[y] : &:r1923_5, r1923_3 +# 1924| r1924_1(glval) = VariableAddress[z] : +# 1924| mu1924_2(int) = Uninitialized[z] : &:r1924_1 +# 1925| r1925_1(glval) = FunctionAddress[static_function] : +# 1925| r1925_2(int) = Constant[10] : +# 1925| r1925_3(int) = Call[static_function] : func:r1925_1, 0:r1925_2 +# 1925| mu1925_4(unknown) = ^CallSideEffect : ~m? +# 1925| r1925_5(glval) = VariableAddress[z] : +# 1925| mu1925_6(int) = Store[z] : &:r1925_5, r1925_3 +# 1926| v1926_1(void) = NoOp : +# 1918| v1918_4(void) = ReturnVoid : +# 1918| v1918_5(void) = AliasedUse : ~m? +# 1918| v1918_6(void) = ExitFunction : + +# 1928| void test_double_assign() +# 1928| Block 0 +# 1928| v1928_1(void) = EnterFunction : +# 1928| mu1928_2(unknown) = AliasedDefinition : +# 1928| mu1928_3(unknown) = InitializeNonLocal : +# 1929| r1929_1(glval) = VariableAddress[i] : +# 1929| mu1929_2(int) = Uninitialized[i] : &:r1929_1 +# 1929| r1929_3(glval) = VariableAddress[j] : +# 1929| mu1929_4(int) = Uninitialized[j] : &:r1929_3 +# 1930| r1930_1(int) = Constant[40] : +# 1930| r1930_2(glval) = VariableAddress[j] : +# 1930| mu1930_3(int) = Store[j] : &:r1930_2, r1930_1 +# 1930| r1930_4(int) = CopyValue : r1930_1 +# 1930| r1930_5(glval) = VariableAddress[i] : +# 1930| mu1930_6(int) = Store[i] : &:r1930_5, r1930_4 +# 1931| v1931_1(void) = NoOp : +# 1928| v1928_4(void) = ReturnVoid : +# 1928| v1928_5(void) = AliasedUse : ~m? +# 1928| v1928_6(void) = ExitFunction : + +# 1933| void test_assign_with_assign_operation() +# 1933| Block 0 +# 1933| v1933_1(void) = EnterFunction : +# 1933| mu1933_2(unknown) = AliasedDefinition : +# 1933| mu1933_3(unknown) = InitializeNonLocal : +# 1934| r1934_1(glval) = VariableAddress[i] : +# 1934| mu1934_2(int) = Uninitialized[i] : &:r1934_1 +# 1934| r1934_3(glval) = VariableAddress[j] : +# 1934| r1934_4(int) = Constant[0] : +# 1934| mu1934_5(int) = Store[j] : &:r1934_3, r1934_4 +# 1935| r1935_1(int) = Constant[40] : +# 1935| r1935_2(glval) = VariableAddress[j] : +# 1935| r1935_3(int) = Load[j] : &:r1935_2, ~m? +# 1935| r1935_4(int) = Add : r1935_3, r1935_1 +# 1935| mu1935_5(int) = Store[j] : &:r1935_2, r1935_4 +# 1935| r1935_6(glval) = VariableAddress[i] : +# 1935| mu1935_7(int) = Store[i] : &:r1935_6, r1935_4 +# 1936| v1936_1(void) = NoOp : +# 1933| v1933_4(void) = ReturnVoid : +# 1933| v1933_5(void) = AliasedUse : ~m? +# 1933| v1933_6(void) = ExitFunction : + perf-regression.cpp: # 6| void Big::Big() # 6| Block 0 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 a5e2ae3b0b6..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 @@ -75,6 +75,7 @@ edges | test.cpp:214:24:214:24 | p | test.cpp:216:10:216:10 | p | | test.cpp:220:43:220:48 | call to malloc | test.cpp:222:15:222:20 | buffer | | test.cpp:222:15:222:20 | buffer | test.cpp:214:24:214:24 | p | +| test.cpp:228:43:228:48 | call to malloc | test.cpp:232:10:232:15 | buffer | | test.cpp:235:40:235:45 | buffer | test.cpp:236:5:236:26 | ... = ... | | test.cpp:236:5:236:26 | ... = ... | test.cpp:236:12:236:17 | p_str indirection [post update] [string] | | test.cpp:241:27:241:32 | call to malloc | test.cpp:242:22:242:27 | buffer | @@ -85,6 +86,7 @@ edges | test.cpp:243:12:243:14 | str indirection [string] | test.cpp:243:16:243:21 | string indirection | | test.cpp:243:16:243:21 | string indirection | test.cpp:243:12:243:21 | string | | test.cpp:249:20:249:27 | call to my_alloc | test.cpp:250:12:250:12 | p | +| test.cpp:256:17:256:22 | call to malloc | test.cpp:257:12:257:12 | p | | test.cpp:262:22:262:27 | call to malloc | test.cpp:266:12:266:12 | p | | test.cpp:264:20:264:25 | call to malloc | test.cpp:266:12:266:12 | p | nodes @@ -153,6 +155,8 @@ nodes | test.cpp:216:10:216:10 | p | semmle.label | p | | test.cpp:220:43:220:48 | call to malloc | semmle.label | call to malloc | | test.cpp:222:15:222:20 | buffer | semmle.label | buffer | +| test.cpp:228:43:228:48 | call to malloc | semmle.label | call to malloc | +| test.cpp:232:10:232:15 | buffer | semmle.label | buffer | | test.cpp:235:40:235:45 | buffer | semmle.label | buffer | | test.cpp:236:5:236:26 | ... = ... | semmle.label | ... = ... | | test.cpp:236:12:236:17 | p_str indirection [post update] [string] | semmle.label | p_str indirection [post update] [string] | @@ -164,6 +168,8 @@ nodes | test.cpp:243:16:243:21 | string indirection | semmle.label | string indirection | | test.cpp:249:20:249:27 | call to my_alloc | semmle.label | call to my_alloc | | test.cpp:250:12:250:12 | p | semmle.label | p | +| test.cpp:256:17:256:22 | call to malloc | semmle.label | call to malloc | +| test.cpp:257:12:257:12 | p | semmle.label | p | | test.cpp:262:22:262:27 | call to malloc | semmle.label | call to malloc | | test.cpp:264:20:264:25 | call to malloc | semmle.label | call to malloc | | test.cpp:266:12:266:12 | p | semmle.label | p | diff --git a/cpp/ql/test/query-tests/Security/CWE/CWE-190/semmle/ComparisonWithWiderType/ComparisonWithWiderType.expected b/cpp/ql/test/query-tests/Security/CWE/CWE-190/semmle/ComparisonWithWiderType/ComparisonWithWiderType.expected index d04bff0a812..fb46cacf4a9 100644 --- a/cpp/ql/test/query-tests/Security/CWE/CWE-190/semmle/ComparisonWithWiderType/ComparisonWithWiderType.expected +++ b/cpp/ql/test/query-tests/Security/CWE/CWE-190/semmle/ComparisonWithWiderType/ComparisonWithWiderType.expected @@ -1,6 +1,7 @@ | test.c:4:14:4:18 | ... < ... | Comparison between $@ of type char and $@ of wider type int. | test.c:3:7:3:7 | c | c | test.c:2:17:2:17 | x | x | | test.c:9:14:9:18 | ... > ... | Comparison between $@ of type char and $@ of wider type int. | test.c:8:7:8:7 | c | c | test.c:7:17:7:17 | x | x | | test.c:14:14:14:18 | ... < ... | Comparison between $@ of type short and $@ of wider type int. | test.c:13:8:13:8 | s | s | test.c:12:17:12:17 | x | x | +| test.c:42:15:42:29 | ... < ... | Comparison between $@ of type short and $@ of wider type int. | test.c:41:9:41:10 | s1 | s1 | test.c:42:20:42:29 | 65535 | 65535 | | test.c:65:14:65:18 | ... < ... | Comparison between $@ of type short and $@ of wider type int. | test.c:64:8:64:8 | s | s | test.c:63:17:63:17 | x | x | | test.c:87:14:87:18 | ... < ... | Comparison between $@ of type unsigned char and $@ of wider type unsigned int. | test.c:83:16:83:16 | c | c | test.c:84:15:84:15 | x | x | | test.c:91:14:91:23 | ... < ... | Comparison between $@ of type unsigned char and $@ of wider type int. | test.c:83:16:83:16 | c | c | test.c:91:18:91:23 | 65280 | 65280 | @@ -13,3 +14,4 @@ | test.c:107:14:107:26 | ... < ... | Comparison between $@ of type unsigned char and $@ of wider type unsigned int. | test.c:83:16:83:16 | c | c | test.c:107:19:107:25 | ... >> ... | ... >> ... | | test.c:128:15:128:21 | ... < ... | Comparison between $@ of type unsigned char and $@ of wider type unsigned int. | test.c:121:16:121:17 | uc | uc | test.c:123:19:123:20 | sz | sz | | test.c:139:15:139:21 | ... < ... | Comparison between $@ of type unsigned char and $@ of wider type unsigned int. | test.c:121:16:121:17 | uc | uc | test.c:123:19:123:20 | sz | sz | +| test.c:156:9:156:14 | ... < ... | Comparison between $@ of type short and $@ of wider type int. | test.c:150:8:150:8 | s | s | test.c:151:6:151:7 | sx | sx | diff --git a/cpp/ql/test/query-tests/Security/CWE/CWE-190/semmle/ComparisonWithWiderType/test.c b/cpp/ql/test/query-tests/Security/CWE/CWE-190/semmle/ComparisonWithWiderType/test.c index f0b7f445aeb..8361ae3e31b 100644 --- a/cpp/ql/test/query-tests/Security/CWE/CWE-190/semmle/ComparisonWithWiderType/test.c +++ b/cpp/ql/test/query-tests/Security/CWE/CWE-190/semmle/ComparisonWithWiderType/test.c @@ -39,7 +39,7 @@ void test5 () { void test6() { short s1; - for (s1 = 0; s1 < 0x0000ffff; s1++) {} + for (s1 = 0; s1 < 0x0000ffff; s1++) {} // BAD } void test7(long long l) { @@ -145,3 +145,22 @@ void test13() { sz = (unsigned)sx & (unsigned)sy; for (uc = 0; uc < sz; uc++) {} // GOOD } + +void test14() { + short s = 0; + int sx = 0x7FFF + 1; + + // BAD: 's' is compared with a value of a wider type. + // 's' overflows before reaching 'sx', + // causing an infinite loop + while (s < sx) { + s += 1; + } + + unsigned int ux = 0; + + // GOOD: 'ux' has a type at least as wide as 'max_get' + while (ux < sx) { + ux += 1; + } +} diff --git a/cpp/ql/test/query-tests/Security/CWE/CWE-457/semmle/tests/UninitializedLocal.expected b/cpp/ql/test/query-tests/Security/CWE/CWE-457/semmle/tests/UninitializedLocal.expected index 6ef3b5d555d..2b5e6549a30 100644 --- a/cpp/ql/test/query-tests/Security/CWE/CWE-457/semmle/tests/UninitializedLocal.expected +++ b/cpp/ql/test/query-tests/Security/CWE/CWE-457/semmle/tests/UninitializedLocal.expected @@ -14,3 +14,7 @@ | test.cpp:378:9:378:11 | val | The variable $@ may not be initialized at this access. | test.cpp:359:6:359:8 | val | val | | test.cpp:417:10:417:10 | j | The variable $@ may not be initialized at this access. | test.cpp:414:9:414:9 | j | j | | test.cpp:436:9:436:9 | j | The variable $@ may not be initialized at this access. | test.cpp:431:9:431:9 | j | j | +| test.cpp:454:2:454:2 | x | The variable $@ may not be initialized at this access. | test.cpp:452:6:452:6 | x | x | +| test.cpp:460:7:460:7 | x | The variable $@ may not be initialized at this access. | test.cpp:458:6:458:6 | x | x | +| test.cpp:467:2:467:2 | x | The variable $@ may not be initialized at this access. | test.cpp:464:6:464:6 | x | x | +| test.cpp:474:7:474:7 | x | The variable $@ may not be initialized at this access. | test.cpp:471:6:471:6 | x | x | diff --git a/cpp/ql/test/query-tests/Security/CWE/CWE-457/semmle/tests/test.cpp b/cpp/ql/test/query-tests/Security/CWE/CWE-457/semmle/tests/test.cpp index d289d223a48..7660f564d6d 100644 --- a/cpp/ql/test/query-tests/Security/CWE/CWE-457/semmle/tests/test.cpp +++ b/cpp/ql/test/query-tests/Security/CWE/CWE-457/semmle/tests/test.cpp @@ -435,3 +435,41 @@ int test38() { return j; // BAD } + +void test39() { + int x; + + x; // GOOD, in void context +} + +void test40() { + int x; + + (void)x; // GOOD, explicitly cast to void +} + +void test41() { + int x; + + x++; // BAD +} + +void test42() { + int x; + + void(x++); // BAD +} + +void test43() { + int x; + int y = 1; + + x + y; // BAD +} + +void test44() { + int x; + int y = 1; + + void(x + y); // BAD +} \ No newline at end of file diff --git a/csharp/extractor/Semmle.Extraction.CSharp.Standalone/AssemblyCache.cs b/csharp/extractor/Semmle.Extraction.CSharp.Standalone/AssemblyCache.cs index c93161e72f0..b92a708878a 100644 --- a/csharp/extractor/Semmle.Extraction.CSharp.Standalone/AssemblyCache.cs +++ b/csharp/extractor/Semmle.Extraction.CSharp.Standalone/AssemblyCache.cs @@ -16,12 +16,12 @@ namespace Semmle.BuildAnalyser /// Locate all reference files and index them. /// /// Directories to search. - /// Callback for progress. - public AssemblyCache(IEnumerable dirs, IProgressMonitor progress) + /// Callback for progress. + public AssemblyCache(IEnumerable dirs, ProgressMonitor progressMonitor) { foreach (var dir in dirs) { - progress.FindingFiles(dir); + progressMonitor.FindingFiles(dir); AddReferenceDirectory(dir); } IndexReferences(); @@ -41,6 +41,8 @@ namespace Semmle.BuildAnalyser } } + private static readonly Version emptyVersion = new Version(0, 0, 0, 0); + /// /// Indexes all DLLs we have located. /// Because this is a potentially time-consuming operation, it is put into a separate stage. @@ -55,7 +57,9 @@ namespace Semmle.BuildAnalyser // Index "assemblyInfo" by version string // The OrderBy is used to ensure that we by default select the highest version number. - foreach (var info in assemblyInfoByFileName.Values.OrderBy(info => info.Id)) + foreach (var info in assemblyInfoByFileName.Values + .OrderBy(info => info.Name) + .ThenBy(info => info.Version ?? emptyVersion)) { foreach (var index in info.IndexStrings) assemblyInfoById[index] = info; diff --git a/csharp/extractor/Semmle.Extraction.CSharp.Standalone/BuildAnalysis.cs b/csharp/extractor/Semmle.Extraction.CSharp.Standalone/BuildAnalysis.cs index c9d9694ad23..f4aa614a345 100644 --- a/csharp/extractor/Semmle.Extraction.CSharp.Standalone/BuildAnalysis.cs +++ b/csharp/extractor/Semmle.Extraction.CSharp.Standalone/BuildAnalysis.cs @@ -8,122 +8,106 @@ using System.Threading.Tasks; using System.Collections.Concurrent; using System.Text; using System.Security.Cryptography; +using System.Text.RegularExpressions; namespace Semmle.BuildAnalyser { - /// - /// The output of a build analysis. - /// - internal interface IBuildAnalysis - { - /// - /// Full filepaths of external references. - /// - IEnumerable ReferenceFiles { get; } - - /// - /// Full filepaths of C# source files from project files. - /// - IEnumerable ProjectSourceFiles { get; } - - /// - /// Full filepaths of C# source files in the filesystem. - /// - IEnumerable AllSourceFiles { get; } - - /// - /// The assembly IDs which could not be resolved. - /// - IEnumerable UnresolvedReferences { get; } - - /// - /// List of source files referenced by projects but - /// which were not found in the filesystem. - /// - IEnumerable MissingSourceFiles { get; } - } - /// /// Main implementation of the build analysis. /// - internal sealed class BuildAnalysis : IBuildAnalysis, IDisposable + internal sealed partial class BuildAnalysis : IDisposable { private readonly AssemblyCache assemblyCache; - private readonly IProgressMonitor progressMonitor; + private readonly ProgressMonitor progressMonitor; private readonly IDictionary usedReferences = new ConcurrentDictionary(); private readonly IDictionary sources = new ConcurrentDictionary(); private readonly IDictionary unresolvedReferences = new ConcurrentDictionary(); - private int failedProjects, succeededProjects; + private int failedProjects; + private int succeededProjects; private readonly string[] allSources; private int conflictedReferences = 0; + private readonly Options options; + private readonly DirectoryInfo sourceDir; + private readonly DotNet dotnet; /// /// Performs a C# build analysis. /// /// Analysis options from the command line. - /// Display of analysis progress. - public BuildAnalysis(Options options, IProgressMonitor progress) + /// Display of analysis progress. + public BuildAnalysis(Options options, ProgressMonitor progressMonitor) { var startTime = DateTime.Now; - progressMonitor = progress; - var sourceDir = new DirectoryInfo(options.SrcDir); + this.options = options; + this.progressMonitor = progressMonitor; + this.sourceDir = new DirectoryInfo(options.SrcDir); - progressMonitor.FindingFiles(options.SrcDir); + try + { + this.dotnet = new DotNet(progressMonitor); + } + catch + { + progressMonitor.MissingDotNet(); + throw; + } - allSources = sourceDir.GetFiles("*.cs", SearchOption.AllDirectories) - .Select(d => d.FullName) - .Where(d => !options.ExcludesFile(d)) - .ToArray(); + this.progressMonitor.FindingFiles(options.SrcDir); + + this.allSources = GetFiles("*.cs").ToArray(); + var allProjects = GetFiles("*.csproj"); + var solutions = options.SolutionFile is not null + ? new[] { options.SolutionFile } + : GetFiles("*.sln"); var dllDirNames = options.DllDirs.Select(Path.GetFullPath).ToList(); - packageDirectory = new TemporaryDirectory(ComputeTempDirectory(sourceDir.FullName)); - - if (options.UseNuGet) - { - try - { - var nuget = new NugetPackages(sourceDir.FullName, packageDirectory); - nuget.InstallPackages(progressMonitor); - } - catch (FileNotFoundException) - { - progressMonitor.MissingNuGet(); - } - } // Find DLLs in the .Net Framework if (options.ScanNetFrameworkDlls) { - var runtimeLocation = Runtime.GetRuntime(options.UseSelfContainedDotnet); + var runtimeLocation = new Runtime(dotnet).GetRuntime(options.UseSelfContainedDotnet); progressMonitor.Log(Util.Logging.Severity.Debug, $"Runtime location selected: {runtimeLocation}"); dllDirNames.Add(runtimeLocation); } - // TODO: remove the below when the required SDK is installed - using (new FileRenamer(sourceDir.GetFiles("global.json", SearchOption.AllDirectories))) - { - var solutions = options.SolutionFile is not null ? - new[] { options.SolutionFile } : - sourceDir.GetFiles("*.sln", SearchOption.AllDirectories).Select(d => d.FullName); - - if (options.UseNuGet) - { - RestoreSolutions(solutions); - } - dllDirNames.Add(packageDirectory.DirInfo.FullName); - assemblyCache = new BuildAnalyser.AssemblyCache(dllDirNames, progress); - AnalyseSolutions(solutions); - - foreach (var filename in assemblyCache.AllAssemblies.Select(a => a.Filename)) - UseReference(filename); - } - if (options.UseMscorlib) { UseReference(typeof(object).Assembly.Location); } + packageDirectory = new TemporaryDirectory(ComputeTempDirectory(sourceDir.FullName)); + + if (options.UseNuGet) + { + dllDirNames.Add(packageDirectory.DirInfo.FullName); + try + { + var nuget = new NugetPackages(sourceDir.FullName, packageDirectory, progressMonitor); + nuget.InstallPackages(); + } + catch (FileNotFoundException) + { + progressMonitor.MissingNuGet(); + } + + // TODO: remove the below when the required SDK is installed + using (new FileRenamer(sourceDir.GetFiles("global.json", SearchOption.AllDirectories))) + { + Restore(solutions); + Restore(allProjects); + DownloadMissingPackages(allProjects); + } + } + + assemblyCache = new AssemblyCache(dllDirNames, progressMonitor); + AnalyseSolutions(solutions); + + foreach (var filename in assemblyCache.AllAssemblies.Select(a => a.Filename)) + { + UseReference(filename); + } + ResolveConflicts(); // Output the findings @@ -149,6 +133,13 @@ namespace Semmle.BuildAnalyser DateTime.Now - startTime); } + private IEnumerable GetFiles(string pattern, bool recurseSubdirectories = true) + { + return sourceDir.GetFiles(pattern, new EnumerationOptions { RecurseSubdirectories = recurseSubdirectories, MatchCasing = MatchCasing.CaseInsensitive }) + .Select(d => d.FullName) + .Where(d => !options.ExcludesFile(d)); + } + /// /// Computes a unique temp directory for the packages associated /// with this source tree. Use a SHA1 of the directory name. @@ -158,9 +149,7 @@ namespace Semmle.BuildAnalyser private static string ComputeTempDirectory(string srcDir) { var bytes = Encoding.Unicode.GetBytes(srcDir); - - using var sha1 = SHA1.Create(); - var sha = sha1.ComputeHash(bytes); + var sha = SHA1.HashData(bytes); var sb = new StringBuilder(); foreach (var b in sha.Take(8)) sb.AppendFormat("{0:x2}", b); @@ -195,12 +184,15 @@ namespace Semmle.BuildAnalyser // Pick the highest version for each assembly name foreach (var r in sortedReferences) + { finalAssemblyList[r.Name] = r; - + } // Update the used references list usedReferences.Clear(); foreach (var r in finalAssemblyList.Select(r => r.Value.Filename)) + { UseReference(r); + } // Report the results foreach (var r in sortedReferences) @@ -278,7 +270,9 @@ namespace Semmle.BuildAnalyser private void AnalyseProjectFiles(IEnumerable projectFiles) { foreach (var proj in projectFiles) + { AnalyseProject(proj); + } } private void AnalyseProject(FileInfo project) @@ -324,36 +318,106 @@ namespace Semmle.BuildAnalyser } - private void Restore(string projectOrSolution) + private bool Restore(string target, string? pathToNugetConfig = null) { - int exit; - try - { - exit = DotNet.RestoreToDirectory(projectOrSolution, packageDirectory.DirInfo.FullName); - } - catch (FileNotFoundException) - { - exit = 2; - } + return dotnet.RestoreToDirectory(target, packageDirectory.DirInfo.FullName, pathToNugetConfig); + } - switch (exit) + private void Restore(IEnumerable targets, string? pathToNugetConfig = null) + { + foreach (var target in targets) { - case 0: - case 1: - // No errors - break; - default: - progressMonitor.CommandFailed("dotnet", $"restore \"{projectOrSolution}\"", exit); - break; + Restore(target, pathToNugetConfig); } } - public void RestoreSolutions(IEnumerable solutions) + private void DownloadMissingPackages(IEnumerable restoreTargets) { - Parallel.ForEach(solutions, new ParallelOptions { MaxDegreeOfParallelism = 4 }, Restore); + var alreadyDownloadedPackages = Directory.GetDirectories(packageDirectory.DirInfo.FullName).Select(d => Path.GetFileName(d).ToLowerInvariant()).ToHashSet(); + var notYetDownloadedPackages = new HashSet(); + + var nugetConfigs = GetFiles("nuget.config", recurseSubdirectories: true).ToArray(); + string? nugetConfig = null; + if (nugetConfigs.Length > 1) + { + progressMonitor.MultipleNugetConfig(nugetConfigs); + nugetConfig = GetFiles("nuget.config", recurseSubdirectories: false).FirstOrDefault(); + if (nugetConfig == null) + { + progressMonitor.NoTopLevelNugetConfig(); + } + } + else + { + nugetConfig = nugetConfigs.FirstOrDefault(); + } + + var allFiles = GetFiles("*.*"); + foreach (var file in allFiles) + { + try + { + using var sr = new StreamReader(file); + ReadOnlySpan line; + while ((line = sr.ReadLine()) != null) + { + foreach (var valueMatch in PackageReference().EnumerateMatches(line)) + { + // We can't get the group from the ValueMatch, so doing it manually: + var match = line.Slice(valueMatch.Index, valueMatch.Length); + var includeIndex = match.IndexOf("Include", StringComparison.InvariantCultureIgnoreCase); + if (includeIndex == -1) + { + continue; + } + + match = match.Slice(includeIndex + "Include".Length + 1); + + var quoteIndex1 = match.IndexOf("\""); + var quoteIndex2 = match.Slice(quoteIndex1 + 1).IndexOf("\""); + + var packageName = match.Slice(quoteIndex1 + 1, quoteIndex2).ToString().ToLowerInvariant(); + if (!alreadyDownloadedPackages.Contains(packageName)) + { + notYetDownloadedPackages.Add(packageName); + } + } + } + } + catch (Exception ex) + { + progressMonitor.FailedToReadFile(file, ex); + continue; + } + } + + foreach (var package in notYetDownloadedPackages) + { + progressMonitor.NugetInstall(package); + using var tempDir = new TemporaryDirectory(ComputeTempDirectory(package)); + var success = dotnet.New(tempDir.DirInfo.FullName); + if (!success) + { + continue; + } + success = dotnet.AddPackage(tempDir.DirInfo.FullName, package); + if (!success) + { + continue; + } + + success = Restore(tempDir.DirInfo.FullName, nugetConfig); + + // TODO: the restore might fail, we could retry with a prerelease (*-* instead of *) version of the package. + + if (!success) + { + progressMonitor.FailedToRestoreNugetPackage(package); + } + } } - public void AnalyseSolutions(IEnumerable solutions) + private void AnalyseSolutions(IEnumerable solutions) { Parallel.ForEach(solutions, new ParallelOptions { MaxDegreeOfParallelism = 4 }, solutionFile => { @@ -374,5 +438,8 @@ namespace Semmle.BuildAnalyser { packageDirectory?.Dispose(); } + + [GeneratedRegex("", RegexOptions.IgnoreCase | RegexOptions.Compiled | RegexOptions.Singleline)] + private static partial Regex PackageReference(); } } diff --git a/csharp/extractor/Semmle.Extraction.CSharp.Standalone/DotNet.cs b/csharp/extractor/Semmle.Extraction.CSharp.Standalone/DotNet.cs index 4045519d3e0..b9e641a9de1 100644 --- a/csharp/extractor/Semmle.Extraction.CSharp.Standalone/DotNet.cs +++ b/csharp/extractor/Semmle.Extraction.CSharp.Standalone/DotNet.cs @@ -1,17 +1,96 @@ -using System.Diagnostics; +using System; +using System.Collections.Generic; +using System.Diagnostics; +using Semmle.Util; namespace Semmle.BuildAnalyser { + internal interface IDotNet + { + bool RestoreToDirectory(string project, string directory, string? pathToNugetConfig = null); + bool New(string folder); + bool AddPackage(string folder, string package); + public IList GetListedRuntimes(); + } + /// /// Utilities to run the "dotnet" command. /// - internal static class DotNet + internal class DotNet : IDotNet { - public static int RestoreToDirectory(string projectOrSolutionFile, string packageDirectory) + private const string dotnet = "dotnet"; + private readonly ProgressMonitor progressMonitor; + + public DotNet(ProgressMonitor progressMonitor) { - using var proc = Process.Start("dotnet", $"restore --no-dependencies \"{projectOrSolutionFile}\" --packages \"{packageDirectory}\" /p:DisableImplicitNuGetFallbackFolder=true"); + this.progressMonitor = progressMonitor; + Info(); + } + + private void Info() + { + // TODO: make sure the below `dotnet` version is matching the one specified in global.json + progressMonitor.RunningProcess($"{dotnet} --info"); + using var proc = Process.Start(dotnet, "--info"); proc.WaitForExit(); - return proc.ExitCode; + var ret = proc.ExitCode; + + if (ret != 0) + { + progressMonitor.CommandFailed(dotnet, "--info", ret); + throw new Exception($"{dotnet} --info failed with exit code {ret}."); + } + } + + private bool RunCommand(string args) + { + progressMonitor.RunningProcess($"{dotnet} {args}"); + using var proc = Process.Start(dotnet, args); + proc.WaitForExit(); + if (proc.ExitCode != 0) + { + progressMonitor.CommandFailed(dotnet, args, proc.ExitCode); + return false; + } + + return true; + } + + public bool RestoreToDirectory(string projectOrSolutionFile, string packageDirectory, string? pathToNugetConfig = null) + { + var args = $"restore --no-dependencies \"{projectOrSolutionFile}\" --packages \"{packageDirectory}\" /p:DisableImplicitNuGetFallbackFolder=true"; + if (pathToNugetConfig != null) + args += $" --configfile \"{pathToNugetConfig}\""; + return RunCommand(args); + } + + public bool New(string folder) + { + var args = $"new console --no-restore --output \"{folder}\""; + return RunCommand(args); + } + + public bool AddPackage(string folder, string package) + { + var args = $"add \"{folder}\" package \"{package}\" --no-restore"; + return RunCommand(args); + } + + public IList GetListedRuntimes() + { + var args = "--list-runtimes"; + var pi = new ProcessStartInfo(dotnet, args) + { + RedirectStandardOutput = true, + UseShellExecute = false + }; + var exitCode = pi.ReadOutput(out var runtimes); + if (exitCode != 0) + { + progressMonitor.CommandFailed(dotnet, args, exitCode); + return new List(); + } + return runtimes; } } } diff --git a/csharp/extractor/Semmle.Extraction.CSharp.Standalone/NugetPackages.cs b/csharp/extractor/Semmle.Extraction.CSharp.Standalone/NugetPackages.cs index 94e65d61462..ab5a71dd2c5 100644 --- a/csharp/extractor/Semmle.Extraction.CSharp.Standalone/NugetPackages.cs +++ b/csharp/extractor/Semmle.Extraction.CSharp.Standalone/NugetPackages.cs @@ -17,26 +17,24 @@ namespace Semmle.BuildAnalyser /// /// Create the package manager for a specified source tree. /// - /// The source directory. - public NugetPackages(string sourceDir, TemporaryDirectory packageDirectory) + public NugetPackages(string sourceDir, TemporaryDirectory packageDirectory, ProgressMonitor progressMonitor) { SourceDirectory = sourceDir; PackageDirectory = packageDirectory; + this.progressMonitor = progressMonitor; // Expect nuget.exe to be in a `nuget` directory under the directory containing this exe. var currentAssembly = System.Reflection.Assembly.GetExecutingAssembly().Location; - var directory = Path.GetDirectoryName(currentAssembly); - if (directory is null) - throw new FileNotFoundException($"Directory path '{currentAssembly}' of current assembly is null"); - + var directory = Path.GetDirectoryName(currentAssembly) + ?? throw new FileNotFoundException($"Directory path '{currentAssembly}' of current assembly is null"); nugetExe = Path.Combine(directory, "nuget", "nuget.exe"); if (!File.Exists(nugetExe)) throw new FileNotFoundException(string.Format("NuGet could not be found at {0}", nugetExe)); - packages = new DirectoryInfo(SourceDirectory). - EnumerateFiles("packages.config", SearchOption.AllDirectories). - ToArray(); + packages = new DirectoryInfo(SourceDirectory) + .EnumerateFiles("packages.config", SearchOption.AllDirectories) + .ToArray(); } // List of package files to download. @@ -51,11 +49,11 @@ namespace Semmle.BuildAnalyser /// Download the packages to the temp folder. /// /// The progress monitor used for reporting errors etc. - public void InstallPackages(IProgressMonitor pm) + public void InstallPackages() { foreach (var package in packages) { - RestoreNugetPackage(package.FullName, pm); + RestoreNugetPackage(package.FullName); } } @@ -80,9 +78,9 @@ namespace Semmle.BuildAnalyser /// /// The package file. /// Where to log progress/errors. - private void RestoreNugetPackage(string package, IProgressMonitor pm) + private void RestoreNugetPackage(string package) { - pm.NugetInstall(package); + progressMonitor.NugetInstall(package); /* Use nuget.exe to install a package. * Note that there is a clutch of NuGet assemblies which could be used to @@ -115,7 +113,7 @@ namespace Semmle.BuildAnalyser if (p is null) { - pm.FailedNugetCommand(pi.FileName, pi.Arguments, "Couldn't start process."); + progressMonitor.FailedNugetCommand(pi.FileName, pi.Arguments, "Couldn't start process."); return; } @@ -125,16 +123,17 @@ namespace Semmle.BuildAnalyser p.WaitForExit(); if (p.ExitCode != 0) { - pm.FailedNugetCommand(pi.FileName, pi.Arguments, output + error); + progressMonitor.FailedNugetCommand(pi.FileName, pi.Arguments, output + error); } } catch (Exception ex) when (ex is System.ComponentModel.Win32Exception || ex is FileNotFoundException) { - pm.FailedNugetCommand(pi.FileName, pi.Arguments, ex.Message); + progressMonitor.FailedNugetCommand(pi.FileName, pi.Arguments, ex.Message); } } private readonly string nugetExe; + private readonly ProgressMonitor progressMonitor; } } diff --git a/csharp/extractor/Semmle.Extraction.CSharp.Standalone/ProgressMonitor.cs b/csharp/extractor/Semmle.Extraction.CSharp.Standalone/ProgressMonitor.cs index 5b1da929251..233ba969fea 100644 --- a/csharp/extractor/Semmle.Extraction.CSharp.Standalone/ProgressMonitor.cs +++ b/csharp/extractor/Semmle.Extraction.CSharp.Standalone/ProgressMonitor.cs @@ -3,27 +3,7 @@ using System; namespace Semmle.BuildAnalyser { - /// - /// Callback for various events that may happen during the build analysis. - /// - internal interface IProgressMonitor - { - void FindingFiles(string dir); - void UnresolvedReference(string id, string project); - void AnalysingSolution(string filename); - void FailedProjectFile(string filename, string reason); - void FailedNugetCommand(string exe, string args, string message); - void NugetInstall(string package); - void ResolvedReference(string filename); - void Summary(int existingSources, int usedSources, int missingSources, int references, int unresolvedReferences, int resolvedConflicts, int totalProjects, int failedProjects, TimeSpan analysisTime); - void Log(Severity severity, string message); - void ResolvedConflict(string asm1, string asm2); - void MissingProject(string projectFile); - void CommandFailed(string exe, string arguments, int exitCode); - void MissingNuGet(); - } - - internal class ProgressMonitor : IProgressMonitor + internal class ProgressMonitor { private readonly ILogger logger; @@ -117,5 +97,36 @@ namespace Semmle.BuildAnalyser { logger.Log(Severity.Error, "Missing nuget.exe"); } + + public void MissingDotNet() + { + logger.Log(Severity.Error, "Missing dotnet CLI"); + } + + public void RunningProcess(string command) + { + logger.Log(Severity.Info, $"Running {command}"); + } + + public void FailedToRestoreNugetPackage(string package) + { + logger.Log(Severity.Info, $"Failed to restore nuget package {package}"); + } + + public void FailedToReadFile(string file, Exception ex) + { + logger.Log(Severity.Info, $"Failed to read file {file}"); + logger.Log(Severity.Debug, $"Failed to read file {file}, exception: {ex}"); + } + + public void MultipleNugetConfig(string[] nugetConfigs) + { + logger.Log(Severity.Info, $"Found multiple nuget.config files: {string.Join(", ", nugetConfigs)}."); + } + + internal void NoTopLevelNugetConfig() + { + logger.Log(Severity.Info, $"Could not find a top-level nuget.config file."); + } } } diff --git a/csharp/extractor/Semmle.Extraction.CSharp.Standalone/Properties/AssemblyInfo.cs b/csharp/extractor/Semmle.Extraction.CSharp.Standalone/Properties/AssemblyInfo.cs index 4c5502fcde5..ed0de1e457b 100644 --- a/csharp/extractor/Semmle.Extraction.CSharp.Standalone/Properties/AssemblyInfo.cs +++ b/csharp/extractor/Semmle.Extraction.CSharp.Standalone/Properties/AssemblyInfo.cs @@ -1,4 +1,5 @@ using System.Reflection; +using System.Runtime.CompilerServices; using System.Runtime.InteropServices; // General Information about an assembly is controlled through the following @@ -13,6 +14,9 @@ using System.Runtime.InteropServices; [assembly: AssemblyTrademark("")] [assembly: AssemblyCulture("")] +// Expose internals for testing purposes. +[assembly: InternalsVisibleTo("Semmle.Extraction.Tests")] + // Setting ComVisible to false makes the types in this assembly not visible // to COM components. If you need to access a type in this assembly from // COM, set the ComVisible attribute to true on that type. diff --git a/csharp/extractor/Semmle.Extraction.CSharp.Standalone/Runtime.cs b/csharp/extractor/Semmle.Extraction.CSharp.Standalone/Runtime.cs index 4b76efc3d65..bb521523ead 100644 --- a/csharp/extractor/Semmle.Extraction.CSharp.Standalone/Runtime.cs +++ b/csharp/extractor/Semmle.Extraction.CSharp.Standalone/Runtime.cs @@ -3,6 +3,8 @@ using System.Collections.Generic; using System.Runtime.InteropServices; using System.IO; using System.Linq; +using System.Text.RegularExpressions; +using Semmle.BuildAnalyser; using Semmle.Util; namespace Semmle.Extraction.CSharp.Standalone @@ -10,31 +12,105 @@ namespace Semmle.Extraction.CSharp.Standalone /// /// Locates .NET Runtimes. /// - internal static class Runtime + internal partial class Runtime { + private const string netCoreApp = "Microsoft.NETCore.App"; + private const string aspNetCoreApp = "Microsoft.AspNetCore.App"; + + private readonly IDotNet dotNet; private static string ExecutingRuntime => RuntimeEnvironment.GetRuntimeDirectory(); - /// - /// Locates .NET Core Runtimes. - /// - private static IEnumerable CoreRuntimes - { - get - { - var dotnetPath = FileUtils.FindProgramOnPath(Win32.IsWindows() ? "dotnet.exe" : "dotnet"); - var dotnetDirs = dotnetPath is not null - ? new[] { dotnetPath } - : new[] { "/usr/share/dotnet", @"C:\Program Files\dotnet" }; - var coreDirs = dotnetDirs.Select(d => Path.Combine(d, "shared", "Microsoft.NETCore.App")); + public Runtime(IDotNet dotNet) => this.dotNet = dotNet; - var dir = coreDirs.FirstOrDefault(Directory.Exists); - if (dir is not null) + internal sealed class RuntimeVersion : IComparable + { + private readonly string dir; + private readonly Version version; + private readonly Version? preReleaseVersion; + private readonly string? preReleaseVersionType; + private bool IsPreRelease => preReleaseVersionType is not null && preReleaseVersion is not null; + public string FullPath + { + get { - return Directory.EnumerateDirectories(dir).OrderByDescending(Path.GetFileName); + var preRelease = IsPreRelease ? $"-{preReleaseVersionType}.{preReleaseVersion}" : ""; + var version = this.version + preRelease; + return Path.Combine(dir, version); + } + } + + public RuntimeVersion(string dir, string version, string preReleaseVersionType, string preReleaseVersion) + { + this.dir = dir; + this.version = Version.Parse(version); + if (!string.IsNullOrEmpty(preReleaseVersion) && !string.IsNullOrEmpty(preReleaseVersionType)) + { + this.preReleaseVersionType = preReleaseVersionType; + this.preReleaseVersion = Version.Parse(preReleaseVersion); + } + } + + public int CompareTo(RuntimeVersion? other) + { + var c = version.CompareTo(other?.version); + if (c == 0 && IsPreRelease) + { + if (!other!.IsPreRelease) + { + return -1; + } + + // Both are pre-release like runtime versions. + // The pre-release version types are sorted alphabetically (e.g. alpha, beta, preview, rc) + // and the pre-release version types are more important that the pre-release version numbers. + return preReleaseVersionType != other!.preReleaseVersionType + ? preReleaseVersionType!.CompareTo(other!.preReleaseVersionType) + : preReleaseVersion!.CompareTo(other!.preReleaseVersion); } - return Enumerable.Empty(); + return c; } + + public override bool Equals(object? obj) => + obj is not null && obj is RuntimeVersion other && other.FullPath == FullPath; + + public override int GetHashCode() => FullPath.GetHashCode(); + + public override string ToString() => FullPath; + } + + [GeneratedRegex(@"^(\S+)\s(\d+\.\d+\.\d+)(-([a-z]+)\.(\d+\.\d+\.\d+))?\s\[(\S+)\]$")] + private static partial Regex RuntimeRegex(); + + /// + /// Parses the output of `dotnet --list-runtimes` to get a map from a runtime to the location of + /// the newest version of the runtime. + /// It is assume that the format of a listed runtime is something like: + /// Microsoft.NETCore.App 7.0.2 [/usr/share/dotnet/shared/Microsoft.NETCore.App] + /// + private static Dictionary ParseRuntimes(IList listed) + { + // Parse listed runtimes. + var runtimes = new Dictionary(); + listed.ForEach(r => + { + var match = RuntimeRegex().Match(r); + if (match.Success) + { + runtimes.AddOrUpdate(match.Groups[1].Value, new RuntimeVersion(match.Groups[6].Value, match.Groups[2].Value, match.Groups[4].Value, match.Groups[5].Value)); + } + }); + + return runtimes; + } + + /// + /// Returns a dictionary mapping runtimes to their newest version. + /// + internal Dictionary GetNewestRuntimes() + { + var listed = dotNet.GetListedRuntimes(); + return ParseRuntimes(listed); } /// @@ -69,24 +145,33 @@ namespace Semmle.Extraction.CSharp.Standalone } } + private IEnumerable GetRuntimes() + { + // Gets the newest version of the installed runtimes. + var newestRuntimes = GetNewestRuntimes(); + + // Location of the newest .NET Core Runtime. + if (newestRuntimes.TryGetValue(netCoreApp, out var netCoreVersion)) + { + yield return netCoreVersion.FullPath; + } + + // Location of the newest ASP.NET Core Runtime. + if (newestRuntimes.TryGetValue(aspNetCoreApp, out var aspNetCoreVersion)) + { + yield return aspNetCoreVersion.FullPath; + } + + foreach (var r in DesktopRuntimes) + yield return r; + + // A bad choice if it's the self-contained runtime distributed in codeql dist. + yield return ExecutingRuntime; + } + /// /// Gets the .NET runtime location to use for extraction /// - public static string GetRuntime(bool useSelfContained) => useSelfContained ? ExecutingRuntime : Runtimes.First(); - - private static IEnumerable Runtimes - { - get - { - foreach (var r in CoreRuntimes) - yield return r; - - foreach (var r in DesktopRuntimes) - yield return r; - - // A bad choice if it's the self-contained runtime distributed in codeql dist. - yield return ExecutingRuntime; - } - } + public string GetRuntime(bool useSelfContained) => useSelfContained ? ExecutingRuntime : GetRuntimes().First(); } } diff --git a/csharp/extractor/Semmle.Extraction.CSharp/Extractor/CsProjFile.cs b/csharp/extractor/Semmle.Extraction.CSharp/Extractor/CsProjFile.cs index 385f1cc87c0..8a8cf79cb59 100644 --- a/csharp/extractor/Semmle.Extraction.CSharp/Extractor/CsProjFile.cs +++ b/csharp/extractor/Semmle.Extraction.CSharp/Extractor/CsProjFile.cs @@ -100,7 +100,7 @@ namespace Semmle.Extraction.CSharp // Figure out if it's dotnet core - var netCoreProjectFile = root.GetAttribute("Sdk") == "Microsoft.NET.Sdk"; + var netCoreProjectFile = root.GetAttribute("Sdk").StartsWith("Microsoft.NET.Sdk"); if (netCoreProjectFile) { diff --git a/csharp/extractor/Semmle.Extraction.Tests/Runtime.cs b/csharp/extractor/Semmle.Extraction.Tests/Runtime.cs new file mode 100644 index 00000000000..07102d9a758 --- /dev/null +++ b/csharp/extractor/Semmle.Extraction.Tests/Runtime.cs @@ -0,0 +1,104 @@ +using Xunit; +using System.Collections.Generic; +using Semmle.BuildAnalyser; +using Semmle.Extraction.CSharp.Standalone; + +namespace Semmle.Extraction.Tests +{ + internal class DotNetStub : IDotNet + { + private readonly IList runtimes; + + public DotNetStub(IList runtimes) => this.runtimes = runtimes; + + public bool AddPackage(string folder, string package) => true; + + public bool New(string folder) => true; + + public bool RestoreToDirectory(string project, string directory, string? pathToNugetConfig = null) => true; + + public IList GetListedRuntimes() => runtimes; + } + + public class RuntimeTests + { + private static string FixExpectedPathOnWindows(string path) => path.Replace('\\', '/'); + + [Fact] + public void TestRuntime1() + { + // Setup + var listedRuntimes = new List { + "Microsoft.AspNetCore.App 5.0.12 [/path/dotnet/shared/Microsoft.AspNetCore.App]", + "Microsoft.AspNetCore.App 6.0.4 [/path/dotnet/shared/Microsoft.AspNetCore.App]", + "Microsoft.AspNetCore.App 7.0.0 [/path/dotnet/shared/Microsoft.AspNetCore.App]", + "Microsoft.AspNetCore.App 7.0.2 [/path/dotnet/shared/Microsoft.AspNetCore.App]", + "Microsoft.NETCore.App 5.0.12 [/path/dotnet/shared/Microsoft.NETCore.App]", + "Microsoft.NETCore.App 6.0.4 [/path/dotnet/shared/Microsoft.NETCore.App]", + "Microsoft.NETCore.App 7.0.0 [/path/dotnet/shared/Microsoft.NETCore.App]", + "Microsoft.NETCore.App 7.0.2 [/path/dotnet/shared/Microsoft.NETCore.App]" + }; + var dotnet = new DotNetStub(listedRuntimes); + var runtime = new Runtime(dotnet); + + // Execute + var runtimes = runtime.GetNewestRuntimes(); + + // Verify + Assert.Equal(2, runtimes.Count); + + Assert.True(runtimes.TryGetValue("Microsoft.AspNetCore.App", out var aspNetCoreApp)); + Assert.Equal("/path/dotnet/shared/Microsoft.AspNetCore.App/7.0.2", FixExpectedPathOnWindows(aspNetCoreApp.FullPath)); + + Assert.True(runtimes.TryGetValue("Microsoft.NETCore.App", out var netCoreApp)); + Assert.Equal("/path/dotnet/shared/Microsoft.NETCore.App/7.0.2", FixExpectedPathOnWindows(netCoreApp.FullPath)); + } + + [Fact] + public void TestRuntime2() + { + // Setup + var listedRuntimes = new List + { + "Microsoft.NETCore.App 7.0.2 [/path/dotnet/shared/Microsoft.NETCore.App]", + "Microsoft.NETCore.App 8.0.0-preview.5.43280.8 [/path/dotnet/shared/Microsoft.NETCore.App]", + "Microsoft.NETCore.App 8.0.0-preview.5.23280.8 [/path/dotnet/shared/Microsoft.NETCore.App]" + }; + var dotnet = new DotNetStub(listedRuntimes); + var runtime = new Runtime(dotnet); + + // Execute + var runtimes = runtime.GetNewestRuntimes(); + + // Verify + Assert.Single(runtimes); + + Assert.True(runtimes.TryGetValue("Microsoft.NETCore.App", out var netCoreApp)); + Assert.Equal("/path/dotnet/shared/Microsoft.NETCore.App/8.0.0-preview.5.43280.8", FixExpectedPathOnWindows(netCoreApp.FullPath)); + } + + [Fact] + public void TestRuntime3() + { + // Setup + var listedRuntimes = new List + { + "Microsoft.NETCore.App 7.0.2 [/path/dotnet/shared/Microsoft.NETCore.App]", + "Microsoft.NETCore.App 8.0.0-rc.4.43280.8 [/path/dotnet/shared/Microsoft.NETCore.App]", + "Microsoft.NETCore.App 8.0.0-preview.5.23280.8 [/path/dotnet/shared/Microsoft.NETCore.App]" + }; + var dotnet = new DotNetStub(listedRuntimes); + var runtime = new Runtime(dotnet); + + // Execute + var runtimes = runtime.GetNewestRuntimes(); + + // Verify + Assert.Single(runtimes); + + Assert.True(runtimes.TryGetValue("Microsoft.NETCore.App", out var netCoreApp)); + Assert.Equal("/path/dotnet/shared/Microsoft.NETCore.App/8.0.0-rc.4.43280.8", FixExpectedPathOnWindows(netCoreApp.FullPath)); + } + + } +} diff --git a/csharp/extractor/Semmle.Util/DictionaryExtensions.cs b/csharp/extractor/Semmle.Util/DictionaryExtensions.cs index 95c5443585f..fa932f0cd9c 100644 --- a/csharp/extractor/Semmle.Util/DictionaryExtensions.cs +++ b/csharp/extractor/Semmle.Util/DictionaryExtensions.cs @@ -1,4 +1,5 @@ -using System.Collections.Generic; +using System; +using System.Collections.Generic; namespace Semmle.Util { @@ -18,5 +19,17 @@ namespace Semmle.Util } list.Add(element); } + + /// + /// Adds a new value or replaces the existing value (if the new value is greater than the existing) + /// in dictionary for the given key. + /// + public static void AddOrUpdate(this Dictionary dict, T1 key, T2 value) where T1 : notnull where T2 : IComparable + { + if (!dict.TryGetValue(key, out var existing) || existing.CompareTo(value) < 0) + { + dict[key] = value; + } + } } } diff --git a/csharp/ql/campaigns/Solorigate/lib/CHANGELOG.md b/csharp/ql/campaigns/Solorigate/lib/CHANGELOG.md index b466881d9d7..f410a14eae6 100644 --- a/csharp/ql/campaigns/Solorigate/lib/CHANGELOG.md +++ b/csharp/ql/campaigns/Solorigate/lib/CHANGELOG.md @@ -1,3 +1,11 @@ +## 1.6.0 + +No user-facing changes. + +## 1.5.4 + +No user-facing changes. + ## 1.5.3 No user-facing changes. diff --git a/csharp/ql/campaigns/Solorigate/lib/change-notes/released/1.5.4.md b/csharp/ql/campaigns/Solorigate/lib/change-notes/released/1.5.4.md new file mode 100644 index 00000000000..5ff5ac8ebb7 --- /dev/null +++ b/csharp/ql/campaigns/Solorigate/lib/change-notes/released/1.5.4.md @@ -0,0 +1,3 @@ +## 1.5.4 + +No user-facing changes. diff --git a/csharp/ql/campaigns/Solorigate/lib/change-notes/released/1.6.0.md b/csharp/ql/campaigns/Solorigate/lib/change-notes/released/1.6.0.md new file mode 100644 index 00000000000..f5bad5808f6 --- /dev/null +++ b/csharp/ql/campaigns/Solorigate/lib/change-notes/released/1.6.0.md @@ -0,0 +1,3 @@ +## 1.6.0 + +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 232224b0e26..c4f0b07d533 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.3 +lastReleaseVersion: 1.6.0 diff --git a/csharp/ql/campaigns/Solorigate/lib/qlpack.yml b/csharp/ql/campaigns/Solorigate/lib/qlpack.yml index 8d19bca1d61..3090b1dab71 100644 --- a/csharp/ql/campaigns/Solorigate/lib/qlpack.yml +++ b/csharp/ql/campaigns/Solorigate/lib/qlpack.yml @@ -1,5 +1,5 @@ name: codeql/csharp-solorigate-all -version: 1.5.4-dev +version: 1.6.1-dev groups: - csharp - solorigate diff --git a/csharp/ql/campaigns/Solorigate/src/CHANGELOG.md b/csharp/ql/campaigns/Solorigate/src/CHANGELOG.md index b466881d9d7..f410a14eae6 100644 --- a/csharp/ql/campaigns/Solorigate/src/CHANGELOG.md +++ b/csharp/ql/campaigns/Solorigate/src/CHANGELOG.md @@ -1,3 +1,11 @@ +## 1.6.0 + +No user-facing changes. + +## 1.5.4 + +No user-facing changes. + ## 1.5.3 No user-facing changes. diff --git a/csharp/ql/campaigns/Solorigate/src/change-notes/released/1.5.4.md b/csharp/ql/campaigns/Solorigate/src/change-notes/released/1.5.4.md new file mode 100644 index 00000000000..5ff5ac8ebb7 --- /dev/null +++ b/csharp/ql/campaigns/Solorigate/src/change-notes/released/1.5.4.md @@ -0,0 +1,3 @@ +## 1.5.4 + +No user-facing changes. diff --git a/csharp/ql/campaigns/Solorigate/src/change-notes/released/1.6.0.md b/csharp/ql/campaigns/Solorigate/src/change-notes/released/1.6.0.md new file mode 100644 index 00000000000..f5bad5808f6 --- /dev/null +++ b/csharp/ql/campaigns/Solorigate/src/change-notes/released/1.6.0.md @@ -0,0 +1,3 @@ +## 1.6.0 + +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 232224b0e26..c4f0b07d533 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.3 +lastReleaseVersion: 1.6.0 diff --git a/csharp/ql/campaigns/Solorigate/src/qlpack.yml b/csharp/ql/campaigns/Solorigate/src/qlpack.yml index 6bcc2def6f8..68379dc0d13 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.4-dev +version: 1.6.1-dev groups: - csharp - solorigate diff --git a/csharp/ql/lib/CHANGELOG.md b/csharp/ql/lib/CHANGELOG.md index 8fc9f20a131..57ddb064fe6 100644 --- a/csharp/ql/lib/CHANGELOG.md +++ b/csharp/ql/lib/CHANGELOG.md @@ -1,3 +1,17 @@ +## 0.7.0 + +### Major Analysis Improvements + +* The data flow library now performs type strengthening. This increases precision for all data flow queries by excluding paths that can be inferred to be impossible due to incompatible types. + +### Minor Analysis Improvements + +* Additional support for `command-injection`, `ldap-injection`, `log-injection`, and `url-redirection` sink kinds for Models as Data. + +## 0.6.4 + +No user-facing changes. + ## 0.6.3 ### Major 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 deleted file mode 100644 index c0abd8f06c0..00000000000 --- a/csharp/ql/lib/change-notes/2023-05-09-models-as-data.md +++ /dev/null @@ -1,4 +0,0 @@ ---- -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-06-08-type-strengthening.md b/csharp/ql/lib/change-notes/2023-06-08-type-strengthening.md deleted file mode 100644 index 60daaa53058..00000000000 --- a/csharp/ql/lib/change-notes/2023-06-08-type-strengthening.md +++ /dev/null @@ -1,4 +0,0 @@ ---- -category: majorAnalysis ---- -* The data flow library now performs type strengthening. This increases precision for all data flow queries by excluding paths that can be inferred to be impossible due to incompatible types. diff --git a/csharp/ql/lib/change-notes/2023-07-12-default-stateconfigsig-predicates.md b/csharp/ql/lib/change-notes/2023-07-12-default-stateconfigsig-predicates.md new file mode 100644 index 00000000000..c9aba58603a --- /dev/null +++ b/csharp/ql/lib/change-notes/2023-07-12-default-stateconfigsig-predicates.md @@ -0,0 +1,6 @@ +--- +category: feature +--- +* The `DataFlow::StateConfigSig` signature module has gained default implementations for `isBarrier/2` and `isAdditionalFlowStep/4`. + Hence it is no longer needed to provide `none()` implementations of these predicates if they are not needed. + diff --git a/csharp/ql/lib/change-notes/released/0.6.4.md b/csharp/ql/lib/change-notes/released/0.6.4.md new file mode 100644 index 00000000000..7e98b0159fc --- /dev/null +++ b/csharp/ql/lib/change-notes/released/0.6.4.md @@ -0,0 +1,3 @@ +## 0.6.4 + +No user-facing changes. diff --git a/csharp/ql/lib/change-notes/released/0.7.0.md b/csharp/ql/lib/change-notes/released/0.7.0.md new file mode 100644 index 00000000000..87b2fbe9b1b --- /dev/null +++ b/csharp/ql/lib/change-notes/released/0.7.0.md @@ -0,0 +1,9 @@ +## 0.7.0 + +### Major Analysis Improvements + +* The data flow library now performs type strengthening. This increases precision for all data flow queries by excluding paths that can be inferred to be impossible due to incompatible types. + +### Minor Analysis Improvements + +* Additional support for `command-injection`, `ldap-injection`, `log-injection`, and `url-redirection` sink kinds for Models as Data. diff --git a/csharp/ql/lib/codeql-pack.release.yml b/csharp/ql/lib/codeql-pack.release.yml index b7dafe32c5d..c761f3e7ab4 100644 --- a/csharp/ql/lib/codeql-pack.release.yml +++ b/csharp/ql/lib/codeql-pack.release.yml @@ -1,2 +1,2 @@ --- -lastReleaseVersion: 0.6.3 +lastReleaseVersion: 0.7.0 diff --git a/csharp/ql/lib/qlpack.yml b/csharp/ql/lib/qlpack.yml index 43f20151fdc..3fd91596063 100644 --- a/csharp/ql/lib/qlpack.yml +++ b/csharp/ql/lib/qlpack.yml @@ -1,5 +1,5 @@ name: codeql/csharp-all -version: 0.6.4-dev +version: 0.7.1-dev groups: csharp dbscheme: semmlecode.csharp.dbscheme extractor: csharp diff --git a/csharp/ql/lib/semmle/code/csharp/dataflow/FlowSummary.qll b/csharp/ql/lib/semmle/code/csharp/dataflow/FlowSummary.qll index fef05b35f0b..a96ac4cedc9 100644 --- a/csharp/ql/lib/semmle/code/csharp/dataflow/FlowSummary.qll +++ b/csharp/ql/lib/semmle/code/csharp/dataflow/FlowSummary.qll @@ -110,8 +110,12 @@ module SummaryComponentStack { result = singleton(SummaryComponent::syntheticGlobal(synthetic)) } - /** Gets a textual representation of this stack used for flow summaries. */ - string getComponentStack(SummaryComponentStack s) { result = Impl::Public::getComponentStack(s) } + /** + * DEPRECATED: Use the member predicate `getMadRepresentation` instead. + * + * Gets a textual representation of this stack used for flow summaries. + */ + deprecated string getComponentStack(SummaryComponentStack s) { result = s.getMadRepresentation() } } class SummarizedCallable = Impl::Public::SummarizedCallable; diff --git a/csharp/ql/lib/semmle/code/csharp/dataflow/internal/ContentDataFlow.qll b/csharp/ql/lib/semmle/code/csharp/dataflow/internal/ContentDataFlow.qll index 9d483900aeb..fc937b88fa9 100644 --- a/csharp/ql/lib/semmle/code/csharp/dataflow/internal/ContentDataFlow.qll +++ b/csharp/ql/lib/semmle/code/csharp/dataflow/internal/ContentDataFlow.qll @@ -107,8 +107,6 @@ module Global { predicate isBarrier = ContentConfig::isBarrier/1; - predicate isBarrier(DataFlow::Node node, FlowState state) { none() } - DataFlow::FlowFeature getAFeature() { result = ContentConfig::getAFeature() } // needed to record reads/stores inside summarized callables diff --git a/csharp/ql/lib/semmle/code/csharp/dataflow/internal/DataFlow.qll b/csharp/ql/lib/semmle/code/csharp/dataflow/internal/DataFlow.qll index f34554d6eea..47329d133a4 100644 --- a/csharp/ql/lib/semmle/code/csharp/dataflow/internal/DataFlow.qll +++ b/csharp/ql/lib/semmle/code/csharp/dataflow/internal/DataFlow.qll @@ -114,7 +114,7 @@ signature module StateConfigSig { * Holds if data flow through `node` is prohibited when the flow state is * `state`. */ - predicate isBarrier(Node node, FlowState state); + default predicate isBarrier(Node node, FlowState state) { none() } /** Holds if data flow into `node` is prohibited. */ default predicate isBarrierIn(Node node) { none() } @@ -131,7 +131,9 @@ signature module StateConfigSig { * Holds if data may flow from `node1` to `node2` in addition to the normal data-flow steps. * This step is only applicable in `state1` and updates the flow state to `state2`. */ - predicate isAdditionalFlowStep(Node node1, FlowState state1, Node node2, FlowState state2); + default predicate isAdditionalFlowStep(Node node1, FlowState state1, Node node2, FlowState state2) { + none() + } /** * Holds if an arbitrary number of implicit read steps of content `c` may be 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 284fff191ae..fe8633e9218 100644 --- a/csharp/ql/lib/semmle/code/csharp/dataflow/internal/DataFlowImpl.qll +++ b/csharp/ql/lib/semmle/code/csharp/dataflow/internal/DataFlowImpl.qll @@ -254,6 +254,11 @@ module Impl { not fullBarrier(node2) } + pragma[nomagic] + private predicate isUnreachableInCall1(NodeEx n, LocalCallContextSpecificCall cc) { + isUnreachableInCallCached(n.asNode(), cc.getCall()) + } + /** * Holds if data can flow in one local step from `node1` to `node2`. */ @@ -460,7 +465,6 @@ module Impl { * The Boolean `cc` records whether the node is reached through an * argument in a call. */ - pragma[assume_small_delta] private predicate fwdFlow(NodeEx node, Cc cc) { sourceNode(node, _) and if hasSourceCallCtx() then cc = true else cc = false @@ -570,7 +574,6 @@ module Impl { /** * Holds if `c` is the target of a store in the flow covered by `fwdFlow`. */ - pragma[assume_small_delta] pragma[nomagic] private predicate fwdFlowConsCand(Content c) { exists(NodeEx mid, NodeEx node | @@ -1216,7 +1219,6 @@ module Impl { fwdFlow1(_, _, _, _, _, _, t0, t, ap, _) and t0 != t } - pragma[assume_small_delta] pragma[nomagic] private predicate fwdFlow0( NodeEx node, FlowState state, Cc cc, ParamNodeOption summaryCtx, TypOption argT, @@ -2111,7 +2113,7 @@ module Impl { NodeEx node1, FlowState state, NodeEx node2, boolean preservesValue, DataFlowType t, LocalCallContext cc ) { - not isUnreachableInCallCached(node2.asNode(), cc.(LocalCallContextSpecificCall).getCall()) and + not isUnreachableInCall1(node2, cc) and ( localFlowEntry(node1, pragma[only_bind_into](state)) and ( @@ -2126,7 +2128,7 @@ module Impl { ) and node1 != node2 and cc.relevantFor(node1.getEnclosingCallable()) and - not isUnreachableInCallCached(node1.asNode(), cc.(LocalCallContextSpecificCall).getCall()) + not isUnreachableInCall1(node1, cc) or exists(NodeEx mid | localFlowStepPlus(node1, pragma[only_bind_into](state), mid, preservesValue, t, cc) and @@ -2163,10 +2165,8 @@ module Impl { preservesValue = false and t = node2.getDataFlowType() and callContext.relevantFor(node1.getEnclosingCallable()) and - not exists(DataFlowCall call | call = callContext.(LocalCallContextSpecificCall).getCall() | - isUnreachableInCallCached(node1.asNode(), call) or - isUnreachableInCallCached(node2.asNode(), call) - ) + not isUnreachableInCall1(node1, callContext) and + not isUnreachableInCall1(node2, callContext) } } @@ -2706,7 +2706,7 @@ module Impl { ParamNodeEx getParamNode() { result = p } - override string toString() { result = p + ": " + ap } + override string toString() { result = p + concat(" : " + ppReprType(t)) + " " + ap } predicate hasLocationInfo( string filepath, int startline, int startcolumn, int endline, int endcolumn @@ -2758,12 +2758,21 @@ module Impl { ) } + private predicate forceUnfold(AccessPathApprox apa) { + forceHighPrecision(apa.getHead()) + or + exists(Content c2 | + apa = TConsCons(_, _, c2, _) and + forceHighPrecision(c2) + ) + } + /** * Holds with `unfold = false` if a precise head-tail representation of `apa` is * expected to be expensive. Holds with `unfold = true` otherwise. */ private predicate evalUnfold(AccessPathApprox apa, boolean unfold) { - if forceHighPrecision(apa.getHead()) + if forceUnfold(apa) then unfold = true else exists(int aps, int nodes, int apLimit, int tupleLimit | @@ -2777,7 +2786,6 @@ module Impl { /** * Gets the number of `AccessPath`s that correspond to `apa`. */ - pragma[assume_small_delta] private int countAps(AccessPathApprox apa) { evalUnfold(apa, false) and result = 1 and @@ -2796,7 +2804,6 @@ module Impl { * that it is expanded to a precise head-tail representation. */ language[monotonicAggregates] - pragma[assume_small_delta] private int countPotentialAps(AccessPathApprox apa) { apa instanceof AccessPathApproxNil and result = 1 or @@ -2833,7 +2840,6 @@ module Impl { } private newtype TPathNode = - pragma[assume_small_delta] TPathNodeMid( NodeEx node, FlowState state, CallContext cc, SummaryCtx sc, DataFlowType t, AccessPath ap ) { @@ -2918,7 +2924,6 @@ module Impl { override AccessPathFrontHead getFront() { result = TFrontHead(head_) } - pragma[assume_small_delta] override AccessPathApproxCons getApprox() { result = TConsNil(head_, t) and tail_ = TAccessPathNil() or @@ -2927,7 +2932,6 @@ module Impl { result = TCons1(head_, this.length()) } - pragma[assume_small_delta] override int length() { result = 1 + tail_.length() } private string toStringImpl(boolean needsSuffix) { @@ -3097,6 +3101,12 @@ module Impl { result = " <" + this.(PathNodeMid).getCallContext().toString() + ">" } + private string ppSummaryCtx() { + this instanceof PathNodeSink and result = "" + or + result = " <" + this.(PathNodeMid).getSummaryCtx().toString() + ">" + } + /** Gets a textual representation of this element. */ string toString() { result = this.getNodeEx().toString() + this.ppType() + this.ppAp() } @@ -3105,7 +3115,9 @@ module Impl { * representation of the call context. */ string toStringWithContext() { - result = this.getNodeEx().toString() + this.ppType() + this.ppAp() + this.ppCtx() + result = + this.getNodeEx().toString() + this.ppType() + this.ppAp() + this.ppCtx() + + this.ppSummaryCtx() } /** @@ -3379,7 +3391,6 @@ module Impl { * Holds if data may flow from `mid` to `node`. The last step in or out of * a callable is recorded by `cc`. */ - pragma[assume_small_delta] pragma[nomagic] private predicate pathStep0( PathNodeMid mid, NodeEx node, FlowState state, CallContext cc, SummaryCtx sc, DataFlowType t, @@ -3592,7 +3603,6 @@ module Impl { ) } - pragma[assume_small_delta] pragma[nomagic] private predicate pathThroughCallable0( DataFlowCall call, PathNodeMid mid, ReturnKindExt kind, FlowState state, CallContext cc, diff --git a/csharp/ql/lib/semmle/code/csharp/dataflow/internal/DataFlowImplCommon.qll b/csharp/ql/lib/semmle/code/csharp/dataflow/internal/DataFlowImplCommon.qll index 0d4c033c95d..aff14e7b44d 100644 --- a/csharp/ql/lib/semmle/code/csharp/dataflow/internal/DataFlowImplCommon.qll +++ b/csharp/ql/lib/semmle/code/csharp/dataflow/internal/DataFlowImplCommon.qll @@ -187,7 +187,6 @@ private module LambdaFlow { else any() } - pragma[assume_small_delta] pragma[nomagic] predicate revLambdaFlow0( DataFlowCall lambdaCall, LambdaCallKind kind, Node node, DataFlowType t, boolean toReturn, @@ -274,7 +273,6 @@ private module LambdaFlow { ) } - pragma[assume_small_delta] pragma[nomagic] predicate revLambdaFlowOut( DataFlowCall lambdaCall, LambdaCallKind kind, TReturnPositionSimple pos, DataFlowType t, diff --git a/csharp/ql/lib/semmle/code/csharp/dataflow/internal/FlowSummaryImpl.qll b/csharp/ql/lib/semmle/code/csharp/dataflow/internal/FlowSummaryImpl.qll index 9ea7c44c50c..7977e18120f 100644 --- a/csharp/ql/lib/semmle/code/csharp/dataflow/internal/FlowSummaryImpl.qll +++ b/csharp/ql/lib/semmle/code/csharp/dataflow/internal/FlowSummaryImpl.qll @@ -23,29 +23,30 @@ module Public { * content type, or a return kind. */ class SummaryComponent extends TSummaryComponent { - /** Gets a textual representation of this summary component. */ - string toString() { - exists(ContentSet c | this = TContentSummaryComponent(c) and result = c.toString()) - or - exists(ContentSet c | this = TWithoutContentSummaryComponent(c) and result = "without " + c) - or - exists(ContentSet c | this = TWithContentSummaryComponent(c) and result = "with " + c) + /** Gets a textual representation of this component used for MaD models. */ + string getMadRepresentation() { + result = getMadRepresentationSpecific(this) or exists(ArgumentPosition pos | - this = TParameterSummaryComponent(pos) and result = "parameter " + pos + this = TParameterSummaryComponent(pos) and + result = "Parameter[" + getArgumentPosition(pos) + "]" ) or exists(ParameterPosition pos | - this = TArgumentSummaryComponent(pos) and result = "argument " + pos + this = TArgumentSummaryComponent(pos) and + result = "Argument[" + getParameterPosition(pos) + "]" ) or - exists(ReturnKind rk | this = TReturnSummaryComponent(rk) and result = "return (" + rk + ")") - or - exists(SummaryComponent::SyntheticGlobal sg | - this = TSyntheticGlobalSummaryComponent(sg) and - result = "synthetic global (" + sg + ")" + exists(string synthetic | + this = TSyntheticGlobalSummaryComponent(synthetic) and + result = "SyntheticGlobal[" + synthetic + "]" ) + or + this = TReturnSummaryComponent(getReturnValueKind()) and result = "ReturnValue" } + + /** Gets a textual representation of this summary component. */ + string toString() { result = this.getMadRepresentation() } } /** Provides predicates for constructing summary components. */ @@ -110,7 +111,6 @@ module Public { } /** Gets the stack obtained by dropping the first `i` elements, if any. */ - pragma[assume_small_delta] SummaryComponentStack drop(int i) { i = 0 and result = this or @@ -125,19 +125,22 @@ module Public { this = TSingletonSummaryComponentStack(result) or result = this.tail().bottom() } - /** Gets a textual representation of this stack. */ - string toString() { + /** Gets a textual representation of this stack used for MaD models. */ + string getMadRepresentation() { exists(SummaryComponent head, SummaryComponentStack tail | head = this.head() and tail = this.tail() and - result = tail + "." + head + result = tail.getMadRepresentation() + "." + head.getMadRepresentation() ) or exists(SummaryComponent c | this = TSingletonSummaryComponentStack(c) and - result = c.toString() + result = c.getMadRepresentation() ) } + + /** Gets a textual representation of this stack. */ + string toString() { result = this.getMadRepresentation() } } /** Provides predicates for constructing stacks of summary components. */ @@ -166,42 +169,6 @@ module Public { SummaryComponentStack return(ReturnKind rk) { result = singleton(SummaryComponent::return(rk)) } } - /** Gets a textual representation of this component used for flow summaries. */ - private string getComponent(SummaryComponent sc) { - result = getComponentSpecific(sc) - or - exists(ArgumentPosition pos | - sc = TParameterSummaryComponent(pos) and - result = "Parameter[" + getArgumentPosition(pos) + "]" - ) - or - exists(ParameterPosition pos | - sc = TArgumentSummaryComponent(pos) and - result = "Argument[" + getParameterPosition(pos) + "]" - ) - or - exists(string synthetic | - sc = TSyntheticGlobalSummaryComponent(synthetic) and - result = "SyntheticGlobal[" + synthetic + "]" - ) - or - sc = TReturnSummaryComponent(getReturnValueKind()) and result = "ReturnValue" - } - - /** Gets a textual representation of this stack used for flow summaries. */ - string getComponentStack(SummaryComponentStack stack) { - exists(SummaryComponent head, SummaryComponentStack tail | - head = stack.head() and - tail = stack.tail() and - result = getComponentStack(tail) + "." + getComponent(head) - ) - or - exists(SummaryComponent c | - stack = TSingletonSummaryComponentStack(c) and - result = getComponent(c) - ) - } - /** * A class that exists for QL technical reasons only (the IPA type used * to represent component stacks needs to be bounded). @@ -1382,8 +1349,8 @@ module Private { c.relevantSummary(input, output, preservesValue) and csv = c.getCallableCsv() // Callable information - + getComponentStack(input) + ";" // input - + getComponentStack(output) + ";" // output + + input.getMadRepresentation() + ";" // input + + output.getMadRepresentation() + ";" // output + renderKind(preservesValue) + ";" // kind + renderProvenance(c) // provenance ) diff --git a/csharp/ql/lib/semmle/code/csharp/dataflow/internal/FlowSummaryImplSpecific.qll b/csharp/ql/lib/semmle/code/csharp/dataflow/internal/FlowSummaryImplSpecific.qll index 096f3b765f4..2145073b9f9 100644 --- a/csharp/ql/lib/semmle/code/csharp/dataflow/internal/FlowSummaryImplSpecific.qll +++ b/csharp/ql/lib/semmle/code/csharp/dataflow/internal/FlowSummaryImplSpecific.qll @@ -186,7 +186,7 @@ SummaryComponent interpretComponentSpecific(AccessPathToken c) { ) } -/** Gets the textual representation of the content in the format used for flow summaries. */ +/** Gets the textual representation of the content in the format used for MaD models. */ private string getContentSpecific(Content c) { c = TElementContent() and result = "Element" or @@ -197,8 +197,8 @@ private string getContentSpecific(Content c) { exists(SyntheticField f | c = TSyntheticFieldContent(f) and result = "SyntheticField[" + f + "]") } -/** Gets the textual representation of a summary component in the format used for flow summaries. */ -string getComponentSpecific(SummaryComponent sc) { +/** Gets the textual representation of a summary component in the format used for MaD models. */ +string getMadRepresentationSpecific(SummaryComponent sc) { exists(Content c | sc = TContentSummaryComponent(c) and result = getContentSpecific(c)) or sc = TWithoutContentSummaryComponent(_) and result = "WithoutElement" diff --git a/csharp/ql/lib/semmle/code/csharp/frameworks/EntityFramework.qll b/csharp/ql/lib/semmle/code/csharp/frameworks/EntityFramework.qll index 14590895b14..4987ab45448 100644 --- a/csharp/ql/lib/semmle/code/csharp/frameworks/EntityFramework.qll +++ b/csharp/ql/lib/semmle/code/csharp/frameworks/EntityFramework.qll @@ -410,7 +410,7 @@ module EntityFramework { ) { this = dbSet.getDbContextClass() and this.output(output, mapped, dbSet) and - result = dbSet.getFullName() + "#" + SummaryComponentStack::getComponentStack(output) + result = dbSet.getFullName() + "#" + output.getMadRepresentation() } } diff --git a/csharp/ql/lib/semmle/code/csharp/security/auth/MissingFunctionLevelAccessControlQuery.qll b/csharp/ql/lib/semmle/code/csharp/security/auth/MissingFunctionLevelAccessControlQuery.qll new file mode 100644 index 00000000000..7623cb6b9f7 --- /dev/null +++ b/csharp/ql/lib/semmle/code/csharp/security/auth/MissingFunctionLevelAccessControlQuery.qll @@ -0,0 +1,183 @@ +/** Definitions for the missing function level access control query */ + +import csharp +import semmle.code.csharp.frameworks.microsoft.AspNetCore +import semmle.code.csharp.frameworks.system.web.UI +import semmle.code.asp.WebConfig + +/** A method representing an action for a web endpoint. */ +abstract class ActionMethod extends Method { + /** + * Gets a string that can indicate what this method does to determine if it should have an auth check; + * such as its method name, class name, or file path. + */ + string getADescription() { + result = + [ + this.getName(), this.getDeclaringType().getBaseClass*().getName(), + this.getDeclaringType().getFile().getRelativePath() + ] + } + + /** Holds if this method may need an authorization check. */ + predicate needsAuth() { + this.getADescription() + .regexpReplaceAll("([a-z])([A-Z])", "$1_$2") + // separate camelCase words + .toLowerCase() + .regexpMatch(".*(edit|delete|modify|admin|superuser).*") + } + + /** Gets a callable for which if it contains an auth check, this method should be considered authenticated. */ + Callable getAnAuthorizingCallable() { result = this } + + /** + * Gets a possible url route that could refer to this action, + * which would be covered by `` configurations specifying a prefix of it. + */ + string getARoute() { result = this.getDeclaringType().getFile().getRelativePath() } +} + +/** An action method in the MVC framework. */ +private class MvcActionMethod extends ActionMethod { + MvcActionMethod() { this = any(MicrosoftAspNetCoreMvcController c).getAnActionMethod() } +} + +/** An action method on a subclass of `System.Web.UI.Page`. */ +private class WebFormActionMethod extends ActionMethod { + WebFormActionMethod() { + this.getDeclaringType().getBaseClass+() instanceof SystemWebUIPageClass and + this.getAParameter().getType().getName().matches("%EventArgs") + } + + override Callable getAnAuthorizingCallable() { + result = super.getAnAuthorizingCallable() + or + result.getDeclaringType() = this.getDeclaringType() and + result.getName() = "Page_Load" + } + + override string getARoute() { + exists(string physicalRoute | physicalRoute = super.getARoute() | + result = physicalRoute + or + exists(string absolutePhysical | + virtualRouteMapping(result, absolutePhysical) and + physicalRouteMatches(absolutePhysical, physicalRoute) + ) + ) + } +} + +/** + * Holds if `virtualRoute` is a URL path + * that can map to the corresponding `physicalRoute` filepath + * through a call to `MapPageRoute` + */ +private predicate virtualRouteMapping(string virtualRoute, string physicalRoute) { + exists(MethodCall mapPageRouteCall, StringLiteral virtualLit, StringLiteral physicalLit | + mapPageRouteCall + .getTarget() + .hasQualifiedName("System.Web.Routing", "RouteCollection", "MapPageRoute") and + virtualLit = mapPageRouteCall.getArgument(1) and + physicalLit = mapPageRouteCall.getArgument(2) and + virtualLit.getValue() = virtualRoute and + physicalLit.getValue() = physicalRoute + ) +} + +/** Holds if the filepath `route` can refer to `actual` after expanding a '~". */ +bindingset[route, actual] +private predicate physicalRouteMatches(string route, string actual) { + route = actual + or + route.charAt(0) = "~" and + exists(string dir | actual = dir + route.suffix(1) + ".cs") +} + +/** An expression that indicates that some authorization/authentication check is being performed. */ +class AuthExpr extends Expr { + AuthExpr() { + this.(MethodCall) + .getTarget() + .hasQualifiedName("System.Security.Principal", "IPrincipal", "IsInRole") + or + this.(PropertyAccess) + .getTarget() + .hasQualifiedName("System.Security.Principal", "IIdentity", ["IsAuthenticated", "Name"]) + or + this.(MethodCall).getTarget().getName().toLowerCase().matches("%auth%") + or + this.(PropertyAccess).getTarget().getName().toLowerCase().matches("%auth%") + } +} + +/** Holds if `m` is a method that should have an auth check, and does indeed have one. */ +predicate hasAuthViaCode(ActionMethod m) { + m.needsAuth() and + exists(Callable caller, AuthExpr auth | + m.getAnAuthorizingCallable().calls*(caller) and + auth.getEnclosingCallable() = caller + ) +} + +/** An `` XML element. */ +class AuthorizationXmlElement extends XmlElement { + AuthorizationXmlElement() { + this.getParent() instanceof SystemWebXmlElement and + this.getName().toLowerCase() = "authorization" + } + + /** Holds if this element has a `` element to deny access to a resource. */ + predicate hasDenyElement() { this.getAChild().getName().toLowerCase() = "deny" } + + /** Gets the physical filepath of this element. */ + string getPhysicalPath() { result = this.getFile().getParentContainer().getRelativePath() } + + /** Gets the path specified by a `` tag containing this element, if any. */ + string getLocationTagPath() { + exists(LocationXmlElement loc, XmlAttribute path | + loc = this.getParent().(SystemWebXmlElement).getParent() and + path = loc.getAnAttribute() and + path.getName().toLowerCase() = "path" and + result = path.getValue() + ) + } + + /** Gets a route prefix that this configuration can refer to. */ + string getARoute() { + result = this.getLocationTagPath() + or + result = this.getPhysicalPath() + "/" + this.getLocationTagPath() + or + not exists(this.getLocationTagPath()) and + result = this.getPhysicalPath() + } +} + +/** + * Holds if the given action has an xml `authorization` tag that refers to it. + */ +predicate hasAuthViaXml(ActionMethod m) { + exists(AuthorizationXmlElement el, string rest | + el.hasDenyElement() and + m.getARoute() = el.getARoute() + rest + ) +} + +/** Holds if the given action has an attribute that indications authorization. */ +predicate hasAuthViaAttribute(ActionMethod m) { + exists(Attribute attr | attr.getType().getName().toLowerCase().matches("%auth%") | + attr = m.getAnAttribute() or + attr = m.getDeclaringType().getABaseType*().getAnAttribute() + ) +} + +/** Holds if `m` is a method that should have an auth check, but is missing it. */ +predicate missingAuth(ActionMethod m) { + m.needsAuth() and + not hasAuthViaCode(m) and + not hasAuthViaXml(m) and + not hasAuthViaAttribute(m) and + exists(m.getBody().getAChildStmt()) // exclude empty methods +} diff --git a/csharp/ql/src/CHANGELOG.md b/csharp/ql/src/CHANGELOG.md index 8e82ab07313..302ba09808f 100644 --- a/csharp/ql/src/CHANGELOG.md +++ b/csharp/ql/src/CHANGELOG.md @@ -1,3 +1,17 @@ +## 0.7.0 + +### New Queries + +* Added a new query, `cs/web/missing-function-level-access-control`, to find instances of missing authorization checks. + +### Bug Fixes + +* The query "Arbitrary file write during zip extraction ("Zip Slip")" (`cs/zipslip`) has been renamed to "Arbitrary file access during archive extraction ("Zip Slip")." + +## 0.6.4 + +No user-facing changes. + ## 0.6.3 No user-facing changes. diff --git a/csharp/ql/src/Security Features/CWE-285/MVC.cs b/csharp/ql/src/Security Features/CWE-285/MVC.cs new file mode 100644 index 00000000000..291c4544456 --- /dev/null +++ b/csharp/ql/src/Security Features/CWE-285/MVC.cs @@ -0,0 +1,13 @@ +public class ProfileController : Controller { + + // BAD: No authorization is used. + public ActionResult Edit(int id) { + ... + } + + // GOOD: The `Authorize` attribute is used. + [Authorize] + public ActionResult Delete(int id) { + ... + } +} \ No newline at end of file diff --git a/csharp/ql/src/Security Features/CWE-285/MissingAccessControl.qhelp b/csharp/ql/src/Security Features/CWE-285/MissingAccessControl.qhelp new file mode 100644 index 00000000000..d65d6010a2f --- /dev/null +++ b/csharp/ql/src/Security Features/CWE-285/MissingAccessControl.qhelp @@ -0,0 +1,54 @@ + + + + +

+Sensitive actions, such as editing or deleting content, or accessing admin pages, should have authorization checks +to ensure that they cannot be used by malicious actors. +

+ +
+ + +

+Ensure that proper authorization checks are made for sensitive actions. +For WebForms applications, the authorization tag in Web.config XML files +can be used to implement access control. The System.Web.UI.Page.User property can also be +used to verify a user's role. +For MVC applications, the Authorize attribute can be used to require authorization on specific +action methods. +

+ +
+ + +

+In the following WebForms example, the case marked BAD has no authorization checks whereas the +case marked GOOD uses User.IsInRole to check for the user's role. +

+ + + +

+The following Web.config file uses the authorization tag to deny access to anonymous users, +in a location tag to have that configuration apply to a specific path. +

+ + + +

+In the following MVC example, the case marked BAD has no authorization +checks whereas the case marked GOOD uses the Authorize attribute. +

+ + + +
+ +
  • Page.User Property - Microsoft Learn.
  • +
  • Control authorization permissions in an ASP.NET application - Microsoft Learn.
  • +
  • Simple authorization in ASP.NET Core - Microsoft Learn.
  • +
    +
    diff --git a/csharp/ql/src/Security Features/CWE-285/MissingAccessControl.ql b/csharp/ql/src/Security Features/CWE-285/MissingAccessControl.ql new file mode 100644 index 00000000000..52c02c05445 --- /dev/null +++ b/csharp/ql/src/Security Features/CWE-285/MissingAccessControl.ql @@ -0,0 +1,20 @@ +/** + * @name Missing function level access control + * @description Sensitive actions should have authorization checks to prevent them from being used by malicious actors. + * @kind problem + * @problem.severity warning + * @security-severity 7.5 + * @precision medium + * @id cs/web/missing-function-level-access-control + * @tags security + * external/cwe/cwe-285 + * external/cwe/cwe-284 + * external/cwe/cwe-862 + */ + +import csharp +import semmle.code.csharp.security.auth.MissingFunctionLevelAccessControlQuery + +from Method m +where missingAuth(m) +select m, "This action is missing an authorization check." diff --git a/csharp/ql/src/Security Features/CWE-285/Web.config b/csharp/ql/src/Security Features/CWE-285/Web.config new file mode 100644 index 00000000000..8e83c8d38e9 --- /dev/null +++ b/csharp/ql/src/Security Features/CWE-285/Web.config @@ -0,0 +1,11 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/csharp/ql/src/Security Features/CWE-285/WebForms.cs b/csharp/ql/src/Security Features/CWE-285/WebForms.cs new file mode 100644 index 00000000000..49dce6097f1 --- /dev/null +++ b/csharp/ql/src/Security Features/CWE-285/WebForms.cs @@ -0,0 +1,14 @@ +class ProfilePage : System.Web.UI.Page { + // BAD: No authorization is used + protected void btn1_Edit_Click(object sender, EventArgs e) { + ... + } + + // GOOD: `User.IsInRole` checks the current user's role. + protected void btn2_Delete_Click(object sender, EventArgs e) { + if (!User.IsInRole("admin")) { + return; + } + ... + } +} \ No newline at end of file diff --git a/csharp/ql/src/Telemetry/ExternalApi.qll b/csharp/ql/src/Telemetry/ExternalApi.qll index 0921bdf7b5c..7be4f104837 100644 --- a/csharp/ql/src/Telemetry/ExternalApi.qll +++ b/csharp/ql/src/Telemetry/ExternalApi.qll @@ -136,7 +136,7 @@ private string nestedName(Declaration declaration) { /** * Gets the limit for the number of results produced by a telemetry query. */ -int resultLimit() { result = 1000 } +int resultLimit() { result = 100 } /** * Holds if it is relevant to count usages of `api`. diff --git a/csharp/ql/src/change-notes/released/0.6.4.md b/csharp/ql/src/change-notes/released/0.6.4.md new file mode 100644 index 00000000000..7e98b0159fc --- /dev/null +++ b/csharp/ql/src/change-notes/released/0.6.4.md @@ -0,0 +1,3 @@ +## 0.6.4 + +No user-facing changes. diff --git a/csharp/ql/src/change-notes/2023-06-16-zipslip-rename.md b/csharp/ql/src/change-notes/released/0.7.0.md similarity index 50% rename from csharp/ql/src/change-notes/2023-06-16-zipslip-rename.md rename to csharp/ql/src/change-notes/released/0.7.0.md index 3c13e6da67a..552b6f631d7 100644 --- a/csharp/ql/src/change-notes/2023-06-16-zipslip-rename.md +++ b/csharp/ql/src/change-notes/released/0.7.0.md @@ -1,4 +1,9 @@ ---- -category: fix ---- +## 0.7.0 + +### New Queries + +* Added a new query, `cs/web/missing-function-level-access-control`, to find instances of missing authorization checks. + +### Bug Fixes + * 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/codeql-pack.release.yml b/csharp/ql/src/codeql-pack.release.yml index b7dafe32c5d..c761f3e7ab4 100644 --- a/csharp/ql/src/codeql-pack.release.yml +++ b/csharp/ql/src/codeql-pack.release.yml @@ -1,2 +1,2 @@ --- -lastReleaseVersion: 0.6.3 +lastReleaseVersion: 0.7.0 diff --git a/csharp/ql/src/experimental/ir/implementation/raw/gvn/internal/ValueNumberingInternal.qll b/csharp/ql/src/experimental/ir/implementation/raw/gvn/internal/ValueNumberingInternal.qll index 85a28fbc677..ec003891774 100644 --- a/csharp/ql/src/experimental/ir/implementation/raw/gvn/internal/ValueNumberingInternal.qll +++ b/csharp/ql/src/experimental/ir/implementation/raw/gvn/internal/ValueNumberingInternal.qll @@ -176,7 +176,6 @@ private predicate binaryValueNumber0( ) } -pragma[assume_small_delta] private predicate binaryValueNumber( BinaryInstruction instr, IRFunction irFunc, Opcode opcode, TValueNumber leftOperand, TValueNumber rightOperand @@ -202,7 +201,6 @@ private predicate pointerArithmeticValueNumber0( ) } -pragma[assume_small_delta] private predicate pointerArithmeticValueNumber( PointerArithmeticInstruction instr, IRFunction irFunc, Opcode opcode, int elementSize, TValueNumber leftOperand, TValueNumber rightOperand @@ -249,7 +247,6 @@ private predicate loadTotalOverlapValueNumber0( ) } -pragma[assume_small_delta] private predicate loadTotalOverlapValueNumber( LoadTotalOverlapInstruction instr, IRFunction irFunc, IRType type, TValueNumber memOperand, TValueNumber operand diff --git a/csharp/ql/src/experimental/ir/implementation/unaliased_ssa/gvn/internal/ValueNumberingInternal.qll b/csharp/ql/src/experimental/ir/implementation/unaliased_ssa/gvn/internal/ValueNumberingInternal.qll index 85a28fbc677..ec003891774 100644 --- a/csharp/ql/src/experimental/ir/implementation/unaliased_ssa/gvn/internal/ValueNumberingInternal.qll +++ b/csharp/ql/src/experimental/ir/implementation/unaliased_ssa/gvn/internal/ValueNumberingInternal.qll @@ -176,7 +176,6 @@ private predicate binaryValueNumber0( ) } -pragma[assume_small_delta] private predicate binaryValueNumber( BinaryInstruction instr, IRFunction irFunc, Opcode opcode, TValueNumber leftOperand, TValueNumber rightOperand @@ -202,7 +201,6 @@ private predicate pointerArithmeticValueNumber0( ) } -pragma[assume_small_delta] private predicate pointerArithmeticValueNumber( PointerArithmeticInstruction instr, IRFunction irFunc, Opcode opcode, int elementSize, TValueNumber leftOperand, TValueNumber rightOperand @@ -249,7 +247,6 @@ private predicate loadTotalOverlapValueNumber0( ) } -pragma[assume_small_delta] private predicate loadTotalOverlapValueNumber( LoadTotalOverlapInstruction instr, IRFunction irFunc, IRType type, TValueNumber memOperand, TValueNumber operand diff --git a/csharp/ql/src/qlpack.yml b/csharp/ql/src/qlpack.yml index 91cba09b8ac..f2c76620fa8 100644 --- a/csharp/ql/src/qlpack.yml +++ b/csharp/ql/src/qlpack.yml @@ -1,5 +1,5 @@ name: codeql/csharp-queries -version: 0.6.4-dev +version: 0.7.1-dev groups: - csharp - queries diff --git a/csharp/ql/src/utils/modelgenerator/internal/CaptureModels.qll b/csharp/ql/src/utils/modelgenerator/internal/CaptureModels.qll index e2a0e130ca4..9c19e5b9cbb 100644 --- a/csharp/ql/src/utils/modelgenerator/internal/CaptureModels.qll +++ b/csharp/ql/src/utils/modelgenerator/internal/CaptureModels.qll @@ -160,8 +160,6 @@ module ThroughFlowConfig implements DataFlow::StateConfigSig { exists(Type t | t = n.getType() and not isRelevantType(t)) } - predicate isBarrier(DataFlow::Node node, FlowState state) { none() } - DataFlow::FlowFeature getAFeature() { result instanceof DataFlow::FeatureEqualSourceSinkCallContext } diff --git a/csharp/ql/test/query-tests/Security Features/CWE-285/MissingAccessControl/MVCTests/MissingAccessControl.expected b/csharp/ql/test/query-tests/Security Features/CWE-285/MissingAccessControl/MVCTests/MissingAccessControl.expected new file mode 100644 index 00000000000..87fc29167be --- /dev/null +++ b/csharp/ql/test/query-tests/Security Features/CWE-285/MissingAccessControl/MVCTests/MissingAccessControl.expected @@ -0,0 +1 @@ +| ProfileController.cs:9:25:9:31 | Delete1 | This action is missing an authorization check. | diff --git a/csharp/ql/test/query-tests/Security Features/CWE-285/MissingAccessControl/MVCTests/MissingAccessControl.qlref b/csharp/ql/test/query-tests/Security Features/CWE-285/MissingAccessControl/MVCTests/MissingAccessControl.qlref new file mode 100644 index 00000000000..a4173778d9f --- /dev/null +++ b/csharp/ql/test/query-tests/Security Features/CWE-285/MissingAccessControl/MVCTests/MissingAccessControl.qlref @@ -0,0 +1 @@ +Security Features/CWE-285/MissingAccessControl.ql diff --git a/csharp/ql/test/query-tests/Security Features/CWE-285/MissingAccessControl/MVCTests/ProfileController.cs b/csharp/ql/test/query-tests/Security Features/CWE-285/MissingAccessControl/MVCTests/ProfileController.cs new file mode 100644 index 00000000000..39943dd4ce4 --- /dev/null +++ b/csharp/ql/test/query-tests/Security Features/CWE-285/MissingAccessControl/MVCTests/ProfileController.cs @@ -0,0 +1,30 @@ +using Microsoft.AspNetCore.Mvc; +using Microsoft.AspNetCore.Authorization; + +public class ProfileController : Controller { + private void doThings() { } + private bool isAuthorized() { return false; } + + // BAD: This is a Delete method, but no auth is specified. + public ActionResult Delete1(int id) { + doThings(); + return View(); + } + + // GOOD: isAuthorized is checked. + public ActionResult Delete2(int id) { + if (!isAuthorized()) { + return null; + } + doThings(); + return View(); + } + + // GOOD: The Authorize attribute is used. + [Authorize] + public ActionResult Delete3(int id) { + doThings(); + return View(); + } + +} \ No newline at end of file diff --git a/csharp/ql/test/query-tests/Security Features/CWE-285/MissingAccessControl/MVCTests/options b/csharp/ql/test/query-tests/Security Features/CWE-285/MissingAccessControl/MVCTests/options new file mode 100644 index 00000000000..19bb87e63b5 --- /dev/null +++ b/csharp/ql/test/query-tests/Security Features/CWE-285/MissingAccessControl/MVCTests/options @@ -0,0 +1,3 @@ +semmle-extractor-options: /nostdlib /noconfig +semmle-extractor-options: --load-sources-from-project:${testdir}/../../../../../resources/stubs/_frameworks/Microsoft.NETCore.App/Microsoft.NETCore.App.csproj +semmle-extractor-options: --load-sources-from-project:${testdir}/../../../../../resources/stubs/_frameworks/Microsoft.AspNetCore.App/Microsoft.AspNetCore.App.csproj \ No newline at end of file diff --git a/csharp/ql/test/query-tests/Security Features/CWE-285/MissingAccessControl/WebFormsTests/MissingAccessControl.expected b/csharp/ql/test/query-tests/Security Features/CWE-285/MissingAccessControl/WebFormsTests/MissingAccessControl.expected new file mode 100644 index 00000000000..5c0df701d81 --- /dev/null +++ b/csharp/ql/test/query-tests/Security Features/CWE-285/MissingAccessControl/WebFormsTests/MissingAccessControl.expected @@ -0,0 +1,3 @@ +| Test1/EditProfile.aspx.cs:10:20:10:29 | btn1_Click | This action is missing an authorization check. | +| Test1/ViewProfile.aspx.cs:14:20:14:36 | btn_delete1_Click | This action is missing an authorization check. | +| Test3/B/EditProfile.aspx.cs:8:20:8:29 | btn1_Click | This action is missing an authorization check. | diff --git a/csharp/ql/test/query-tests/Security Features/CWE-285/MissingAccessControl/WebFormsTests/MissingAccessControl.qlref b/csharp/ql/test/query-tests/Security Features/CWE-285/MissingAccessControl/WebFormsTests/MissingAccessControl.qlref new file mode 100644 index 00000000000..a4173778d9f --- /dev/null +++ b/csharp/ql/test/query-tests/Security Features/CWE-285/MissingAccessControl/WebFormsTests/MissingAccessControl.qlref @@ -0,0 +1 @@ +Security Features/CWE-285/MissingAccessControl.ql diff --git a/csharp/ql/test/query-tests/Security Features/CWE-285/MissingAccessControl/WebFormsTests/Test1/EditProfile.aspx.cs b/csharp/ql/test/query-tests/Security Features/CWE-285/MissingAccessControl/WebFormsTests/Test1/EditProfile.aspx.cs new file mode 100644 index 00000000000..b023dc11e80 --- /dev/null +++ b/csharp/ql/test/query-tests/Security Features/CWE-285/MissingAccessControl/WebFormsTests/Test1/EditProfile.aspx.cs @@ -0,0 +1,20 @@ +using System; +using System.Web.UI; + +class EditProfile : System.Web.UI.Page { + private void doThings() { } + + private bool isAuthorized() { return false; } + + // BAD: The class name indicates that this may be an Edit method, but there is no auth check + protected void btn1_Click(object sender, EventArgs e) { + doThings(); + } + + // GOOD: There is a call to isAuthorized + protected void btn2_Click(object sender, EventArgs e) { + if (isAuthorized()) { + doThings(); + } + } +} \ No newline at end of file diff --git a/csharp/ql/test/query-tests/Security Features/CWE-285/MissingAccessControl/WebFormsTests/Test1/ViewProfile.aspx.cs b/csharp/ql/test/query-tests/Security Features/CWE-285/MissingAccessControl/WebFormsTests/Test1/ViewProfile.aspx.cs new file mode 100644 index 00000000000..f9d7316d50b --- /dev/null +++ b/csharp/ql/test/query-tests/Security Features/CWE-285/MissingAccessControl/WebFormsTests/Test1/ViewProfile.aspx.cs @@ -0,0 +1,24 @@ +using System; +using System.Web.UI; +using System.Web.Security; + +class ViewProfile : System.Web.UI.Page { + private void doThings() { } + + // GOOD: This method and class name do not indicate a sensitive method. + protected void btn_safe_Click(object sender, EventArgs e) { + doThings(); + } + + // BAD: The name indicates a Delete method, but no auth is present. + protected void btn_delete1_Click(object sender, EventArgs e) { + doThings(); + } + + // GOOD: User.IsInRole is checked. + protected void btn_delete2_Click(object sender, EventArgs e) { + if (User.IsInRole("admin")) { + doThings(); + } + } +} \ No newline at end of file diff --git a/csharp/ql/test/query-tests/Security Features/CWE-285/MissingAccessControl/WebFormsTests/Test2/EditProfile.aspx.cs b/csharp/ql/test/query-tests/Security Features/CWE-285/MissingAccessControl/WebFormsTests/Test2/EditProfile.aspx.cs new file mode 100644 index 00000000000..0d0b2b7b864 --- /dev/null +++ b/csharp/ql/test/query-tests/Security Features/CWE-285/MissingAccessControl/WebFormsTests/Test2/EditProfile.aspx.cs @@ -0,0 +1,11 @@ +using System; +using System.Web.UI; + +class EditProfile2 : System.Web.UI.Page { + private void doThings() { } + + // GOOD: The Web.config file specifies auth for this path. + protected void btn1_Click(object sender, EventArgs e) { + doThings(); + } +} \ No newline at end of file diff --git a/csharp/ql/test/query-tests/Security Features/CWE-285/MissingAccessControl/WebFormsTests/Test2/Web.config b/csharp/ql/test/query-tests/Security Features/CWE-285/MissingAccessControl/WebFormsTests/Test2/Web.config new file mode 100644 index 00000000000..5810b0a0593 --- /dev/null +++ b/csharp/ql/test/query-tests/Security Features/CWE-285/MissingAccessControl/WebFormsTests/Test2/Web.config @@ -0,0 +1,12 @@ + + + + + + + + + + + + \ No newline at end of file diff --git a/csharp/ql/test/query-tests/Security Features/CWE-285/MissingAccessControl/WebFormsTests/Test3/A/EditProfile.aspx.cs b/csharp/ql/test/query-tests/Security Features/CWE-285/MissingAccessControl/WebFormsTests/Test3/A/EditProfile.aspx.cs new file mode 100644 index 00000000000..4f5025a4a51 --- /dev/null +++ b/csharp/ql/test/query-tests/Security Features/CWE-285/MissingAccessControl/WebFormsTests/Test3/A/EditProfile.aspx.cs @@ -0,0 +1,11 @@ +using System; +using System.Web.UI; + +class EditProfile3 : System.Web.UI.Page { + private void doThings() { } + + // GOOD: This is covered by the Web.config's location tag referring to A + protected void btn1_Click(object sender, EventArgs e) { + doThings(); + } +} \ No newline at end of file diff --git a/csharp/ql/test/query-tests/Security Features/CWE-285/MissingAccessControl/WebFormsTests/Test3/B/EditProfile.aspx.cs b/csharp/ql/test/query-tests/Security Features/CWE-285/MissingAccessControl/WebFormsTests/Test3/B/EditProfile.aspx.cs new file mode 100644 index 00000000000..4b7697f0f88 --- /dev/null +++ b/csharp/ql/test/query-tests/Security Features/CWE-285/MissingAccessControl/WebFormsTests/Test3/B/EditProfile.aspx.cs @@ -0,0 +1,11 @@ +using System; +using System.Web.UI; + +class EditProfile4 : System.Web.UI.Page { + private void doThings() { } + + // BAD: The Web.config file does not specify auth for this path. + protected void btn1_Click(object sender, EventArgs e) { + doThings(); + } +} \ No newline at end of file diff --git a/csharp/ql/test/query-tests/Security Features/CWE-285/MissingAccessControl/WebFormsTests/Test3/C/EditProfile.aspx.cs b/csharp/ql/test/query-tests/Security Features/CWE-285/MissingAccessControl/WebFormsTests/Test3/C/EditProfile.aspx.cs new file mode 100644 index 00000000000..a8ad0654689 --- /dev/null +++ b/csharp/ql/test/query-tests/Security Features/CWE-285/MissingAccessControl/WebFormsTests/Test3/C/EditProfile.aspx.cs @@ -0,0 +1,11 @@ +using System; +using System.Web.UI; + +class EditProfile5 : System.Web.UI.Page { + private void doThings() { } + + // GOOD: The Web.config file specifies auth for the path Virtual, which is mapped to C in Global.asax + protected void btn1_Click(object sender, EventArgs e) { + doThings(); + } +} \ No newline at end of file diff --git a/csharp/ql/test/query-tests/Security Features/CWE-285/MissingAccessControl/WebFormsTests/Test3/Global.asax.cs b/csharp/ql/test/query-tests/Security Features/CWE-285/MissingAccessControl/WebFormsTests/Test3/Global.asax.cs new file mode 100644 index 00000000000..f1bb5aadd8b --- /dev/null +++ b/csharp/ql/test/query-tests/Security Features/CWE-285/MissingAccessControl/WebFormsTests/Test3/Global.asax.cs @@ -0,0 +1,26 @@ +using System; +using System.Web; +using System.Web.Routing; + +public class Global : System.Web.HttpApplication { + + void Application_Start(object sender, EventArgs e) { + RegisterRoutes(RouteTable.Routes); + } + + void Application_End(object sender, EventArgs e) { } + + void Application_Error(object sender, EventArgs e) { } + + void Session_Start(object sender, EventArgs e) { } + + void Session_End(object sender, EventArgs e) { } + + static void RegisterRoutes(RouteCollection routes) { + routes.MapPageRoute("VirtualEditProfile", + "Virtual/Edit", + "~/C/EditProfile.aspx", + false + ); + } +} \ No newline at end of file diff --git a/csharp/ql/test/query-tests/Security Features/CWE-285/MissingAccessControl/WebFormsTests/Test3/Web.config b/csharp/ql/test/query-tests/Security Features/CWE-285/MissingAccessControl/WebFormsTests/Test3/Web.config new file mode 100644 index 00000000000..fd9409f3042 --- /dev/null +++ b/csharp/ql/test/query-tests/Security Features/CWE-285/MissingAccessControl/WebFormsTests/Test3/Web.config @@ -0,0 +1,19 @@ + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/csharp/ql/test/query-tests/Security Features/CWE-285/MissingAccessControl/WebFormsTests/options b/csharp/ql/test/query-tests/Security Features/CWE-285/MissingAccessControl/WebFormsTests/options new file mode 100644 index 00000000000..fb93d69d6b4 --- /dev/null +++ b/csharp/ql/test/query-tests/Security Features/CWE-285/MissingAccessControl/WebFormsTests/options @@ -0,0 +1,3 @@ +semmle-extractor-options: /nostdlib /noconfig +semmle-extractor-options: --load-sources-from-project:${testdir}/../../../../../resources/stubs/_frameworks/Microsoft.NETCore.App/Microsoft.NETCore.App.csproj +semmle-extractor-options: ${testdir}/../../../../../resources/stubs/System.Web.cs \ No newline at end of file diff --git a/csharp/ql/test/query-tests/Stubs/Minimal/MinimalStubsFromSource.expected b/csharp/ql/test/query-tests/Stubs/Minimal/MinimalStubsFromSource.expected index 629addbb336..dc42176a7cb 100644 --- a/csharp/ql/test/query-tests/Stubs/Minimal/MinimalStubsFromSource.expected +++ b/csharp/ql/test/query-tests/Stubs/Minimal/MinimalStubsFromSource.expected @@ -1 +1 @@ -| // This file contains auto-generated code.\n// Generated from `System.Collections, Version=7.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a`.\n\nnamespace System\n{\nnamespace Collections\n{\nnamespace Generic\n{\npublic class Stack : System.Collections.Generic.IEnumerable, System.Collections.Generic.IReadOnlyCollection, System.Collections.ICollection, System.Collections.IEnumerable\n{\n void System.Collections.ICollection.CopyTo(System.Array array, int arrayIndex) => throw null;\n public int Count { get => throw null; }\n System.Collections.Generic.IEnumerator System.Collections.Generic.IEnumerable.GetEnumerator() => throw null;\n System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() => throw null;\n bool System.Collections.ICollection.IsSynchronized { get => throw null; }\n public T Peek() => throw null;\n object System.Collections.ICollection.SyncRoot { get => throw null; }\n}\n\n}\n}\n}\n\n\n// This file contains auto-generated code.\n// Generated from `System.Collections.NonGeneric, Version=7.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a`.\n\nnamespace System\n{\nnamespace Collections\n{\npublic class SortedList : System.Collections.ICollection, System.Collections.IDictionary, System.Collections.IEnumerable, System.ICloneable\n{\n public virtual void Add(object key, object value) => throw null;\n public virtual void Clear() => throw null;\n public virtual object Clone() => throw null;\n public virtual bool Contains(object key) => throw null;\n public virtual void CopyTo(System.Array array, int arrayIndex) => throw null;\n public virtual int Count { get => throw null; }\n public virtual object GetByIndex(int index) => throw null;\n public virtual System.Collections.IDictionaryEnumerator GetEnumerator() => throw null;\n System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() => throw null;\n public virtual bool IsFixedSize { get => throw null; }\n public virtual bool IsReadOnly { get => throw null; }\n public virtual bool IsSynchronized { get => throw null; }\n public virtual object this[object key] { get => throw null; set => throw null; }\n public virtual System.Collections.ICollection Keys { get => throw null; }\n public virtual void Remove(object key) => throw null;\n public virtual object SyncRoot { get => throw null; }\n public virtual System.Collections.ICollection Values { get => throw null; }\n}\n\n}\n}\n\n\n// This file contains auto-generated code.\n// Generated from `System.Collections.Specialized, Version=7.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a`.\n\nnamespace System\n{\nnamespace Collections\n{\nnamespace Specialized\n{\npublic abstract class NameObjectCollectionBase : System.Collections.ICollection, System.Collections.IEnumerable, System.Runtime.Serialization.IDeserializationCallback, System.Runtime.Serialization.ISerializable\n{\n void System.Collections.ICollection.CopyTo(System.Array array, int index) => throw null;\n public virtual int Count { get => throw null; }\n public virtual System.Collections.IEnumerator GetEnumerator() => throw null;\n public virtual void GetObjectData(System.Runtime.Serialization.SerializationInfo info, System.Runtime.Serialization.StreamingContext context) => throw null;\n bool System.Collections.ICollection.IsSynchronized { get => throw null; }\n public virtual void OnDeserialization(object sender) => throw null;\n object System.Collections.ICollection.SyncRoot { get => throw null; }\n}\n\npublic class NameValueCollection : System.Collections.Specialized.NameObjectCollectionBase\n{\n public string this[string name] { get => throw null; set => throw null; }\n}\n\n}\n}\n}\n\n\n// This file contains auto-generated code.\n// Generated from `System.ComponentModel.TypeConverter, Version=7.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a`.\n\nnamespace System\n{\nnamespace ComponentModel\n{\npublic class ComponentConverter : System.ComponentModel.ReferenceConverter\n{\n}\n\npublic class DefaultEventAttribute : System.Attribute\n{\n public DefaultEventAttribute(string name) => throw null;\n public override bool Equals(object obj) => throw null;\n public override int GetHashCode() => throw null;\n}\n\npublic class DefaultPropertyAttribute : System.Attribute\n{\n public DefaultPropertyAttribute(string name) => throw null;\n public override bool Equals(object obj) => throw null;\n public override int GetHashCode() => throw null;\n}\n\npublic class ReferenceConverter : System.ComponentModel.TypeConverter\n{\n}\n\npublic class TypeConverter\n{\n}\n\n}\nnamespace Timers\n{\npublic class TimersDescriptionAttribute\n{\n public TimersDescriptionAttribute(string description) => throw null;\n internal TimersDescriptionAttribute(string description, string unused) => throw null;\n}\n\n}\n}\n\n\n// This file contains auto-generated code.\n// Generated from `System.Linq, Version=7.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a`.\n\nnamespace System\n{\nnamespace Linq\n{\npublic static class Enumerable\n{\n public static System.Collections.Generic.IEnumerable Select(this System.Collections.Generic.IEnumerable source, System.Func selector) => throw null;\n}\n\n}\n}\n\n\n// This file contains auto-generated code.\n// Generated from `System.Linq.Expressions, Version=7.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a`.\n\nnamespace System\n{\nnamespace Linq\n{\npublic interface IQueryable : System.Collections.IEnumerable\n{\n}\n\n}\nnamespace Runtime\n{\nnamespace CompilerServices\n{\npublic class CallSite\n{\n internal CallSite(System.Runtime.CompilerServices.CallSiteBinder binder) => throw null;\n}\n\npublic class CallSite : System.Runtime.CompilerServices.CallSite where T: class\n{\n private CallSite() : base(default(System.Runtime.CompilerServices.CallSiteBinder)) => throw null;\n private CallSite(System.Runtime.CompilerServices.CallSiteBinder binder) : base(default(System.Runtime.CompilerServices.CallSiteBinder)) => throw null;\n}\n\npublic abstract class CallSiteBinder\n{\n}\n\n}\n}\n}\n\n\n// This file contains auto-generated code.\n// Generated from `System.Linq.Parallel, Version=7.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a`.\n\nnamespace System\n{\nnamespace Linq\n{\npublic static class ParallelEnumerable\n{\n public static System.Linq.ParallelQuery AsParallel(this System.Collections.IEnumerable source) => throw null;\n}\n\npublic class ParallelQuery : System.Collections.IEnumerable\n{\n System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() => throw null;\n internal ParallelQuery(System.Linq.Parallel.QuerySettings specifiedSettings) => throw null;\n}\n\nnamespace Parallel\n{\ninternal struct QuerySettings\n{\n}\n\n}\n}\n}\n\n\n// This file contains auto-generated code.\n// Generated from `System.Linq.Queryable, Version=7.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a`.\n\nnamespace System\n{\nnamespace Linq\n{\npublic static class Queryable\n{\n public static System.Linq.IQueryable AsQueryable(this System.Collections.IEnumerable source) => throw null;\n}\n\n}\n}\n\n\n// This file contains auto-generated code.\n// Generated from `System.ObjectModel, Version=7.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a`.\n\nnamespace System\n{\nnamespace ComponentModel\n{\npublic class TypeConverterAttribute : System.Attribute\n{\n public override bool Equals(object obj) => throw null;\n public override int GetHashCode() => throw null;\n public TypeConverterAttribute() => throw null;\n public TypeConverterAttribute(System.Type type) => throw null;\n public TypeConverterAttribute(string typeName) => throw null;\n}\n\npublic class TypeDescriptionProviderAttribute : System.Attribute\n{\n public TypeDescriptionProviderAttribute(System.Type type) => throw null;\n public TypeDescriptionProviderAttribute(string typeName) => throw null;\n}\n\n}\nnamespace Windows\n{\nnamespace Markup\n{\npublic class ValueSerializerAttribute : System.Attribute\n{\n public ValueSerializerAttribute(System.Type valueSerializerType) => throw null;\n public ValueSerializerAttribute(string valueSerializerTypeName) => throw null;\n}\n\n}\n}\n}\n\n\n// This file contains auto-generated code.\n// Generated from `System.Private.Uri, Version=7.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a`.\n\nnamespace System\n{\npublic class Uri : System.Runtime.Serialization.ISerializable\n{\n public override bool Equals(object comparand) => throw null;\n public override int GetHashCode() => throw null;\n void System.Runtime.Serialization.ISerializable.GetObjectData(System.Runtime.Serialization.SerializationInfo serializationInfo, System.Runtime.Serialization.StreamingContext streamingContext) => throw null;\n public override string ToString() => throw null;\n}\n\n}\n\n\n// This file contains auto-generated code.\n// Generated from `System.Runtime.Serialization.Primitives, Version=7.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a`.\n\nnamespace System\n{\nnamespace Runtime\n{\nnamespace Serialization\n{\npublic class DataContractAttribute : System.Attribute\n{\n public DataContractAttribute() => throw null;\n}\n\npublic class DataMemberAttribute : System.Attribute\n{\n public DataMemberAttribute() => throw null;\n}\n\n}\n}\n}\n\n\n// This file contains auto-generated code.\n// Generated from `System.Text.RegularExpressions, Version=7.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a`.\n\nnamespace System\n{\nnamespace Text\n{\nnamespace RegularExpressions\n{\npublic class Capture\n{\n internal Capture(string text, int index, int length) => throw null;\n public override string ToString() => throw null;\n}\n\npublic class GeneratedRegexAttribute : System.Attribute\n{\n public GeneratedRegexAttribute(string pattern) => throw null;\n public GeneratedRegexAttribute(string pattern, System.Text.RegularExpressions.RegexOptions options) => throw null;\n public GeneratedRegexAttribute(string pattern, System.Text.RegularExpressions.RegexOptions options, int matchTimeoutMilliseconds) => throw null;\n public GeneratedRegexAttribute(string pattern, System.Text.RegularExpressions.RegexOptions options, int matchTimeoutMilliseconds, string cultureName) => throw null;\n public GeneratedRegexAttribute(string pattern, System.Text.RegularExpressions.RegexOptions options, string cultureName) => throw null;\n}\n\npublic class Group : System.Text.RegularExpressions.Capture\n{\n internal Group(string text, int[] caps, int capcount, string name) : base(default(string), default(int), default(int)) => throw null;\n}\n\npublic class Match : System.Text.RegularExpressions.Group\n{\n internal Match(System.Text.RegularExpressions.Regex regex, int capcount, string text, int textLength) : base(default(string), default(int[]), default(int), default(string)) => throw null;\n}\n\npublic class Regex : System.Runtime.Serialization.ISerializable\n{\n void System.Runtime.Serialization.ISerializable.GetObjectData(System.Runtime.Serialization.SerializationInfo si, System.Runtime.Serialization.StreamingContext context) => throw null;\n public System.Text.RegularExpressions.Match Match(string input) => throw null;\n public static System.Text.RegularExpressions.Match Match(string input, string pattern) => throw null;\n public static System.Text.RegularExpressions.Match Match(string input, string pattern, System.Text.RegularExpressions.RegexOptions options, System.TimeSpan matchTimeout) => throw null;\n public Regex(string pattern) => throw null;\n public Regex(string pattern, System.Text.RegularExpressions.RegexOptions options, System.TimeSpan matchTimeout) => throw null;\n public string Replace(string input, string replacement) => throw null;\n public override string ToString() => throw null;\n}\n\n[System.Flags]\npublic enum RegexOptions : int\n{\n IgnoreCase = 1,\n}\n\n}\n}\n}\n\n\n// This file contains auto-generated code.\n// Generated from `System.Web, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null`.\n\nnamespace System\n{\nnamespace Web\n{\npublic class HtmlString : System.Web.IHtmlString\n{\n}\n\npublic class HttpContextBase\n{\n public virtual System.Web.HttpRequestBase Request { get => throw null; }\n}\n\npublic class HttpCookie\n{\n}\n\npublic abstract class HttpCookieCollection : System.Collections.Specialized.NameObjectCollectionBase\n{\n}\n\npublic class HttpRequest\n{\n}\n\npublic class HttpRequestBase\n{\n public virtual System.Collections.Specialized.NameValueCollection QueryString { get => throw null; }\n}\n\npublic class HttpResponse\n{\n}\n\npublic class HttpResponseBase\n{\n}\n\npublic class HttpServerUtility\n{\n}\n\npublic interface IHtmlString\n{\n}\n\npublic interface IHttpHandler\n{\n}\n\npublic interface IServiceProvider\n{\n}\n\npublic class UnvalidatedRequestValues\n{\n}\n\npublic class UnvalidatedRequestValuesBase\n{\n}\n\nnamespace Mvc\n{\npublic class ActionMethodSelectorAttribute : System.Attribute\n{\n}\n\npublic class ActionResult\n{\n}\n\npublic class ControllerContext\n{\n}\n\npublic class FilterAttribute : System.Attribute\n{\n}\n\npublic class GlobalFilterCollection\n{\n}\n\ninternal interface IFilterProvider\n{\n}\n\npublic interface IViewDataContainer\n{\n}\n\npublic class ViewContext : System.Web.Mvc.ControllerContext\n{\n}\n\npublic class ViewResult : System.Web.Mvc.ViewResultBase\n{\n}\n\npublic class ViewResultBase : System.Web.Mvc.ActionResult\n{\n}\n\n}\nnamespace Routing\n{\npublic class RequestContext\n{\n}\n\n}\nnamespace Script\n{\nnamespace Serialization\n{\npublic abstract class JavaScriptTypeResolver\n{\n}\n\n}\n}\nnamespace Security\n{\npublic class MembershipUser\n{\n}\n\n}\nnamespace SessionState\n{\npublic class HttpSessionState\n{\n}\n\n}\nnamespace UI\n{\npublic class Control\n{\n}\n\nnamespace WebControls\n{\npublic class WebControl : System.Web.UI.Control\n{\n}\n\n}\n}\n}\n}\n\n\n | +| // This file contains auto-generated code.\n// Generated from `System.Collections, Version=7.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a`.\n\nnamespace System\n{\nnamespace Collections\n{\nnamespace Generic\n{\npublic class Stack : System.Collections.Generic.IEnumerable, System.Collections.Generic.IReadOnlyCollection, System.Collections.ICollection, System.Collections.IEnumerable\n{\n void System.Collections.ICollection.CopyTo(System.Array array, int arrayIndex) => throw null;\n public int Count { get => throw null; }\n System.Collections.Generic.IEnumerator System.Collections.Generic.IEnumerable.GetEnumerator() => throw null;\n System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() => throw null;\n bool System.Collections.ICollection.IsSynchronized { get => throw null; }\n public T Peek() => throw null;\n object System.Collections.ICollection.SyncRoot { get => throw null; }\n}\n\n}\n}\n}\n\n\n// This file contains auto-generated code.\n// Generated from `System.Collections.NonGeneric, Version=7.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a`.\n\nnamespace System\n{\nnamespace Collections\n{\npublic class SortedList : System.Collections.ICollection, System.Collections.IDictionary, System.Collections.IEnumerable, System.ICloneable\n{\n public virtual void Add(object key, object value) => throw null;\n public virtual void Clear() => throw null;\n public virtual object Clone() => throw null;\n public virtual bool Contains(object key) => throw null;\n public virtual void CopyTo(System.Array array, int arrayIndex) => throw null;\n public virtual int Count { get => throw null; }\n public virtual object GetByIndex(int index) => throw null;\n public virtual System.Collections.IDictionaryEnumerator GetEnumerator() => throw null;\n System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() => throw null;\n public virtual bool IsFixedSize { get => throw null; }\n public virtual bool IsReadOnly { get => throw null; }\n public virtual bool IsSynchronized { get => throw null; }\n public virtual object this[object key] { get => throw null; set => throw null; }\n public virtual System.Collections.ICollection Keys { get => throw null; }\n public virtual void Remove(object key) => throw null;\n public virtual object SyncRoot { get => throw null; }\n public virtual System.Collections.ICollection Values { get => throw null; }\n}\n\n}\n}\n\n\n// This file contains auto-generated code.\n// Generated from `System.Collections.Specialized, Version=7.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a`.\n\nnamespace System\n{\nnamespace Collections\n{\nnamespace Specialized\n{\npublic abstract class NameObjectCollectionBase : System.Collections.ICollection, System.Collections.IEnumerable, System.Runtime.Serialization.IDeserializationCallback, System.Runtime.Serialization.ISerializable\n{\n void System.Collections.ICollection.CopyTo(System.Array array, int index) => throw null;\n public virtual int Count { get => throw null; }\n public virtual System.Collections.IEnumerator GetEnumerator() => throw null;\n public virtual void GetObjectData(System.Runtime.Serialization.SerializationInfo info, System.Runtime.Serialization.StreamingContext context) => throw null;\n bool System.Collections.ICollection.IsSynchronized { get => throw null; }\n public virtual void OnDeserialization(object sender) => throw null;\n object System.Collections.ICollection.SyncRoot { get => throw null; }\n}\n\npublic class NameValueCollection : System.Collections.Specialized.NameObjectCollectionBase\n{\n public string this[string name] { get => throw null; set => throw null; }\n}\n\n}\n}\n}\n\n\n// This file contains auto-generated code.\n// Generated from `System.ComponentModel.TypeConverter, Version=7.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a`.\n\nnamespace System\n{\nnamespace ComponentModel\n{\npublic class ComponentConverter : System.ComponentModel.ReferenceConverter\n{\n}\n\npublic class DefaultEventAttribute : System.Attribute\n{\n public DefaultEventAttribute(string name) => throw null;\n public override bool Equals(object obj) => throw null;\n public override int GetHashCode() => throw null;\n}\n\npublic class DefaultPropertyAttribute : System.Attribute\n{\n public DefaultPropertyAttribute(string name) => throw null;\n public override bool Equals(object obj) => throw null;\n public override int GetHashCode() => throw null;\n}\n\npublic class ReferenceConverter : System.ComponentModel.TypeConverter\n{\n}\n\npublic class TypeConverter\n{\n}\n\n}\nnamespace Timers\n{\npublic class TimersDescriptionAttribute\n{\n public TimersDescriptionAttribute(string description) => throw null;\n internal TimersDescriptionAttribute(string description, string unused) => throw null;\n}\n\n}\n}\n\n\n// This file contains auto-generated code.\n// Generated from `System.Linq, Version=7.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a`.\n\nnamespace System\n{\nnamespace Linq\n{\npublic static class Enumerable\n{\n public static System.Collections.Generic.IEnumerable Select(this System.Collections.Generic.IEnumerable source, System.Func selector) => throw null;\n}\n\n}\n}\n\n\n// This file contains auto-generated code.\n// Generated from `System.Linq.Expressions, Version=7.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a`.\n\nnamespace System\n{\nnamespace Linq\n{\npublic interface IQueryable : System.Collections.IEnumerable\n{\n}\n\n}\nnamespace Runtime\n{\nnamespace CompilerServices\n{\npublic class CallSite\n{\n internal CallSite(System.Runtime.CompilerServices.CallSiteBinder binder) => throw null;\n}\n\npublic class CallSite : System.Runtime.CompilerServices.CallSite where T: class\n{\n private CallSite() : base(default(System.Runtime.CompilerServices.CallSiteBinder)) => throw null;\n private CallSite(System.Runtime.CompilerServices.CallSiteBinder binder) : base(default(System.Runtime.CompilerServices.CallSiteBinder)) => throw null;\n}\n\npublic abstract class CallSiteBinder\n{\n}\n\n}\n}\n}\n\n\n// This file contains auto-generated code.\n// Generated from `System.Linq.Parallel, Version=7.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a`.\n\nnamespace System\n{\nnamespace Linq\n{\npublic static class ParallelEnumerable\n{\n public static System.Linq.ParallelQuery AsParallel(this System.Collections.IEnumerable source) => throw null;\n}\n\npublic class ParallelQuery : System.Collections.IEnumerable\n{\n System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() => throw null;\n internal ParallelQuery(System.Linq.Parallel.QuerySettings specifiedSettings) => throw null;\n}\n\nnamespace Parallel\n{\ninternal struct QuerySettings\n{\n}\n\n}\n}\n}\n\n\n// This file contains auto-generated code.\n// Generated from `System.Linq.Queryable, Version=7.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a`.\n\nnamespace System\n{\nnamespace Linq\n{\npublic static class Queryable\n{\n public static System.Linq.IQueryable AsQueryable(this System.Collections.IEnumerable source) => throw null;\n}\n\n}\n}\n\n\n// This file contains auto-generated code.\n// Generated from `System.ObjectModel, Version=7.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a`.\n\nnamespace System\n{\nnamespace ComponentModel\n{\npublic class TypeConverterAttribute : System.Attribute\n{\n public override bool Equals(object obj) => throw null;\n public override int GetHashCode() => throw null;\n public TypeConverterAttribute() => throw null;\n public TypeConverterAttribute(System.Type type) => throw null;\n public TypeConverterAttribute(string typeName) => throw null;\n}\n\npublic class TypeDescriptionProviderAttribute : System.Attribute\n{\n public TypeDescriptionProviderAttribute(System.Type type) => throw null;\n public TypeDescriptionProviderAttribute(string typeName) => throw null;\n}\n\n}\nnamespace Windows\n{\nnamespace Markup\n{\npublic class ValueSerializerAttribute : System.Attribute\n{\n public ValueSerializerAttribute(System.Type valueSerializerType) => throw null;\n public ValueSerializerAttribute(string valueSerializerTypeName) => throw null;\n}\n\n}\n}\n}\n\n\n// This file contains auto-generated code.\n// Generated from `System.Private.Uri, Version=7.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a`.\n\nnamespace System\n{\npublic class Uri : System.Runtime.Serialization.ISerializable\n{\n public override bool Equals(object comparand) => throw null;\n public override int GetHashCode() => throw null;\n void System.Runtime.Serialization.ISerializable.GetObjectData(System.Runtime.Serialization.SerializationInfo serializationInfo, System.Runtime.Serialization.StreamingContext streamingContext) => throw null;\n public override string ToString() => throw null;\n}\n\n}\n\n\n// This file contains auto-generated code.\n// Generated from `System.Runtime.Serialization.Primitives, Version=7.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a`.\n\nnamespace System\n{\nnamespace Runtime\n{\nnamespace Serialization\n{\npublic class DataContractAttribute : System.Attribute\n{\n public DataContractAttribute() => throw null;\n}\n\npublic class DataMemberAttribute : System.Attribute\n{\n public DataMemberAttribute() => throw null;\n}\n\n}\n}\n}\n\n\n// This file contains auto-generated code.\n// Generated from `System.Text.RegularExpressions, Version=7.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a`.\n\nnamespace System\n{\nnamespace Text\n{\nnamespace RegularExpressions\n{\npublic class Capture\n{\n internal Capture(string text, int index, int length) => throw null;\n public override string ToString() => throw null;\n}\n\npublic class GeneratedRegexAttribute : System.Attribute\n{\n public GeneratedRegexAttribute(string pattern) => throw null;\n public GeneratedRegexAttribute(string pattern, System.Text.RegularExpressions.RegexOptions options) => throw null;\n public GeneratedRegexAttribute(string pattern, System.Text.RegularExpressions.RegexOptions options, int matchTimeoutMilliseconds) => throw null;\n public GeneratedRegexAttribute(string pattern, System.Text.RegularExpressions.RegexOptions options, int matchTimeoutMilliseconds, string cultureName) => throw null;\n public GeneratedRegexAttribute(string pattern, System.Text.RegularExpressions.RegexOptions options, string cultureName) => throw null;\n}\n\npublic class Group : System.Text.RegularExpressions.Capture\n{\n internal Group(string text, int[] caps, int capcount, string name) : base(default(string), default(int), default(int)) => throw null;\n}\n\npublic class Match : System.Text.RegularExpressions.Group\n{\n internal Match(System.Text.RegularExpressions.Regex regex, int capcount, string text, int textLength) : base(default(string), default(int[]), default(int), default(string)) => throw null;\n}\n\npublic class Regex : System.Runtime.Serialization.ISerializable\n{\n void System.Runtime.Serialization.ISerializable.GetObjectData(System.Runtime.Serialization.SerializationInfo si, System.Runtime.Serialization.StreamingContext context) => throw null;\n public System.Text.RegularExpressions.Match Match(string input) => throw null;\n public static System.Text.RegularExpressions.Match Match(string input, string pattern) => throw null;\n public static System.Text.RegularExpressions.Match Match(string input, string pattern, System.Text.RegularExpressions.RegexOptions options, System.TimeSpan matchTimeout) => throw null;\n public Regex(string pattern) => throw null;\n public Regex(string pattern, System.Text.RegularExpressions.RegexOptions options, System.TimeSpan matchTimeout) => throw null;\n public string Replace(string input, string replacement) => throw null;\n public override string ToString() => throw null;\n}\n\n[System.Flags]\npublic enum RegexOptions : int\n{\n IgnoreCase = 1,\n}\n\n}\n}\n}\n\n\n// This file contains auto-generated code.\n// Generated from `System.Web, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null`.\n\nnamespace System\n{\nnamespace Web\n{\npublic class HtmlString : System.Web.IHtmlString\n{\n}\n\npublic class HttpContextBase\n{\n public virtual System.Web.HttpRequestBase Request { get => throw null; }\n}\n\npublic class HttpCookie\n{\n}\n\npublic abstract class HttpCookieCollection : System.Collections.Specialized.NameObjectCollectionBase\n{\n}\n\npublic class HttpRequest\n{\n}\n\npublic class HttpRequestBase\n{\n public virtual System.Collections.Specialized.NameValueCollection QueryString { get => throw null; }\n}\n\npublic class HttpResponse\n{\n}\n\npublic class HttpResponseBase\n{\n}\n\npublic class HttpServerUtility\n{\n}\n\npublic interface IHtmlString\n{\n}\n\npublic interface IHttpHandler\n{\n}\n\npublic interface IServiceProvider\n{\n}\n\npublic class UnvalidatedRequestValues\n{\n}\n\npublic class UnvalidatedRequestValuesBase\n{\n}\n\nnamespace Mvc\n{\npublic class ActionMethodSelectorAttribute : System.Attribute\n{\n}\n\npublic class ActionResult\n{\n}\n\npublic class ControllerContext\n{\n}\n\npublic class FilterAttribute : System.Attribute\n{\n}\n\npublic class GlobalFilterCollection\n{\n}\n\ninternal interface IFilterProvider\n{\n}\n\npublic interface IViewDataContainer\n{\n}\n\npublic class ViewContext : System.Web.Mvc.ControllerContext\n{\n}\n\npublic class ViewResult : System.Web.Mvc.ViewResultBase\n{\n}\n\npublic class ViewResultBase : System.Web.Mvc.ActionResult\n{\n}\n\n}\nnamespace Routing\n{\npublic class RequestContext\n{\n}\n\npublic class Route\n{\n}\n\npublic class RouteCollection\n{\n}\n\npublic class RouteTable\n{\n}\n\n}\nnamespace Script\n{\nnamespace Serialization\n{\npublic abstract class JavaScriptTypeResolver\n{\n}\n\n}\n}\nnamespace Security\n{\npublic class MembershipUser\n{\n}\n\n}\nnamespace SessionState\n{\npublic class HttpSessionState\n{\n}\n\n}\nnamespace UI\n{\npublic class Control\n{\n}\n\nnamespace WebControls\n{\npublic class WebControl : System.Web.UI.Control\n{\n}\n\n}\n}\n}\n}\n\n\n | diff --git a/csharp/ql/test/resources/stubs/System.Web.cs b/csharp/ql/test/resources/stubs/System.Web.cs index 58865f82e6a..c3c8a0a753f 100644 --- a/csharp/ql/test/resources/stubs/System.Web.cs +++ b/csharp/ql/test/resources/stubs/System.Web.cs @@ -48,6 +48,8 @@ namespace System.Web public class HttpApplication : IHttpHandler { public HttpServerUtility Server { get; } + + public Routing.RouteTable RouteTable { get; } } } @@ -79,6 +81,7 @@ namespace System.Web.UI public class Page { + public System.Security.Principal.IPrincipal User { get; } } interface IPostBackDataHandler @@ -301,6 +304,19 @@ namespace System.Web.Routing public class RequestContext { } + + public class Route + { + } + + public class RouteTable { + public RouteCollection Routes { get; } + } + + public class RouteCollection + { + public Route MapPageRoute(string routeName, string routeUrl, string physicalFile, bool checkPhysicalUrlAccess) { return null; } + } } namespace System.Web.Security diff --git a/docs/codeql/ql-language-reference/ql-language-specification.rst b/docs/codeql/ql-language-reference/ql-language-specification.rst index 9328a0f3ec3..04cddf80a87 100644 --- a/docs/codeql/ql-language-reference/ql-language-specification.rst +++ b/docs/codeql/ql-language-reference/ql-language-specification.rst @@ -959,13 +959,18 @@ The types specified after the ``extends`` keyword are the *base types* of the cl The types specified after the ``instanceof`` keyword are the *instanceof types* of the class. -A class type is said to *inherit* from the base types. In addition, inheritance is transitive: If a type ``A`` inherits from a type ``B``, and ``B`` inherits from a type ``C``, then ``A`` inherits from ``C``. +A class type is said to *final inherit* from base types that are final or referenced through final aliases, and a class type is said to *inherit* from its other base types. In addition, inheritance is transitive: + +- If a type ``A`` inherits from a type ``B``, and ``B`` inherits from a type ``C``, then ``A`` inherits from ``C``. +- If a type ``A`` final inherits from a type ``B``, and ``B`` inherits from a type ``C``, then ``A`` final inherits from ``C``. +- If a type ``A`` inherits from a type ``B``, and ``B`` final inherits from a type ``C``, then ``A`` final inherits from ``C``. +- If a type ``A`` final inherits from a type ``B``, and ``B`` final inherits from a type ``C``, then ``A`` final inherits from ``C``. A class adds a mapping from the class name to the class declaration to the current module's declared type environment. A valid class can be annotated with ``abstract``, ``final``, ``library``, and ``private``. Any other annotation renders the class invalid. -A valid class may not inherit from a final class, from itself, or from more than one primitive type. +A valid class may not inherit from itself, or from more than one primitive type. The set of types that a valid class inherits from must be disjoint from the set of types that it final inherits from. A valid class must have at least one base type or instanceof type. @@ -975,9 +980,10 @@ Class dependencies The program is invalid if there is a cycle of class dependencies. The following are class dependencies: + - ``C`` depends on ``C.C`` - ``C.C`` depends on ``C.extends`` -- If ``C`` is abstract then it depends on all classes ``D`` such that ``C`` is a base type of ``D``. +- If ``C`` is abstract then it depends on all classes ``D`` such that ``C`` is a base type of ``D`` and ``D`` inherits from ``C``. - ``C.extends`` depends on ``D.D`` for each base type ``D`` of ``C``. - ``C.extends`` depends on ``D`` for each instanceof type ``D`` of ``C``. @@ -1029,7 +1035,9 @@ A valid member predicate can be annotated with ``abstract``, ``cached``, ``final If a type is provided before the name of the member predicate, then that type is the *result type* of the predicate. Otherwise, the predicate has no result type. The types of the variables in the ``var_decls`` are called the predicate's *argument types*. -A member predicate ``p`` with enclosing class ``C`` *overrides* a member predicate ``p'`` with enclosing class ``D`` when ``C`` inherits from ``D``, ``p'`` is visible in ``C``, and both ``p`` and ``p'`` have the same name and the same arity. An overriding predicate must have the same sequence of argument types as any predicates which it overrides, otherwise the program is invalid. +A member predicate ``p`` with enclosing class ``C`` *overrides* a member predicate ``p'`` with enclosing class ``D`` when ``p`` is annotated ``overrride``, ``C`` inherits from ``D``, ``p'`` is visible in ``C``, ``p'`` is not final, and both ``p`` and ``p'`` have the same name and the same arity. An overriding predicate must have the same sequence of argument types as any predicates which it overrides, otherwise the program is invalid. + +A member predicate ``p`` with enclosing class ``C`` *shadows* a member predicate ``p'`` with enclosing class ``D`` when ``C`` final inherits from ``D``, ``p'`` is visible in ``C``, and both ``p`` and ``p'`` have the same name and the same arity. Additionally, a member predicate ``p`` with enclosing class ``C`` *shadows* a member predicate ``p'`` with enclosing class ``D`` when ``C`` inherits from ``D``, ``p'`` is visible in ``C``, ``p'`` is final, and both ``p`` and ``p'`` have the same name and the same arity. Member predicates have one or more *root definitions*. If a member predicate overrides no other member predicate, then it is its own root definition. Otherwise, its root definitions are those of any member predicate that it overrides. @@ -1043,7 +1051,9 @@ A class may not inherit from a class with an abstract member predicate unless it A valid class must include a non-private predicate named ``toString`` with no arguments and a result type of ``string``, or it must inherit from a class that does. -A valid class may not inherit from two different classes that include a predicate with the same name and number of arguments, unless either one of the predicates overrides the other, or the class defines a predicate that overrides both of them. +A valid class may not inherit from two different classes that include a predicate with the same name and number of arguments, unless either one of the predicates overrides or shadows the other, or the class defines a predicate that overrides or shadows both of them. + +A valid class may not final inherit from two different classes that include a predicate with the same name and number of arguments, unless either one of the predicates overrides or shadows the other, or the class defines a predicate that shadows both of them. The typing environment for a member predicate or character is the same as if it were a non-member predicate, except that it additionally maps ``this`` to a type and also maps any fields on a class to a type. If the member is a character, then the typing environment maps ``this`` to the class domain type of the class. Otherwise, it maps ``this`` to the class type of the class itself. The typing environment also maps any field to the type of the field. @@ -1053,9 +1063,13 @@ Fields A field declaration introduces a mapping from the field name to the field declaration in the class's declared field environment. -A field ``f`` with enclosing class ``C`` *overrides* a field ``f'`` with enclosing class ``D`` when ``f`` is annotated ``override``, ``C`` inherits from ``D``, ``p'`` is visible in ``C``, and both ``p`` and ``p'`` have the same name. +A field ``f`` with enclosing class ``C`` *overrides* a field ``f'`` with enclosing class ``D`` when ``f`` is annotated ``override``, ``C`` inherits from ``D``, ``p'`` is visible in ``C``, ``p'`` is not final, and both ``p`` and ``p'`` have the same name. -A valid class may not inherit from two different classes that include a field with the same name, unless either one of the fields overrides the other, or the class defines a field that overrides both of them. +A field ``f`` with enclosing class ``C`` *shadows* a field ``f'`` with enclosing class ``D`` when ``C`` final inherits from ``D``, ``p'`` is visible in ``C``, and both ``p`` and ``p'`` have the same name. Additionally, a field ``f`` with enclosing class ``C`` *shadows* a field ``f'`` with enclosing class ``D`` when ``C`` inherits from ``D``, ``p'`` is visible in ``C``, ``p'`` is final, and both ``p`` and ``p'`` have the same name. + +A valid class may not inherit from two different classes that include a field with the same name, unless either one of the fields overrides or shadows the other, or the class defines a field that overrides or shadows both of them. + +A valid class may not final inherit from two different classes that include a field with the same name, unless either one of the fields overrides or shadows the other, or the class defines a field that shadows both of them. A valid field must override another field if it is annotated ``override``. @@ -1349,9 +1363,10 @@ If the call includes a closure, then all declared predicate arguments, the enclo A call to a member predicate may be a *direct* call: - If the receiver is not a super expression it is not direct. - - If the receiver is ``A.super`` and ``A`` is an instanceof type and not a base type then it is not direct. - - If the receiver is ``A.super`` and ``A`` is a base type type and not an instanceof type then it is direct. - - If the receiver is ``A.super`` and ``A`` is a base type and an instanceof type then the call is not valid. + - If the receiver is ``A.super`` and ``A`` is an instanceof type and not a base type that is inherited from then it is not direct. + - If the receiver is ``A.super`` and ``A`` is a base type that is final inherited from then it is not direct. + - If the receiver is ``A.super`` and ``A`` is a base type that is inherited from and not an instanceof type then it is direct. + - If the receiver is ``A.super`` and ``A`` is a base type that is inherited from and an instanceof type then the call is not valid. - If the receiver is ``super`` and the member predicate is in the exported member predicate environment of an instanceof type and not in the exported member predicate environment of a base type then it isn't direct. - If the receiver is ``super`` and the member predicate is in the exported member predicate environment of a base type and not in the exported member predicate environment of an instanceof type then it is direct. - If the receiver is ``super`` and the member predicate is in the exported member predicate environment of a base type and in the exported member predicate environment of an instanceof type then the call is not valid. @@ -2123,7 +2138,7 @@ Predicates, and types can *depend* and *strictly depend* on each other. Such dep - For each class ``C`` with a characteristic predicate, ``C.C`` depends on the characteristic predicate. -- For each abstract class ``A`` in the program, for each type ``C`` that has ``A`` as a base type, ``A.class`` depends on ``C.class``. +- For each abstract class ``A`` in the program, for each type ``C`` that inherits from ``A`` and has ``A`` as a base type, ``A.class`` depends on ``C.class``. - A predicate with a higher-order body may strictly depend or depend on each predicate reference within the body. The exact dependencies are left unspecified. @@ -2175,7 +2190,7 @@ Each layer of the stratification is *populated* in order. To populate a layer, e - To populate the type ``C.class`` for an abstract class type ``C``, identify each named tuple that has the following properties: - It is a member of ``C.C``. - - For each class ``D`` that has ``C`` as a base type then there is a named tuple with variables from the public fields of ``C`` and ``this`` that the given tuple and a tuple in ``D.class`` both extend. + - For each class ``D`` that inherits from ``C`` and has ``C`` as a base type then there is a named tuple with variables from the public fields of ``C`` and ``this`` that the given tuple and a tuple in ``D.class`` both extend. Query evaluation diff --git a/docs/codeql/reusables/supported-versions-compilers.rst b/docs/codeql/reusables/supported-versions-compilers.rst index 0d38f92a8c8..42d4eaad956 100644 --- a/docs/codeql/reusables/supported-versions-compilers.rst +++ b/docs/codeql/reusables/supported-versions-compilers.rst @@ -24,7 +24,7 @@ JavaScript,ECMAScript 2022 or lower,Not applicable,"``.js``, ``.jsx``, ``.mjs``, ``.es``, ``.es6``, ``.htm``, ``.html``, ``.xhtm``, ``.xhtml``, ``.vue``, ``.hbs``, ``.ejs``, ``.njk``, ``.json``, ``.yaml``, ``.yml``, ``.raml``, ``.xml`` [7]_" Python [8]_,"2.7, 3.5, 3.6, 3.7, 3.8, 3.9, 3.10, 3.11",Not applicable,``.py`` Ruby [9]_,"up to 3.2",Not applicable,"``.rb``, ``.erb``, ``.gemspec``, ``Gemfile``" - Swift [10]_,"Swift 5.4-5.7","Swift compiler","``.swift``" + Swift [10]_,"Swift 5.4-5.8.1","Swift compiler","``.swift``" TypeScript [11]_,"2.6-5.1",Standard TypeScript compiler,"``.ts``, ``.tsx``, ``.mts``, ``.cts``" .. container:: footnote-group @@ -38,5 +38,5 @@ .. [7] JSX and Flow code, YAML, JSON, HTML, and XML files may also be analyzed with JavaScript files. .. [8] The extractor requires Python 3 to run. To analyze Python 2.7 you should install both versions of Python. .. [9] Requires glibc 2.17. - .. [10] Swift support is currently in beta. Support for the analysis of Swift 5.4-5.7 requires macOS. Swift 5.7.3 can also be analyzed using Linux. + .. [10] Swift support is currently in beta. Support for the analysis of Swift 5.4-5.8.1 requires macOS or Linux. .. [11] TypeScript analysis is performed by running the JavaScript extractor with TypeScript enabled. This is the default. diff --git a/go/extractor/cli/go-autobuilder/go-autobuilder.go b/go/extractor/cli/go-autobuilder/go-autobuilder.go index fc56d4b8715..3f065dc3597 100644 --- a/go/extractor/cli/go-autobuilder/go-autobuilder.go +++ b/go/extractor/cli/go-autobuilder/go-autobuilder.go @@ -10,6 +10,7 @@ import ( "path/filepath" "regexp" "runtime" + "sort" "strings" "golang.org/x/mod/semver" @@ -206,6 +207,12 @@ func (m ModMode) argsForGoVersion(version string) []string { return nil } +type BuildInfo struct { + DepMode DependencyInstallerMode + ModMode ModMode + BaseDir string +} + // addVersionToMod add a go version directive, e.g. `go 1.14` to a `go.mod` file. func addVersionToMod(version string) bool { cmd := exec.Command("go", "mod", "edit", "-go="+version) @@ -240,28 +247,131 @@ func getSourceDir() string { return srcdir } +func getDirs(paths []string) []string { + dirs := make([]string, len(paths)) + for i, path := range paths { + dirs[i] = filepath.Dir(path) + } + return dirs +} + +func checkDirsNested(inputDirs []string) (string, bool) { + // replace "." with "" so that we can check if all the paths are nested + dirs := make([]string, len(inputDirs)) + for i, inputDir := range inputDirs { + if inputDir == "." { + dirs[i] = "" + } else { + dirs[i] = inputDir + } + } + // the paths were generated by a depth-first search so I think they might + // be sorted, but we sort them just in case + sort.Strings(dirs) + for _, dir := range dirs { + if !strings.HasPrefix(dir, dirs[0]) { + return "", false + } + } + return dirs[0], true +} + +// Returns the directory to run the go build in and whether to use a go.mod +// file. +func findGoModFiles(emitDiagnostics bool) (baseDir string, useGoMod bool) { + goModPaths := util.FindAllFilesWithName(".", "go.mod", "vendor") + if len(goModPaths) == 0 { + baseDir = "." + useGoMod = false + return + } + goModDirs := getDirs(goModPaths) + if util.AnyGoFilesOutsideDirs(".", goModDirs...) { + if emitDiagnostics { + diagnostics.EmitGoFilesOutsideGoModules(goModPaths) + } + baseDir = "." + useGoMod = false + return + } + if len(goModPaths) > 1 { + // currently not supported + baseDir = "." + commonRoot, nested := checkDirsNested(goModDirs) + if nested && commonRoot == "" { + useGoMod = true + } else { + useGoMod = false + } + if emitDiagnostics { + if nested { + diagnostics.EmitMultipleGoModFoundNested(goModPaths) + } else { + diagnostics.EmitMultipleGoModFoundNotNested(goModPaths) + } + } + return + } + if emitDiagnostics { + if goModDirs[0] == "." { + diagnostics.EmitSingleRootGoModFound(goModPaths[0]) + } else { + diagnostics.EmitSingleNonRootGoModFound(goModPaths[0]) + } + } + baseDir = goModDirs[0] + useGoMod = true + return +} + // Returns the appropriate DependencyInstallerMode for the current project -func getDepMode() DependencyInstallerMode { - if util.FileExists("go.mod") { +func getDepMode(emitDiagnostics bool) (DependencyInstallerMode, string) { + bazelPaths := util.FindAllFilesWithName(".", "BUILD", "vendor") + bazelPaths = append(bazelPaths, util.FindAllFilesWithName(".", "BUILD.bazel", "vendor")...) + if len(bazelPaths) > 0 { + // currently not supported + if emitDiagnostics { + diagnostics.EmitBazelBuildFilesFound(bazelPaths) + } + } + + goWorkPaths := util.FindAllFilesWithName(".", "go.work", "vendor") + if len(goWorkPaths) > 0 { + // currently not supported + if emitDiagnostics { + diagnostics.EmitGoWorkFound(goWorkPaths) + } + } + + baseDir, useGoMod := findGoModFiles(emitDiagnostics) + if useGoMod { log.Println("Found go.mod, enabling go modules") - return GoGetWithModules + return GoGetWithModules, baseDir } + if util.FileExists("Gopkg.toml") { + if emitDiagnostics { + diagnostics.EmitGopkgTomlFound() + } log.Println("Found Gopkg.toml, using dep instead of go get") - return Dep + return Dep, "." } + if util.FileExists("glide.yaml") { - log.Println("Found glide.yaml, enabling go modules") - return Glide + if emitDiagnostics { + diagnostics.EmitGlideYamlFound() + } + log.Println("Found glide.yaml, using Glide instead of go get") + return Glide, "." } - return GoGetNoModules + return GoGetNoModules, "." } // Tries to open `go.mod` and read a go directive, returning the version and whether it was found. -func tryReadGoDirective(depMode DependencyInstallerMode) (string, bool) { - if depMode == GoGetWithModules { +func tryReadGoDirective(buildInfo BuildInfo) (string, bool) { + if buildInfo.DepMode == GoGetWithModules { versionRe := regexp.MustCompile(`(?m)^go[ \t\r]+([0-9]+\.[0-9]+)$`) - goMod, err := os.ReadFile("go.mod") + goMod, err := os.ReadFile(filepath.Join(buildInfo.BaseDir, "go.mod")) if err != nil { log.Println("Failed to read go.mod to check for missing Go version") } else { @@ -277,13 +387,13 @@ func tryReadGoDirective(depMode DependencyInstallerMode) (string, bool) { } // Returns the appropriate ModMode for the current project -func getModMode(depMode DependencyInstallerMode) ModMode { +func getModMode(depMode DependencyInstallerMode, baseDir string) ModMode { if depMode == GoGetWithModules { // if a vendor/modules.txt file exists, we assume that there are vendored Go dependencies, and // skip the dependency installation step and run the extractor with `-mod=vendor` - if util.FileExists("vendor/modules.txt") { + if util.FileExists(filepath.Join(baseDir, "vendor", "modules.txt")) { return ModVendor - } else if util.DirExists("vendor") { + } else if util.DirExists(filepath.Join(baseDir, "vendor")) { return ModMod } } @@ -291,8 +401,8 @@ func getModMode(depMode DependencyInstallerMode) ModMode { } // fixGoVendorIssues fixes issues with go vendor for go version >= 1.14 -func fixGoVendorIssues(modMode ModMode, depMode DependencyInstallerMode, goModVersionFound bool) ModMode { - if modMode == ModVendor { +func fixGoVendorIssues(buildInfo *BuildInfo, goModVersionFound bool) { + if buildInfo.ModMode == ModVendor { // fix go vendor issues with go versions >= 1.14 when no go version is specified in the go.mod // if this is the case, and dependencies were vendored with an old go version (and therefore // do not contain a '## explicit' annotation, the go command will fail and refuse to do any @@ -300,7 +410,7 @@ func fixGoVendorIssues(modMode ModMode, depMode DependencyInstallerMode, goModVe // // we work around this by adding an explicit go version of 1.13, which is the last version // where this is not an issue - if depMode == GoGetWithModules { + if buildInfo.DepMode == GoGetWithModules { if !goModVersionFound { // if the go.mod does not contain a version line modulesTxt, err := os.ReadFile("vendor/modules.txt") @@ -311,19 +421,18 @@ func fixGoVendorIssues(modMode ModMode, depMode DependencyInstallerMode, goModVe log.Println("Adding a version directive to the go.mod file as the modules.txt does not have explicit annotations") if !addVersionToMod("1.13") { log.Println("Failed to add a version to the go.mod file to fix explicitly required package bug; not using vendored dependencies") - return ModMod + buildInfo.ModMode = ModMod } } } } } - return modMode } // Determines whether the project needs a GOPATH set up -func getNeedGopath(depMode DependencyInstallerMode, importpath string) bool { +func getNeedGopath(buildInfo BuildInfo, importpath string) bool { needGopath := true - if depMode == GoGetWithModules { + if buildInfo.DepMode == GoGetWithModules { needGopath = false } // if `LGTM_INDEX_NEED_GOPATH` is set, it overrides the value for `needGopath` inferred above @@ -344,25 +453,29 @@ func getNeedGopath(depMode DependencyInstallerMode, importpath string) bool { } // Try to update `go.mod` and `go.sum` if the go version is >= 1.16. -func tryUpdateGoModAndGoSum(modMode ModMode, depMode DependencyInstallerMode) { +func tryUpdateGoModAndGoSum(buildInfo BuildInfo) { // Go 1.16 and later won't automatically attempt to update go.mod / go.sum during package loading, so try to update them here: - if modMode != ModVendor && depMode == GoGetWithModules && semver.Compare(getEnvGoSemVer(), "v1.16") >= 0 { + if buildInfo.ModMode != ModVendor && buildInfo.DepMode == GoGetWithModules && semver.Compare(getEnvGoSemVer(), "v1.16") >= 0 { // stat go.mod and go.sum - beforeGoModFileInfo, beforeGoModErr := os.Stat("go.mod") + goModPath := filepath.Join(buildInfo.BaseDir, "go.mod") + beforeGoModFileInfo, beforeGoModErr := os.Stat(goModPath) if beforeGoModErr != nil { log.Println("Failed to stat go.mod before running `go mod tidy -e`") } - beforeGoSumFileInfo, beforeGoSumErr := os.Stat("go.sum") + goSumPath := filepath.Join(buildInfo.BaseDir, "go.sum") + beforeGoSumFileInfo, beforeGoSumErr := os.Stat(goSumPath) // run `go mod tidy -e` - res := util.RunCmd(exec.Command("go", "mod", "tidy", "-e")) + cmd := exec.Command("go", "mod", "tidy", "-e") + cmd.Dir = buildInfo.BaseDir + res := util.RunCmd(cmd) if !res { log.Println("Failed to run `go mod tidy -e`") } else { if beforeGoModFileInfo != nil { - afterGoModFileInfo, afterGoModErr := os.Stat("go.mod") + afterGoModFileInfo, afterGoModErr := os.Stat(goModPath) if afterGoModErr != nil { log.Println("Failed to stat go.mod after running `go mod tidy -e`") } else if afterGoModFileInfo.ModTime().After(beforeGoModFileInfo.ModTime()) { @@ -371,7 +484,7 @@ func tryUpdateGoModAndGoSum(modMode ModMode, depMode DependencyInstallerMode) { } } - afterGoSumFileInfo, afterGoSumErr := os.Stat("go.sum") + afterGoSumFileInfo, afterGoSumErr := os.Stat(goSumPath) if afterGoSumErr != nil { log.Println("Failed to stat go.sum after running `go mod tidy -e`") } else { @@ -560,10 +673,10 @@ func buildWithCustomCommands(inst string) { } // Install dependencies using the given dependency installer mode. -func installDependencies(depMode DependencyInstallerMode) { +func installDependencies(buildInfo BuildInfo) { // automatically determine command to install dependencies var install *exec.Cmd - if depMode == Dep { + if buildInfo.DepMode == Dep { // set up the dep cache if SEMMLE_CACHE is set cacheDir := os.Getenv("SEMMLE_CACHE") if cacheDir != "" { @@ -593,44 +706,41 @@ func installDependencies(depMode DependencyInstallerMode) { install = exec.Command("dep", "ensure", "-v") } log.Println("Installing dependencies using `dep ensure`.") - } else if depMode == Glide { + } else if buildInfo.DepMode == Glide { install = exec.Command("glide", "install") log.Println("Installing dependencies using `glide install`") } else { // explicitly set go module support - if depMode == GoGetWithModules { + if buildInfo.DepMode == GoGetWithModules { os.Setenv("GO111MODULE", "on") - } else if depMode == GoGetNoModules { + } else if buildInfo.DepMode == GoGetNoModules { os.Setenv("GO111MODULE", "off") } // get dependencies install = exec.Command("go", "get", "-v", "./...") - log.Println("Installing dependencies using `go get -v ./...`.") + install.Dir = buildInfo.BaseDir + log.Printf("Installing dependencies using `go get -v ./...` in `%s`.\n", buildInfo.BaseDir) } util.RunCmd(install) } // Run the extractor. -func extract(depMode DependencyInstallerMode, modMode ModMode) { +func extract(buildInfo BuildInfo) { extractor, err := util.GetExtractorPath() if err != nil { log.Fatalf("Could not determine path of extractor: %v.\n", err) } - cwd, err := os.Getwd() - if err != nil { - log.Fatalf("Unable to determine current directory: %s\n", err.Error()) - } - extractorArgs := []string{} - if depMode == GoGetWithModules { - extractorArgs = append(extractorArgs, modMode.argsForGoVersion(getEnvGoSemVer())...) + if buildInfo.DepMode == GoGetWithModules { + extractorArgs = append(extractorArgs, buildInfo.ModMode.argsForGoVersion(getEnvGoSemVer())...) } extractorArgs = append(extractorArgs, "./...") - log.Printf("Running extractor command '%s %v' from directory '%s'.\n", extractor, extractorArgs, cwd) + log.Printf("Running extractor command '%s %v' from directory '%s'.\n", extractor, extractorArgs, buildInfo.BaseDir) cmd := exec.Command(extractor, extractorArgs...) + cmd.Dir = buildInfo.BaseDir cmd.Stdout = os.Stdout cmd.Stderr = os.Stderr err = cmd.Run() @@ -639,6 +749,12 @@ func extract(depMode DependencyInstallerMode, modMode ModMode) { } } +func getBuildInfo(emitDiagnostics bool) BuildInfo { + depMode, baseDir := getDepMode(true) + modMode := getModMode(depMode, baseDir) + return BuildInfo{depMode, modMode, baseDir} +} + // Build the project and run the extractor. func installDependenciesAndBuild() { log.Printf("Autobuilder was built with %s, environment has %s\n", runtime.Version(), getEnvGoVersion()) @@ -650,24 +766,23 @@ func installDependenciesAndBuild() { // determine how to install dependencies and whether a GOPATH needs to be set up before // extraction - depMode := getDepMode() + buildInfo := getBuildInfo(true) if _, present := os.LookupEnv("GO111MODULE"); !present { os.Setenv("GO111MODULE", "auto") } - goModVersion, goModVersionFound := tryReadGoDirective(depMode) + goModVersion, goModVersionFound := tryReadGoDirective(buildInfo) - if semver.Compare("v"+goModVersion, getEnvGoSemVer()) >= 0 { + if goModVersionFound && semver.Compare("v"+goModVersion, getEnvGoSemVer()) >= 0 { diagnostics.EmitNewerGoVersionNeeded() } - modMode := getModMode(depMode) - modMode = fixGoVendorIssues(modMode, depMode, goModVersionFound) + fixGoVendorIssues(&buildInfo, goModVersionFound) - tryUpdateGoModAndGoSum(modMode, depMode) + tryUpdateGoModAndGoSum(buildInfo) importpath := getImportPath() - needGopath := getNeedGopath(depMode, importpath) + needGopath := getNeedGopath(buildInfo, importpath) inLGTM := os.Getenv("LGTM_SRC") != "" || os.Getenv("LGTM_INDEX_NEED_GOPATH") != "" @@ -688,30 +803,30 @@ func installDependenciesAndBuild() { inst := util.Getenv("CODEQL_EXTRACTOR_GO_BUILD_COMMAND", "LGTM_INDEX_BUILD_COMMAND") shouldInstallDependencies := false if inst == "" { - shouldInstallDependencies = buildWithoutCustomCommands(modMode) + shouldInstallDependencies = buildWithoutCustomCommands(buildInfo.ModMode) } else { buildWithCustomCommands(inst) } - if modMode == ModVendor { + if buildInfo.ModMode == ModVendor { // test if running `go` with -mod=vendor works, and if it doesn't, try to fallback to -mod=mod // or not set if the go version < 1.14. Note we check this post-build in case the build brings // the vendor directory up to date. if !checkVendor() { - modMode = ModMod + buildInfo.ModMode = ModMod log.Println("The vendor directory is not consistent with the go.mod; not using vendored dependencies.") } } if shouldInstallDependencies { - if modMode == ModVendor { + if buildInfo.ModMode == ModVendor { log.Printf("Skipping dependency installation because a Go vendor directory was found.") } else { - installDependencies(depMode) + installDependencies(buildInfo) } } - extract(depMode, modMode) + extract(buildInfo) } const minGoVersion = "1.11" @@ -976,8 +1091,8 @@ func isGoInstalled() bool { // Get the version of Go to install and output it to stdout as json. func identifyEnvironment() { var v versionInfo - depMode := getDepMode() - v.goModVersion, v.goModVersionFound = tryReadGoDirective(depMode) + buildInfo := getBuildInfo(false) + v.goModVersion, v.goModVersionFound = tryReadGoDirective(buildInfo) v.goEnvVersionFound = isGoInstalled() if v.goEnvVersionFound { diff --git a/go/extractor/diagnostics/diagnostics.go b/go/extractor/diagnostics/diagnostics.go index e92599417a5..728122cb787 100644 --- a/go/extractor/diagnostics/diagnostics.go +++ b/go/extractor/diagnostics/diagnostics.go @@ -129,16 +129,19 @@ func EmitPackageDifferentOSArchitecture(pkgPath string) { ) } +func plural(n int, singular, plural string) string { + if n == 1 { + return singular + } else { + return plural + } +} + const maxNumPkgPaths = 5 func EmitCannotFindPackages(pkgPaths []string) { numPkgPaths := len(pkgPaths) - ending := "s" - if numPkgPaths == 1 { - ending = "" - } - numPrinted := numPkgPaths truncated := false if numPrinted > maxNumPkgPaths { @@ -154,7 +157,11 @@ func EmitCannotFindPackages(pkgPaths []string) { emitDiagnostic( "go/autobuilder/package-not-found", "Some packages could not be found", - fmt.Sprintf("%d package%s could not be found:\n\n%s.\n\nDefinitions in those packages may not be recognized by CodeQL, and files that use them may only be partially analyzed.\n\nCheck that the paths are correct and make sure any private packages can be accessed. If any of the packages are present in the repository then you may need a [custom build command](https://docs.github.com/en/code-security/code-scanning/automatically-scanning-your-code-for-vulnerabilities-and-errors/configuring-the-codeql-workflow-for-compiled-languages).", numPkgPaths, ending, secondLine), + fmt.Sprintf( + "%d package%s could not be found:\n\n%s.\n\nDefinitions in those packages may not be recognized by CodeQL, and files that use them may only be partially analyzed.\n\nCheck that the paths are correct and make sure any private packages can be accessed. If any of the packages are present in the repository then you may need a [custom build command](https://docs.github.com/en/code-security/code-scanning/automatically-scanning-your-code-for-vulnerabilities-and-errors/configuring-the-codeql-workflow-for-compiled-languages).", + numPkgPaths, + plural(len(pkgPaths), "", "s"), + secondLine), severityWarning, fullVisibility, noLocation, @@ -194,6 +201,123 @@ func EmitRelativeImportPaths() { ) } +// The following diagnostics are telemetry-only. + +func EmitBazelBuildFilesFound(bazelPaths []string) { + emitDiagnostic( + "go/autobuilder/bazel-build-file-found", + "Bazel BUILD files were found", + fmt.Sprintf( + "%d bazel BUILD %s found:\n\n`%s`", + len(bazelPaths), + plural(len(bazelPaths), "file was", "files were"), + strings.Join(bazelPaths, "`, `")), + severityNote, + telemetryOnly, + noLocation, + ) +} + +func EmitGopkgTomlFound() { + emitDiagnostic( + "go/autobuilder/gopkg-toml-found", + "A dep `Gopkg.toml` file was found", + "A dep `Gopkg.toml` file was found", + severityNote, + telemetryOnly, + noLocation, + ) +} + +func EmitGlideYamlFound() { + emitDiagnostic( + "go/autobuilder/glide-yaml-found", + "A Glide `glide.yaml` file was found", + "A Glide `glide.yaml` file was found", + severityNote, + telemetryOnly, + noLocation, + ) +} + +func EmitGoWorkFound(goWorkPaths []string) { + emitDiagnostic( + "go/autobuilder/go-work-found", + "`go.work` file found", + fmt.Sprintf( + "%d `go.work` %s found:\n\n`%s`", + len(goWorkPaths), + plural(len(goWorkPaths), "file was", "files were"), + strings.Join(goWorkPaths, "`, `")), + severityNote, + telemetryOnly, + noLocation, + ) +} + +func EmitGoFilesOutsideGoModules(goModPaths []string) { + emitDiagnostic( + "go/autobuilder/go-files-outside-go-modules", + "Go files were found outside Go modules", + "Go files were found outside of the Go modules corresponding to these `go.mod` files.\n\n`"+strings.Join(goModPaths, "`, `")+"`", + severityNote, + telemetryOnly, + noLocation, + ) +} + +func EmitMultipleGoModFoundNested(goModPaths []string) { + emitDiagnostic( + "go/autobuilder/multiple-go-mod-found-nested", + "Multiple `go.mod` files were found, all nested under one root `go.mod` file", + fmt.Sprintf( + "%d `go.mod` files were found:\n\n`%s`", + len(goModPaths), + strings.Join(goModPaths, "`, `")), + severityNote, + telemetryOnly, + noLocation, + ) +} + +func EmitMultipleGoModFoundNotNested(goModPaths []string) { + emitDiagnostic( + "go/autobuilder/multiple-go-mod-found-not-nested", + "Multiple `go.mod` files found, not all nested under one root `go.mod` file", + fmt.Sprintf( + "%d `go.mod` files were found:\n\n`%s`", + len(goModPaths), + strings.Join(goModPaths, "`, `")), + severityNote, + telemetryOnly, + noLocation, + ) +} + +func EmitSingleRootGoModFound(goModPath string) { + emitDiagnostic( + "go/autobuilder/single-root-go-mod-found", + "A single `go.mod` file was found in the root", + "A single `go.mod` file was found.\n\n`"+goModPath+"`", + severityNote, + telemetryOnly, + noLocation, + ) +} + +func EmitSingleNonRootGoModFound(goModPath string) { + emitDiagnostic( + "go/autobuilder/single-non-root-go-mod-found", + "A single, non-root `go.mod` file was found", + "A single, non-root `go.mod` file was found.\n\n`"+goModPath+"`", + severityNote, + telemetryOnly, + noLocation, + ) +} + +// The following diagnostics are related to identifying the build environment. + func EmitNoGoModAndNoGoEnv(msg string) { emitDiagnostic( "go/autobuilder/env-no-go-mod-no-go-env", diff --git a/go/extractor/util/util.go b/go/extractor/util/util.go index 813938e1f73..313c000d30d 100644 --- a/go/extractor/util/util.go +++ b/go/extractor/util/util.go @@ -297,3 +297,46 @@ func FindGoFiles(root string) bool { }) return found } + +func FindAllFilesWithName(root string, name string, dirsToSkip ...string) []string { + paths := make([]string, 0, 1) + filepath.WalkDir(root, func(path string, d fs.DirEntry, err error) error { + if err != nil { + return err + } + if d.IsDir() { + for _, dirToSkip := range dirsToSkip { + if path == dirToSkip { + return filepath.SkipDir + } + } + } + if d.Name() == name { + paths = append(paths, path) + } + return nil + }) + return paths +} + +func AnyGoFilesOutsideDirs(root string, dirsToSkip ...string) bool { + found := false + filepath.WalkDir(root, func(path string, d fs.DirEntry, err error) error { + if err != nil { + return err + } + if d.IsDir() { + for _, dirToSkip := range dirsToSkip { + if path == dirToSkip { + return filepath.SkipDir + } + } + } + if filepath.Ext(d.Name()) == ".go" { + found = true + return filepath.SkipAll + } + return nil + }) + return found +} diff --git a/go/ql/integration-tests/all-platforms/go/bazel-sample-1/BUILD.bazel b/go/ql/integration-tests/all-platforms/go/bazel-sample-1/BUILD.bazel new file mode 100644 index 00000000000..e69de29bb2d diff --git a/go/ql/integration-tests/all-platforms/go/bazel-sample-1/diagnostics.expected b/go/ql/integration-tests/all-platforms/go/bazel-sample-1/diagnostics.expected new file mode 100644 index 00000000000..4e061f03fc3 --- /dev/null +++ b/go/ql/integration-tests/all-platforms/go/bazel-sample-1/diagnostics.expected @@ -0,0 +1,28 @@ +{ + "markdownMessage": "1 bazel BUILD file was found:\n\n`BUILD.bazel`", + "severity": "note", + "source": { + "extractorName": "go", + "id": "go/autobuilder/bazel-build-file-found", + "name": "Bazel BUILD files were found" + }, + "visibility": { + "cliSummaryTable": false, + "statusPage": false, + "telemetry": true + } +} +{ + "markdownMessage": "A single `go.mod` file was found.\n\n`go.mod`", + "severity": "note", + "source": { + "extractorName": "go", + "id": "go/autobuilder/single-root-go-mod-found", + "name": "A single `go.mod` file was found in the root" + }, + "visibility": { + "cliSummaryTable": false, + "statusPage": false, + "telemetry": true + } +} diff --git a/go/ql/integration-tests/all-platforms/go/bazel-sample-1/go.mod b/go/ql/integration-tests/all-platforms/go/bazel-sample-1/go.mod new file mode 100644 index 00000000000..64e39913d21 --- /dev/null +++ b/go/ql/integration-tests/all-platforms/go/bazel-sample-1/go.mod @@ -0,0 +1,7 @@ +go 1.14 + +require ( + golang.org/x/net v0.0.0-20200505041828-1ed23360d12c +) + +module bazelsample diff --git a/go/ql/integration-tests/all-platforms/go/bazel-sample-1/go.sum b/go/ql/integration-tests/all-platforms/go/bazel-sample-1/go.sum new file mode 100644 index 00000000000..6c5ffa613d0 --- /dev/null +++ b/go/ql/integration-tests/all-platforms/go/bazel-sample-1/go.sum @@ -0,0 +1,7 @@ +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/net v0.0.0-20200505041828-1ed23360d12c h1:zJ0mtu4jCalhKg6Oaukv6iIkb+cOvDrajDH9DH46Q4M= +golang.org/x/net v0.0.0-20200505041828-1ed23360d12c/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd h1:xhmwyvizuTgC2qz7ZlMluP20uW+C3Rm0FD/WLDX8884= +golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= diff --git a/go/ql/integration-tests/all-platforms/go/bazel-sample-1/test.expected b/go/ql/integration-tests/all-platforms/go/bazel-sample-1/test.expected new file mode 100644 index 00000000000..00f0cbe3e01 --- /dev/null +++ b/go/ql/integration-tests/all-platforms/go/bazel-sample-1/test.expected @@ -0,0 +1,7 @@ +htmlFiles +extractionErrors +| Extraction failed in test.go with error \tother declaration of test | 2 | +| Extraction failed in todel.go with error test redeclared in this block | 2 | +#select +| test.go:0:0:0:0 | test.go | +| todel.go:0:0:0:0 | todel.go | diff --git a/go/ql/integration-tests/all-platforms/go/bazel-sample-1/test.go b/go/ql/integration-tests/all-platforms/go/bazel-sample-1/test.go new file mode 100644 index 00000000000..57b29dc9cc0 --- /dev/null +++ b/go/ql/integration-tests/all-platforms/go/bazel-sample-1/test.go @@ -0,0 +1,12 @@ +package bazelsample + +import ( + "golang.org/x/net/ipv4" +) + +func test() { + + header := ipv4.Header{} + header.Version = 4 + +} diff --git a/go/ql/integration-tests/all-platforms/go/bazel-sample-1/test.py b/go/ql/integration-tests/all-platforms/go/bazel-sample-1/test.py new file mode 100644 index 00000000000..71ace021c22 --- /dev/null +++ b/go/ql/integration-tests/all-platforms/go/bazel-sample-1/test.py @@ -0,0 +1,8 @@ +import sys + +from create_database_utils import * +from diagnostics_test_utils import * + +run_codeql_database_create([], lang="go") + +check_diagnostics() diff --git a/go/ql/integration-tests/all-platforms/go/bazel-sample-1/test.ql b/go/ql/integration-tests/all-platforms/go/bazel-sample-1/test.ql new file mode 100644 index 00000000000..362eddafa59 --- /dev/null +++ b/go/ql/integration-tests/all-platforms/go/bazel-sample-1/test.ql @@ -0,0 +1,9 @@ +import go +import semmle.go.DiagnosticsReporting + +from GoFile f +select f + +query predicate htmlFiles(HtmlFile x) { any() } + +query predicate extractionErrors(string msg, int sev) { reportableDiagnostics(_, msg, sev) } diff --git a/go/ql/integration-tests/all-platforms/go/bazel-sample-1/todel.go b/go/ql/integration-tests/all-platforms/go/bazel-sample-1/todel.go new file mode 100644 index 00000000000..57b29dc9cc0 --- /dev/null +++ b/go/ql/integration-tests/all-platforms/go/bazel-sample-1/todel.go @@ -0,0 +1,12 @@ +package bazelsample + +import ( + "golang.org/x/net/ipv4" +) + +func test() { + + header := ipv4.Header{} + header.Version = 4 + +} diff --git a/go/ql/integration-tests/all-platforms/go/bazel-sample-2/BUILD b/go/ql/integration-tests/all-platforms/go/bazel-sample-2/BUILD new file mode 100644 index 00000000000..e69de29bb2d diff --git a/go/ql/integration-tests/all-platforms/go/bazel-sample-2/diagnostics.expected b/go/ql/integration-tests/all-platforms/go/bazel-sample-2/diagnostics.expected new file mode 100644 index 00000000000..2ee5be03391 --- /dev/null +++ b/go/ql/integration-tests/all-platforms/go/bazel-sample-2/diagnostics.expected @@ -0,0 +1,28 @@ +{ + "markdownMessage": "1 bazel BUILD file was found:\n\n`BUILD`", + "severity": "note", + "source": { + "extractorName": "go", + "id": "go/autobuilder/bazel-build-file-found", + "name": "Bazel BUILD files were found" + }, + "visibility": { + "cliSummaryTable": false, + "statusPage": false, + "telemetry": true + } +} +{ + "markdownMessage": "A single `go.mod` file was found.\n\n`go.mod`", + "severity": "note", + "source": { + "extractorName": "go", + "id": "go/autobuilder/single-root-go-mod-found", + "name": "A single `go.mod` file was found in the root" + }, + "visibility": { + "cliSummaryTable": false, + "statusPage": false, + "telemetry": true + } +} diff --git a/go/ql/integration-tests/all-platforms/go/bazel-sample-2/go.mod b/go/ql/integration-tests/all-platforms/go/bazel-sample-2/go.mod new file mode 100644 index 00000000000..64e39913d21 --- /dev/null +++ b/go/ql/integration-tests/all-platforms/go/bazel-sample-2/go.mod @@ -0,0 +1,7 @@ +go 1.14 + +require ( + golang.org/x/net v0.0.0-20200505041828-1ed23360d12c +) + +module bazelsample diff --git a/go/ql/integration-tests/all-platforms/go/bazel-sample-2/go.sum b/go/ql/integration-tests/all-platforms/go/bazel-sample-2/go.sum new file mode 100644 index 00000000000..6c5ffa613d0 --- /dev/null +++ b/go/ql/integration-tests/all-platforms/go/bazel-sample-2/go.sum @@ -0,0 +1,7 @@ +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/net v0.0.0-20200505041828-1ed23360d12c h1:zJ0mtu4jCalhKg6Oaukv6iIkb+cOvDrajDH9DH46Q4M= +golang.org/x/net v0.0.0-20200505041828-1ed23360d12c/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd h1:xhmwyvizuTgC2qz7ZlMluP20uW+C3Rm0FD/WLDX8884= +golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= diff --git a/go/ql/integration-tests/all-platforms/go/bazel-sample-2/test.expected b/go/ql/integration-tests/all-platforms/go/bazel-sample-2/test.expected new file mode 100644 index 00000000000..00f0cbe3e01 --- /dev/null +++ b/go/ql/integration-tests/all-platforms/go/bazel-sample-2/test.expected @@ -0,0 +1,7 @@ +htmlFiles +extractionErrors +| Extraction failed in test.go with error \tother declaration of test | 2 | +| Extraction failed in todel.go with error test redeclared in this block | 2 | +#select +| test.go:0:0:0:0 | test.go | +| todel.go:0:0:0:0 | todel.go | diff --git a/go/ql/integration-tests/all-platforms/go/bazel-sample-2/test.go b/go/ql/integration-tests/all-platforms/go/bazel-sample-2/test.go new file mode 100644 index 00000000000..57b29dc9cc0 --- /dev/null +++ b/go/ql/integration-tests/all-platforms/go/bazel-sample-2/test.go @@ -0,0 +1,12 @@ +package bazelsample + +import ( + "golang.org/x/net/ipv4" +) + +func test() { + + header := ipv4.Header{} + header.Version = 4 + +} diff --git a/go/ql/integration-tests/all-platforms/go/bazel-sample-2/test.py b/go/ql/integration-tests/all-platforms/go/bazel-sample-2/test.py new file mode 100644 index 00000000000..71ace021c22 --- /dev/null +++ b/go/ql/integration-tests/all-platforms/go/bazel-sample-2/test.py @@ -0,0 +1,8 @@ +import sys + +from create_database_utils import * +from diagnostics_test_utils import * + +run_codeql_database_create([], lang="go") + +check_diagnostics() diff --git a/go/ql/integration-tests/all-platforms/go/bazel-sample-2/test.ql b/go/ql/integration-tests/all-platforms/go/bazel-sample-2/test.ql new file mode 100644 index 00000000000..362eddafa59 --- /dev/null +++ b/go/ql/integration-tests/all-platforms/go/bazel-sample-2/test.ql @@ -0,0 +1,9 @@ +import go +import semmle.go.DiagnosticsReporting + +from GoFile f +select f + +query predicate htmlFiles(HtmlFile x) { any() } + +query predicate extractionErrors(string msg, int sev) { reportableDiagnostics(_, msg, sev) } diff --git a/go/ql/integration-tests/all-platforms/go/bazel-sample-2/todel.go b/go/ql/integration-tests/all-platforms/go/bazel-sample-2/todel.go new file mode 100644 index 00000000000..57b29dc9cc0 --- /dev/null +++ b/go/ql/integration-tests/all-platforms/go/bazel-sample-2/todel.go @@ -0,0 +1,12 @@ +package bazelsample + +import ( + "golang.org/x/net/ipv4" +) + +func test() { + + header := ipv4.Header{} + header.Version = 4 + +} diff --git a/go/ql/integration-tests/all-platforms/go/diagnostics/build-constraints-exclude-all-go-files/diagnostics.expected b/go/ql/integration-tests/all-platforms/go/diagnostics/build-constraints-exclude-all-go-files/diagnostics.expected index dc7a5dca282..65a0f5be04c 100644 --- a/go/ql/integration-tests/all-platforms/go/diagnostics/build-constraints-exclude-all-go-files/diagnostics.expected +++ b/go/ql/integration-tests/all-platforms/go/diagnostics/build-constraints-exclude-all-go-files/diagnostics.expected @@ -1,3 +1,17 @@ +{ + "markdownMessage": "A single `go.mod` file was found.\n\n`go.mod`", + "severity": "note", + "source": { + "extractorName": "go", + "id": "go/autobuilder/single-root-go-mod-found", + "name": "A single `go.mod` file was found in the root" + }, + "visibility": { + "cliSummaryTable": false, + "statusPage": false, + "telemetry": true + } +} { "markdownMessage": "`syscall/js` could not be imported. Make sure the `GOOS` and `GOARCH` [environment variables are correctly set](https://docs.github.com/en/actions/learn-github-actions/variables#defining-environment-variables-for-a-single-workflow). Alternatively, [change your OS and architecture](https://docs.github.com/en/actions/using-github-hosted-runners/about-github-hosted-runners#using-a-github-hosted-runner).", "severity": "warning", diff --git a/go/ql/integration-tests/all-platforms/go/diagnostics/go-files-found-not-processed/diagnostics.expected b/go/ql/integration-tests/all-platforms/go/diagnostics/go-files-found-not-processed/diagnostics.expected index 405113fe93a..f17d5a9a242 100644 --- a/go/ql/integration-tests/all-platforms/go/diagnostics/go-files-found-not-processed/diagnostics.expected +++ b/go/ql/integration-tests/all-platforms/go/diagnostics/go-files-found-not-processed/diagnostics.expected @@ -1,3 +1,17 @@ +{ + "markdownMessage": "2 `go.mod` files were found:\n\n`go.mod`, `subdir/go.mod`", + "severity": "note", + "source": { + "extractorName": "go", + "id": "go/autobuilder/multiple-go-mod-found-nested", + "name": "Multiple `go.mod` files were found, all nested under one root `go.mod` file" + }, + "visibility": { + "cliSummaryTable": false, + "statusPage": false, + "telemetry": true + } +} { "markdownMessage": "[Specify a custom build command](https://docs.github.com/en/code-security/code-scanning/automatically-scanning-your-code-for-vulnerabilities-and-errors/configuring-the-codeql-workflow-for-compiled-languages) that includes one or more `go build` commands to build the `.go` files to be analyzed.", "severity": "error", diff --git a/go/ql/integration-tests/all-platforms/go/diagnostics/go-files-found-not-processed/work/subdir/test.go b/go/ql/integration-tests/all-platforms/go/diagnostics/go-files-found-not-processed/work/subdir/test.go index 3f8cd1e2951..eede07b9874 100644 --- a/go/ql/integration-tests/all-platforms/go/diagnostics/go-files-found-not-processed/work/subdir/test.go +++ b/go/ql/integration-tests/all-platforms/go/diagnostics/go-files-found-not-processed/work/subdir/test.go @@ -1,4 +1,4 @@ -package test/subdir +package subdir func Test() { } diff --git a/go/ql/integration-tests/all-platforms/go/diagnostics/newer-go-version-needed/diagnostics.expected b/go/ql/integration-tests/all-platforms/go/diagnostics/newer-go-version-needed/diagnostics.expected index e50dcf5c093..1db1354f164 100644 --- a/go/ql/integration-tests/all-platforms/go/diagnostics/newer-go-version-needed/diagnostics.expected +++ b/go/ql/integration-tests/all-platforms/go/diagnostics/newer-go-version-needed/diagnostics.expected @@ -1,3 +1,17 @@ +{ + "markdownMessage": "A single `go.mod` file was found.\n\n`go.mod`", + "severity": "note", + "source": { + "extractorName": "go", + "id": "go/autobuilder/single-root-go-mod-found", + "name": "A single `go.mod` file was found in the root" + }, + "visibility": { + "cliSummaryTable": false, + "statusPage": false, + "telemetry": true + } +} { "markdownMessage": "The detected version of Go is lower than the version specified in `go.mod`. [Install a newer version](https://github.com/actions/setup-go#basic).", "severity": "error", diff --git a/go/ql/integration-tests/all-platforms/go/diagnostics/package-not-found-with-go-mod/diagnostics.expected b/go/ql/integration-tests/all-platforms/go/diagnostics/package-not-found-with-go-mod/diagnostics.expected index dff5dc5bb92..31204020a02 100644 --- a/go/ql/integration-tests/all-platforms/go/diagnostics/package-not-found-with-go-mod/diagnostics.expected +++ b/go/ql/integration-tests/all-platforms/go/diagnostics/package-not-found-with-go-mod/diagnostics.expected @@ -12,3 +12,17 @@ "telemetry": true } } +{ + "markdownMessage": "A single `go.mod` file was found.\n\n`go.mod`", + "severity": "note", + "source": { + "extractorName": "go", + "id": "go/autobuilder/single-root-go-mod-found", + "name": "A single `go.mod` file was found in the root" + }, + "visibility": { + "cliSummaryTable": false, + "statusPage": false, + "telemetry": true + } +} diff --git a/go/ql/integration-tests/all-platforms/go/single-go-mod-and-go-files-not-under-it/diagnostics.expected b/go/ql/integration-tests/all-platforms/go/single-go-mod-and-go-files-not-under-it/diagnostics.expected new file mode 100644 index 00000000000..ed2d87ac207 --- /dev/null +++ b/go/ql/integration-tests/all-platforms/go/single-go-mod-and-go-files-not-under-it/diagnostics.expected @@ -0,0 +1,28 @@ +{ + "markdownMessage": "1 package could not be found:\n\n`subdir/subsubdir`.\n\nDefinitions in those packages may not be recognized by CodeQL, and files that use them may only be partially analyzed.\n\nCheck that the paths are correct and make sure any private packages can be accessed. If any of the packages are present in the repository then you may need a [custom build command](https://docs.github.com/en/code-security/code-scanning/automatically-scanning-your-code-for-vulnerabilities-and-errors/configuring-the-codeql-workflow-for-compiled-languages).", + "severity": "warning", + "source": { + "extractorName": "go", + "id": "go/autobuilder/package-not-found", + "name": "Some packages could not be found" + }, + "visibility": { + "cliSummaryTable": true, + "statusPage": true, + "telemetry": true + } +} +{ + "markdownMessage": "Go files were found outside of the Go modules corresponding to these `go.mod` files.\n\n`subdir/go.mod`", + "severity": "note", + "source": { + "extractorName": "go", + "id": "go/autobuilder/go-files-outside-go-modules", + "name": "Go files were found outside Go modules" + }, + "visibility": { + "cliSummaryTable": false, + "statusPage": false, + "telemetry": true + } +} diff --git a/go/ql/integration-tests/all-platforms/go/single-go-mod-and-go-files-not-under-it/main.go b/go/ql/integration-tests/all-platforms/go/single-go-mod-and-go-files-not-under-it/main.go new file mode 100644 index 00000000000..8f618807311 --- /dev/null +++ b/go/ql/integration-tests/all-platforms/go/single-go-mod-and-go-files-not-under-it/main.go @@ -0,0 +1,9 @@ +package main + +import ( + "fmt" +) + +func main() { + fmt.Println("Hello, world!") +} diff --git a/go/ql/integration-tests/all-platforms/go/single-go-mod-and-go-files-not-under-it/subdir/go.mod b/go/ql/integration-tests/all-platforms/go/single-go-mod-and-go-files-not-under-it/subdir/go.mod new file mode 100644 index 00000000000..e217b3613a6 --- /dev/null +++ b/go/ql/integration-tests/all-platforms/go/single-go-mod-and-go-files-not-under-it/subdir/go.mod @@ -0,0 +1,5 @@ +go 1.14 + +require golang.org/x/net v0.0.0-20200505041828-1ed23360d12c + +module subdir diff --git a/go/ql/integration-tests/all-platforms/go/single-go-mod-and-go-files-not-under-it/subdir/go.sum b/go/ql/integration-tests/all-platforms/go/single-go-mod-and-go-files-not-under-it/subdir/go.sum new file mode 100644 index 00000000000..6c5ffa613d0 --- /dev/null +++ b/go/ql/integration-tests/all-platforms/go/single-go-mod-and-go-files-not-under-it/subdir/go.sum @@ -0,0 +1,7 @@ +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/net v0.0.0-20200505041828-1ed23360d12c h1:zJ0mtu4jCalhKg6Oaukv6iIkb+cOvDrajDH9DH46Q4M= +golang.org/x/net v0.0.0-20200505041828-1ed23360d12c/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd h1:xhmwyvizuTgC2qz7ZlMluP20uW+C3Rm0FD/WLDX8884= +golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= diff --git a/go/ql/integration-tests/all-platforms/go/single-go-mod-and-go-files-not-under-it/subdir/subsubdir/add.go b/go/ql/integration-tests/all-platforms/go/single-go-mod-and-go-files-not-under-it/subdir/subsubdir/add.go new file mode 100644 index 00000000000..15233c47f62 --- /dev/null +++ b/go/ql/integration-tests/all-platforms/go/single-go-mod-and-go-files-not-under-it/subdir/subsubdir/add.go @@ -0,0 +1,5 @@ +package subsubdir + +func Add(a, b int) int { + return a + b +} diff --git a/go/ql/integration-tests/all-platforms/go/single-go-mod-and-go-files-not-under-it/subdir/test.go b/go/ql/integration-tests/all-platforms/go/single-go-mod-and-go-files-not-under-it/subdir/test.go new file mode 100644 index 00000000000..291a8591372 --- /dev/null +++ b/go/ql/integration-tests/all-platforms/go/single-go-mod-and-go-files-not-under-it/subdir/test.go @@ -0,0 +1,14 @@ +package subdir + +import ( + "subdir/subsubdir" + + "golang.org/x/net/ipv4" +) + +func test() { + + header := ipv4.Header{} + header.Version = subsubdir.Add(2, 2) + +} diff --git a/go/ql/integration-tests/all-platforms/go/single-go-mod-and-go-files-not-under-it/test.expected b/go/ql/integration-tests/all-platforms/go/single-go-mod-and-go-files-not-under-it/test.expected new file mode 100644 index 00000000000..18120f350ef --- /dev/null +++ b/go/ql/integration-tests/all-platforms/go/single-go-mod-and-go-files-not-under-it/test.expected @@ -0,0 +1,2 @@ +| Extraction failed in subdir/test.go with error cannot find package "subdir/subsubdir" in any of:\n\t(absolute path) (from $GOROOT)\n\t(absolute path) (from $GOPATH) | 2 | +| Extraction failed in subdir/test.go with error could not import subdir/subsubdir (invalid package name: "") | 2 | diff --git a/go/ql/integration-tests/all-platforms/go/single-go-mod-and-go-files-not-under-it/test.py b/go/ql/integration-tests/all-platforms/go/single-go-mod-and-go-files-not-under-it/test.py new file mode 100644 index 00000000000..71ace021c22 --- /dev/null +++ b/go/ql/integration-tests/all-platforms/go/single-go-mod-and-go-files-not-under-it/test.py @@ -0,0 +1,8 @@ +import sys + +from create_database_utils import * +from diagnostics_test_utils import * + +run_codeql_database_create([], lang="go") + +check_diagnostics() diff --git a/go/ql/integration-tests/all-platforms/go/single-go-mod-and-go-files-not-under-it/test.ql b/go/ql/integration-tests/all-platforms/go/single-go-mod-and-go-files-not-under-it/test.ql new file mode 100644 index 00000000000..e817bb2938a --- /dev/null +++ b/go/ql/integration-tests/all-platforms/go/single-go-mod-and-go-files-not-under-it/test.ql @@ -0,0 +1,6 @@ +import go +import semmle.go.DiagnosticsReporting + +from string msg, int sev +where reportableDiagnostics(_, msg, sev) +select msg, sev diff --git a/go/ql/integration-tests/all-platforms/go/single-go-mod-in-root/diagnostics.expected b/go/ql/integration-tests/all-platforms/go/single-go-mod-in-root/diagnostics.expected new file mode 100644 index 00000000000..56d774b7037 --- /dev/null +++ b/go/ql/integration-tests/all-platforms/go/single-go-mod-in-root/diagnostics.expected @@ -0,0 +1,14 @@ +{ + "markdownMessage": "A single `go.mod` file was found.\n\n`go.mod`", + "severity": "note", + "source": { + "extractorName": "go", + "id": "go/autobuilder/single-root-go-mod-found", + "name": "A single `go.mod` file was found in the root" + }, + "visibility": { + "cliSummaryTable": false, + "statusPage": false, + "telemetry": true + } +} diff --git a/go/ql/integration-tests/all-platforms/go/single-go-mod-in-root/go.mod b/go/ql/integration-tests/all-platforms/go/single-go-mod-in-root/go.mod new file mode 100644 index 00000000000..4a38c9773d7 --- /dev/null +++ b/go/ql/integration-tests/all-platforms/go/single-go-mod-in-root/go.mod @@ -0,0 +1,5 @@ +go 1.14 + +require golang.org/x/net v0.0.0-20200505041828-1ed23360d12c + +module test diff --git a/go/ql/integration-tests/all-platforms/go/single-go-mod-in-root/go.sum b/go/ql/integration-tests/all-platforms/go/single-go-mod-in-root/go.sum new file mode 100644 index 00000000000..6c5ffa613d0 --- /dev/null +++ b/go/ql/integration-tests/all-platforms/go/single-go-mod-in-root/go.sum @@ -0,0 +1,7 @@ +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/net v0.0.0-20200505041828-1ed23360d12c h1:zJ0mtu4jCalhKg6Oaukv6iIkb+cOvDrajDH9DH46Q4M= +golang.org/x/net v0.0.0-20200505041828-1ed23360d12c/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd h1:xhmwyvizuTgC2qz7ZlMluP20uW+C3Rm0FD/WLDX8884= +golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= diff --git a/go/ql/integration-tests/all-platforms/go/single-go-mod-in-root/subdir/add.go b/go/ql/integration-tests/all-platforms/go/single-go-mod-in-root/subdir/add.go new file mode 100644 index 00000000000..910b449d808 --- /dev/null +++ b/go/ql/integration-tests/all-platforms/go/single-go-mod-in-root/subdir/add.go @@ -0,0 +1,5 @@ +package subdir + +func Add(a, b int) int { + return a + b +} diff --git a/go/ql/integration-tests/all-platforms/go/single-go-mod-in-root/test.expected b/go/ql/integration-tests/all-platforms/go/single-go-mod-in-root/test.expected new file mode 100644 index 00000000000..e69de29bb2d diff --git a/go/ql/integration-tests/all-platforms/go/single-go-mod-in-root/test.go b/go/ql/integration-tests/all-platforms/go/single-go-mod-in-root/test.go new file mode 100644 index 00000000000..15e54322353 --- /dev/null +++ b/go/ql/integration-tests/all-platforms/go/single-go-mod-in-root/test.go @@ -0,0 +1,14 @@ +package test + +import ( + "test/subdir" + + "golang.org/x/net/ipv4" +) + +func test() { + + header := ipv4.Header{} + header.Version = subdir.Add(2, 2) + +} diff --git a/go/ql/integration-tests/all-platforms/go/single-go-mod-in-root/test.py b/go/ql/integration-tests/all-platforms/go/single-go-mod-in-root/test.py new file mode 100644 index 00000000000..71ace021c22 --- /dev/null +++ b/go/ql/integration-tests/all-platforms/go/single-go-mod-in-root/test.py @@ -0,0 +1,8 @@ +import sys + +from create_database_utils import * +from diagnostics_test_utils import * + +run_codeql_database_create([], lang="go") + +check_diagnostics() diff --git a/go/ql/integration-tests/all-platforms/go/single-go-mod-in-root/test.ql b/go/ql/integration-tests/all-platforms/go/single-go-mod-in-root/test.ql new file mode 100644 index 00000000000..e817bb2938a --- /dev/null +++ b/go/ql/integration-tests/all-platforms/go/single-go-mod-in-root/test.ql @@ -0,0 +1,6 @@ +import go +import semmle.go.DiagnosticsReporting + +from string msg, int sev +where reportableDiagnostics(_, msg, sev) +select msg, sev diff --git a/go/ql/integration-tests/all-platforms/go/single-go-mod-not-in-root/diagnostics.expected b/go/ql/integration-tests/all-platforms/go/single-go-mod-not-in-root/diagnostics.expected new file mode 100644 index 00000000000..1c7a2d28fe1 --- /dev/null +++ b/go/ql/integration-tests/all-platforms/go/single-go-mod-not-in-root/diagnostics.expected @@ -0,0 +1,14 @@ +{ + "markdownMessage": "A single, non-root `go.mod` file was found.\n\n`subdir/go.mod`", + "severity": "note", + "source": { + "extractorName": "go", + "id": "go/autobuilder/single-non-root-go-mod-found", + "name": "A single, non-root `go.mod` file was found" + }, + "visibility": { + "cliSummaryTable": false, + "statusPage": false, + "telemetry": true + } +} diff --git a/go/ql/integration-tests/all-platforms/go/single-go-mod-not-in-root/subdir/go.mod b/go/ql/integration-tests/all-platforms/go/single-go-mod-not-in-root/subdir/go.mod new file mode 100644 index 00000000000..e217b3613a6 --- /dev/null +++ b/go/ql/integration-tests/all-platforms/go/single-go-mod-not-in-root/subdir/go.mod @@ -0,0 +1,5 @@ +go 1.14 + +require golang.org/x/net v0.0.0-20200505041828-1ed23360d12c + +module subdir diff --git a/go/ql/integration-tests/all-platforms/go/single-go-mod-not-in-root/subdir/go.sum b/go/ql/integration-tests/all-platforms/go/single-go-mod-not-in-root/subdir/go.sum new file mode 100644 index 00000000000..6c5ffa613d0 --- /dev/null +++ b/go/ql/integration-tests/all-platforms/go/single-go-mod-not-in-root/subdir/go.sum @@ -0,0 +1,7 @@ +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/net v0.0.0-20200505041828-1ed23360d12c h1:zJ0mtu4jCalhKg6Oaukv6iIkb+cOvDrajDH9DH46Q4M= +golang.org/x/net v0.0.0-20200505041828-1ed23360d12c/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd h1:xhmwyvizuTgC2qz7ZlMluP20uW+C3Rm0FD/WLDX8884= +golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= diff --git a/go/ql/integration-tests/all-platforms/go/single-go-mod-not-in-root/subdir/subsubdir/add.go b/go/ql/integration-tests/all-platforms/go/single-go-mod-not-in-root/subdir/subsubdir/add.go new file mode 100644 index 00000000000..15233c47f62 --- /dev/null +++ b/go/ql/integration-tests/all-platforms/go/single-go-mod-not-in-root/subdir/subsubdir/add.go @@ -0,0 +1,5 @@ +package subsubdir + +func Add(a, b int) int { + return a + b +} diff --git a/go/ql/integration-tests/all-platforms/go/single-go-mod-not-in-root/subdir/test.go b/go/ql/integration-tests/all-platforms/go/single-go-mod-not-in-root/subdir/test.go new file mode 100644 index 00000000000..291a8591372 --- /dev/null +++ b/go/ql/integration-tests/all-platforms/go/single-go-mod-not-in-root/subdir/test.go @@ -0,0 +1,14 @@ +package subdir + +import ( + "subdir/subsubdir" + + "golang.org/x/net/ipv4" +) + +func test() { + + header := ipv4.Header{} + header.Version = subsubdir.Add(2, 2) + +} diff --git a/go/ql/integration-tests/all-platforms/go/single-go-mod-not-in-root/test.expected b/go/ql/integration-tests/all-platforms/go/single-go-mod-not-in-root/test.expected new file mode 100644 index 00000000000..e69de29bb2d diff --git a/go/ql/integration-tests/all-platforms/go/single-go-mod-not-in-root/test.py b/go/ql/integration-tests/all-platforms/go/single-go-mod-not-in-root/test.py new file mode 100644 index 00000000000..71ace021c22 --- /dev/null +++ b/go/ql/integration-tests/all-platforms/go/single-go-mod-not-in-root/test.py @@ -0,0 +1,8 @@ +import sys + +from create_database_utils import * +from diagnostics_test_utils import * + +run_codeql_database_create([], lang="go") + +check_diagnostics() diff --git a/go/ql/integration-tests/all-platforms/go/single-go-mod-not-in-root/test.ql b/go/ql/integration-tests/all-platforms/go/single-go-mod-not-in-root/test.ql new file mode 100644 index 00000000000..e817bb2938a --- /dev/null +++ b/go/ql/integration-tests/all-platforms/go/single-go-mod-not-in-root/test.ql @@ -0,0 +1,6 @@ +import go +import semmle.go.DiagnosticsReporting + +from string msg, int sev +where reportableDiagnostics(_, msg, sev) +select msg, sev diff --git a/go/ql/integration-tests/all-platforms/go/single-go-work-not-in-root/diagnostics.expected b/go/ql/integration-tests/all-platforms/go/single-go-work-not-in-root/diagnostics.expected new file mode 100644 index 00000000000..dc018d92b3d --- /dev/null +++ b/go/ql/integration-tests/all-platforms/go/single-go-work-not-in-root/diagnostics.expected @@ -0,0 +1,42 @@ +{ + "markdownMessage": "1 `go.work` file was found:\n\n`modules/go.work`", + "severity": "note", + "source": { + "extractorName": "go", + "id": "go/autobuilder/go-work-found", + "name": "`go.work` file found" + }, + "visibility": { + "cliSummaryTable": false, + "statusPage": false, + "telemetry": true + } +} +{ + "markdownMessage": "2 `go.mod` files were found:\n\n`modules/subdir1/go.mod`, `modules/subdir2/go.mod`", + "severity": "note", + "source": { + "extractorName": "go", + "id": "go/autobuilder/multiple-go-mod-found-not-nested", + "name": "Multiple `go.mod` files found, not all nested under one root `go.mod` file" + }, + "visibility": { + "cliSummaryTable": false, + "statusPage": false, + "telemetry": true + } +} +{ + "markdownMessage": "2 packages could not be found:\n\n`subdir1/subsubdir1`, `subdir2/subsubdir2`.\n\nDefinitions in those packages may not be recognized by CodeQL, and files that use them may only be partially analyzed.\n\nCheck that the paths are correct and make sure any private packages can be accessed. If any of the packages are present in the repository then you may need a [custom build command](https://docs.github.com/en/code-security/code-scanning/automatically-scanning-your-code-for-vulnerabilities-and-errors/configuring-the-codeql-workflow-for-compiled-languages).", + "severity": "warning", + "source": { + "extractorName": "go", + "id": "go/autobuilder/package-not-found", + "name": "Some packages could not be found" + }, + "visibility": { + "cliSummaryTable": true, + "statusPage": true, + "telemetry": true + } +} diff --git a/go/ql/integration-tests/all-platforms/go/single-go-work-not-in-root/modules/go.work b/go/ql/integration-tests/all-platforms/go/single-go-work-not-in-root/modules/go.work new file mode 100644 index 00000000000..6610d43e9c0 --- /dev/null +++ b/go/ql/integration-tests/all-platforms/go/single-go-work-not-in-root/modules/go.work @@ -0,0 +1,6 @@ +go 1.19 + +use ( + ./subdir1 + ./subdir2 +) diff --git a/go/ql/integration-tests/all-platforms/go/single-go-work-not-in-root/modules/subdir1/go.mod b/go/ql/integration-tests/all-platforms/go/single-go-work-not-in-root/modules/subdir1/go.mod new file mode 100644 index 00000000000..147c51b8386 --- /dev/null +++ b/go/ql/integration-tests/all-platforms/go/single-go-work-not-in-root/modules/subdir1/go.mod @@ -0,0 +1,5 @@ +go 1.14 + +require golang.org/x/net v0.0.0-20200505041828-1ed23360d12c + +module subdir1 diff --git a/go/ql/integration-tests/all-platforms/go/single-go-work-not-in-root/modules/subdir1/go.sum b/go/ql/integration-tests/all-platforms/go/single-go-work-not-in-root/modules/subdir1/go.sum new file mode 100644 index 00000000000..6c5ffa613d0 --- /dev/null +++ b/go/ql/integration-tests/all-platforms/go/single-go-work-not-in-root/modules/subdir1/go.sum @@ -0,0 +1,7 @@ +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/net v0.0.0-20200505041828-1ed23360d12c h1:zJ0mtu4jCalhKg6Oaukv6iIkb+cOvDrajDH9DH46Q4M= +golang.org/x/net v0.0.0-20200505041828-1ed23360d12c/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd h1:xhmwyvizuTgC2qz7ZlMluP20uW+C3Rm0FD/WLDX8884= +golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= diff --git a/go/ql/integration-tests/all-platforms/go/single-go-work-not-in-root/modules/subdir1/subsubdir1/add.go b/go/ql/integration-tests/all-platforms/go/single-go-work-not-in-root/modules/subdir1/subsubdir1/add.go new file mode 100644 index 00000000000..900c2b5f266 --- /dev/null +++ b/go/ql/integration-tests/all-platforms/go/single-go-work-not-in-root/modules/subdir1/subsubdir1/add.go @@ -0,0 +1,5 @@ +package subsubdir1 + +func Add(a, b int) int { + return a + b +} diff --git a/go/ql/integration-tests/all-platforms/go/single-go-work-not-in-root/modules/subdir1/test.go b/go/ql/integration-tests/all-platforms/go/single-go-work-not-in-root/modules/subdir1/test.go new file mode 100644 index 00000000000..6081be52248 --- /dev/null +++ b/go/ql/integration-tests/all-platforms/go/single-go-work-not-in-root/modules/subdir1/test.go @@ -0,0 +1,14 @@ +package subdir + +import ( + "subdir1/subsubdir1" + + "golang.org/x/net/ipv4" +) + +func test() { + + header := ipv4.Header{} + header.Version = subsubdir1.Add(2, 2) + +} diff --git a/go/ql/integration-tests/all-platforms/go/single-go-work-not-in-root/modules/subdir2/go.mod b/go/ql/integration-tests/all-platforms/go/single-go-work-not-in-root/modules/subdir2/go.mod new file mode 100644 index 00000000000..c6eec7d9ca5 --- /dev/null +++ b/go/ql/integration-tests/all-platforms/go/single-go-work-not-in-root/modules/subdir2/go.mod @@ -0,0 +1,5 @@ +go 1.14 + +require golang.org/x/net v0.0.0-20200505041828-1ed23360d12c + +module subdir2 diff --git a/go/ql/integration-tests/all-platforms/go/single-go-work-not-in-root/modules/subdir2/go.sum b/go/ql/integration-tests/all-platforms/go/single-go-work-not-in-root/modules/subdir2/go.sum new file mode 100644 index 00000000000..6c5ffa613d0 --- /dev/null +++ b/go/ql/integration-tests/all-platforms/go/single-go-work-not-in-root/modules/subdir2/go.sum @@ -0,0 +1,7 @@ +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/net v0.0.0-20200505041828-1ed23360d12c h1:zJ0mtu4jCalhKg6Oaukv6iIkb+cOvDrajDH9DH46Q4M= +golang.org/x/net v0.0.0-20200505041828-1ed23360d12c/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd h1:xhmwyvizuTgC2qz7ZlMluP20uW+C3Rm0FD/WLDX8884= +golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= diff --git a/go/ql/integration-tests/all-platforms/go/single-go-work-not-in-root/modules/subdir2/subsubdir2/add.go b/go/ql/integration-tests/all-platforms/go/single-go-work-not-in-root/modules/subdir2/subsubdir2/add.go new file mode 100644 index 00000000000..99810d11db0 --- /dev/null +++ b/go/ql/integration-tests/all-platforms/go/single-go-work-not-in-root/modules/subdir2/subsubdir2/add.go @@ -0,0 +1,5 @@ +package subsubdir2 + +func Add(a, b int) int { + return a + b +} diff --git a/go/ql/integration-tests/all-platforms/go/single-go-work-not-in-root/modules/subdir2/test.go b/go/ql/integration-tests/all-platforms/go/single-go-work-not-in-root/modules/subdir2/test.go new file mode 100644 index 00000000000..e2d423cc2b6 --- /dev/null +++ b/go/ql/integration-tests/all-platforms/go/single-go-work-not-in-root/modules/subdir2/test.go @@ -0,0 +1,14 @@ +package subdir + +import ( + "subdir2/subsubdir2" + + "golang.org/x/net/ipv4" +) + +func test() { + + header := ipv4.Header{} + header.Version = subsubdir2.Add(2, 2) + +} diff --git a/go/ql/integration-tests/all-platforms/go/single-go-work-not-in-root/test.expected b/go/ql/integration-tests/all-platforms/go/single-go-work-not-in-root/test.expected new file mode 100644 index 00000000000..a2c09b1171a --- /dev/null +++ b/go/ql/integration-tests/all-platforms/go/single-go-work-not-in-root/test.expected @@ -0,0 +1,4 @@ +| Extraction failed in modules/subdir1/test.go with error cannot find package "subdir1/subsubdir1" in any of:\n\t(absolute path) (from $GOROOT)\n\t(absolute path) (from $GOPATH) | 2 | +| Extraction failed in modules/subdir1/test.go with error could not import subdir1/subsubdir1 (invalid package name: "") | 2 | +| Extraction failed in modules/subdir2/test.go with error cannot find package "subdir2/subsubdir2" in any of:\n\t(absolute path) (from $GOROOT)\n\t(absolute path) (from $GOPATH) | 2 | +| Extraction failed in modules/subdir2/test.go with error could not import subdir2/subsubdir2 (invalid package name: "") | 2 | diff --git a/go/ql/integration-tests/all-platforms/go/single-go-work-not-in-root/test.py b/go/ql/integration-tests/all-platforms/go/single-go-work-not-in-root/test.py new file mode 100644 index 00000000000..71ace021c22 --- /dev/null +++ b/go/ql/integration-tests/all-platforms/go/single-go-work-not-in-root/test.py @@ -0,0 +1,8 @@ +import sys + +from create_database_utils import * +from diagnostics_test_utils import * + +run_codeql_database_create([], lang="go") + +check_diagnostics() diff --git a/go/ql/integration-tests/all-platforms/go/single-go-work-not-in-root/test.ql b/go/ql/integration-tests/all-platforms/go/single-go-work-not-in-root/test.ql new file mode 100644 index 00000000000..e817bb2938a --- /dev/null +++ b/go/ql/integration-tests/all-platforms/go/single-go-work-not-in-root/test.ql @@ -0,0 +1,6 @@ +import go +import semmle.go.DiagnosticsReporting + +from string msg, int sev +where reportableDiagnostics(_, msg, sev) +select msg, sev diff --git a/go/ql/integration-tests/all-platforms/go/two-go-mods-nested-none-in-root/diagnostics.expected b/go/ql/integration-tests/all-platforms/go/two-go-mods-nested-none-in-root/diagnostics.expected new file mode 100644 index 00000000000..226eb35d3ab --- /dev/null +++ b/go/ql/integration-tests/all-platforms/go/two-go-mods-nested-none-in-root/diagnostics.expected @@ -0,0 +1,28 @@ +{ + "markdownMessage": "2 `go.mod` files were found:\n\n`subdir0/go.mod`, `subdir0/subdir1/go.mod`", + "severity": "note", + "source": { + "extractorName": "go", + "id": "go/autobuilder/multiple-go-mod-found-nested", + "name": "Multiple `go.mod` files were found, all nested under one root `go.mod` file" + }, + "visibility": { + "cliSummaryTable": false, + "statusPage": false, + "telemetry": true + } +} +{ + "markdownMessage": "2 packages could not be found:\n\n`test/subdir2`, `subdir1/subsubdir1`.\n\nDefinitions in those packages may not be recognized by CodeQL, and files that use them may only be partially analyzed.\n\nCheck that the paths are correct and make sure any private packages can be accessed. If any of the packages are present in the repository then you may need a [custom build command](https://docs.github.com/en/code-security/code-scanning/automatically-scanning-your-code-for-vulnerabilities-and-errors/configuring-the-codeql-workflow-for-compiled-languages).", + "severity": "warning", + "source": { + "extractorName": "go", + "id": "go/autobuilder/package-not-found", + "name": "Some packages could not be found" + }, + "visibility": { + "cliSummaryTable": true, + "statusPage": true, + "telemetry": true + } +} diff --git a/go/ql/integration-tests/all-platforms/go/two-go-mods-nested-none-in-root/subdir0/go.mod b/go/ql/integration-tests/all-platforms/go/two-go-mods-nested-none-in-root/subdir0/go.mod new file mode 100644 index 00000000000..4a38c9773d7 --- /dev/null +++ b/go/ql/integration-tests/all-platforms/go/two-go-mods-nested-none-in-root/subdir0/go.mod @@ -0,0 +1,5 @@ +go 1.14 + +require golang.org/x/net v0.0.0-20200505041828-1ed23360d12c + +module test diff --git a/go/ql/integration-tests/all-platforms/go/two-go-mods-nested-none-in-root/subdir0/go.sum b/go/ql/integration-tests/all-platforms/go/two-go-mods-nested-none-in-root/subdir0/go.sum new file mode 100644 index 00000000000..6c5ffa613d0 --- /dev/null +++ b/go/ql/integration-tests/all-platforms/go/two-go-mods-nested-none-in-root/subdir0/go.sum @@ -0,0 +1,7 @@ +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/net v0.0.0-20200505041828-1ed23360d12c h1:zJ0mtu4jCalhKg6Oaukv6iIkb+cOvDrajDH9DH46Q4M= +golang.org/x/net v0.0.0-20200505041828-1ed23360d12c/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd h1:xhmwyvizuTgC2qz7ZlMluP20uW+C3Rm0FD/WLDX8884= +golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= diff --git a/go/ql/integration-tests/all-platforms/go/two-go-mods-nested-none-in-root/subdir0/subdir1/go.mod b/go/ql/integration-tests/all-platforms/go/two-go-mods-nested-none-in-root/subdir0/subdir1/go.mod new file mode 100644 index 00000000000..147c51b8386 --- /dev/null +++ b/go/ql/integration-tests/all-platforms/go/two-go-mods-nested-none-in-root/subdir0/subdir1/go.mod @@ -0,0 +1,5 @@ +go 1.14 + +require golang.org/x/net v0.0.0-20200505041828-1ed23360d12c + +module subdir1 diff --git a/go/ql/integration-tests/all-platforms/go/two-go-mods-nested-none-in-root/subdir0/subdir1/go.sum b/go/ql/integration-tests/all-platforms/go/two-go-mods-nested-none-in-root/subdir0/subdir1/go.sum new file mode 100644 index 00000000000..6c5ffa613d0 --- /dev/null +++ b/go/ql/integration-tests/all-platforms/go/two-go-mods-nested-none-in-root/subdir0/subdir1/go.sum @@ -0,0 +1,7 @@ +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/net v0.0.0-20200505041828-1ed23360d12c h1:zJ0mtu4jCalhKg6Oaukv6iIkb+cOvDrajDH9DH46Q4M= +golang.org/x/net v0.0.0-20200505041828-1ed23360d12c/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd h1:xhmwyvizuTgC2qz7ZlMluP20uW+C3Rm0FD/WLDX8884= +golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= diff --git a/go/ql/integration-tests/all-platforms/go/two-go-mods-nested-none-in-root/subdir0/subdir1/subsubdir1/add.go b/go/ql/integration-tests/all-platforms/go/two-go-mods-nested-none-in-root/subdir0/subdir1/subsubdir1/add.go new file mode 100644 index 00000000000..900c2b5f266 --- /dev/null +++ b/go/ql/integration-tests/all-platforms/go/two-go-mods-nested-none-in-root/subdir0/subdir1/subsubdir1/add.go @@ -0,0 +1,5 @@ +package subsubdir1 + +func Add(a, b int) int { + return a + b +} diff --git a/go/ql/integration-tests/all-platforms/go/two-go-mods-nested-none-in-root/subdir0/subdir1/test.go b/go/ql/integration-tests/all-platforms/go/two-go-mods-nested-none-in-root/subdir0/subdir1/test.go new file mode 100644 index 00000000000..5d4e906107b --- /dev/null +++ b/go/ql/integration-tests/all-platforms/go/two-go-mods-nested-none-in-root/subdir0/subdir1/test.go @@ -0,0 +1,14 @@ +package subdir1 + +import ( + "subdir1/subsubdir1" + + "golang.org/x/net/ipv4" +) + +func test() { + + header := ipv4.Header{} + header.Version = subsubdir1.Add(2, 2) + +} diff --git a/go/ql/integration-tests/all-platforms/go/two-go-mods-nested-none-in-root/subdir0/subdir2/add.go b/go/ql/integration-tests/all-platforms/go/two-go-mods-nested-none-in-root/subdir0/subdir2/add.go new file mode 100644 index 00000000000..dfbcd101cf4 --- /dev/null +++ b/go/ql/integration-tests/all-platforms/go/two-go-mods-nested-none-in-root/subdir0/subdir2/add.go @@ -0,0 +1,5 @@ +package subdir2 + +func Add(a, b int) int { + return a + b +} diff --git a/go/ql/integration-tests/all-platforms/go/two-go-mods-nested-none-in-root/subdir0/test.go b/go/ql/integration-tests/all-platforms/go/two-go-mods-nested-none-in-root/subdir0/test.go new file mode 100644 index 00000000000..9db7bd16521 --- /dev/null +++ b/go/ql/integration-tests/all-platforms/go/two-go-mods-nested-none-in-root/subdir0/test.go @@ -0,0 +1,14 @@ +package test + +import ( + "test/subdir2" + + "golang.org/x/net/ipv4" +) + +func test() { + + header := ipv4.Header{} + header.Version = subdir2.Add(2, 2) + +} diff --git a/go/ql/integration-tests/all-platforms/go/two-go-mods-nested-none-in-root/test.expected b/go/ql/integration-tests/all-platforms/go/two-go-mods-nested-none-in-root/test.expected new file mode 100644 index 00000000000..38b4f954433 --- /dev/null +++ b/go/ql/integration-tests/all-platforms/go/two-go-mods-nested-none-in-root/test.expected @@ -0,0 +1,4 @@ +| Extraction failed in subdir0/subdir1/test.go with error cannot find package "subdir1/subsubdir1" in any of:\n\t(absolute path) (from $GOROOT)\n\t(absolute path) (from $GOPATH) | 2 | +| Extraction failed in subdir0/subdir1/test.go with error could not import subdir1/subsubdir1 (invalid package name: "") | 2 | +| Extraction failed in subdir0/test.go with error cannot find package "test/subdir2" in any of:\n\t(absolute path) (from $GOROOT)\n\t(absolute path) (from $GOPATH) | 2 | +| Extraction failed in subdir0/test.go with error could not import test/subdir2 (invalid package name: "") | 2 | diff --git a/go/ql/integration-tests/all-platforms/go/two-go-mods-nested-none-in-root/test.py b/go/ql/integration-tests/all-platforms/go/two-go-mods-nested-none-in-root/test.py new file mode 100644 index 00000000000..71ace021c22 --- /dev/null +++ b/go/ql/integration-tests/all-platforms/go/two-go-mods-nested-none-in-root/test.py @@ -0,0 +1,8 @@ +import sys + +from create_database_utils import * +from diagnostics_test_utils import * + +run_codeql_database_create([], lang="go") + +check_diagnostics() diff --git a/go/ql/integration-tests/all-platforms/go/two-go-mods-nested-none-in-root/test.ql b/go/ql/integration-tests/all-platforms/go/two-go-mods-nested-none-in-root/test.ql new file mode 100644 index 00000000000..e817bb2938a --- /dev/null +++ b/go/ql/integration-tests/all-platforms/go/two-go-mods-nested-none-in-root/test.ql @@ -0,0 +1,6 @@ +import go +import semmle.go.DiagnosticsReporting + +from string msg, int sev +where reportableDiagnostics(_, msg, sev) +select msg, sev diff --git a/go/ql/integration-tests/all-platforms/go/two-go-mods-nested-one-in-root/diagnostics.expected b/go/ql/integration-tests/all-platforms/go/two-go-mods-nested-one-in-root/diagnostics.expected new file mode 100644 index 00000000000..9b5d4cbc784 --- /dev/null +++ b/go/ql/integration-tests/all-platforms/go/two-go-mods-nested-one-in-root/diagnostics.expected @@ -0,0 +1,14 @@ +{ + "markdownMessage": "2 `go.mod` files were found:\n\n`go.mod`, `subdir1/go.mod`", + "severity": "note", + "source": { + "extractorName": "go", + "id": "go/autobuilder/multiple-go-mod-found-nested", + "name": "Multiple `go.mod` files were found, all nested under one root `go.mod` file" + }, + "visibility": { + "cliSummaryTable": false, + "statusPage": false, + "telemetry": true + } +} diff --git a/go/ql/integration-tests/all-platforms/go/two-go-mods-nested-one-in-root/go.mod b/go/ql/integration-tests/all-platforms/go/two-go-mods-nested-one-in-root/go.mod new file mode 100644 index 00000000000..944de0f05b0 --- /dev/null +++ b/go/ql/integration-tests/all-platforms/go/two-go-mods-nested-one-in-root/go.mod @@ -0,0 +1,5 @@ +go 1.14 + +require golang.org/x/net v0.0.0-20200505041828-1ed23360d12c + +module main diff --git a/go/ql/integration-tests/all-platforms/go/two-go-mods-nested-one-in-root/go.sum b/go/ql/integration-tests/all-platforms/go/two-go-mods-nested-one-in-root/go.sum new file mode 100644 index 00000000000..6c5ffa613d0 --- /dev/null +++ b/go/ql/integration-tests/all-platforms/go/two-go-mods-nested-one-in-root/go.sum @@ -0,0 +1,7 @@ +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/net v0.0.0-20200505041828-1ed23360d12c h1:zJ0mtu4jCalhKg6Oaukv6iIkb+cOvDrajDH9DH46Q4M= +golang.org/x/net v0.0.0-20200505041828-1ed23360d12c/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd h1:xhmwyvizuTgC2qz7ZlMluP20uW+C3Rm0FD/WLDX8884= +golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= diff --git a/go/ql/integration-tests/all-platforms/go/two-go-mods-nested-one-in-root/subdir1/go.mod b/go/ql/integration-tests/all-platforms/go/two-go-mods-nested-one-in-root/subdir1/go.mod new file mode 100644 index 00000000000..147c51b8386 --- /dev/null +++ b/go/ql/integration-tests/all-platforms/go/two-go-mods-nested-one-in-root/subdir1/go.mod @@ -0,0 +1,5 @@ +go 1.14 + +require golang.org/x/net v0.0.0-20200505041828-1ed23360d12c + +module subdir1 diff --git a/go/ql/integration-tests/all-platforms/go/two-go-mods-nested-one-in-root/subdir1/go.sum b/go/ql/integration-tests/all-platforms/go/two-go-mods-nested-one-in-root/subdir1/go.sum new file mode 100644 index 00000000000..6c5ffa613d0 --- /dev/null +++ b/go/ql/integration-tests/all-platforms/go/two-go-mods-nested-one-in-root/subdir1/go.sum @@ -0,0 +1,7 @@ +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/net v0.0.0-20200505041828-1ed23360d12c h1:zJ0mtu4jCalhKg6Oaukv6iIkb+cOvDrajDH9DH46Q4M= +golang.org/x/net v0.0.0-20200505041828-1ed23360d12c/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd h1:xhmwyvizuTgC2qz7ZlMluP20uW+C3Rm0FD/WLDX8884= +golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= diff --git a/go/ql/integration-tests/all-platforms/go/two-go-mods-nested-one-in-root/subdir1/subsubdir1/add.go b/go/ql/integration-tests/all-platforms/go/two-go-mods-nested-one-in-root/subdir1/subsubdir1/add.go new file mode 100644 index 00000000000..900c2b5f266 --- /dev/null +++ b/go/ql/integration-tests/all-platforms/go/two-go-mods-nested-one-in-root/subdir1/subsubdir1/add.go @@ -0,0 +1,5 @@ +package subsubdir1 + +func Add(a, b int) int { + return a + b +} diff --git a/go/ql/integration-tests/all-platforms/go/two-go-mods-nested-one-in-root/subdir1/test.go b/go/ql/integration-tests/all-platforms/go/two-go-mods-nested-one-in-root/subdir1/test.go new file mode 100644 index 00000000000..6081be52248 --- /dev/null +++ b/go/ql/integration-tests/all-platforms/go/two-go-mods-nested-one-in-root/subdir1/test.go @@ -0,0 +1,14 @@ +package subdir + +import ( + "subdir1/subsubdir1" + + "golang.org/x/net/ipv4" +) + +func test() { + + header := ipv4.Header{} + header.Version = subsubdir1.Add(2, 2) + +} diff --git a/go/ql/integration-tests/all-platforms/go/two-go-mods-nested-one-in-root/subdir2/add.go b/go/ql/integration-tests/all-platforms/go/two-go-mods-nested-one-in-root/subdir2/add.go new file mode 100644 index 00000000000..dfbcd101cf4 --- /dev/null +++ b/go/ql/integration-tests/all-platforms/go/two-go-mods-nested-one-in-root/subdir2/add.go @@ -0,0 +1,5 @@ +package subdir2 + +func Add(a, b int) int { + return a + b +} diff --git a/go/ql/integration-tests/all-platforms/go/two-go-mods-nested-one-in-root/test.expected b/go/ql/integration-tests/all-platforms/go/two-go-mods-nested-one-in-root/test.expected new file mode 100644 index 00000000000..e69de29bb2d diff --git a/go/ql/integration-tests/all-platforms/go/two-go-mods-nested-one-in-root/test.go b/go/ql/integration-tests/all-platforms/go/two-go-mods-nested-one-in-root/test.go new file mode 100644 index 00000000000..a298e0070ca --- /dev/null +++ b/go/ql/integration-tests/all-platforms/go/two-go-mods-nested-one-in-root/test.go @@ -0,0 +1,14 @@ +package main + +import ( + "main/subdir2" + + "golang.org/x/net/ipv4" +) + +func main() { + + header := ipv4.Header{} + header.Version = subdir2.Add(2, 2) + +} diff --git a/go/ql/integration-tests/all-platforms/go/two-go-mods-nested-one-in-root/test.py b/go/ql/integration-tests/all-platforms/go/two-go-mods-nested-one-in-root/test.py new file mode 100644 index 00000000000..71ace021c22 --- /dev/null +++ b/go/ql/integration-tests/all-platforms/go/two-go-mods-nested-one-in-root/test.py @@ -0,0 +1,8 @@ +import sys + +from create_database_utils import * +from diagnostics_test_utils import * + +run_codeql_database_create([], lang="go") + +check_diagnostics() diff --git a/go/ql/integration-tests/all-platforms/go/two-go-mods-nested-one-in-root/test.ql b/go/ql/integration-tests/all-platforms/go/two-go-mods-nested-one-in-root/test.ql new file mode 100644 index 00000000000..e817bb2938a --- /dev/null +++ b/go/ql/integration-tests/all-platforms/go/two-go-mods-nested-one-in-root/test.ql @@ -0,0 +1,6 @@ +import go +import semmle.go.DiagnosticsReporting + +from string msg, int sev +where reportableDiagnostics(_, msg, sev) +select msg, sev diff --git a/go/ql/integration-tests/all-platforms/go/two-go-mods-not-nested/diagnostics.expected b/go/ql/integration-tests/all-platforms/go/two-go-mods-not-nested/diagnostics.expected new file mode 100644 index 00000000000..e07da8154d4 --- /dev/null +++ b/go/ql/integration-tests/all-platforms/go/two-go-mods-not-nested/diagnostics.expected @@ -0,0 +1,28 @@ +{ + "markdownMessage": "2 `go.mod` files were found:\n\n`subdir1/go.mod`, `subdir2/go.mod`", + "severity": "note", + "source": { + "extractorName": "go", + "id": "go/autobuilder/multiple-go-mod-found-not-nested", + "name": "Multiple `go.mod` files found, not all nested under one root `go.mod` file" + }, + "visibility": { + "cliSummaryTable": false, + "statusPage": false, + "telemetry": true + } +} +{ + "markdownMessage": "2 packages could not be found:\n\n`subdir1/subsubdir1`, `subdir2/subsubdir2`.\n\nDefinitions in those packages may not be recognized by CodeQL, and files that use them may only be partially analyzed.\n\nCheck that the paths are correct and make sure any private packages can be accessed. If any of the packages are present in the repository then you may need a [custom build command](https://docs.github.com/en/code-security/code-scanning/automatically-scanning-your-code-for-vulnerabilities-and-errors/configuring-the-codeql-workflow-for-compiled-languages).", + "severity": "warning", + "source": { + "extractorName": "go", + "id": "go/autobuilder/package-not-found", + "name": "Some packages could not be found" + }, + "visibility": { + "cliSummaryTable": true, + "statusPage": true, + "telemetry": true + } +} diff --git a/go/ql/integration-tests/all-platforms/go/two-go-mods-not-nested/subdir1/go.mod b/go/ql/integration-tests/all-platforms/go/two-go-mods-not-nested/subdir1/go.mod new file mode 100644 index 00000000000..147c51b8386 --- /dev/null +++ b/go/ql/integration-tests/all-platforms/go/two-go-mods-not-nested/subdir1/go.mod @@ -0,0 +1,5 @@ +go 1.14 + +require golang.org/x/net v0.0.0-20200505041828-1ed23360d12c + +module subdir1 diff --git a/go/ql/integration-tests/all-platforms/go/two-go-mods-not-nested/subdir1/go.sum b/go/ql/integration-tests/all-platforms/go/two-go-mods-not-nested/subdir1/go.sum new file mode 100644 index 00000000000..6c5ffa613d0 --- /dev/null +++ b/go/ql/integration-tests/all-platforms/go/two-go-mods-not-nested/subdir1/go.sum @@ -0,0 +1,7 @@ +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/net v0.0.0-20200505041828-1ed23360d12c h1:zJ0mtu4jCalhKg6Oaukv6iIkb+cOvDrajDH9DH46Q4M= +golang.org/x/net v0.0.0-20200505041828-1ed23360d12c/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd h1:xhmwyvizuTgC2qz7ZlMluP20uW+C3Rm0FD/WLDX8884= +golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= diff --git a/go/ql/integration-tests/all-platforms/go/two-go-mods-not-nested/subdir1/subsubdir1/add.go b/go/ql/integration-tests/all-platforms/go/two-go-mods-not-nested/subdir1/subsubdir1/add.go new file mode 100644 index 00000000000..900c2b5f266 --- /dev/null +++ b/go/ql/integration-tests/all-platforms/go/two-go-mods-not-nested/subdir1/subsubdir1/add.go @@ -0,0 +1,5 @@ +package subsubdir1 + +func Add(a, b int) int { + return a + b +} diff --git a/go/ql/integration-tests/all-platforms/go/two-go-mods-not-nested/subdir1/test.go b/go/ql/integration-tests/all-platforms/go/two-go-mods-not-nested/subdir1/test.go new file mode 100644 index 00000000000..6081be52248 --- /dev/null +++ b/go/ql/integration-tests/all-platforms/go/two-go-mods-not-nested/subdir1/test.go @@ -0,0 +1,14 @@ +package subdir + +import ( + "subdir1/subsubdir1" + + "golang.org/x/net/ipv4" +) + +func test() { + + header := ipv4.Header{} + header.Version = subsubdir1.Add(2, 2) + +} diff --git a/go/ql/integration-tests/all-platforms/go/two-go-mods-not-nested/subdir2/go.mod b/go/ql/integration-tests/all-platforms/go/two-go-mods-not-nested/subdir2/go.mod new file mode 100644 index 00000000000..c6eec7d9ca5 --- /dev/null +++ b/go/ql/integration-tests/all-platforms/go/two-go-mods-not-nested/subdir2/go.mod @@ -0,0 +1,5 @@ +go 1.14 + +require golang.org/x/net v0.0.0-20200505041828-1ed23360d12c + +module subdir2 diff --git a/go/ql/integration-tests/all-platforms/go/two-go-mods-not-nested/subdir2/go.sum b/go/ql/integration-tests/all-platforms/go/two-go-mods-not-nested/subdir2/go.sum new file mode 100644 index 00000000000..6c5ffa613d0 --- /dev/null +++ b/go/ql/integration-tests/all-platforms/go/two-go-mods-not-nested/subdir2/go.sum @@ -0,0 +1,7 @@ +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/net v0.0.0-20200505041828-1ed23360d12c h1:zJ0mtu4jCalhKg6Oaukv6iIkb+cOvDrajDH9DH46Q4M= +golang.org/x/net v0.0.0-20200505041828-1ed23360d12c/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd h1:xhmwyvizuTgC2qz7ZlMluP20uW+C3Rm0FD/WLDX8884= +golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= diff --git a/go/ql/integration-tests/all-platforms/go/two-go-mods-not-nested/subdir2/subsubdir2/add.go b/go/ql/integration-tests/all-platforms/go/two-go-mods-not-nested/subdir2/subsubdir2/add.go new file mode 100644 index 00000000000..99810d11db0 --- /dev/null +++ b/go/ql/integration-tests/all-platforms/go/two-go-mods-not-nested/subdir2/subsubdir2/add.go @@ -0,0 +1,5 @@ +package subsubdir2 + +func Add(a, b int) int { + return a + b +} diff --git a/go/ql/integration-tests/all-platforms/go/two-go-mods-not-nested/subdir2/test.go b/go/ql/integration-tests/all-platforms/go/two-go-mods-not-nested/subdir2/test.go new file mode 100644 index 00000000000..e2d423cc2b6 --- /dev/null +++ b/go/ql/integration-tests/all-platforms/go/two-go-mods-not-nested/subdir2/test.go @@ -0,0 +1,14 @@ +package subdir + +import ( + "subdir2/subsubdir2" + + "golang.org/x/net/ipv4" +) + +func test() { + + header := ipv4.Header{} + header.Version = subsubdir2.Add(2, 2) + +} diff --git a/go/ql/integration-tests/all-platforms/go/two-go-mods-not-nested/test.expected b/go/ql/integration-tests/all-platforms/go/two-go-mods-not-nested/test.expected new file mode 100644 index 00000000000..8435a54f44a --- /dev/null +++ b/go/ql/integration-tests/all-platforms/go/two-go-mods-not-nested/test.expected @@ -0,0 +1,4 @@ +| Extraction failed in subdir1/test.go with error cannot find package "subdir1/subsubdir1" in any of:\n\t(absolute path) (from $GOROOT)\n\t(absolute path) (from $GOPATH) | 2 | +| Extraction failed in subdir1/test.go with error could not import subdir1/subsubdir1 (invalid package name: "") | 2 | +| Extraction failed in subdir2/test.go with error cannot find package "subdir2/subsubdir2" in any of:\n\t(absolute path) (from $GOROOT)\n\t(absolute path) (from $GOPATH) | 2 | +| Extraction failed in subdir2/test.go with error could not import subdir2/subsubdir2 (invalid package name: "") | 2 | diff --git a/go/ql/integration-tests/all-platforms/go/two-go-mods-not-nested/test.py b/go/ql/integration-tests/all-platforms/go/two-go-mods-not-nested/test.py new file mode 100644 index 00000000000..71ace021c22 --- /dev/null +++ b/go/ql/integration-tests/all-platforms/go/two-go-mods-not-nested/test.py @@ -0,0 +1,8 @@ +import sys + +from create_database_utils import * +from diagnostics_test_utils import * + +run_codeql_database_create([], lang="go") + +check_diagnostics() diff --git a/go/ql/integration-tests/all-platforms/go/two-go-mods-not-nested/test.ql b/go/ql/integration-tests/all-platforms/go/two-go-mods-not-nested/test.ql new file mode 100644 index 00000000000..e817bb2938a --- /dev/null +++ b/go/ql/integration-tests/all-platforms/go/two-go-mods-not-nested/test.ql @@ -0,0 +1,6 @@ +import go +import semmle.go.DiagnosticsReporting + +from string msg, int sev +where reportableDiagnostics(_, msg, sev) +select msg, sev diff --git a/go/ql/integration-tests/linux-only/go/dep-sample/diagnostics.expected b/go/ql/integration-tests/linux-only/go/dep-sample/diagnostics.expected new file mode 100644 index 00000000000..e62ab7a3421 --- /dev/null +++ b/go/ql/integration-tests/linux-only/go/dep-sample/diagnostics.expected @@ -0,0 +1,14 @@ +{ + "markdownMessage": "A dep `Gopkg.toml` file was found", + "severity": "note", + "source": { + "extractorName": "go", + "id": "go/autobuilder/gopkg-toml-found", + "name": "A dep `Gopkg.toml` file was found" + }, + "visibility": { + "cliSummaryTable": false, + "statusPage": false, + "telemetry": true + } +} diff --git a/go/ql/integration-tests/linux-only/go/dep-sample/test.py b/go/ql/integration-tests/linux-only/go/dep-sample/test.py index 43b1914296f..11673fd71e9 100644 --- a/go/ql/integration-tests/linux-only/go/dep-sample/test.py +++ b/go/ql/integration-tests/linux-only/go/dep-sample/test.py @@ -2,6 +2,9 @@ import os import sys from create_database_utils import * +from diagnostics_test_utils import * os.environ['LGTM_INDEX_IMPORT_PATH'] = "deptest" run_codeql_database_create([], lang="go", source="work") + +check_diagnostics() diff --git a/go/ql/integration-tests/linux-only/go/glide-sample/diagnostics.expected b/go/ql/integration-tests/linux-only/go/glide-sample/diagnostics.expected new file mode 100644 index 00000000000..1cf9bcfa389 --- /dev/null +++ b/go/ql/integration-tests/linux-only/go/glide-sample/diagnostics.expected @@ -0,0 +1,14 @@ +{ + "markdownMessage": "A Glide `glide.yaml` file was found", + "severity": "note", + "source": { + "extractorName": "go", + "id": "go/autobuilder/glide-yaml-found", + "name": "A Glide `glide.yaml` file was found" + }, + "visibility": { + "cliSummaryTable": false, + "statusPage": false, + "telemetry": true + } +} diff --git a/go/ql/integration-tests/linux-only/go/glide-sample/test.py b/go/ql/integration-tests/linux-only/go/glide-sample/test.py index 6497dfe9cf7..0595b51c07b 100644 --- a/go/ql/integration-tests/linux-only/go/glide-sample/test.py +++ b/go/ql/integration-tests/linux-only/go/glide-sample/test.py @@ -2,6 +2,9 @@ import os import sys from create_database_utils import * +from diagnostics_test_utils import * os.environ['LGTM_INDEX_IMPORT_PATH'] = "glidetest" run_codeql_database_create([], lang="go", source="work") + +check_diagnostics() diff --git a/go/ql/lib/CHANGELOG.md b/go/ql/lib/CHANGELOG.md index 0e0d00161e1..6a9a07074b8 100644 --- a/go/ql/lib/CHANGELOG.md +++ b/go/ql/lib/CHANGELOG.md @@ -1,3 +1,17 @@ +## 0.6.0 + +### Deprecated APIs + +* The `LogInjection::Configuration` taint flow configuration class has been deprecated. Use the `LogInjection::Flow` module instead. + +### Minor Analysis Improvements + +* 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. + +## 0.5.4 + +No user-facing changes. + ## 0.5.3 No user-facing changes. 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 deleted file mode 100644 index 88ec05c17ce..00000000000 --- a/go/ql/lib/change-notes/2023-06-14-log-injection-deprecation.md +++ /dev/null @@ -1,4 +0,0 @@ ---- -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-28--add-support-for-bun-framework.md b/go/ql/lib/change-notes/2023-06-28--add-support-for-bun-framework.md new file mode 100644 index 00000000000..1eec83af074 --- /dev/null +++ b/go/ql/lib/change-notes/2023-06-28--add-support-for-bun-framework.md @@ -0,0 +1,3 @@ +lgtm,codescanning +* Support for the [Bun framework](https://bun.uptrace.dev/) has been added. + diff --git a/go/ql/lib/change-notes/2023-06-29-modelling-go-micro.md b/go/ql/lib/change-notes/2023-06-29-modelling-go-micro.md new file mode 100644 index 00000000000..8cb5ada5804 --- /dev/null +++ b/go/ql/lib/change-notes/2023-06-29-modelling-go-micro.md @@ -0,0 +1,4 @@ +--- +category: minorAnalysis +--- +* Add support for v4 of the [Go Micro framework](https://github.com/go-micro/go-micro). \ No newline at end of file diff --git a/go/ql/lib/change-notes/2023-07-05-parameter-nodes-for-unused-parameters.md b/go/ql/lib/change-notes/2023-07-05-parameter-nodes-for-unused-parameters.md new file mode 100644 index 00000000000..1d55a4a9396 --- /dev/null +++ b/go/ql/lib/change-notes/2023-07-05-parameter-nodes-for-unused-parameters.md @@ -0,0 +1,4 @@ +--- +category: minorAnalysis +--- +* Parameter nodes now exist for unused parameters as well as used parameters. diff --git a/go/ql/lib/change-notes/2023-07-12-default-stateconfigsig-predicates.md b/go/ql/lib/change-notes/2023-07-12-default-stateconfigsig-predicates.md new file mode 100644 index 00000000000..c9aba58603a --- /dev/null +++ b/go/ql/lib/change-notes/2023-07-12-default-stateconfigsig-predicates.md @@ -0,0 +1,6 @@ +--- +category: feature +--- +* The `DataFlow::StateConfigSig` signature module has gained default implementations for `isBarrier/2` and `isAdditionalFlowStep/4`. + Hence it is no longer needed to provide `none()` implementations of these predicates if they are not needed. + diff --git a/go/ql/lib/change-notes/released/0.5.4.md b/go/ql/lib/change-notes/released/0.5.4.md new file mode 100644 index 00000000000..1686ab4354d --- /dev/null +++ b/go/ql/lib/change-notes/released/0.5.4.md @@ -0,0 +1,3 @@ +## 0.5.4 + +No user-facing changes. diff --git a/go/ql/lib/change-notes/2023-06-20-function-model-path-nodes.md b/go/ql/lib/change-notes/released/0.6.0.md similarity index 58% rename from go/ql/lib/change-notes/2023-06-20-function-model-path-nodes.md rename to go/ql/lib/change-notes/released/0.6.0.md index 5c616481326..23b2a7f6847 100644 --- a/go/ql/lib/change-notes/2023-06-20-function-model-path-nodes.md +++ b/go/ql/lib/change-notes/released/0.6.0.md @@ -1,4 +1,9 @@ ---- -category: minorAnalysis ---- +## 0.6.0 + +### Deprecated APIs + +* The `LogInjection::Configuration` taint flow configuration class has been deprecated. Use the `LogInjection::Flow` module instead. + +### Minor Analysis Improvements + * 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/codeql-pack.release.yml b/go/ql/lib/codeql-pack.release.yml index 2164e038a5d..a3f820f884d 100644 --- a/go/ql/lib/codeql-pack.release.yml +++ b/go/ql/lib/codeql-pack.release.yml @@ -1,2 +1,2 @@ --- -lastReleaseVersion: 0.5.3 +lastReleaseVersion: 0.6.0 diff --git a/go/ql/lib/go.qll b/go/ql/lib/go.qll index 038916ed76a..b41fcceb23c 100644 --- a/go/ql/lib/go.qll +++ b/go/ql/lib/go.qll @@ -40,6 +40,7 @@ import semmle.go.frameworks.Email import semmle.go.frameworks.Encoding import semmle.go.frameworks.Gin import semmle.go.frameworks.Glog +import semmle.go.frameworks.GoMicro import semmle.go.frameworks.GoRestfulHttp import semmle.go.frameworks.K8sIoApimachineryPkgRuntime import semmle.go.frameworks.K8sIoApiCoreV1 diff --git a/go/ql/lib/qlpack.yml b/go/ql/lib/qlpack.yml index bf8cd0f648e..dc7e2c2f0e9 100644 --- a/go/ql/lib/qlpack.yml +++ b/go/ql/lib/qlpack.yml @@ -1,5 +1,5 @@ name: codeql/go-all -version: 0.5.4-dev +version: 0.6.1-dev groups: go dbscheme: go.dbscheme extractor: go diff --git a/go/ql/lib/semmle/go/DiagnosticsReporting.qll b/go/ql/lib/semmle/go/DiagnosticsReporting.qll index d74a709dffb..653e3ad7c5e 100644 --- a/go/ql/lib/semmle/go/DiagnosticsReporting.qll +++ b/go/ql/lib/semmle/go/DiagnosticsReporting.qll @@ -35,6 +35,26 @@ private class Diagnostic extends @diagnostic { string toString() { result = this.getMessage() } } +bindingset[msg] +private string removeAbsolutePaths(string msg) { + exists(string r | + // turn both + // cannot find package "subdir1/subsubdir1" in any of:\n\t/usr/local/Cellar/go/1.20.5/libexec/src/subdir1/subsubdir1 (from $GOROOT)\n\t/Users/owen-mc/go/src/subdir1/subsubdir1 (from $GOPATH) + // and + // cannot find package "subdir1/subsubdir1" in any of:\n\tC:\\hostedtoolcache\\windows\\go\\1.20.5\\x64\\src\\subdir1\\subsubdir1 (from $GOROOT)\n\tC:\\Users\\runneradmin\\go\\src\\subdir1\\subsubdir1 (from $GOPATH) + // into + // cannot find package "subdir1/subsubdir1" in any of:\n\t(absolute path) (from $GOROOT)\n\t(absolute path) (from $GOPATH) + r = + "(cannot find package [^ ]* in any of:\\n\\t).*( \\(from \\$GOROOT\\)\\n\\t).*( \\(from \\$GOPATH\\))" and + if exists(msg.regexpCapture(r, 1)) + then + result = + msg.regexpCapture(r, 1) + "(absolute path)" + msg.regexpCapture(r, 2) + "(absolute path)" + + msg.regexpCapture(r, 3) + else result = msg + ) +} + /** * Holds if an extraction error or warning occurred that should be reported to end users, * with the error message `msg` and SARIF severity `sev`. @@ -47,10 +67,11 @@ predicate reportableDiagnostics(Diagnostic d, string msg, int sev) { exists(File f | f = d.getFile() | exists(f.getAChild()) and msg = - "Extraction failed in " + d.getFile().getRelativePath() + " with error " + d.getMessage() + "Extraction failed in " + d.getFile().getRelativePath() + " with error " + + removeAbsolutePaths(d.getMessage()) ) or not exists(d.getFile()) and - msg = "Extraction failed with error " + d.getMessage() + msg = "Extraction failed with error " + removeAbsolutePaths(d.getMessage()) ) } diff --git a/go/ql/lib/semmle/go/dataflow/internal/DataFlow.qll b/go/ql/lib/semmle/go/dataflow/internal/DataFlow.qll index f34554d6eea..47329d133a4 100644 --- a/go/ql/lib/semmle/go/dataflow/internal/DataFlow.qll +++ b/go/ql/lib/semmle/go/dataflow/internal/DataFlow.qll @@ -114,7 +114,7 @@ signature module StateConfigSig { * Holds if data flow through `node` is prohibited when the flow state is * `state`. */ - predicate isBarrier(Node node, FlowState state); + default predicate isBarrier(Node node, FlowState state) { none() } /** Holds if data flow into `node` is prohibited. */ default predicate isBarrierIn(Node node) { none() } @@ -131,7 +131,9 @@ signature module StateConfigSig { * Holds if data may flow from `node1` to `node2` in addition to the normal data-flow steps. * This step is only applicable in `state1` and updates the flow state to `state2`. */ - predicate isAdditionalFlowStep(Node node1, FlowState state1, Node node2, FlowState state2); + default predicate isAdditionalFlowStep(Node node1, FlowState state1, Node node2, FlowState state2) { + none() + } /** * Holds if an arbitrary number of implicit read steps of content `c` may be diff --git a/go/ql/lib/semmle/go/dataflow/internal/DataFlowImpl.qll b/go/ql/lib/semmle/go/dataflow/internal/DataFlowImpl.qll index 284fff191ae..fe8633e9218 100644 --- a/go/ql/lib/semmle/go/dataflow/internal/DataFlowImpl.qll +++ b/go/ql/lib/semmle/go/dataflow/internal/DataFlowImpl.qll @@ -254,6 +254,11 @@ module Impl { not fullBarrier(node2) } + pragma[nomagic] + private predicate isUnreachableInCall1(NodeEx n, LocalCallContextSpecificCall cc) { + isUnreachableInCallCached(n.asNode(), cc.getCall()) + } + /** * Holds if data can flow in one local step from `node1` to `node2`. */ @@ -460,7 +465,6 @@ module Impl { * The Boolean `cc` records whether the node is reached through an * argument in a call. */ - pragma[assume_small_delta] private predicate fwdFlow(NodeEx node, Cc cc) { sourceNode(node, _) and if hasSourceCallCtx() then cc = true else cc = false @@ -570,7 +574,6 @@ module Impl { /** * Holds if `c` is the target of a store in the flow covered by `fwdFlow`. */ - pragma[assume_small_delta] pragma[nomagic] private predicate fwdFlowConsCand(Content c) { exists(NodeEx mid, NodeEx node | @@ -1216,7 +1219,6 @@ module Impl { fwdFlow1(_, _, _, _, _, _, t0, t, ap, _) and t0 != t } - pragma[assume_small_delta] pragma[nomagic] private predicate fwdFlow0( NodeEx node, FlowState state, Cc cc, ParamNodeOption summaryCtx, TypOption argT, @@ -2111,7 +2113,7 @@ module Impl { NodeEx node1, FlowState state, NodeEx node2, boolean preservesValue, DataFlowType t, LocalCallContext cc ) { - not isUnreachableInCallCached(node2.asNode(), cc.(LocalCallContextSpecificCall).getCall()) and + not isUnreachableInCall1(node2, cc) and ( localFlowEntry(node1, pragma[only_bind_into](state)) and ( @@ -2126,7 +2128,7 @@ module Impl { ) and node1 != node2 and cc.relevantFor(node1.getEnclosingCallable()) and - not isUnreachableInCallCached(node1.asNode(), cc.(LocalCallContextSpecificCall).getCall()) + not isUnreachableInCall1(node1, cc) or exists(NodeEx mid | localFlowStepPlus(node1, pragma[only_bind_into](state), mid, preservesValue, t, cc) and @@ -2163,10 +2165,8 @@ module Impl { preservesValue = false and t = node2.getDataFlowType() and callContext.relevantFor(node1.getEnclosingCallable()) and - not exists(DataFlowCall call | call = callContext.(LocalCallContextSpecificCall).getCall() | - isUnreachableInCallCached(node1.asNode(), call) or - isUnreachableInCallCached(node2.asNode(), call) - ) + not isUnreachableInCall1(node1, callContext) and + not isUnreachableInCall1(node2, callContext) } } @@ -2706,7 +2706,7 @@ module Impl { ParamNodeEx getParamNode() { result = p } - override string toString() { result = p + ": " + ap } + override string toString() { result = p + concat(" : " + ppReprType(t)) + " " + ap } predicate hasLocationInfo( string filepath, int startline, int startcolumn, int endline, int endcolumn @@ -2758,12 +2758,21 @@ module Impl { ) } + private predicate forceUnfold(AccessPathApprox apa) { + forceHighPrecision(apa.getHead()) + or + exists(Content c2 | + apa = TConsCons(_, _, c2, _) and + forceHighPrecision(c2) + ) + } + /** * Holds with `unfold = false` if a precise head-tail representation of `apa` is * expected to be expensive. Holds with `unfold = true` otherwise. */ private predicate evalUnfold(AccessPathApprox apa, boolean unfold) { - if forceHighPrecision(apa.getHead()) + if forceUnfold(apa) then unfold = true else exists(int aps, int nodes, int apLimit, int tupleLimit | @@ -2777,7 +2786,6 @@ module Impl { /** * Gets the number of `AccessPath`s that correspond to `apa`. */ - pragma[assume_small_delta] private int countAps(AccessPathApprox apa) { evalUnfold(apa, false) and result = 1 and @@ -2796,7 +2804,6 @@ module Impl { * that it is expanded to a precise head-tail representation. */ language[monotonicAggregates] - pragma[assume_small_delta] private int countPotentialAps(AccessPathApprox apa) { apa instanceof AccessPathApproxNil and result = 1 or @@ -2833,7 +2840,6 @@ module Impl { } private newtype TPathNode = - pragma[assume_small_delta] TPathNodeMid( NodeEx node, FlowState state, CallContext cc, SummaryCtx sc, DataFlowType t, AccessPath ap ) { @@ -2918,7 +2924,6 @@ module Impl { override AccessPathFrontHead getFront() { result = TFrontHead(head_) } - pragma[assume_small_delta] override AccessPathApproxCons getApprox() { result = TConsNil(head_, t) and tail_ = TAccessPathNil() or @@ -2927,7 +2932,6 @@ module Impl { result = TCons1(head_, this.length()) } - pragma[assume_small_delta] override int length() { result = 1 + tail_.length() } private string toStringImpl(boolean needsSuffix) { @@ -3097,6 +3101,12 @@ module Impl { result = " <" + this.(PathNodeMid).getCallContext().toString() + ">" } + private string ppSummaryCtx() { + this instanceof PathNodeSink and result = "" + or + result = " <" + this.(PathNodeMid).getSummaryCtx().toString() + ">" + } + /** Gets a textual representation of this element. */ string toString() { result = this.getNodeEx().toString() + this.ppType() + this.ppAp() } @@ -3105,7 +3115,9 @@ module Impl { * representation of the call context. */ string toStringWithContext() { - result = this.getNodeEx().toString() + this.ppType() + this.ppAp() + this.ppCtx() + result = + this.getNodeEx().toString() + this.ppType() + this.ppAp() + this.ppCtx() + + this.ppSummaryCtx() } /** @@ -3379,7 +3391,6 @@ module Impl { * Holds if data may flow from `mid` to `node`. The last step in or out of * a callable is recorded by `cc`. */ - pragma[assume_small_delta] pragma[nomagic] private predicate pathStep0( PathNodeMid mid, NodeEx node, FlowState state, CallContext cc, SummaryCtx sc, DataFlowType t, @@ -3592,7 +3603,6 @@ module Impl { ) } - pragma[assume_small_delta] pragma[nomagic] private predicate pathThroughCallable0( DataFlowCall call, PathNodeMid mid, ReturnKindExt kind, FlowState state, CallContext cc, diff --git a/go/ql/lib/semmle/go/dataflow/internal/DataFlowImplCommon.qll b/go/ql/lib/semmle/go/dataflow/internal/DataFlowImplCommon.qll index 0d4c033c95d..aff14e7b44d 100644 --- a/go/ql/lib/semmle/go/dataflow/internal/DataFlowImplCommon.qll +++ b/go/ql/lib/semmle/go/dataflow/internal/DataFlowImplCommon.qll @@ -187,7 +187,6 @@ private module LambdaFlow { else any() } - pragma[assume_small_delta] pragma[nomagic] predicate revLambdaFlow0( DataFlowCall lambdaCall, LambdaCallKind kind, Node node, DataFlowType t, boolean toReturn, @@ -274,7 +273,6 @@ private module LambdaFlow { ) } - pragma[assume_small_delta] pragma[nomagic] predicate revLambdaFlowOut( DataFlowCall lambdaCall, LambdaCallKind kind, TReturnPositionSimple pos, DataFlowType t, diff --git a/go/ql/lib/semmle/go/dataflow/internal/DataFlowNodes.qll b/go/ql/lib/semmle/go/dataflow/internal/DataFlowNodes.qll index 84ed6d5f61c..59224024ec3 100644 --- a/go/ql/lib/semmle/go/dataflow/internal/DataFlowNodes.qll +++ b/go/ql/lib/semmle/go/dataflow/internal/DataFlowNodes.qll @@ -680,6 +680,24 @@ module Public { } } + /** A representation of a parameter initialization, defined in source via an SSA node. */ + class UnusedParameterNode extends ParameterNode, InstructionNode { + override IR::InitParameterInstruction insn; + Parameter parm; + + UnusedParameterNode() { + insn = IR::initParamInstruction(parm) and + not exists(SsaExplicitDefinition ssa | ssa.getInstruction() = insn) + } + + /** Gets the parameter this node initializes. */ + override Parameter asParameter() { result = parm } + + override predicate isParameterOf(DataFlowCallable c, int i) { + parm.isParameterOf(c.asCallable().getFuncDef(), i) + } + } + /** A representation of a parameter initialization, defined in source via an SSA node. */ class SsaParameterNode extends ParameterNode, SsaNode { override SsaExplicitDefinition ssa; diff --git a/go/ql/lib/semmle/go/dataflow/internal/FlowSummaryImpl.qll b/go/ql/lib/semmle/go/dataflow/internal/FlowSummaryImpl.qll index 9ea7c44c50c..7977e18120f 100644 --- a/go/ql/lib/semmle/go/dataflow/internal/FlowSummaryImpl.qll +++ b/go/ql/lib/semmle/go/dataflow/internal/FlowSummaryImpl.qll @@ -23,29 +23,30 @@ module Public { * content type, or a return kind. */ class SummaryComponent extends TSummaryComponent { - /** Gets a textual representation of this summary component. */ - string toString() { - exists(ContentSet c | this = TContentSummaryComponent(c) and result = c.toString()) - or - exists(ContentSet c | this = TWithoutContentSummaryComponent(c) and result = "without " + c) - or - exists(ContentSet c | this = TWithContentSummaryComponent(c) and result = "with " + c) + /** Gets a textual representation of this component used for MaD models. */ + string getMadRepresentation() { + result = getMadRepresentationSpecific(this) or exists(ArgumentPosition pos | - this = TParameterSummaryComponent(pos) and result = "parameter " + pos + this = TParameterSummaryComponent(pos) and + result = "Parameter[" + getArgumentPosition(pos) + "]" ) or exists(ParameterPosition pos | - this = TArgumentSummaryComponent(pos) and result = "argument " + pos + this = TArgumentSummaryComponent(pos) and + result = "Argument[" + getParameterPosition(pos) + "]" ) or - exists(ReturnKind rk | this = TReturnSummaryComponent(rk) and result = "return (" + rk + ")") - or - exists(SummaryComponent::SyntheticGlobal sg | - this = TSyntheticGlobalSummaryComponent(sg) and - result = "synthetic global (" + sg + ")" + exists(string synthetic | + this = TSyntheticGlobalSummaryComponent(synthetic) and + result = "SyntheticGlobal[" + synthetic + "]" ) + or + this = TReturnSummaryComponent(getReturnValueKind()) and result = "ReturnValue" } + + /** Gets a textual representation of this summary component. */ + string toString() { result = this.getMadRepresentation() } } /** Provides predicates for constructing summary components. */ @@ -110,7 +111,6 @@ module Public { } /** Gets the stack obtained by dropping the first `i` elements, if any. */ - pragma[assume_small_delta] SummaryComponentStack drop(int i) { i = 0 and result = this or @@ -125,19 +125,22 @@ module Public { this = TSingletonSummaryComponentStack(result) or result = this.tail().bottom() } - /** Gets a textual representation of this stack. */ - string toString() { + /** Gets a textual representation of this stack used for MaD models. */ + string getMadRepresentation() { exists(SummaryComponent head, SummaryComponentStack tail | head = this.head() and tail = this.tail() and - result = tail + "." + head + result = tail.getMadRepresentation() + "." + head.getMadRepresentation() ) or exists(SummaryComponent c | this = TSingletonSummaryComponentStack(c) and - result = c.toString() + result = c.getMadRepresentation() ) } + + /** Gets a textual representation of this stack. */ + string toString() { result = this.getMadRepresentation() } } /** Provides predicates for constructing stacks of summary components. */ @@ -166,42 +169,6 @@ module Public { SummaryComponentStack return(ReturnKind rk) { result = singleton(SummaryComponent::return(rk)) } } - /** Gets a textual representation of this component used for flow summaries. */ - private string getComponent(SummaryComponent sc) { - result = getComponentSpecific(sc) - or - exists(ArgumentPosition pos | - sc = TParameterSummaryComponent(pos) and - result = "Parameter[" + getArgumentPosition(pos) + "]" - ) - or - exists(ParameterPosition pos | - sc = TArgumentSummaryComponent(pos) and - result = "Argument[" + getParameterPosition(pos) + "]" - ) - or - exists(string synthetic | - sc = TSyntheticGlobalSummaryComponent(synthetic) and - result = "SyntheticGlobal[" + synthetic + "]" - ) - or - sc = TReturnSummaryComponent(getReturnValueKind()) and result = "ReturnValue" - } - - /** Gets a textual representation of this stack used for flow summaries. */ - string getComponentStack(SummaryComponentStack stack) { - exists(SummaryComponent head, SummaryComponentStack tail | - head = stack.head() and - tail = stack.tail() and - result = getComponentStack(tail) + "." + getComponent(head) - ) - or - exists(SummaryComponent c | - stack = TSingletonSummaryComponentStack(c) and - result = getComponent(c) - ) - } - /** * A class that exists for QL technical reasons only (the IPA type used * to represent component stacks needs to be bounded). @@ -1382,8 +1349,8 @@ module Private { c.relevantSummary(input, output, preservesValue) and csv = c.getCallableCsv() // Callable information - + getComponentStack(input) + ";" // input - + getComponentStack(output) + ";" // output + + input.getMadRepresentation() + ";" // input + + output.getMadRepresentation() + ";" // output + renderKind(preservesValue) + ";" // kind + renderProvenance(c) // provenance ) diff --git a/go/ql/lib/semmle/go/dataflow/internal/FlowSummaryImplSpecific.qll b/go/ql/lib/semmle/go/dataflow/internal/FlowSummaryImplSpecific.qll index 609790659f8..88844f24537 100644 --- a/go/ql/lib/semmle/go/dataflow/internal/FlowSummaryImplSpecific.qll +++ b/go/ql/lib/semmle/go/dataflow/internal/FlowSummaryImplSpecific.qll @@ -110,8 +110,8 @@ private string getContentSpecific(Content c) { c instanceof PointerContent and result = "Dereference" } -/** Gets the textual representation of the content in the format used for flow summaries. */ -string getComponentSpecific(SummaryComponent sc) { +/** Gets the textual representation of the content in the format used for MaD models. */ +string getMadRepresentationSpecific(SummaryComponent sc) { exists(Content c | sc = TContentSummaryComponent(c) and result = getContentSpecific(c)) or exists(ReturnKind rk | diff --git a/go/ql/lib/semmle/go/frameworks/GoMicro.qll b/go/ql/lib/semmle/go/frameworks/GoMicro.qll new file mode 100644 index 00000000000..de775761c64 --- /dev/null +++ b/go/ql/lib/semmle/go/frameworks/GoMicro.qll @@ -0,0 +1,154 @@ +/** + * Provides models of the [Go Micro library](https://github.com/go-micro/go-micro). + */ + +import go +private import semmle.go.security.RequestForgeryCustomizations + +/** + * Module for Go Micro framework. + */ +module GoMicro { + /** + * A GoMicro server type. + */ + class GoMicroServerType extends Type { + GoMicroServerType() { this.hasQualifiedName("go-micro.dev/v4/server", "Server") } + } + + /** + * A GoMicro client type. + */ + class GoMicroClientType extends Type { + GoMicroClientType() { this.hasQualifiedName("go-micro.dev/v4/client", "Client") } + } + + /** + * A file that is generated by the protobuf compiler. + */ + class ProtocGeneratedFile extends File { + ProtocGeneratedFile() { this.getBaseName().regexpMatch(".*\\.pb(\\.micro)?\\.go$") } + } + + /** + * A type that is generated by the protobuf compiler. + */ + class ProtocMessageType extends Type { + ProtocMessageType() { + this.hasLocationInfo(any(ProtocGeneratedFile f).getAbsolutePath(), _, _, _, _) and + exists(MethodDecl md | + md.getName() = "ProtoMessage" and + this = md.getReceiverDecl().getTypeExpr().getAChild().(TypeName).getType() + ) + } + } + + /** + * A Server Interface type. + */ + class ServiceInterfaceType extends InterfaceType { + NamedType namedType; + + ServiceInterfaceType() { + this = namedType.getUnderlyingType() and + namedType.hasLocationInfo(any(ProtocGeneratedFile f).getAbsolutePath(), _, _, _, _) + } + + /** + * Gets the name of the interface. + */ + override string getName() { result = namedType.getName() } + + /** + * Gets the named type on top of this interface type. + */ + NamedType getNamedType() { result = namedType } + } + + /** + * A Service server handler type. + */ + class ServiceServerType extends NamedType { + ServiceServerType() { + this.implements(any(ServiceInterfaceType i)) and + this.getName().regexpMatch("(?i).*Handler") and + this.hasLocationInfo(any(ProtocGeneratedFile f).getAbsolutePath(), _, _, _, _) + } + } + + /** + * A Client server handler type. + */ + class ClientServiceType extends NamedType { + ClientServiceType() { + this.implements(any(ServiceInterfaceType i)) and + this.getName().regexpMatch("(?i).*Service") and + this.hasLocationInfo(any(ProtocGeneratedFile f).getAbsolutePath(), _, _, _, _) + } + } + + /** + * A service register handler. + */ + class ServiceRegisterHandler extends Function { + ServiceRegisterHandler() { + this.getName().regexpMatch("(?i)register" + any(ServiceServerType c).getName()) and + this.getParameterType(0) instanceof GoMicroServerType and + this.hasLocationInfo(any(ProtocGeneratedFile f).getAbsolutePath(), _, _, _, _) + } + } + + /** + * A service handler. + */ + class ServiceHandler extends Method { + ServiceHandler() { + exists(DataFlow::CallNode call | + call.getTarget() instanceof ServiceRegisterHandler and + this = call.getArgument(1).getType().getMethod(_) and + this.implements(any(ServiceInterfaceType i).getNamedType().getMethod(_)) + ) + } + } + + /** + * A client service function. + */ + class ClientService extends Function { + ClientService() { + this.getName().regexpMatch("(?i)new" + any(ClientServiceType c).getName()) and + this.getParameterType(0) instanceof StringType and + this.getParameterType(1) instanceof GoMicroClientType and + this.hasLocationInfo(any(ProtocGeneratedFile f).getAbsolutePath(), _, _, _, _) + } + } + + /** + * An SSRF sink for the Client service function. + */ + class ClientRequestUrlAsSink extends RequestForgery::Sink { + ClientRequestUrlAsSink() { + exists(DataFlow::CallNode call | + call.getArgument(0) = this and + call.getTarget() instanceof ClientService + ) + } + + override DataFlow::Node getARequest() { result = this } + + override string getKind() { result = "URL" } + } + + /** + * A set of remote requests from a service handler. + */ + class Request extends UntrustedFlowSource::Range instanceof DataFlow::ParameterNode { + Request() { + exists(ServiceHandler handler | + this.asParameter().isParameterOf(handler.getFuncDecl(), 1) and + handler.getParameterType(0).hasQualifiedName("context", "Context") and + this.getType().(PointerType).getBaseType() instanceof ProtocMessageType + ) + } + } +} diff --git a/go/ql/lib/semmle/go/frameworks/SQL.qll b/go/ql/lib/semmle/go/frameworks/SQL.qll index 185f0b3f2bf..f809c27393f 100644 --- a/go/ql/lib/semmle/go/frameworks/SQL.qll +++ b/go/ql/lib/semmle/go/frameworks/SQL.qll @@ -289,3 +289,47 @@ module Xorm { } } } + +/** + * Provides classes for working with the [Bun](https://bun.uptrace.dev/) package. + */ +module Bun { + /** Gets the package name for Bun package. */ + private string packagePath() { result = package("github.com/uptrace/bun", "") } + + /** A model for sinks of Bun. */ + private class BunSink extends SQL::QueryString::Range { + BunSink() { + exists(Function f, string m, int arg | this = f.getACall().getArgument(arg) | + f.hasQualifiedName(packagePath(), m) and + m = "NewRawQuery" and + arg = 1 + ) + or + exists(Method f, string tp, string m, int arg | this = f.getACall().getArgument(arg) | + f.hasQualifiedName(packagePath(), tp, m) and + ( + tp = ["DB", "Conn"] and + m = ["ExecContext", "PrepareContext", "QueryContext", "QueryRowContext"] and + arg = 1 + or + tp = ["DB", "Conn"] and + m = ["Exec", "NewRaw", "Prepare", "Query", "QueryRow", "Raw"] and + arg = 0 + or + tp.matches("%Query") and + m = + [ + "ColumnExpr", "DistinctOn", "For", "GroupExpr", "Having", "ModelTableExpr", + "OrderExpr", "TableExpr", "Where", "WhereOr" + ] and + arg = 0 + or + tp = "RawQuery" and + m = "NewRaw" and + arg = 0 + ) + ) + } + } +} diff --git a/go/ql/lib/semmle/go/frameworks/Twirp.qll b/go/ql/lib/semmle/go/frameworks/Twirp.qll index c4fc0737ac1..03451fcac8d 100644 --- a/go/ql/lib/semmle/go/frameworks/Twirp.qll +++ b/go/ql/lib/semmle/go/frameworks/Twirp.qll @@ -35,83 +35,59 @@ module Twirp { } } - /** - * A type representing a protobuf message. - */ + /** A type representing a protobuf message. */ class ProtobufMessageType extends Type { ProtobufMessageType() { - exists(TypeEntity te | - te.getType() = this and - te.getDeclaration().getLocation().getFile() instanceof ProtobufGeneratedFile - ) + this.hasLocationInfo(any(ProtobufGeneratedFile f).getAbsolutePath(), _, _, _, _) } } - /** - * An interface type representing a Twirp service. - */ + /** An interface type representing a Twirp service. */ class ServiceInterfaceType extends InterfaceType { NamedType namedType; ServiceInterfaceType() { - exists(TypeEntity te | - te.getType() = namedType and - namedType.getUnderlyingType() = this and - te.getDeclaration().getLocation().getFile() instanceof ServicesGeneratedFile - ) + namedType.getUnderlyingType() = this and + namedType.hasLocationInfo(any(ServicesGeneratedFile f).getAbsolutePath(), _, _, _, _) } - /** - * Gets the name of the interface. - */ + /** Gets the name of the interface. */ override string getName() { result = namedType.getName() } - /** - * Gets the named type on top of this interface type. - */ + /** Gets the named type on top of this interface type. */ NamedType getNamedType() { result = namedType } } - /** - * A Twirp client. - */ + /** A Twirp client. */ class ServiceClientType extends NamedType { ServiceClientType() { - exists(ServiceInterfaceType i, PointerType p, TypeEntity te | + exists(ServiceInterfaceType i, PointerType p | p.implements(i) and this = p.getBaseType() and this.getName().regexpMatch("(?i)" + i.getName() + "(protobuf|json)client") and - te.getType() = this and - te.getDeclaration().getLocation().getFile() instanceof ServicesGeneratedFile + this.hasLocationInfo(any(ServicesGeneratedFile f).getAbsolutePath(), _, _, _, _) ) } } - /** - * A Twirp server. - */ + /** A Twirp server. */ class ServiceServerType extends NamedType { ServiceServerType() { - exists(ServiceInterfaceType i, TypeEntity te | + exists(ServiceInterfaceType i | this.implements(i) and this.getName().regexpMatch("(?i)" + i.getName() + "server") and - te.getType() = this and - te.getDeclaration().getLocation().getFile() instanceof ServicesGeneratedFile + this.hasLocationInfo(any(ServicesGeneratedFile f).getAbsolutePath(), _, _, _, _) ) } } - /** - * A Twirp function to construct a Client. - */ + /** A Twirp function to construct a Client. */ class ClientConstructor extends Function { ClientConstructor() { - exists(ServiceClientType c | - this.getName().regexpMatch("(?i)new" + c.getName()) and - this.getParameterType(0) instanceof StringType and - this.getParameterType(1).getName() = "HTTPClient" and - this.getDeclaration().getLocation().getFile() instanceof ServicesGeneratedFile - ) + this.getName().regexpMatch("(?i)new" + any(ServiceClientType c).getName()) and + this.getParameterType(0) instanceof StringType and + this.getParameterType(1).getName() = "HTTPClient" and + this.hasLocationInfo(any(ServicesGeneratedFile f).getAbsolutePath(), _, _, _, _) } } @@ -122,17 +98,13 @@ module Twirp { */ class ServerConstructor extends Function { ServerConstructor() { - exists(ServiceServerType c, ServiceInterfaceType i | - this.getName().regexpMatch("(?i)new" + c.getName()) and - this.getParameterType(0) = i.getNamedType() and - this.getDeclaration().getLocation().getFile() instanceof ServicesGeneratedFile - ) + this.getName().regexpMatch("(?i)new" + any(ServiceServerType c).getName()) and + this.getParameterType(0) = any(ServiceInterfaceType i).getNamedType() and + this.hasLocationInfo(any(ServicesGeneratedFile f).getAbsolutePath(), _, _, _, _) } } - /** - * An SSRF sink for the Client constructor. - */ + /** An SSRF sink for the Client constructor. */ class ClientRequestUrlAsSink extends RequestForgery::Sink { ClientRequestUrlAsSink() { exists(DataFlow::CallNode call | @@ -146,27 +118,22 @@ module Twirp { override string getKind() { result = "URL" } } - /** - * A service handler. - */ + /** A service handler. */ class ServiceHandler extends Method { ServiceHandler() { - exists(DataFlow::CallNode call, Type handlerType, ServiceInterfaceType i | + exists(DataFlow::CallNode call | call.getTarget() instanceof ServerConstructor and - call.getArgument(0).getType() = handlerType and - this = handlerType.getMethod(_) and - this.implements(i.getNamedType().getMethod(_)) + this = call.getArgument(0).getType().getMethod(_) and + this.implements(any(ServiceInterfaceType i).getNamedType().getMethod(_)) ) } } - /** - * A request coming to the service handler. - */ + /** A request coming to the service handler. */ class Request extends UntrustedFlowSource::Range instanceof DataFlow::ParameterNode { Request() { - exists(FuncDef c, ServiceHandler handler | handler.getFuncDecl() = c | - this.asParameter().isParameterOf(c, 1) and + exists(ServiceHandler handler | + this.asParameter().isParameterOf(handler.getFuncDecl(), 1) and handler.getParameterType(0).hasQualifiedName("context", "Context") and this.getType().(PointerType).getBaseType() instanceof ProtobufMessageType ) diff --git a/go/ql/src/CHANGELOG.md b/go/ql/src/CHANGELOG.md index 61712c5e790..2b87cb252c4 100644 --- a/go/ql/src/CHANGELOG.md +++ b/go/ql/src/CHANGELOG.md @@ -1,3 +1,13 @@ +## 0.6.0 + +### Bug Fixes + +* The query "Arbitrary file write during zip extraction ("zip slip")" (`go/zipslip`) has been renamed to "Arbitrary file access during archive extraction ("Zip Slip")." + +## 0.5.4 + +No user-facing changes. + ## 0.5.3 No user-facing changes. diff --git a/go/ql/src/change-notes/released/0.5.4.md b/go/ql/src/change-notes/released/0.5.4.md new file mode 100644 index 00000000000..1686ab4354d --- /dev/null +++ b/go/ql/src/change-notes/released/0.5.4.md @@ -0,0 +1,3 @@ +## 0.5.4 + +No user-facing changes. diff --git a/go/ql/src/change-notes/2023-06-16-zipslip-rename.md b/go/ql/src/change-notes/released/0.6.0.md similarity index 87% rename from go/ql/src/change-notes/2023-06-16-zipslip-rename.md rename to go/ql/src/change-notes/released/0.6.0.md index 72913f37c06..a994c79cdf0 100644 --- a/go/ql/src/change-notes/2023-06-16-zipslip-rename.md +++ b/go/ql/src/change-notes/released/0.6.0.md @@ -1,4 +1,5 @@ ---- -category: fix ---- +## 0.6.0 + +### Bug Fixes + * 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/codeql-pack.release.yml b/go/ql/src/codeql-pack.release.yml index 2164e038a5d..a3f820f884d 100644 --- a/go/ql/src/codeql-pack.release.yml +++ b/go/ql/src/codeql-pack.release.yml @@ -1,2 +1,2 @@ --- -lastReleaseVersion: 0.5.3 +lastReleaseVersion: 0.6.0 diff --git a/go/ql/src/qlpack.yml b/go/ql/src/qlpack.yml index ad8b0d5db16..0a414f7769f 100644 --- a/go/ql/src/qlpack.yml +++ b/go/ql/src/qlpack.yml @@ -1,5 +1,5 @@ name: codeql/go-queries -version: 0.5.4-dev +version: 0.6.1-dev groups: - go - queries diff --git a/go/ql/test/library-tests/semmle/go/dataflow/FlowSteps/LocalTaintStep.expected b/go/ql/test/library-tests/semmle/go/dataflow/FlowSteps/LocalTaintStep.expected index d875c24c49a..898f5bfc54c 100644 --- a/go/ql/test/library-tests/semmle/go/dataflow/FlowSteps/LocalTaintStep.expected +++ b/go/ql/test/library-tests/semmle/go/dataflow/FlowSteps/LocalTaintStep.expected @@ -1,286 +1,286 @@ -| file://:0:0:0:0 | [summary param] 0 in AppendQuote | file://:0:0:0:0 | [summary] to write: return (return[0]) in AppendQuote | -| file://:0:0:0:0 | [summary param] 0 in AppendQuoteToASCII | file://:0:0:0:0 | [summary] to write: return (return[0]) in AppendQuoteToASCII | -| file://:0:0:0:0 | [summary param] 0 in AppendQuoteToGraphic | file://:0:0:0:0 | [summary] to write: return (return[0]) in AppendQuoteToGraphic | -| file://:0:0:0:0 | [summary param] 0 in AppendSlice | file://:0:0:0:0 | [summary] to write: return (return[0]) in AppendSlice | -| file://:0:0:0:0 | [summary param] 0 in As | file://:0:0:0:0 | [summary] to write: argument 1 in As | -| file://:0:0:0:0 | [summary param] 0 in Base | file://:0:0:0:0 | [summary] to write: return (return[0]) in Base | -| file://:0:0:0:0 | [summary param] 0 in BytePtrFromString | file://:0:0:0:0 | [summary] to write: return (return[0]) in BytePtrFromString | -| file://:0:0:0:0 | [summary param] 0 in ByteSliceFromString | file://:0:0:0:0 | [summary] to write: return (return[0]) in ByteSliceFromString | -| file://:0:0:0:0 | [summary param] 0 in Clean | file://:0:0:0:0 | [summary] to write: return (return[0]) in Clean | -| file://:0:0:0:0 | [summary param] 0 in Dir | file://:0:0:0:0 | [summary] to write: return (return[0]) in Dir | -| file://:0:0:0:0 | [summary param] 0 in Expand | file://:0:0:0:0 | [summary] to write: return (return[0]) in Expand | -| file://:0:0:0:0 | [summary param] 0 in ExpandEnv | file://:0:0:0:0 | [summary] to write: return (return[0]) in ExpandEnv | -| file://:0:0:0:0 | [summary param] 0 in Ext | file://:0:0:0:0 | [summary] to write: return (return[0]) in Ext | -| file://:0:0:0:0 | [summary param] 0 in Fields | file://:0:0:0:0 | [summary] to write: return (return[0]) in Fields | -| file://:0:0:0:0 | [summary param] 0 in FieldsFunc | file://:0:0:0:0 | [summary] to write: return (return[0]) in FieldsFunc | -| file://:0:0:0:0 | [summary param] 0 in FileInfoToDirEntry | file://:0:0:0:0 | [summary] to write: return (return[0]) in FileInfoToDirEntry | -| file://:0:0:0:0 | [summary param] 0 in Glob | file://:0:0:0:0 | [summary] to write: return (return[0]) in Glob | -| file://:0:0:0:0 | [summary param] 0 in Indirect | file://:0:0:0:0 | [summary] to write: return (return[0]) in Indirect | -| file://:0:0:0:0 | [summary param] 0 in Join | file://:0:0:0:0 | [summary] to write: return (return[0]) in Join | -| file://:0:0:0:0 | [summary param] 0 in LimitReader | file://:0:0:0:0 | [summary] to write: return (return[0]) in LimitReader | -| file://:0:0:0:0 | [summary param] 0 in LoadOrStore | file://:0:0:0:0 | [summary] to write: argument -1 in LoadOrStore | -| file://:0:0:0:0 | [summary param] 0 in LoadOrStore | file://:0:0:0:0 | [summary] to write: return (return[0]) in LoadOrStore | -| file://:0:0:0:0 | [summary param] 0 in LoadPointer | file://:0:0:0:0 | [summary] to write: return (return[0]) in LoadPointer | -| file://:0:0:0:0 | [summary param] 0 in LoadUintptr | file://:0:0:0:0 | [summary] to write: return (return[0]) in LoadUintptr | -| file://:0:0:0:0 | [summary param] 0 in New | file://:0:0:0:0 | [summary] to write: return (return[0]) in New | -| file://:0:0:0:0 | [summary param] 0 in NewFile | file://:0:0:0:0 | [summary] to write: return (return[0]) in NewFile | -| file://:0:0:0:0 | [summary param] 0 in NewReader | file://:0:0:0:0 | [summary] to write: return (return[0]) in NewReader | -| file://:0:0:0:0 | [summary param] 0 in NewSectionReader | file://:0:0:0:0 | [summary] to write: return (return[0]) in NewSectionReader | -| file://:0:0:0:0 | [summary param] 0 in NopCloser | file://:0:0:0:0 | [summary] to write: return (return[0]) in NopCloser | -| file://:0:0:0:0 | [summary param] 0 in Parse | file://:0:0:0:0 | [summary] to write: return (return[0]) in Parse | -| file://:0:0:0:0 | [summary param] 0 in Parse | file://:0:0:0:0 | [summary] to write: return (return[0]) in Parse | -| file://:0:0:0:0 | [summary param] 0 in ParseQuery | file://:0:0:0:0 | [summary] to write: return (return[0]) in ParseQuery | -| file://:0:0:0:0 | [summary param] 0 in ParseRequestURI | file://:0:0:0:0 | [summary] to write: return (return[0]) in ParseRequestURI | -| file://:0:0:0:0 | [summary param] 0 in PathEscape | file://:0:0:0:0 | [summary] to write: return (return[0]) in PathEscape | -| file://:0:0:0:0 | [summary param] 0 in PathUnescape | file://:0:0:0:0 | [summary] to write: return (return[0]) in PathUnescape | -| file://:0:0:0:0 | [summary param] 0 in Put | file://:0:0:0:0 | [summary] to write: argument -1 in Put | -| file://:0:0:0:0 | [summary param] 0 in QueryEscape | file://:0:0:0:0 | [summary] to write: return (return[0]) in QueryEscape | -| file://:0:0:0:0 | [summary param] 0 in QueryUnescape | file://:0:0:0:0 | [summary] to write: return (return[0]) in QueryUnescape | -| file://:0:0:0:0 | [summary param] 0 in Quote | file://:0:0:0:0 | [summary] to write: return (return[0]) in Quote | -| file://:0:0:0:0 | [summary param] 0 in QuoteToASCII | file://:0:0:0:0 | [summary] to write: return (return[0]) in QuoteToASCII | -| file://:0:0:0:0 | [summary param] 0 in QuoteToGraphic | file://:0:0:0:0 | [summary] to write: return (return[0]) in QuoteToGraphic | -| file://:0:0:0:0 | [summary param] 0 in QuotedPrefix | file://:0:0:0:0 | [summary] to write: return (return[0]) in QuotedPrefix | -| file://:0:0:0:0 | [summary param] 0 in ReadAll | file://:0:0:0:0 | [summary] to write: return (return[0]) in ReadAll | -| file://:0:0:0:0 | [summary param] 0 in ReadAtLeast | file://:0:0:0:0 | [summary] to write: argument 1 in ReadAtLeast | -| file://:0:0:0:0 | [summary param] 0 in ReadDir | file://:0:0:0:0 | [summary] to write: return (return[0]) in ReadDir | -| file://:0:0:0:0 | [summary param] 0 in ReadFile | file://:0:0:0:0 | [summary] to write: return (return[0]) in ReadFile | -| file://:0:0:0:0 | [summary param] 0 in ReadFrom | file://:0:0:0:0 | [summary] to write: argument -1 in ReadFrom | -| file://:0:0:0:0 | [summary param] 0 in ReadFrom | file://:0:0:0:0 | [summary] to write: argument -1 in ReadFrom | -| file://:0:0:0:0 | [summary param] 0 in ReadFrom | file://:0:0:0:0 | [summary] to write: argument -1 in ReadFrom | -| file://:0:0:0:0 | [summary param] 0 in ReadFull | file://:0:0:0:0 | [summary] to write: argument 1 in ReadFull | -| file://:0:0:0:0 | [summary param] 0 in Repeat | file://:0:0:0:0 | [summary] to write: return (return[0]) in Repeat | -| file://:0:0:0:0 | [summary param] 0 in Replace | file://:0:0:0:0 | [summary] to write: return (return[0]) in Replace | -| file://:0:0:0:0 | [summary param] 0 in Replace | file://:0:0:0:0 | [summary] to write: return (return[0]) in Replace | -| file://:0:0:0:0 | [summary param] 0 in ReplaceAll | file://:0:0:0:0 | [summary] to write: return (return[0]) in ReplaceAll | -| file://:0:0:0:0 | [summary param] 0 in Reset | file://:0:0:0:0 | [summary] to write: argument -1 in Reset | -| file://:0:0:0:0 | [summary param] 0 in ResolveReference | file://:0:0:0:0 | [summary] to write: return (return[0]) in ResolveReference | -| file://:0:0:0:0 | [summary param] 0 in Reverse | file://:0:0:0:0 | [summary] to write: return (return[0]) in Reverse | -| file://:0:0:0:0 | [summary param] 0 in Send | file://:0:0:0:0 | [summary] to write: argument -1 in Send | -| file://:0:0:0:0 | [summary param] 0 in Set | file://:0:0:0:0 | [summary] to write: argument -1 in Set | -| file://:0:0:0:0 | [summary param] 0 in SetBytes | file://:0:0:0:0 | [summary] to write: argument -1 in SetBytes | -| file://:0:0:0:0 | [summary param] 0 in SetMapIndex | file://:0:0:0:0 | [summary] to write: argument -1 in SetMapIndex | -| file://:0:0:0:0 | [summary param] 0 in SetPointer | file://:0:0:0:0 | [summary] to write: argument -1 in SetPointer | -| file://:0:0:0:0 | [summary param] 0 in SetString | file://:0:0:0:0 | [summary] to write: argument -1 in SetString | -| file://:0:0:0:0 | [summary param] 0 in Split | file://:0:0:0:0 | [summary] to write: return (return[0]) in Split | -| file://:0:0:0:0 | [summary param] 0 in Split | file://:0:0:0:0 | [summary] to write: return (return[0]) in Split | -| file://:0:0:0:0 | [summary param] 0 in Split | file://:0:0:0:0 | [summary] to write: return (return[1]) in Split | -| file://:0:0:0:0 | [summary param] 0 in SplitAfter | file://:0:0:0:0 | [summary] to write: return (return[0]) in SplitAfter | -| file://:0:0:0:0 | [summary param] 0 in SplitAfterN | file://:0:0:0:0 | [summary] to write: return (return[0]) in SplitAfterN | -| file://:0:0:0:0 | [summary param] 0 in SplitN | file://:0:0:0:0 | [summary] to write: return (return[0]) in SplitN | -| file://:0:0:0:0 | [summary param] 0 in Store | file://:0:0:0:0 | [summary] to write: argument -1 in Store | -| file://:0:0:0:0 | [summary param] 0 in Store | file://:0:0:0:0 | [summary] to write: argument -1 in Store | -| file://:0:0:0:0 | [summary param] 0 in Store | file://:0:0:0:0 | [summary] to write: argument -1 in Store | -| file://:0:0:0:0 | [summary param] 0 in Store | file://:0:0:0:0 | [summary] to write: argument -1 in Store | -| file://:0:0:0:0 | [summary param] 0 in StringBytePtr | file://:0:0:0:0 | [summary] to write: return (return[0]) in StringBytePtr | -| file://:0:0:0:0 | [summary param] 0 in StringByteSlice | file://:0:0:0:0 | [summary] to write: return (return[0]) in StringByteSlice | -| file://:0:0:0:0 | [summary param] 0 in Sub | file://:0:0:0:0 | [summary] to write: return (return[0]) in Sub | -| file://:0:0:0:0 | [summary param] 0 in Swap | file://:0:0:0:0 | [summary] to write: argument -1 in Swap | -| file://:0:0:0:0 | [summary param] 0 in Swap | file://:0:0:0:0 | [summary] to write: argument -1 in Swap | -| file://:0:0:0:0 | [summary param] 0 in Swap | file://:0:0:0:0 | [summary] to write: argument -1 in Swap | -| file://:0:0:0:0 | [summary param] 0 in Swap | file://:0:0:0:0 | [summary] to write: argument -1 in Swap | -| file://:0:0:0:0 | [summary param] 0 in SwapPointer | file://:0:0:0:0 | [summary] to write: return (return[0]) in SwapPointer | -| file://:0:0:0:0 | [summary param] 0 in SwapUintptr | file://:0:0:0:0 | [summary] to write: return (return[0]) in SwapUintptr | -| file://:0:0:0:0 | [summary param] 0 in TeeReader | file://:0:0:0:0 | [summary] to write: argument 1 in TeeReader | -| file://:0:0:0:0 | [summary param] 0 in TeeReader | file://:0:0:0:0 | [summary] to write: return (return[0]) in TeeReader | -| file://:0:0:0:0 | [summary param] 0 in Title | file://:0:0:0:0 | [summary] to write: return (return[0]) in Title | -| file://:0:0:0:0 | [summary param] 0 in ToLower | file://:0:0:0:0 | [summary] to write: return (return[0]) in ToLower | -| file://:0:0:0:0 | [summary param] 0 in ToTitle | file://:0:0:0:0 | [summary] to write: return (return[0]) in ToTitle | -| file://:0:0:0:0 | [summary param] 0 in ToUpper | file://:0:0:0:0 | [summary] to write: return (return[0]) in ToUpper | -| file://:0:0:0:0 | [summary param] 0 in ToValidUTF8 | file://:0:0:0:0 | [summary] to write: return (return[0]) in ToValidUTF8 | -| file://:0:0:0:0 | [summary param] 0 in Trim | file://:0:0:0:0 | [summary] to write: return (return[0]) in Trim | -| file://:0:0:0:0 | [summary param] 0 in TrimFunc | file://:0:0:0:0 | [summary] to write: return (return[0]) in TrimFunc | -| file://:0:0:0:0 | [summary param] 0 in TrimLeft | file://:0:0:0:0 | [summary] to write: return (return[0]) in TrimLeft | -| file://:0:0:0:0 | [summary param] 0 in TrimLeftFunc | file://:0:0:0:0 | [summary] to write: return (return[0]) in TrimLeftFunc | -| file://:0:0:0:0 | [summary param] 0 in TrimPrefix | file://:0:0:0:0 | [summary] to write: return (return[0]) in TrimPrefix | -| file://:0:0:0:0 | [summary param] 0 in TrimRight | file://:0:0:0:0 | [summary] to write: return (return[0]) in TrimRight | -| file://:0:0:0:0 | [summary param] 0 in TrimRightFunc | file://:0:0:0:0 | [summary] to write: return (return[0]) in TrimRightFunc | -| file://:0:0:0:0 | [summary param] 0 in TrimSpace | file://:0:0:0:0 | [summary] to write: return (return[0]) in TrimSpace | -| file://:0:0:0:0 | [summary param] 0 in TrimSuffix | file://:0:0:0:0 | [summary] to write: return (return[0]) in TrimSuffix | -| file://:0:0:0:0 | [summary param] 0 in TrySend | file://:0:0:0:0 | [summary] to write: argument -1 in TrySend | -| file://:0:0:0:0 | [summary param] 0 in Unquote | file://:0:0:0:0 | [summary] to write: return (return[0]) in Unquote | -| file://:0:0:0:0 | [summary param] 0 in UnquoteChar | file://:0:0:0:0 | [summary] to write: return (return[2]) in UnquoteChar | -| file://:0:0:0:0 | [summary param] 0 in Unwrap | file://:0:0:0:0 | [summary] to write: return (return[0]) in Unwrap | -| file://:0:0:0:0 | [summary param] 0 in User | file://:0:0:0:0 | [summary] to write: return (return[0]) in User | -| file://:0:0:0:0 | [summary param] 0 in UserPassword | file://:0:0:0:0 | [summary] to write: return (return[0]) in UserPassword | -| file://:0:0:0:0 | [summary param] 0 in ValueOf | file://:0:0:0:0 | [summary] to write: return (return[0]) in ValueOf | -| file://:0:0:0:0 | [summary param] 0 in Write | file://:0:0:0:0 | [summary] to write: argument -1 in Write | -| file://:0:0:0:0 | [summary param] 0 in Write | file://:0:0:0:0 | [summary] to write: argument -1 in Write | -| file://:0:0:0:0 | [summary param] 0 in Write | file://:0:0:0:0 | [summary] to write: argument -1 in Write | -| file://:0:0:0:0 | [summary param] 0 in Write | file://:0:0:0:0 | [summary] to write: argument -1 in Write | -| file://:0:0:0:0 | [summary param] 0 in Write | file://:0:0:0:0 | [summary] to write: argument -1 in Write | -| file://:0:0:0:0 | [summary param] 0 in Write | file://:0:0:0:0 | [summary] to write: argument -1 in Write | -| file://:0:0:0:0 | [summary param] 0 in Write | file://:0:0:0:0 | [summary] to write: argument -1 in Write | -| file://:0:0:0:0 | [summary param] 0 in Write | file://:0:0:0:0 | [summary] to write: argument -1 in Write | -| file://:0:0:0:0 | [summary param] 0 in Write | file://:0:0:0:0 | [summary] to write: argument -1 in Write | -| file://:0:0:0:0 | [summary param] 0 in Write | file://:0:0:0:0 | [summary] to write: argument -1 in Write | -| file://:0:0:0:0 | [summary param] 0 in Write | file://:0:0:0:0 | [summary] to write: argument -1 in Write | -| file://:0:0:0:0 | [summary param] 0 in Write | file://:0:0:0:0 | [summary] to write: argument -1 in Write | -| file://:0:0:0:0 | [summary param] 0 in Write | file://:0:0:0:0 | [summary] to write: argument -1 in Write | -| file://:0:0:0:0 | [summary param] 0 in WriteAt | file://:0:0:0:0 | [summary] to write: argument -1 in WriteAt | -| file://:0:0:0:0 | [summary param] 0 in WriteAt | file://:0:0:0:0 | [summary] to write: argument -1 in WriteAt | -| file://:0:0:0:0 | [summary param] 0 in WriteAt | file://:0:0:0:0 | [summary] to write: argument -1 in WriteAt | -| file://:0:0:0:0 | [summary param] 0 in WriteString | file://:0:0:0:0 | [summary] to write: argument -1 in WriteString | -| file://:0:0:0:0 | [summary param] 0 in WriteString | file://:0:0:0:0 | [summary] to write: argument -1 in WriteString | -| file://:0:0:0:0 | [summary param] 0 in WriteString | file://:0:0:0:0 | [summary] to write: argument -1 in WriteString | -| file://:0:0:0:0 | [summary param] 0 in WriteString | file://:0:0:0:0 | [summary] to write: argument -1 in WriteString | -| file://:0:0:0:0 | [summary param] 0 in WriteString | file://:0:0:0:0 | [summary] to write: argument -1 in WriteString | -| file://:0:0:0:0 | [summary param] 0 in WriteString | file://:0:0:0:0 | [summary] to write: argument -1 in WriteString | -| file://:0:0:0:0 | [summary param] 0 in WriteString | file://:0:0:0:0 | [summary] to write: argument -1 in WriteString | -| file://:0:0:0:0 | [summary param] 0 in WriteString | file://:0:0:0:0 | [summary] to write: argument -1 in WriteString | -| file://:0:0:0:0 | [summary param] 1 in AddUintptr | file://:0:0:0:0 | [summary] to write: argument 0 in AddUintptr | -| file://:0:0:0:0 | [summary param] 1 in AddUintptr | file://:0:0:0:0 | [summary] to write: return (return[0]) in AddUintptr | -| file://:0:0:0:0 | [summary param] 1 in AppendQuote | file://:0:0:0:0 | [summary] to write: return (return[0]) in AppendQuote | -| file://:0:0:0:0 | [summary param] 1 in AppendQuoteToASCII | file://:0:0:0:0 | [summary] to write: return (return[0]) in AppendQuoteToASCII | -| file://:0:0:0:0 | [summary param] 1 in AppendQuoteToGraphic | file://:0:0:0:0 | [summary] to write: return (return[0]) in AppendQuoteToGraphic | -| file://:0:0:0:0 | [summary param] 1 in AppendSlice | file://:0:0:0:0 | [summary] to write: return (return[0]) in AppendSlice | -| file://:0:0:0:0 | [summary param] 1 in Copy | file://:0:0:0:0 | [summary] to write: argument 0 in Copy | -| file://:0:0:0:0 | [summary param] 1 in Copy | file://:0:0:0:0 | [summary] to write: argument 0 in Copy | -| file://:0:0:0:0 | [summary param] 1 in CopyBuffer | file://:0:0:0:0 | [summary] to write: argument 0 in CopyBuffer | -| file://:0:0:0:0 | [summary param] 1 in CopyN | file://:0:0:0:0 | [summary] to write: argument 0 in CopyN | -| file://:0:0:0:0 | [summary param] 1 in Join | file://:0:0:0:0 | [summary] to write: return (return[0]) in Join | -| file://:0:0:0:0 | [summary param] 1 in LoadOrStore | file://:0:0:0:0 | [summary] to write: argument -1 in LoadOrStore | -| file://:0:0:0:0 | [summary param] 1 in LoadOrStore | file://:0:0:0:0 | [summary] to write: return (return[0]) in LoadOrStore | -| file://:0:0:0:0 | [summary param] 1 in Map | file://:0:0:0:0 | [summary] to write: return (return[0]) in Map | -| file://:0:0:0:0 | [summary param] 1 in SetMapIndex | file://:0:0:0:0 | [summary] to write: argument -1 in SetMapIndex | -| file://:0:0:0:0 | [summary param] 1 in Store | file://:0:0:0:0 | [summary] to write: argument -1 in Store | -| file://:0:0:0:0 | [summary param] 1 in StorePointer | file://:0:0:0:0 | [summary] to write: argument 0 in StorePointer | -| file://:0:0:0:0 | [summary param] 1 in StoreUintptr | file://:0:0:0:0 | [summary] to write: argument 0 in StoreUintptr | -| file://:0:0:0:0 | [summary param] 1 in Swap | file://:0:0:0:0 | [summary] to write: argument -1 in Swap | -| file://:0:0:0:0 | [summary param] 1 in SwapPointer | file://:0:0:0:0 | [summary] to write: argument 0 in SwapPointer | -| file://:0:0:0:0 | [summary param] 1 in SwapUintptr | file://:0:0:0:0 | [summary] to write: argument 0 in SwapUintptr | -| file://:0:0:0:0 | [summary param] 1 in ToLowerSpecial | file://:0:0:0:0 | [summary] to write: return (return[0]) in ToLowerSpecial | -| file://:0:0:0:0 | [summary param] 1 in ToTitleSpecial | file://:0:0:0:0 | [summary] to write: return (return[0]) in ToTitleSpecial | -| file://:0:0:0:0 | [summary param] 1 in ToUpperSpecial | file://:0:0:0:0 | [summary] to write: return (return[0]) in ToUpperSpecial | -| file://:0:0:0:0 | [summary param] 1 in ToValidUTF8 | file://:0:0:0:0 | [summary] to write: return (return[0]) in ToValidUTF8 | -| file://:0:0:0:0 | [summary param] 1 in UserPassword | file://:0:0:0:0 | [summary] to write: return (return[0]) in UserPassword | -| file://:0:0:0:0 | [summary param] 1 in WriteString | file://:0:0:0:0 | [summary] to write: argument 0 in WriteString | -| file://:0:0:0:0 | [summary param] 1 in WriteString | file://:0:0:0:0 | [summary] to write: argument 0 in WriteString | -| file://:0:0:0:0 | [summary param] 2 in CompareAndSwap | file://:0:0:0:0 | [summary] to write: argument -1 in CompareAndSwap | -| file://:0:0:0:0 | [summary param] 2 in CompareAndSwapPointer | file://:0:0:0:0 | [summary] to write: argument 0 in CompareAndSwapPointer | -| file://:0:0:0:0 | [summary param] 2 in CompareAndSwapUintptr | file://:0:0:0:0 | [summary] to write: argument 0 in CompareAndSwapUintptr | -| file://:0:0:0:0 | [summary param] 2 in Replace | file://:0:0:0:0 | [summary] to write: return (return[0]) in Replace | -| file://:0:0:0:0 | [summary param] 2 in ReplaceAll | file://:0:0:0:0 | [summary] to write: return (return[0]) in ReplaceAll | -| file://:0:0:0:0 | [summary param] -1 in Addr | file://:0:0:0:0 | [summary] to write: return (return[0]) in Addr | -| file://:0:0:0:0 | [summary param] -1 in Bytes | file://:0:0:0:0 | [summary] to write: return (return[0]) in Bytes | -| file://:0:0:0:0 | [summary param] -1 in Convert | file://:0:0:0:0 | [summary] to write: return (return[0]) in Convert | -| file://:0:0:0:0 | [summary param] -1 in Elem | file://:0:0:0:0 | [summary] to write: return (return[0]) in Elem | -| file://:0:0:0:0 | [summary param] -1 in Encode | file://:0:0:0:0 | [summary] to write: return (return[0]) in Encode | -| file://:0:0:0:0 | [summary param] -1 in EscapedPath | file://:0:0:0:0 | [summary] to write: return (return[0]) in EscapedPath | -| file://:0:0:0:0 | [summary param] -1 in Fd | file://:0:0:0:0 | [summary] to write: return (return[0]) in Fd | -| file://:0:0:0:0 | [summary param] -1 in Field | file://:0:0:0:0 | [summary] to write: return (return[0]) in Field | -| file://:0:0:0:0 | [summary param] -1 in FieldByIndex | file://:0:0:0:0 | [summary] to write: return (return[0]) in FieldByIndex | -| file://:0:0:0:0 | [summary param] -1 in FieldByName | file://:0:0:0:0 | [summary] to write: return (return[0]) in FieldByName | -| file://:0:0:0:0 | [summary param] -1 in FieldByNameFunc | file://:0:0:0:0 | [summary] to write: return (return[0]) in FieldByNameFunc | -| file://:0:0:0:0 | [summary param] -1 in Get | file://:0:0:0:0 | [summary] to write: return (return[0]) in Get | -| file://:0:0:0:0 | [summary param] -1 in Get | file://:0:0:0:0 | [summary] to write: return (return[0]) in Get | -| file://:0:0:0:0 | [summary param] -1 in Get | file://:0:0:0:0 | [summary] to write: return (return[0]) in Get | -| file://:0:0:0:0 | [summary param] -1 in Glob | file://:0:0:0:0 | [summary] to write: return (return[0]) in Glob | -| file://:0:0:0:0 | [summary param] -1 in Glob | file://:0:0:0:0 | [summary] to write: return (return[0]) in Glob | -| file://:0:0:0:0 | [summary param] -1 in GoString | file://:0:0:0:0 | [summary] to write: return (return[0]) in GoString | -| file://:0:0:0:0 | [summary param] -1 in GoString | file://:0:0:0:0 | [summary] to write: return (return[0]) in GoString | -| file://:0:0:0:0 | [summary param] -1 in Hostname | file://:0:0:0:0 | [summary] to write: return (return[0]) in Hostname | -| file://:0:0:0:0 | [summary param] -1 in Index | file://:0:0:0:0 | [summary] to write: return (return[0]) in Index | -| file://:0:0:0:0 | [summary param] -1 in Info | file://:0:0:0:0 | [summary] to write: return (return[0]) in Info | -| file://:0:0:0:0 | [summary param] -1 in Info | file://:0:0:0:0 | [summary] to write: return (return[0]) in Info | -| file://:0:0:0:0 | [summary param] -1 in Info | file://:0:0:0:0 | [summary] to write: return (return[0]) in Info | -| file://:0:0:0:0 | [summary param] -1 in Interface | file://:0:0:0:0 | [summary] to write: return (return[0]) in Interface | -| file://:0:0:0:0 | [summary param] -1 in InterfaceData | file://:0:0:0:0 | [summary] to write: return (return[0]) in InterfaceData | -| file://:0:0:0:0 | [summary param] -1 in Key | file://:0:0:0:0 | [summary] to write: return (return[0]) in Key | -| file://:0:0:0:0 | [summary param] -1 in Load | file://:0:0:0:0 | [summary] to write: return (return[0]) in Load | -| file://:0:0:0:0 | [summary param] -1 in Load | file://:0:0:0:0 | [summary] to write: return (return[0]) in Load | -| file://:0:0:0:0 | [summary param] -1 in Load | file://:0:0:0:0 | [summary] to write: return (return[0]) in Load | -| file://:0:0:0:0 | [summary param] -1 in Load | file://:0:0:0:0 | [summary] to write: return (return[0]) in Load | -| file://:0:0:0:0 | [summary param] -1 in LoadOrStore | file://:0:0:0:0 | [summary] to write: return (return[0]) in LoadOrStore | -| file://:0:0:0:0 | [summary param] -1 in Lookup | file://:0:0:0:0 | [summary] to write: return (return[0]) in Lookup | -| file://:0:0:0:0 | [summary param] -1 in MapIndex | file://:0:0:0:0 | [summary] to write: return (return[0]) in MapIndex | -| file://:0:0:0:0 | [summary param] -1 in MapKeys | file://:0:0:0:0 | [summary] to write: return (return[0]) in MapKeys | -| file://:0:0:0:0 | [summary param] -1 in MapRange | file://:0:0:0:0 | [summary] to write: return (return[0]) in MapRange | -| file://:0:0:0:0 | [summary param] -1 in MarshalBinary | file://:0:0:0:0 | [summary] to write: return (return[0]) in MarshalBinary | -| file://:0:0:0:0 | [summary param] -1 in Method | file://:0:0:0:0 | [summary] to write: return (return[0]) in Method | -| file://:0:0:0:0 | [summary param] -1 in MethodByName | file://:0:0:0:0 | [summary] to write: return (return[0]) in MethodByName | -| file://:0:0:0:0 | [summary param] -1 in Name | file://:0:0:0:0 | [summary] to write: return (return[0]) in Name | -| file://:0:0:0:0 | [summary param] -1 in Name | file://:0:0:0:0 | [summary] to write: return (return[0]) in Name | -| file://:0:0:0:0 | [summary param] -1 in Name | file://:0:0:0:0 | [summary] to write: return (return[0]) in Name | -| file://:0:0:0:0 | [summary param] -1 in Open | file://:0:0:0:0 | [summary] to write: return (return[0]) in Open | -| file://:0:0:0:0 | [summary param] -1 in Open | file://:0:0:0:0 | [summary] to write: return (return[0]) in Open | -| file://:0:0:0:0 | [summary param] -1 in Open | file://:0:0:0:0 | [summary] to write: return (return[0]) in Open | -| file://:0:0:0:0 | [summary param] -1 in Parse | file://:0:0:0:0 | [summary] to write: return (return[0]) in Parse | -| file://:0:0:0:0 | [summary param] -1 in Password | file://:0:0:0:0 | [summary] to write: return (return[0]) in Password | -| file://:0:0:0:0 | [summary param] -1 in Pointer | file://:0:0:0:0 | [summary] to write: return (return[0]) in Pointer | -| file://:0:0:0:0 | [summary param] -1 in Port | file://:0:0:0:0 | [summary] to write: return (return[0]) in Port | -| file://:0:0:0:0 | [summary param] -1 in Query | file://:0:0:0:0 | [summary] to write: return (return[0]) in Query | -| file://:0:0:0:0 | [summary param] -1 in Read | file://:0:0:0:0 | [summary] to write: argument 0 in Read | -| file://:0:0:0:0 | [summary param] -1 in Read | file://:0:0:0:0 | [summary] to write: argument 0 in Read | -| file://:0:0:0:0 | [summary param] -1 in Read | file://:0:0:0:0 | [summary] to write: argument 0 in Read | -| file://:0:0:0:0 | [summary param] -1 in Read | file://:0:0:0:0 | [summary] to write: argument 0 in Read | -| file://:0:0:0:0 | [summary param] -1 in Read | file://:0:0:0:0 | [summary] to write: argument 0 in Read | -| file://:0:0:0:0 | [summary param] -1 in Read | file://:0:0:0:0 | [summary] to write: argument 0 in Read | -| file://:0:0:0:0 | [summary param] -1 in Read | file://:0:0:0:0 | [summary] to write: argument 0 in Read | -| file://:0:0:0:0 | [summary param] -1 in Read | file://:0:0:0:0 | [summary] to write: argument 0 in Read | -| file://:0:0:0:0 | [summary param] -1 in Read | file://:0:0:0:0 | [summary] to write: argument 0 in Read | -| file://:0:0:0:0 | [summary param] -1 in Read | file://:0:0:0:0 | [summary] to write: argument 0 in Read | -| file://:0:0:0:0 | [summary param] -1 in Read | file://:0:0:0:0 | [summary] to write: argument 0 in Read | -| file://:0:0:0:0 | [summary param] -1 in Read | file://:0:0:0:0 | [summary] to write: argument 0 in Read | -| file://:0:0:0:0 | [summary param] -1 in Read | file://:0:0:0:0 | [summary] to write: argument 0 in Read | -| file://:0:0:0:0 | [summary param] -1 in Read | file://:0:0:0:0 | [summary] to write: argument 0 in Read | -| file://:0:0:0:0 | [summary param] -1 in Read | file://:0:0:0:0 | [summary] to write: argument 0 in Read | -| file://:0:0:0:0 | [summary param] -1 in Read | file://:0:0:0:0 | [summary] to write: argument 0 in Read | -| file://:0:0:0:0 | [summary param] -1 in ReadAt | file://:0:0:0:0 | [summary] to write: argument 0 in ReadAt | -| file://:0:0:0:0 | [summary param] -1 in ReadAt | file://:0:0:0:0 | [summary] to write: argument 0 in ReadAt | -| file://:0:0:0:0 | [summary param] -1 in ReadAt | file://:0:0:0:0 | [summary] to write: argument 0 in ReadAt | -| file://:0:0:0:0 | [summary param] -1 in ReadAt | file://:0:0:0:0 | [summary] to write: argument 0 in ReadAt | -| file://:0:0:0:0 | [summary param] -1 in ReadDir | file://:0:0:0:0 | [summary] to write: return (return[0]) in ReadDir | -| file://:0:0:0:0 | [summary param] -1 in ReadDir | file://:0:0:0:0 | [summary] to write: return (return[0]) in ReadDir | -| file://:0:0:0:0 | [summary param] -1 in ReadFile | file://:0:0:0:0 | [summary] to write: return (return[0]) in ReadFile | -| file://:0:0:0:0 | [summary param] -1 in ReadFile | file://:0:0:0:0 | [summary] to write: return (return[0]) in ReadFile | -| file://:0:0:0:0 | [summary param] -1 in Recv | file://:0:0:0:0 | [summary] to write: return (return[0]) in Recv | -| file://:0:0:0:0 | [summary param] -1 in RequestURI | file://:0:0:0:0 | [summary] to write: return (return[0]) in RequestURI | -| file://:0:0:0:0 | [summary param] -1 in ResolveReference | file://:0:0:0:0 | [summary] to write: return (return[0]) in ResolveReference | -| file://:0:0:0:0 | [summary param] -1 in Slice | file://:0:0:0:0 | [summary] to write: return (return[0]) in Slice | -| file://:0:0:0:0 | [summary param] -1 in Slice3 | file://:0:0:0:0 | [summary] to write: return (return[0]) in Slice3 | -| file://:0:0:0:0 | [summary param] -1 in String | file://:0:0:0:0 | [summary] to write: return (return[0]) in String | -| file://:0:0:0:0 | [summary param] -1 in String | file://:0:0:0:0 | [summary] to write: return (return[0]) in String | -| file://:0:0:0:0 | [summary param] -1 in String | file://:0:0:0:0 | [summary] to write: return (return[0]) in String | -| file://:0:0:0:0 | [summary param] -1 in String | file://:0:0:0:0 | [summary] to write: return (return[0]) in String | -| file://:0:0:0:0 | [summary param] -1 in String | file://:0:0:0:0 | [summary] to write: return (return[0]) in String | -| file://:0:0:0:0 | [summary param] -1 in String | file://:0:0:0:0 | [summary] to write: return (return[0]) in String | -| file://:0:0:0:0 | [summary param] -1 in String | file://:0:0:0:0 | [summary] to write: return (return[0]) in String | -| file://:0:0:0:0 | [summary param] -1 in String | file://:0:0:0:0 | [summary] to write: return (return[0]) in String | -| file://:0:0:0:0 | [summary param] -1 in String | file://:0:0:0:0 | [summary] to write: return (return[0]) in String | -| file://:0:0:0:0 | [summary param] -1 in String | file://:0:0:0:0 | [summary] to write: return (return[0]) in String | -| file://:0:0:0:0 | [summary param] -1 in String | file://:0:0:0:0 | [summary] to write: return (return[0]) in String | -| file://:0:0:0:0 | [summary param] -1 in String | file://:0:0:0:0 | [summary] to write: return (return[0]) in String | -| file://:0:0:0:0 | [summary param] -1 in String | file://:0:0:0:0 | [summary] to write: return (return[0]) in String | -| file://:0:0:0:0 | [summary param] -1 in String | file://:0:0:0:0 | [summary] to write: return (return[0]) in String | -| file://:0:0:0:0 | [summary param] -1 in String | file://:0:0:0:0 | [summary] to write: return (return[0]) in String | -| file://:0:0:0:0 | [summary param] -1 in String | file://:0:0:0:0 | [summary] to write: return (return[0]) in String | -| file://:0:0:0:0 | [summary param] -1 in String | file://:0:0:0:0 | [summary] to write: return (return[0]) in String | -| file://:0:0:0:0 | [summary param] -1 in String | file://:0:0:0:0 | [summary] to write: return (return[0]) in String | -| file://:0:0:0:0 | [summary param] -1 in String | file://:0:0:0:0 | [summary] to write: return (return[0]) in String | -| file://:0:0:0:0 | [summary param] -1 in String | file://:0:0:0:0 | [summary] to write: return (return[0]) in String | -| file://:0:0:0:0 | [summary param] -1 in String | file://:0:0:0:0 | [summary] to write: return (return[0]) in String | -| file://:0:0:0:0 | [summary param] -1 in String | file://:0:0:0:0 | [summary] to write: return (return[0]) in String | -| file://:0:0:0:0 | [summary param] -1 in String | file://:0:0:0:0 | [summary] to write: return (return[0]) in String | -| file://:0:0:0:0 | [summary param] -1 in String | file://:0:0:0:0 | [summary] to write: return (return[0]) in String | -| file://:0:0:0:0 | [summary param] -1 in String | file://:0:0:0:0 | [summary] to write: return (return[0]) in String | -| file://:0:0:0:0 | [summary param] -1 in Sub | file://:0:0:0:0 | [summary] to write: return (return[0]) in Sub | -| file://:0:0:0:0 | [summary param] -1 in Sub | file://:0:0:0:0 | [summary] to write: return (return[0]) in Sub | -| file://:0:0:0:0 | [summary param] -1 in Swap | file://:0:0:0:0 | [summary] to write: return (return[0]) in Swap | -| file://:0:0:0:0 | [summary param] -1 in Swap | file://:0:0:0:0 | [summary] to write: return (return[0]) in Swap | -| file://:0:0:0:0 | [summary param] -1 in Swap | file://:0:0:0:0 | [summary] to write: return (return[0]) in Swap | -| file://:0:0:0:0 | [summary param] -1 in Swap | file://:0:0:0:0 | [summary] to write: return (return[0]) in Swap | -| file://:0:0:0:0 | [summary param] -1 in SyscallConn | file://:0:0:0:0 | [summary] to write: return (return[0]) in SyscallConn | -| file://:0:0:0:0 | [summary param] -1 in SyscallConn | file://:0:0:0:0 | [summary] to write: return (return[0]) in SyscallConn | -| file://:0:0:0:0 | [summary param] -1 in Token | file://:0:0:0:0 | [summary] to write: return (return[0]) in Token | -| file://:0:0:0:0 | [summary param] -1 in Token | file://:0:0:0:0 | [summary] to write: return (return[0]) in Token | -| file://:0:0:0:0 | [summary param] -1 in TryRecv | file://:0:0:0:0 | [summary] to write: return (return[0]) in TryRecv | -| file://:0:0:0:0 | [summary param] -1 in UnsafeAddr | file://:0:0:0:0 | [summary] to write: return (return[0]) in UnsafeAddr | -| file://:0:0:0:0 | [summary param] -1 in Username | file://:0:0:0:0 | [summary] to write: return (return[0]) in Username | -| file://:0:0:0:0 | [summary param] -1 in Value | file://:0:0:0:0 | [summary] to write: return (return[0]) in Value | -| file://:0:0:0:0 | [summary param] -1 in WriteTo | file://:0:0:0:0 | [summary] to write: argument 0 in WriteTo | -| file://:0:0:0:0 | [summary param] -1 in WriteTo | file://:0:0:0:0 | [summary] to write: argument 0 in WriteTo | -| file://:0:0:0:0 | [summary param] -1 in WriteTo | file://:0:0:0:0 | [summary] to write: argument 0 in WriteTo | -| file://:0:0:0:0 | [summary param] -1 in WriteTo | file://:0:0:0:0 | [summary] to write: argument 0 in WriteTo | +| file://:0:0:0:0 | [summary param] 0 in AppendQuote | file://:0:0:0:0 | [summary] to write: ReturnValue in AppendQuote | +| file://:0:0:0:0 | [summary param] 0 in AppendQuoteToASCII | file://:0:0:0:0 | [summary] to write: ReturnValue in AppendQuoteToASCII | +| file://:0:0:0:0 | [summary param] 0 in AppendQuoteToGraphic | file://:0:0:0:0 | [summary] to write: ReturnValue in AppendQuoteToGraphic | +| file://:0:0:0:0 | [summary param] 0 in AppendSlice | file://:0:0:0:0 | [summary] to write: ReturnValue in AppendSlice | +| file://:0:0:0:0 | [summary param] 0 in As | file://:0:0:0:0 | [summary] to write: Argument[1] in As | +| file://:0:0:0:0 | [summary param] 0 in Base | file://:0:0:0:0 | [summary] to write: ReturnValue in Base | +| file://:0:0:0:0 | [summary param] 0 in BytePtrFromString | file://:0:0:0:0 | [summary] to write: ReturnValue in BytePtrFromString | +| file://:0:0:0:0 | [summary param] 0 in ByteSliceFromString | file://:0:0:0:0 | [summary] to write: ReturnValue in ByteSliceFromString | +| file://:0:0:0:0 | [summary param] 0 in Clean | file://:0:0:0:0 | [summary] to write: ReturnValue in Clean | +| file://:0:0:0:0 | [summary param] 0 in Dir | file://:0:0:0:0 | [summary] to write: ReturnValue in Dir | +| file://:0:0:0:0 | [summary param] 0 in Expand | file://:0:0:0:0 | [summary] to write: ReturnValue in Expand | +| file://:0:0:0:0 | [summary param] 0 in ExpandEnv | file://:0:0:0:0 | [summary] to write: ReturnValue in ExpandEnv | +| file://:0:0:0:0 | [summary param] 0 in Ext | file://:0:0:0:0 | [summary] to write: ReturnValue in Ext | +| file://:0:0:0:0 | [summary param] 0 in Fields | file://:0:0:0:0 | [summary] to write: ReturnValue in Fields | +| file://:0:0:0:0 | [summary param] 0 in FieldsFunc | file://:0:0:0:0 | [summary] to write: ReturnValue in FieldsFunc | +| file://:0:0:0:0 | [summary param] 0 in FileInfoToDirEntry | file://:0:0:0:0 | [summary] to write: ReturnValue in FileInfoToDirEntry | +| file://:0:0:0:0 | [summary param] 0 in Glob | file://:0:0:0:0 | [summary] to write: ReturnValue in Glob | +| file://:0:0:0:0 | [summary param] 0 in Indirect | file://:0:0:0:0 | [summary] to write: ReturnValue in Indirect | +| file://:0:0:0:0 | [summary param] 0 in Join | file://:0:0:0:0 | [summary] to write: ReturnValue in Join | +| file://:0:0:0:0 | [summary param] 0 in LimitReader | file://:0:0:0:0 | [summary] to write: ReturnValue in LimitReader | +| file://:0:0:0:0 | [summary param] 0 in LoadOrStore | file://:0:0:0:0 | [summary] to write: Argument[-1] in LoadOrStore | +| file://:0:0:0:0 | [summary param] 0 in LoadOrStore | file://:0:0:0:0 | [summary] to write: ReturnValue in LoadOrStore | +| file://:0:0:0:0 | [summary param] 0 in LoadPointer | file://:0:0:0:0 | [summary] to write: ReturnValue in LoadPointer | +| file://:0:0:0:0 | [summary param] 0 in LoadUintptr | file://:0:0:0:0 | [summary] to write: ReturnValue in LoadUintptr | +| file://:0:0:0:0 | [summary param] 0 in New | file://:0:0:0:0 | [summary] to write: ReturnValue in New | +| file://:0:0:0:0 | [summary param] 0 in NewFile | file://:0:0:0:0 | [summary] to write: ReturnValue in NewFile | +| file://:0:0:0:0 | [summary param] 0 in NewReader | file://:0:0:0:0 | [summary] to write: ReturnValue in NewReader | +| file://:0:0:0:0 | [summary param] 0 in NewSectionReader | file://:0:0:0:0 | [summary] to write: ReturnValue in NewSectionReader | +| file://:0:0:0:0 | [summary param] 0 in NopCloser | file://:0:0:0:0 | [summary] to write: ReturnValue in NopCloser | +| file://:0:0:0:0 | [summary param] 0 in Parse | file://:0:0:0:0 | [summary] to write: ReturnValue in Parse | +| file://:0:0:0:0 | [summary param] 0 in Parse | file://:0:0:0:0 | [summary] to write: ReturnValue in Parse | +| file://:0:0:0:0 | [summary param] 0 in ParseQuery | file://:0:0:0:0 | [summary] to write: ReturnValue in ParseQuery | +| file://:0:0:0:0 | [summary param] 0 in ParseRequestURI | file://:0:0:0:0 | [summary] to write: ReturnValue in ParseRequestURI | +| file://:0:0:0:0 | [summary param] 0 in PathEscape | file://:0:0:0:0 | [summary] to write: ReturnValue in PathEscape | +| file://:0:0:0:0 | [summary param] 0 in PathUnescape | file://:0:0:0:0 | [summary] to write: ReturnValue in PathUnescape | +| file://:0:0:0:0 | [summary param] 0 in Put | file://:0:0:0:0 | [summary] to write: Argument[-1] in Put | +| file://:0:0:0:0 | [summary param] 0 in QueryEscape | file://:0:0:0:0 | [summary] to write: ReturnValue in QueryEscape | +| file://:0:0:0:0 | [summary param] 0 in QueryUnescape | file://:0:0:0:0 | [summary] to write: ReturnValue in QueryUnescape | +| file://:0:0:0:0 | [summary param] 0 in Quote | file://:0:0:0:0 | [summary] to write: ReturnValue in Quote | +| file://:0:0:0:0 | [summary param] 0 in QuoteToASCII | file://:0:0:0:0 | [summary] to write: ReturnValue in QuoteToASCII | +| file://:0:0:0:0 | [summary param] 0 in QuoteToGraphic | file://:0:0:0:0 | [summary] to write: ReturnValue in QuoteToGraphic | +| file://:0:0:0:0 | [summary param] 0 in QuotedPrefix | file://:0:0:0:0 | [summary] to write: ReturnValue in QuotedPrefix | +| file://:0:0:0:0 | [summary param] 0 in ReadAll | file://:0:0:0:0 | [summary] to write: ReturnValue in ReadAll | +| file://:0:0:0:0 | [summary param] 0 in ReadAtLeast | file://:0:0:0:0 | [summary] to write: Argument[1] in ReadAtLeast | +| file://:0:0:0:0 | [summary param] 0 in ReadDir | file://:0:0:0:0 | [summary] to write: ReturnValue in ReadDir | +| file://:0:0:0:0 | [summary param] 0 in ReadFile | file://:0:0:0:0 | [summary] to write: ReturnValue in ReadFile | +| file://:0:0:0:0 | [summary param] 0 in ReadFrom | file://:0:0:0:0 | [summary] to write: Argument[-1] in ReadFrom | +| file://:0:0:0:0 | [summary param] 0 in ReadFrom | file://:0:0:0:0 | [summary] to write: Argument[-1] in ReadFrom | +| file://:0:0:0:0 | [summary param] 0 in ReadFrom | file://:0:0:0:0 | [summary] to write: Argument[-1] in ReadFrom | +| file://:0:0:0:0 | [summary param] 0 in ReadFull | file://:0:0:0:0 | [summary] to write: Argument[1] in ReadFull | +| file://:0:0:0:0 | [summary param] 0 in Repeat | file://:0:0:0:0 | [summary] to write: ReturnValue in Repeat | +| file://:0:0:0:0 | [summary param] 0 in Replace | file://:0:0:0:0 | [summary] to write: ReturnValue in Replace | +| file://:0:0:0:0 | [summary param] 0 in Replace | file://:0:0:0:0 | [summary] to write: ReturnValue in Replace | +| file://:0:0:0:0 | [summary param] 0 in ReplaceAll | file://:0:0:0:0 | [summary] to write: ReturnValue in ReplaceAll | +| file://:0:0:0:0 | [summary param] 0 in Reset | file://:0:0:0:0 | [summary] to write: Argument[-1] in Reset | +| file://:0:0:0:0 | [summary param] 0 in ResolveReference | file://:0:0:0:0 | [summary] to write: ReturnValue in ResolveReference | +| file://:0:0:0:0 | [summary param] 0 in Reverse | file://:0:0:0:0 | [summary] to write: ReturnValue in Reverse | +| file://:0:0:0:0 | [summary param] 0 in Send | file://:0:0:0:0 | [summary] to write: Argument[-1] in Send | +| file://:0:0:0:0 | [summary param] 0 in Set | file://:0:0:0:0 | [summary] to write: Argument[-1] in Set | +| file://:0:0:0:0 | [summary param] 0 in SetBytes | file://:0:0:0:0 | [summary] to write: Argument[-1] in SetBytes | +| file://:0:0:0:0 | [summary param] 0 in SetMapIndex | file://:0:0:0:0 | [summary] to write: Argument[-1] in SetMapIndex | +| file://:0:0:0:0 | [summary param] 0 in SetPointer | file://:0:0:0:0 | [summary] to write: Argument[-1] in SetPointer | +| file://:0:0:0:0 | [summary param] 0 in SetString | file://:0:0:0:0 | [summary] to write: Argument[-1] in SetString | +| file://:0:0:0:0 | [summary param] 0 in Split | file://:0:0:0:0 | [summary] to write: ReturnValue in Split | +| file://:0:0:0:0 | [summary param] 0 in Split | file://:0:0:0:0 | [summary] to write: ReturnValue in Split | +| file://:0:0:0:0 | [summary param] 0 in Split | file://:0:0:0:0 | [summary] to write: ReturnValue[1] in Split | +| file://:0:0:0:0 | [summary param] 0 in SplitAfter | file://:0:0:0:0 | [summary] to write: ReturnValue in SplitAfter | +| file://:0:0:0:0 | [summary param] 0 in SplitAfterN | file://:0:0:0:0 | [summary] to write: ReturnValue in SplitAfterN | +| file://:0:0:0:0 | [summary param] 0 in SplitN | file://:0:0:0:0 | [summary] to write: ReturnValue in SplitN | +| file://:0:0:0:0 | [summary param] 0 in Store | file://:0:0:0:0 | [summary] to write: Argument[-1] in Store | +| file://:0:0:0:0 | [summary param] 0 in Store | file://:0:0:0:0 | [summary] to write: Argument[-1] in Store | +| file://:0:0:0:0 | [summary param] 0 in Store | file://:0:0:0:0 | [summary] to write: Argument[-1] in Store | +| file://:0:0:0:0 | [summary param] 0 in Store | file://:0:0:0:0 | [summary] to write: Argument[-1] in Store | +| file://:0:0:0:0 | [summary param] 0 in StringBytePtr | file://:0:0:0:0 | [summary] to write: ReturnValue in StringBytePtr | +| file://:0:0:0:0 | [summary param] 0 in StringByteSlice | file://:0:0:0:0 | [summary] to write: ReturnValue in StringByteSlice | +| file://:0:0:0:0 | [summary param] 0 in Sub | file://:0:0:0:0 | [summary] to write: ReturnValue in Sub | +| file://:0:0:0:0 | [summary param] 0 in Swap | file://:0:0:0:0 | [summary] to write: Argument[-1] in Swap | +| file://:0:0:0:0 | [summary param] 0 in Swap | file://:0:0:0:0 | [summary] to write: Argument[-1] in Swap | +| file://:0:0:0:0 | [summary param] 0 in Swap | file://:0:0:0:0 | [summary] to write: Argument[-1] in Swap | +| file://:0:0:0:0 | [summary param] 0 in Swap | file://:0:0:0:0 | [summary] to write: Argument[-1] in Swap | +| file://:0:0:0:0 | [summary param] 0 in SwapPointer | file://:0:0:0:0 | [summary] to write: ReturnValue in SwapPointer | +| file://:0:0:0:0 | [summary param] 0 in SwapUintptr | file://:0:0:0:0 | [summary] to write: ReturnValue in SwapUintptr | +| file://:0:0:0:0 | [summary param] 0 in TeeReader | file://:0:0:0:0 | [summary] to write: Argument[1] in TeeReader | +| file://:0:0:0:0 | [summary param] 0 in TeeReader | file://:0:0:0:0 | [summary] to write: ReturnValue in TeeReader | +| file://:0:0:0:0 | [summary param] 0 in Title | file://:0:0:0:0 | [summary] to write: ReturnValue in Title | +| file://:0:0:0:0 | [summary param] 0 in ToLower | file://:0:0:0:0 | [summary] to write: ReturnValue in ToLower | +| file://:0:0:0:0 | [summary param] 0 in ToTitle | file://:0:0:0:0 | [summary] to write: ReturnValue in ToTitle | +| file://:0:0:0:0 | [summary param] 0 in ToUpper | file://:0:0:0:0 | [summary] to write: ReturnValue in ToUpper | +| file://:0:0:0:0 | [summary param] 0 in ToValidUTF8 | file://:0:0:0:0 | [summary] to write: ReturnValue in ToValidUTF8 | +| file://:0:0:0:0 | [summary param] 0 in Trim | file://:0:0:0:0 | [summary] to write: ReturnValue in Trim | +| file://:0:0:0:0 | [summary param] 0 in TrimFunc | file://:0:0:0:0 | [summary] to write: ReturnValue in TrimFunc | +| file://:0:0:0:0 | [summary param] 0 in TrimLeft | file://:0:0:0:0 | [summary] to write: ReturnValue in TrimLeft | +| file://:0:0:0:0 | [summary param] 0 in TrimLeftFunc | file://:0:0:0:0 | [summary] to write: ReturnValue in TrimLeftFunc | +| file://:0:0:0:0 | [summary param] 0 in TrimPrefix | file://:0:0:0:0 | [summary] to write: ReturnValue in TrimPrefix | +| file://:0:0:0:0 | [summary param] 0 in TrimRight | file://:0:0:0:0 | [summary] to write: ReturnValue in TrimRight | +| file://:0:0:0:0 | [summary param] 0 in TrimRightFunc | file://:0:0:0:0 | [summary] to write: ReturnValue in TrimRightFunc | +| file://:0:0:0:0 | [summary param] 0 in TrimSpace | file://:0:0:0:0 | [summary] to write: ReturnValue in TrimSpace | +| file://:0:0:0:0 | [summary param] 0 in TrimSuffix | file://:0:0:0:0 | [summary] to write: ReturnValue in TrimSuffix | +| file://:0:0:0:0 | [summary param] 0 in TrySend | file://:0:0:0:0 | [summary] to write: Argument[-1] in TrySend | +| file://:0:0:0:0 | [summary param] 0 in Unquote | file://:0:0:0:0 | [summary] to write: ReturnValue in Unquote | +| file://:0:0:0:0 | [summary param] 0 in UnquoteChar | file://:0:0:0:0 | [summary] to write: ReturnValue[2] in UnquoteChar | +| file://:0:0:0:0 | [summary param] 0 in Unwrap | file://:0:0:0:0 | [summary] to write: ReturnValue in Unwrap | +| file://:0:0:0:0 | [summary param] 0 in User | file://:0:0:0:0 | [summary] to write: ReturnValue in User | +| file://:0:0:0:0 | [summary param] 0 in UserPassword | file://:0:0:0:0 | [summary] to write: ReturnValue in UserPassword | +| file://:0:0:0:0 | [summary param] 0 in ValueOf | file://:0:0:0:0 | [summary] to write: ReturnValue in ValueOf | +| file://:0:0:0:0 | [summary param] 0 in Write | file://:0:0:0:0 | [summary] to write: Argument[-1] in Write | +| file://:0:0:0:0 | [summary param] 0 in Write | file://:0:0:0:0 | [summary] to write: Argument[-1] in Write | +| file://:0:0:0:0 | [summary param] 0 in Write | file://:0:0:0:0 | [summary] to write: Argument[-1] in Write | +| file://:0:0:0:0 | [summary param] 0 in Write | file://:0:0:0:0 | [summary] to write: Argument[-1] in Write | +| file://:0:0:0:0 | [summary param] 0 in Write | file://:0:0:0:0 | [summary] to write: Argument[-1] in Write | +| file://:0:0:0:0 | [summary param] 0 in Write | file://:0:0:0:0 | [summary] to write: Argument[-1] in Write | +| file://:0:0:0:0 | [summary param] 0 in Write | file://:0:0:0:0 | [summary] to write: Argument[-1] in Write | +| file://:0:0:0:0 | [summary param] 0 in Write | file://:0:0:0:0 | [summary] to write: Argument[-1] in Write | +| file://:0:0:0:0 | [summary param] 0 in Write | file://:0:0:0:0 | [summary] to write: Argument[-1] in Write | +| file://:0:0:0:0 | [summary param] 0 in Write | file://:0:0:0:0 | [summary] to write: Argument[-1] in Write | +| file://:0:0:0:0 | [summary param] 0 in Write | file://:0:0:0:0 | [summary] to write: Argument[-1] in Write | +| file://:0:0:0:0 | [summary param] 0 in Write | file://:0:0:0:0 | [summary] to write: Argument[-1] in Write | +| file://:0:0:0:0 | [summary param] 0 in Write | file://:0:0:0:0 | [summary] to write: Argument[-1] in Write | +| file://:0:0:0:0 | [summary param] 0 in WriteAt | file://:0:0:0:0 | [summary] to write: Argument[-1] in WriteAt | +| file://:0:0:0:0 | [summary param] 0 in WriteAt | file://:0:0:0:0 | [summary] to write: Argument[-1] in WriteAt | +| file://:0:0:0:0 | [summary param] 0 in WriteAt | file://:0:0:0:0 | [summary] to write: Argument[-1] in WriteAt | +| file://:0:0:0:0 | [summary param] 0 in WriteString | file://:0:0:0:0 | [summary] to write: Argument[-1] in WriteString | +| file://:0:0:0:0 | [summary param] 0 in WriteString | file://:0:0:0:0 | [summary] to write: Argument[-1] in WriteString | +| file://:0:0:0:0 | [summary param] 0 in WriteString | file://:0:0:0:0 | [summary] to write: Argument[-1] in WriteString | +| file://:0:0:0:0 | [summary param] 0 in WriteString | file://:0:0:0:0 | [summary] to write: Argument[-1] in WriteString | +| file://:0:0:0:0 | [summary param] 0 in WriteString | file://:0:0:0:0 | [summary] to write: Argument[-1] in WriteString | +| file://:0:0:0:0 | [summary param] 0 in WriteString | file://:0:0:0:0 | [summary] to write: Argument[-1] in WriteString | +| file://:0:0:0:0 | [summary param] 0 in WriteString | file://:0:0:0:0 | [summary] to write: Argument[-1] in WriteString | +| file://:0:0:0:0 | [summary param] 0 in WriteString | file://:0:0:0:0 | [summary] to write: Argument[-1] in WriteString | +| file://:0:0:0:0 | [summary param] 1 in AddUintptr | file://:0:0:0:0 | [summary] to write: Argument[0] in AddUintptr | +| file://:0:0:0:0 | [summary param] 1 in AddUintptr | file://:0:0:0:0 | [summary] to write: ReturnValue in AddUintptr | +| file://:0:0:0:0 | [summary param] 1 in AppendQuote | file://:0:0:0:0 | [summary] to write: ReturnValue in AppendQuote | +| file://:0:0:0:0 | [summary param] 1 in AppendQuoteToASCII | file://:0:0:0:0 | [summary] to write: ReturnValue in AppendQuoteToASCII | +| file://:0:0:0:0 | [summary param] 1 in AppendQuoteToGraphic | file://:0:0:0:0 | [summary] to write: ReturnValue in AppendQuoteToGraphic | +| file://:0:0:0:0 | [summary param] 1 in AppendSlice | file://:0:0:0:0 | [summary] to write: ReturnValue in AppendSlice | +| file://:0:0:0:0 | [summary param] 1 in Copy | file://:0:0:0:0 | [summary] to write: Argument[0] in Copy | +| file://:0:0:0:0 | [summary param] 1 in Copy | file://:0:0:0:0 | [summary] to write: Argument[0] in Copy | +| file://:0:0:0:0 | [summary param] 1 in CopyBuffer | file://:0:0:0:0 | [summary] to write: Argument[0] in CopyBuffer | +| file://:0:0:0:0 | [summary param] 1 in CopyN | file://:0:0:0:0 | [summary] to write: Argument[0] in CopyN | +| file://:0:0:0:0 | [summary param] 1 in Join | file://:0:0:0:0 | [summary] to write: ReturnValue in Join | +| file://:0:0:0:0 | [summary param] 1 in LoadOrStore | file://:0:0:0:0 | [summary] to write: Argument[-1] in LoadOrStore | +| file://:0:0:0:0 | [summary param] 1 in LoadOrStore | file://:0:0:0:0 | [summary] to write: ReturnValue in LoadOrStore | +| file://:0:0:0:0 | [summary param] 1 in Map | file://:0:0:0:0 | [summary] to write: ReturnValue in Map | +| file://:0:0:0:0 | [summary param] 1 in SetMapIndex | file://:0:0:0:0 | [summary] to write: Argument[-1] in SetMapIndex | +| file://:0:0:0:0 | [summary param] 1 in Store | file://:0:0:0:0 | [summary] to write: Argument[-1] in Store | +| file://:0:0:0:0 | [summary param] 1 in StorePointer | file://:0:0:0:0 | [summary] to write: Argument[0] in StorePointer | +| file://:0:0:0:0 | [summary param] 1 in StoreUintptr | file://:0:0:0:0 | [summary] to write: Argument[0] in StoreUintptr | +| file://:0:0:0:0 | [summary param] 1 in Swap | file://:0:0:0:0 | [summary] to write: Argument[-1] in Swap | +| file://:0:0:0:0 | [summary param] 1 in SwapPointer | file://:0:0:0:0 | [summary] to write: Argument[0] in SwapPointer | +| file://:0:0:0:0 | [summary param] 1 in SwapUintptr | file://:0:0:0:0 | [summary] to write: Argument[0] in SwapUintptr | +| file://:0:0:0:0 | [summary param] 1 in ToLowerSpecial | file://:0:0:0:0 | [summary] to write: ReturnValue in ToLowerSpecial | +| file://:0:0:0:0 | [summary param] 1 in ToTitleSpecial | file://:0:0:0:0 | [summary] to write: ReturnValue in ToTitleSpecial | +| file://:0:0:0:0 | [summary param] 1 in ToUpperSpecial | file://:0:0:0:0 | [summary] to write: ReturnValue in ToUpperSpecial | +| file://:0:0:0:0 | [summary param] 1 in ToValidUTF8 | file://:0:0:0:0 | [summary] to write: ReturnValue in ToValidUTF8 | +| file://:0:0:0:0 | [summary param] 1 in UserPassword | file://:0:0:0:0 | [summary] to write: ReturnValue in UserPassword | +| file://:0:0:0:0 | [summary param] 1 in WriteString | file://:0:0:0:0 | [summary] to write: Argument[0] in WriteString | +| file://:0:0:0:0 | [summary param] 1 in WriteString | file://:0:0:0:0 | [summary] to write: Argument[0] in WriteString | +| file://:0:0:0:0 | [summary param] 2 in CompareAndSwap | file://:0:0:0:0 | [summary] to write: Argument[-1] in CompareAndSwap | +| file://:0:0:0:0 | [summary param] 2 in CompareAndSwapPointer | file://:0:0:0:0 | [summary] to write: Argument[0] in CompareAndSwapPointer | +| file://:0:0:0:0 | [summary param] 2 in CompareAndSwapUintptr | file://:0:0:0:0 | [summary] to write: Argument[0] in CompareAndSwapUintptr | +| file://:0:0:0:0 | [summary param] 2 in Replace | file://:0:0:0:0 | [summary] to write: ReturnValue in Replace | +| file://:0:0:0:0 | [summary param] 2 in ReplaceAll | file://:0:0:0:0 | [summary] to write: ReturnValue in ReplaceAll | +| file://:0:0:0:0 | [summary param] -1 in Addr | file://:0:0:0:0 | [summary] to write: ReturnValue in Addr | +| file://:0:0:0:0 | [summary param] -1 in Bytes | file://:0:0:0:0 | [summary] to write: ReturnValue in Bytes | +| file://:0:0:0:0 | [summary param] -1 in Convert | file://:0:0:0:0 | [summary] to write: ReturnValue in Convert | +| file://:0:0:0:0 | [summary param] -1 in Elem | file://:0:0:0:0 | [summary] to write: ReturnValue in Elem | +| file://:0:0:0:0 | [summary param] -1 in Encode | file://:0:0:0:0 | [summary] to write: ReturnValue in Encode | +| file://:0:0:0:0 | [summary param] -1 in EscapedPath | file://:0:0:0:0 | [summary] to write: ReturnValue in EscapedPath | +| file://:0:0:0:0 | [summary param] -1 in Fd | file://:0:0:0:0 | [summary] to write: ReturnValue in Fd | +| file://:0:0:0:0 | [summary param] -1 in Field | file://:0:0:0:0 | [summary] to write: ReturnValue in Field | +| file://:0:0:0:0 | [summary param] -1 in FieldByIndex | file://:0:0:0:0 | [summary] to write: ReturnValue in FieldByIndex | +| file://:0:0:0:0 | [summary param] -1 in FieldByName | file://:0:0:0:0 | [summary] to write: ReturnValue in FieldByName | +| file://:0:0:0:0 | [summary param] -1 in FieldByNameFunc | file://:0:0:0:0 | [summary] to write: ReturnValue in FieldByNameFunc | +| file://:0:0:0:0 | [summary param] -1 in Get | file://:0:0:0:0 | [summary] to write: ReturnValue in Get | +| file://:0:0:0:0 | [summary param] -1 in Get | file://:0:0:0:0 | [summary] to write: ReturnValue in Get | +| file://:0:0:0:0 | [summary param] -1 in Get | file://:0:0:0:0 | [summary] to write: ReturnValue in Get | +| file://:0:0:0:0 | [summary param] -1 in Glob | file://:0:0:0:0 | [summary] to write: ReturnValue in Glob | +| file://:0:0:0:0 | [summary param] -1 in Glob | file://:0:0:0:0 | [summary] to write: ReturnValue in Glob | +| file://:0:0:0:0 | [summary param] -1 in GoString | file://:0:0:0:0 | [summary] to write: ReturnValue in GoString | +| file://:0:0:0:0 | [summary param] -1 in GoString | file://:0:0:0:0 | [summary] to write: ReturnValue in GoString | +| file://:0:0:0:0 | [summary param] -1 in Hostname | file://:0:0:0:0 | [summary] to write: ReturnValue in Hostname | +| file://:0:0:0:0 | [summary param] -1 in Index | file://:0:0:0:0 | [summary] to write: ReturnValue in Index | +| file://:0:0:0:0 | [summary param] -1 in Info | file://:0:0:0:0 | [summary] to write: ReturnValue in Info | +| file://:0:0:0:0 | [summary param] -1 in Info | file://:0:0:0:0 | [summary] to write: ReturnValue in Info | +| file://:0:0:0:0 | [summary param] -1 in Info | file://:0:0:0:0 | [summary] to write: ReturnValue in Info | +| file://:0:0:0:0 | [summary param] -1 in Interface | file://:0:0:0:0 | [summary] to write: ReturnValue in Interface | +| file://:0:0:0:0 | [summary param] -1 in InterfaceData | file://:0:0:0:0 | [summary] to write: ReturnValue in InterfaceData | +| file://:0:0:0:0 | [summary param] -1 in Key | file://:0:0:0:0 | [summary] to write: ReturnValue in Key | +| file://:0:0:0:0 | [summary param] -1 in Load | file://:0:0:0:0 | [summary] to write: ReturnValue in Load | +| file://:0:0:0:0 | [summary param] -1 in Load | file://:0:0:0:0 | [summary] to write: ReturnValue in Load | +| file://:0:0:0:0 | [summary param] -1 in Load | file://:0:0:0:0 | [summary] to write: ReturnValue in Load | +| file://:0:0:0:0 | [summary param] -1 in Load | file://:0:0:0:0 | [summary] to write: ReturnValue in Load | +| file://:0:0:0:0 | [summary param] -1 in LoadOrStore | file://:0:0:0:0 | [summary] to write: ReturnValue in LoadOrStore | +| file://:0:0:0:0 | [summary param] -1 in Lookup | file://:0:0:0:0 | [summary] to write: ReturnValue in Lookup | +| file://:0:0:0:0 | [summary param] -1 in MapIndex | file://:0:0:0:0 | [summary] to write: ReturnValue in MapIndex | +| file://:0:0:0:0 | [summary param] -1 in MapKeys | file://:0:0:0:0 | [summary] to write: ReturnValue in MapKeys | +| file://:0:0:0:0 | [summary param] -1 in MapRange | file://:0:0:0:0 | [summary] to write: ReturnValue in MapRange | +| file://:0:0:0:0 | [summary param] -1 in MarshalBinary | file://:0:0:0:0 | [summary] to write: ReturnValue in MarshalBinary | +| file://:0:0:0:0 | [summary param] -1 in Method | file://:0:0:0:0 | [summary] to write: ReturnValue in Method | +| file://:0:0:0:0 | [summary param] -1 in MethodByName | file://:0:0:0:0 | [summary] to write: ReturnValue in MethodByName | +| file://:0:0:0:0 | [summary param] -1 in Name | file://:0:0:0:0 | [summary] to write: ReturnValue in Name | +| file://:0:0:0:0 | [summary param] -1 in Name | file://:0:0:0:0 | [summary] to write: ReturnValue in Name | +| file://:0:0:0:0 | [summary param] -1 in Name | file://:0:0:0:0 | [summary] to write: ReturnValue in Name | +| file://:0:0:0:0 | [summary param] -1 in Open | file://:0:0:0:0 | [summary] to write: ReturnValue in Open | +| file://:0:0:0:0 | [summary param] -1 in Open | file://:0:0:0:0 | [summary] to write: ReturnValue in Open | +| file://:0:0:0:0 | [summary param] -1 in Open | file://:0:0:0:0 | [summary] to write: ReturnValue in Open | +| file://:0:0:0:0 | [summary param] -1 in Parse | file://:0:0:0:0 | [summary] to write: ReturnValue in Parse | +| file://:0:0:0:0 | [summary param] -1 in Password | file://:0:0:0:0 | [summary] to write: ReturnValue in Password | +| file://:0:0:0:0 | [summary param] -1 in Pointer | file://:0:0:0:0 | [summary] to write: ReturnValue in Pointer | +| file://:0:0:0:0 | [summary param] -1 in Port | file://:0:0:0:0 | [summary] to write: ReturnValue in Port | +| file://:0:0:0:0 | [summary param] -1 in Query | file://:0:0:0:0 | [summary] to write: ReturnValue in Query | +| file://:0:0:0:0 | [summary param] -1 in Read | file://:0:0:0:0 | [summary] to write: Argument[0] in Read | +| file://:0:0:0:0 | [summary param] -1 in Read | file://:0:0:0:0 | [summary] to write: Argument[0] in Read | +| file://:0:0:0:0 | [summary param] -1 in Read | file://:0:0:0:0 | [summary] to write: Argument[0] in Read | +| file://:0:0:0:0 | [summary param] -1 in Read | file://:0:0:0:0 | [summary] to write: Argument[0] in Read | +| file://:0:0:0:0 | [summary param] -1 in Read | file://:0:0:0:0 | [summary] to write: Argument[0] in Read | +| file://:0:0:0:0 | [summary param] -1 in Read | file://:0:0:0:0 | [summary] to write: Argument[0] in Read | +| file://:0:0:0:0 | [summary param] -1 in Read | file://:0:0:0:0 | [summary] to write: Argument[0] in Read | +| file://:0:0:0:0 | [summary param] -1 in Read | file://:0:0:0:0 | [summary] to write: Argument[0] in Read | +| file://:0:0:0:0 | [summary param] -1 in Read | file://:0:0:0:0 | [summary] to write: Argument[0] in Read | +| file://:0:0:0:0 | [summary param] -1 in Read | file://:0:0:0:0 | [summary] to write: Argument[0] in Read | +| file://:0:0:0:0 | [summary param] -1 in Read | file://:0:0:0:0 | [summary] to write: Argument[0] in Read | +| file://:0:0:0:0 | [summary param] -1 in Read | file://:0:0:0:0 | [summary] to write: Argument[0] in Read | +| file://:0:0:0:0 | [summary param] -1 in Read | file://:0:0:0:0 | [summary] to write: Argument[0] in Read | +| file://:0:0:0:0 | [summary param] -1 in Read | file://:0:0:0:0 | [summary] to write: Argument[0] in Read | +| file://:0:0:0:0 | [summary param] -1 in Read | file://:0:0:0:0 | [summary] to write: Argument[0] in Read | +| file://:0:0:0:0 | [summary param] -1 in Read | file://:0:0:0:0 | [summary] to write: Argument[0] in Read | +| file://:0:0:0:0 | [summary param] -1 in ReadAt | file://:0:0:0:0 | [summary] to write: Argument[0] in ReadAt | +| file://:0:0:0:0 | [summary param] -1 in ReadAt | file://:0:0:0:0 | [summary] to write: Argument[0] in ReadAt | +| file://:0:0:0:0 | [summary param] -1 in ReadAt | file://:0:0:0:0 | [summary] to write: Argument[0] in ReadAt | +| file://:0:0:0:0 | [summary param] -1 in ReadAt | file://:0:0:0:0 | [summary] to write: Argument[0] in ReadAt | +| file://:0:0:0:0 | [summary param] -1 in ReadDir | file://:0:0:0:0 | [summary] to write: ReturnValue in ReadDir | +| file://:0:0:0:0 | [summary param] -1 in ReadDir | file://:0:0:0:0 | [summary] to write: ReturnValue in ReadDir | +| file://:0:0:0:0 | [summary param] -1 in ReadFile | file://:0:0:0:0 | [summary] to write: ReturnValue in ReadFile | +| file://:0:0:0:0 | [summary param] -1 in ReadFile | file://:0:0:0:0 | [summary] to write: ReturnValue in ReadFile | +| file://:0:0:0:0 | [summary param] -1 in Recv | file://:0:0:0:0 | [summary] to write: ReturnValue in Recv | +| file://:0:0:0:0 | [summary param] -1 in RequestURI | file://:0:0:0:0 | [summary] to write: ReturnValue in RequestURI | +| file://:0:0:0:0 | [summary param] -1 in ResolveReference | file://:0:0:0:0 | [summary] to write: ReturnValue in ResolveReference | +| file://:0:0:0:0 | [summary param] -1 in Slice | file://:0:0:0:0 | [summary] to write: ReturnValue in Slice | +| file://:0:0:0:0 | [summary param] -1 in Slice3 | file://:0:0:0:0 | [summary] to write: ReturnValue in Slice3 | +| file://:0:0:0:0 | [summary param] -1 in String | file://:0:0:0:0 | [summary] to write: ReturnValue in String | +| file://:0:0:0:0 | [summary param] -1 in String | file://:0:0:0:0 | [summary] to write: ReturnValue in String | +| file://:0:0:0:0 | [summary param] -1 in String | file://:0:0:0:0 | [summary] to write: ReturnValue in String | +| file://:0:0:0:0 | [summary param] -1 in String | file://:0:0:0:0 | [summary] to write: ReturnValue in String | +| file://:0:0:0:0 | [summary param] -1 in String | file://:0:0:0:0 | [summary] to write: ReturnValue in String | +| file://:0:0:0:0 | [summary param] -1 in String | file://:0:0:0:0 | [summary] to write: ReturnValue in String | +| file://:0:0:0:0 | [summary param] -1 in String | file://:0:0:0:0 | [summary] to write: ReturnValue in String | +| file://:0:0:0:0 | [summary param] -1 in String | file://:0:0:0:0 | [summary] to write: ReturnValue in String | +| file://:0:0:0:0 | [summary param] -1 in String | file://:0:0:0:0 | [summary] to write: ReturnValue in String | +| file://:0:0:0:0 | [summary param] -1 in String | file://:0:0:0:0 | [summary] to write: ReturnValue in String | +| file://:0:0:0:0 | [summary param] -1 in String | file://:0:0:0:0 | [summary] to write: ReturnValue in String | +| file://:0:0:0:0 | [summary param] -1 in String | file://:0:0:0:0 | [summary] to write: ReturnValue in String | +| file://:0:0:0:0 | [summary param] -1 in String | file://:0:0:0:0 | [summary] to write: ReturnValue in String | +| file://:0:0:0:0 | [summary param] -1 in String | file://:0:0:0:0 | [summary] to write: ReturnValue in String | +| file://:0:0:0:0 | [summary param] -1 in String | file://:0:0:0:0 | [summary] to write: ReturnValue in String | +| file://:0:0:0:0 | [summary param] -1 in String | file://:0:0:0:0 | [summary] to write: ReturnValue in String | +| file://:0:0:0:0 | [summary param] -1 in String | file://:0:0:0:0 | [summary] to write: ReturnValue in String | +| file://:0:0:0:0 | [summary param] -1 in String | file://:0:0:0:0 | [summary] to write: ReturnValue in String | +| file://:0:0:0:0 | [summary param] -1 in String | file://:0:0:0:0 | [summary] to write: ReturnValue in String | +| file://:0:0:0:0 | [summary param] -1 in String | file://:0:0:0:0 | [summary] to write: ReturnValue in String | +| file://:0:0:0:0 | [summary param] -1 in String | file://:0:0:0:0 | [summary] to write: ReturnValue in String | +| file://:0:0:0:0 | [summary param] -1 in String | file://:0:0:0:0 | [summary] to write: ReturnValue in String | +| file://:0:0:0:0 | [summary param] -1 in String | file://:0:0:0:0 | [summary] to write: ReturnValue in String | +| file://:0:0:0:0 | [summary param] -1 in String | file://:0:0:0:0 | [summary] to write: ReturnValue in String | +| file://:0:0:0:0 | [summary param] -1 in String | file://:0:0:0:0 | [summary] to write: ReturnValue in String | +| file://:0:0:0:0 | [summary param] -1 in Sub | file://:0:0:0:0 | [summary] to write: ReturnValue in Sub | +| file://:0:0:0:0 | [summary param] -1 in Sub | file://:0:0:0:0 | [summary] to write: ReturnValue in Sub | +| file://:0:0:0:0 | [summary param] -1 in Swap | file://:0:0:0:0 | [summary] to write: ReturnValue in Swap | +| file://:0:0:0:0 | [summary param] -1 in Swap | file://:0:0:0:0 | [summary] to write: ReturnValue in Swap | +| file://:0:0:0:0 | [summary param] -1 in Swap | file://:0:0:0:0 | [summary] to write: ReturnValue in Swap | +| file://:0:0:0:0 | [summary param] -1 in Swap | file://:0:0:0:0 | [summary] to write: ReturnValue in Swap | +| file://:0:0:0:0 | [summary param] -1 in SyscallConn | file://:0:0:0:0 | [summary] to write: ReturnValue in SyscallConn | +| file://:0:0:0:0 | [summary param] -1 in SyscallConn | file://:0:0:0:0 | [summary] to write: ReturnValue in SyscallConn | +| file://:0:0:0:0 | [summary param] -1 in Token | file://:0:0:0:0 | [summary] to write: ReturnValue in Token | +| file://:0:0:0:0 | [summary param] -1 in Token | file://:0:0:0:0 | [summary] to write: ReturnValue in Token | +| file://:0:0:0:0 | [summary param] -1 in TryRecv | file://:0:0:0:0 | [summary] to write: ReturnValue in TryRecv | +| file://:0:0:0:0 | [summary param] -1 in UnsafeAddr | file://:0:0:0:0 | [summary] to write: ReturnValue in UnsafeAddr | +| file://:0:0:0:0 | [summary param] -1 in Username | file://:0:0:0:0 | [summary] to write: ReturnValue in Username | +| file://:0:0:0:0 | [summary param] -1 in Value | file://:0:0:0:0 | [summary] to write: ReturnValue in Value | +| file://:0:0:0:0 | [summary param] -1 in WriteTo | file://:0:0:0:0 | [summary] to write: Argument[0] in WriteTo | +| file://:0:0:0:0 | [summary param] -1 in WriteTo | file://:0:0:0:0 | [summary] to write: Argument[0] in WriteTo | +| file://:0:0:0:0 | [summary param] -1 in WriteTo | file://:0:0:0:0 | [summary] to write: Argument[0] in WriteTo | +| file://:0:0:0:0 | [summary param] -1 in WriteTo | file://:0:0:0:0 | [summary] to write: Argument[0] in WriteTo | | main.go:26:11:26:17 | type assertion | main.go:26:2:26:17 | ... := ...[0] | | main.go:26:11:26:17 | type assertion | main.go:26:2:26:17 | ... := ...[1] | | main.go:38:13:38:13 | 1 | main.go:38:7:38:20 | slice literal | diff --git a/go/ql/test/library-tests/semmle/go/dataflow/FunctionInputsAndOutputs/FunctionInput_getExitNode.expected b/go/ql/test/library-tests/semmle/go/dataflow/FunctionInputsAndOutputs/FunctionInput_getExitNode.expected index a78fff479a4..b9878f7e169 100644 --- a/go/ql/test/library-tests/semmle/go/dataflow/FunctionInputsAndOutputs/FunctionInput_getExitNode.expected +++ b/go/ql/test/library-tests/semmle/go/dataflow/FunctionInputsAndOutputs/FunctionInput_getExitNode.expected @@ -4,6 +4,7 @@ | parameter 0 | reset.go:8:1:16:1 | function declaration | reset.go:8:27:8:27 | definition of r | | parameter 0 | tst2.go:8:1:12:1 | function declaration | tst2.go:8:12:8:15 | definition of data | | parameter 0 | tst.go:8:1:11:1 | function declaration | tst.go:8:12:8:17 | definition of reader | +| parameter 0 | tst.go:13:1:13:25 | function declaration | tst.go:13:12:13:13 | initialization of xs | | parameter 0 | tst.go:15:1:19:1 | function declaration | tst.go:15:12:15:12 | definition of x | | parameter 1 | main.go:5:1:11:1 | function declaration | main.go:5:20:5:20 | definition of x | | parameter 1 | main.go:13:1:20:1 | function declaration | main.go:13:21:13:21 | definition of x | diff --git a/go/ql/test/library-tests/semmle/go/frameworks/GoMicro/LogInjection.expected b/go/ql/test/library-tests/semmle/go/frameworks/GoMicro/LogInjection.expected new file mode 100644 index 00000000000..d23496fe4c5 --- /dev/null +++ b/go/ql/test/library-tests/semmle/go/frameworks/GoMicro/LogInjection.expected @@ -0,0 +1,24 @@ +edges +| main.go:18:46:18:48 | definition of req | main.go:18:46:18:48 | definition of req | +| main.go:18:46:18:48 | definition of req | main.go:18:46:18:48 | definition of req | +| main.go:18:46:18:48 | definition of req | main.go:21:28:21:31 | name | +| main.go:18:46:18:48 | definition of req | main.go:21:28:21:31 | name | +| main.go:18:46:18:48 | definition of req | proto/Hello.pb.micro.go:85:53:85:54 | definition of in | +| proto/Hello.pb.micro.go:85:53:85:54 | definition of in | proto/Hello.pb.micro.go:85:53:85:54 | definition of in | +| proto/Hello.pb.micro.go:85:53:85:54 | definition of in | proto/Hello.pb.micro.go:86:37:86:38 | in | +| proto/Hello.pb.micro.go:85:53:85:54 | definition of in | proto/Hello.pb.micro.go:86:37:86:38 | in | +| proto/Hello.pb.micro.go:86:37:86:38 | in | main.go:18:46:18:48 | definition of req | +| proto/Hello.pb.micro.go:86:37:86:38 | in | main.go:18:46:18:48 | definition of req | +| proto/Hello.pb.micro.go:86:37:86:38 | in | proto/Hello.pb.micro.go:85:53:85:54 | definition of in | +| proto/Hello.pb.micro.go:86:37:86:38 | in | proto/Hello.pb.micro.go:85:53:85:54 | definition of in | +nodes +| main.go:18:46:18:48 | definition of req | semmle.label | definition of req | +| main.go:18:46:18:48 | definition of req | semmle.label | definition of req | +| main.go:21:28:21:31 | name | semmle.label | name | +| proto/Hello.pb.micro.go:85:53:85:54 | definition of in | semmle.label | definition of in | +| proto/Hello.pb.micro.go:85:53:85:54 | definition of in | semmle.label | definition of in | +| proto/Hello.pb.micro.go:86:37:86:38 | in | semmle.label | in | +| proto/Hello.pb.micro.go:86:37:86:38 | in | semmle.label | in | +subpaths +#select +| main.go:21:28:21:31 | name | main.go:18:46:18:48 | definition of req | main.go:21:28:21:31 | name | This log entry depends on a $@. | main.go:18:46:18:48 | definition of req | user-provided value | diff --git a/go/ql/test/library-tests/semmle/go/frameworks/GoMicro/LogInjection.qlref b/go/ql/test/library-tests/semmle/go/frameworks/GoMicro/LogInjection.qlref new file mode 100644 index 00000000000..1837c628c33 --- /dev/null +++ b/go/ql/test/library-tests/semmle/go/frameworks/GoMicro/LogInjection.qlref @@ -0,0 +1 @@ +Security/CWE-117/LogInjection.ql diff --git a/go/ql/test/library-tests/semmle/go/frameworks/GoMicro/client/main.go b/go/ql/test/library-tests/semmle/go/frameworks/GoMicro/client/main.go new file mode 100644 index 00000000000..5126e855bb8 --- /dev/null +++ b/go/ql/test/library-tests/semmle/go/frameworks/GoMicro/client/main.go @@ -0,0 +1,29 @@ +package main + +import ( + pb "codeql-go-tests/frameworks/GoMicro/proto" + "context" + "fmt" + "log" + + micro "go-micro.dev/v4" +) + +func main() { + // service + service := micro.NewService() + service.Init() + // context + ctx := context.Background() + + greeterService := pb.NewGreeterService("http://localhost:8000", service.Client()) // $ clientRequest="http:\/\/localhost:8000" + // request + req := pb.Request{Name: "Mona"} + resp, err := greeterService.Hello(ctx, &req) + + if err != nil { + log.Fatal(err) + } + + fmt.Println("Hello :: %s", resp.Greeting) +} diff --git a/go/ql/test/library-tests/semmle/go/frameworks/GoMicro/go.mod b/go/ql/test/library-tests/semmle/go/frameworks/GoMicro/go.mod new file mode 100644 index 00000000000..ee80bdcd3e1 --- /dev/null +++ b/go/ql/test/library-tests/semmle/go/frameworks/GoMicro/go.mod @@ -0,0 +1,8 @@ +module codeql-go-tests/frameworks/GoMicro + +go 1.15 + +require ( + go-micro.dev/v4 v4.10.2 + google.golang.org/protobuf v1.28.1 +) diff --git a/ruby/ql/test/library-tests/dataflow/api-graphs/use.expected b/go/ql/test/library-tests/semmle/go/frameworks/GoMicro/gomicro.expected similarity index 100% rename from ruby/ql/test/library-tests/dataflow/api-graphs/use.expected rename to go/ql/test/library-tests/semmle/go/frameworks/GoMicro/gomicro.expected diff --git a/go/ql/test/library-tests/semmle/go/frameworks/GoMicro/gomicro.ql b/go/ql/test/library-tests/semmle/go/frameworks/GoMicro/gomicro.ql new file mode 100644 index 00000000000..c4aa98a0048 --- /dev/null +++ b/go/ql/test/library-tests/semmle/go/frameworks/GoMicro/gomicro.ql @@ -0,0 +1,26 @@ +import go +import TestUtilities.InlineExpectationsTest + +module GoMicroTest implements TestSig { + string getARelevantTag() { result = ["serverRequest", "clientRequest"] } + + predicate hasActualResult(Location location, string element, string tag, string value) { + exists(DataFlow::Node node | + node.hasLocationInfo(location.getFile().getAbsolutePath(), location.getStartLine(), + location.getStartColumn(), location.getEndLine(), location.getEndColumn()) and + ( + node instanceof GoMicro::Request and + element = node.toString() and + value = "\"" + node.toString() + "\"" and + tag = "serverRequest" + or + node instanceof GoMicro::ClientRequestUrlAsSink and + element = node.toString() and + value = node.toString().replaceAll("/", "\\/") and + tag = "clientRequest" + ) + ) + } +} + +import MakeTest diff --git a/go/ql/test/library-tests/semmle/go/frameworks/GoMicro/main.go b/go/ql/test/library-tests/semmle/go/frameworks/GoMicro/main.go new file mode 100644 index 00000000000..3eaacef9822 --- /dev/null +++ b/go/ql/test/library-tests/semmle/go/frameworks/GoMicro/main.go @@ -0,0 +1,39 @@ +package main + +//go:generate depstubber -vendor go-micro.dev/v4 Service,Option,Options NewService,Name,Handle,Server,Client +//go:generate depstubber -vendor go-micro.dev/v4/server Server Handle +//go:generate depstubber -vendor go-micro.dev/v4/client Client Call + +import ( + pb "codeql-go-tests/frameworks/GoMicro/proto" + "context" + "fmt" + "log" + + micro "go-micro.dev/v4" +) + +type Greeter struct{} + +func (g *Greeter) Hello(ctx context.Context, req *pb.Request, rsp *pb.Response) error { // $ serverRequest="definition of req" + // var access + name := req.Name + fmt.Println("Name :: %s", name) + return nil +} + +func main() { + // service + service := micro.NewService( + micro.Name("helloworld"), + micro.Handle(":8080"), + ) + + service.Init() + + pb.RegisterGreeterHandler(service.Server(), new(Greeter)) + + if err := service.Run(); err != nil { + log.Fatal(err) + } +} diff --git a/go/ql/test/library-tests/semmle/go/frameworks/GoMicro/proto/Hello.pb.go b/go/ql/test/library-tests/semmle/go/frameworks/GoMicro/proto/Hello.pb.go new file mode 100644 index 00000000000..cd0f3f66c27 --- /dev/null +++ b/go/ql/test/library-tests/semmle/go/frameworks/GoMicro/proto/Hello.pb.go @@ -0,0 +1,209 @@ +// Code generated by protoc-gen-go. DO NOT EDIT. +// versions: +// protoc-gen-go v1.31.0 +// protoc v4.23.3 +// source: proto/Hello.proto + +package proto + +import ( + protoreflect "google.golang.org/protobuf/reflect/protoreflect" + protoimpl "google.golang.org/protobuf/runtime/protoimpl" + reflect "reflect" + sync "sync" +) + +const ( + // Verify that this generated code is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) + // Verify that runtime/protoimpl is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) +) + +type Request struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` +} + +func (x *Request) Reset() { + *x = Request{} + if protoimpl.UnsafeEnabled { + mi := &file_proto_Hello_proto_msgTypes[0] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *Request) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*Request) ProtoMessage() {} + +func (x *Request) ProtoReflect() protoreflect.Message { + mi := &file_proto_Hello_proto_msgTypes[0] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use Request.ProtoReflect.Descriptor instead. +func (*Request) Descriptor() ([]byte, []int) { + return file_proto_Hello_proto_rawDescGZIP(), []int{0} +} + +func (x *Request) GetName() string { + if x != nil { + return x.Name + } + return "" +} + +type Response struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Greeting string `protobuf:"bytes,2,opt,name=greeting,proto3" json:"greeting,omitempty"` +} + +func (x *Response) Reset() { + *x = Response{} + if protoimpl.UnsafeEnabled { + mi := &file_proto_Hello_proto_msgTypes[1] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *Response) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*Response) ProtoMessage() {} + +func (x *Response) ProtoReflect() protoreflect.Message { + mi := &file_proto_Hello_proto_msgTypes[1] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use Response.ProtoReflect.Descriptor instead. +func (*Response) Descriptor() ([]byte, []int) { + return file_proto_Hello_proto_rawDescGZIP(), []int{1} +} + +func (x *Response) GetGreeting() string { + if x != nil { + return x.Greeting + } + return "" +} + +var File_proto_Hello_proto protoreflect.FileDescriptor + +var file_proto_Hello_proto_rawDesc = []byte{ + 0x0a, 0x11, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2f, 0x48, 0x65, 0x6c, 0x6c, 0x6f, 0x2e, 0x70, 0x72, + 0x6f, 0x74, 0x6f, 0x12, 0x07, 0x67, 0x72, 0x65, 0x65, 0x74, 0x65, 0x72, 0x22, 0x1d, 0x0a, 0x07, + 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, + 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x22, 0x26, 0x0a, 0x08, 0x52, + 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x1a, 0x0a, 0x08, 0x67, 0x72, 0x65, 0x65, 0x74, + 0x69, 0x6e, 0x67, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x67, 0x72, 0x65, 0x65, 0x74, + 0x69, 0x6e, 0x67, 0x32, 0x39, 0x0a, 0x07, 0x47, 0x72, 0x65, 0x65, 0x74, 0x65, 0x72, 0x12, 0x2e, + 0x0a, 0x05, 0x48, 0x65, 0x6c, 0x6c, 0x6f, 0x12, 0x10, 0x2e, 0x67, 0x72, 0x65, 0x65, 0x74, 0x65, + 0x72, 0x2e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x11, 0x2e, 0x67, 0x72, 0x65, 0x65, + 0x74, 0x65, 0x72, 0x2e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x42, 0x08, + 0x5a, 0x06, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, +} + +var ( + file_proto_Hello_proto_rawDescOnce sync.Once + file_proto_Hello_proto_rawDescData = file_proto_Hello_proto_rawDesc +) + +func file_proto_Hello_proto_rawDescGZIP() []byte { + file_proto_Hello_proto_rawDescOnce.Do(func() { + file_proto_Hello_proto_rawDescData = protoimpl.X.CompressGZIP(file_proto_Hello_proto_rawDescData) + }) + return file_proto_Hello_proto_rawDescData +} + +var file_proto_Hello_proto_msgTypes = make([]protoimpl.MessageInfo, 2) +var file_proto_Hello_proto_goTypes = []interface{}{ + (*Request)(nil), // 0: greeter.Request + (*Response)(nil), // 1: greeter.Response +} +var file_proto_Hello_proto_depIdxs = []int32{ + 0, // 0: greeter.Greeter.Hello:input_type -> greeter.Request + 1, // 1: greeter.Greeter.Hello:output_type -> greeter.Response + 1, // [1:2] is the sub-list for method output_type + 0, // [0:1] is the sub-list for method input_type + 0, // [0:0] is the sub-list for extension type_name + 0, // [0:0] is the sub-list for extension extendee + 0, // [0:0] is the sub-list for field type_name +} + +func init() { file_proto_Hello_proto_init() } +func file_proto_Hello_proto_init() { + if File_proto_Hello_proto != nil { + return + } + if !protoimpl.UnsafeEnabled { + file_proto_Hello_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*Request); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_proto_Hello_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*Response); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + } + type x struct{} + out := protoimpl.TypeBuilder{ + File: protoimpl.DescBuilder{ + GoPackagePath: reflect.TypeOf(x{}).PkgPath(), + RawDescriptor: file_proto_Hello_proto_rawDesc, + NumEnums: 0, + NumMessages: 2, + NumExtensions: 0, + NumServices: 1, + }, + GoTypes: file_proto_Hello_proto_goTypes, + DependencyIndexes: file_proto_Hello_proto_depIdxs, + MessageInfos: file_proto_Hello_proto_msgTypes, + }.Build() + File_proto_Hello_proto = out.File + file_proto_Hello_proto_rawDesc = nil + file_proto_Hello_proto_goTypes = nil + file_proto_Hello_proto_depIdxs = nil +} diff --git a/go/ql/test/library-tests/semmle/go/frameworks/GoMicro/proto/Hello.pb.micro.go b/go/ql/test/library-tests/semmle/go/frameworks/GoMicro/proto/Hello.pb.micro.go new file mode 100644 index 00000000000..51e6ab03b93 --- /dev/null +++ b/go/ql/test/library-tests/semmle/go/frameworks/GoMicro/proto/Hello.pb.micro.go @@ -0,0 +1,87 @@ +// Code generated by protoc-gen-micro. DO NOT EDIT. +// source: proto/Hello.proto + +package proto + +import ( + fmt "fmt" + proto "google.golang.org/protobuf/proto" + math "math" +) + +import ( + context "context" + api "go-micro.dev/v4/api" + client "go-micro.dev/v4/client" + server "go-micro.dev/v4/server" +) + +// Reference imports to suppress errors if they are not otherwise used. +var _ = proto.Marshal +var _ = fmt.Errorf +var _ = math.Inf + +// Reference imports to suppress errors if they are not otherwise used. +var _ api.Endpoint +var _ context.Context +var _ client.Option +var _ server.Option + +// Api Endpoints for Greeter service + +func NewGreeterEndpoints() []*api.Endpoint { + return []*api.Endpoint{} +} + +// Client API for Greeter service + +type GreeterService interface { + Hello(ctx context.Context, in *Request, opts ...client.CallOption) (*Response, error) +} + +type greeterService struct { + c client.Client + name string +} + +func NewGreeterService(name string, c client.Client) GreeterService { + return &greeterService{ + c: c, + name: name, + } +} + +func (c *greeterService) Hello(ctx context.Context, in *Request, opts ...client.CallOption) (*Response, error) { + req := c.c.NewRequest(c.name, "Greeter.Hello", in) + out := new(Response) + err := c.c.Call(ctx, req, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +// Server API for Greeter service + +type GreeterHandler interface { + Hello(context.Context, *Request, *Response) error +} + +func RegisterGreeterHandler(s server.Server, hdlr GreeterHandler, opts ...server.HandlerOption) error { + type greeter interface { + Hello(ctx context.Context, in *Request, out *Response) error + } + type Greeter struct { + greeter + } + h := &greeterHandler{hdlr} + return s.Handle(s.NewHandler(&Greeter{h}, opts...)) +} + +type greeterHandler struct { + GreeterHandler +} + +func (h *greeterHandler) Hello(ctx context.Context, in *Request, out *Response) error { + return h.GreeterHandler.Hello(ctx, in, out) +} diff --git a/go/ql/test/library-tests/semmle/go/frameworks/GoMicro/proto/Hello.proto b/go/ql/test/library-tests/semmle/go/frameworks/GoMicro/proto/Hello.proto new file mode 100644 index 00000000000..d2015853398 --- /dev/null +++ b/go/ql/test/library-tests/semmle/go/frameworks/GoMicro/proto/Hello.proto @@ -0,0 +1,16 @@ +syntax = "proto3"; + +package greeter; +option go_package = "/proto"; + +service Greeter { + rpc Hello(Request) returns (Response) {} +} + +message Request { + string name = 1; +} + +message Response { + string greeting = 2; +} \ No newline at end of file diff --git a/go/ql/test/library-tests/semmle/go/frameworks/GoMicro/vendor/go-micro.dev/v4/api/stub.go b/go/ql/test/library-tests/semmle/go/frameworks/GoMicro/vendor/go-micro.dev/v4/api/stub.go new file mode 100644 index 00000000000..965bcabc1c5 --- /dev/null +++ b/go/ql/test/library-tests/semmle/go/frameworks/GoMicro/vendor/go-micro.dev/v4/api/stub.go @@ -0,0 +1,18 @@ +// Code generated by depstubber. DO NOT EDIT. +// This is a simple stub for go-micro.dev/v4/api, strictly for use in testing. + +// See the LICENSE file for information about the licensing of the original library. +// Source: go-micro.dev/v4/api (exports: Endpoint; functions: ) + +// Package api is a stub of go-micro.dev/v4/api, generated by depstubber. +package api + +type Endpoint struct { + Name string + Description string + Handler string + Host []string + Method []string + Path []string + Stream bool +} diff --git a/go/ql/test/library-tests/semmle/go/frameworks/GoMicro/vendor/go-micro.dev/v4/client/stub.go b/go/ql/test/library-tests/semmle/go/frameworks/GoMicro/vendor/go-micro.dev/v4/client/stub.go new file mode 100644 index 00000000000..a6b4e405a7d --- /dev/null +++ b/go/ql/test/library-tests/semmle/go/frameworks/GoMicro/vendor/go-micro.dev/v4/client/stub.go @@ -0,0 +1,146 @@ +// Code generated by depstubber. DO NOT EDIT. +// This is a simple stub for go-micro.dev/v4/client, strictly for use in testing. + +// See the LICENSE file for information about the licensing of the original library. +// Source: go-micro.dev/v4/client (exports: Client; functions: Call) + +// Package client is a stub of go-micro.dev/v4/client, generated by depstubber. +package client + +import ( + context "context" + time "time" +) + +type BackoffFunc func(context.Context, Request, int) (time.Duration, error) + +type Cache struct{} + +func (_ *Cache) Get(_ context.Context, _ *Request) (interface{}, bool) { + return nil, false +} + +func (_ *Cache) List() map[string]string { + return nil +} + +func (_ *Cache) Set(_ context.Context, _ *Request, _ interface{}, _ time.Duration) {} + +func Call(_ context.Context, _ Request, _ interface{}, _ ...CallOption) error { + return nil +} + +type CallFunc func(context.Context, interface{}, Request, interface{}, CallOptions) error + +type CallOption func(*CallOptions) + +type CallOptions struct { + Context context.Context + Backoff BackoffFunc + Retry RetryFunc + SelectOptions []interface{} + Address []string + CallWrappers []CallWrapper + ConnectionTimeout time.Duration + RequestTimeout time.Duration + StreamTimeout time.Duration + CacheExpiry time.Duration + DialTimeout time.Duration + Retries int + ServiceToken bool + ConnClose bool +} + +type CallWrapper func(CallFunc) CallFunc + +type Client interface { + Call(_ context.Context, _ Request, _ interface{}, _ ...CallOption) error + Init(_ ...Option) error + NewMessage(_ string, _ interface{}, _ ...MessageOption) Message + NewRequest(_ string, _ string, _ interface{}, _ ...RequestOption) Request + Options() Options + Publish(_ context.Context, _ Message, _ ...PublishOption) error + Stream(_ context.Context, _ Request, _ ...CallOption) (Stream, error) + String() string +} + +type Message interface { + ContentType() string + Payload() interface{} + Topic() string +} + +type MessageOption func(*MessageOptions) + +type MessageOptions struct { + ContentType string +} + +type Option func(*Options) + +type Options struct { + CallOptions CallOptions + Router Router + Registry interface{} + Selector interface{} + Transport interface{} + Broker interface{} + Logger interface{} + Context context.Context + Codecs map[string]interface{} + Cache *Cache + ContentType string + Wrappers []Wrapper + PoolSize int + PoolTTL time.Duration +} + +type PublishOption func(*PublishOptions) + +type PublishOptions struct { + Context context.Context + Exchange string +} + +type Request interface { + Body() interface{} + Codec() interface{} + ContentType() string + Endpoint() string + Method() string + Service() string + Stream() bool +} + +type RequestOption func(*RequestOptions) + +type RequestOptions struct { + Context context.Context + ContentType string + Stream bool +} + +type Response interface { + Codec() interface{} + Header() map[string]string + Read() ([]byte, error) +} + +type RetryFunc func(context.Context, Request, int, error) (bool, error) + +type Router interface { + SendRequest(_ context.Context, _ Request) (Response, error) +} + +type Stream interface { + Close() error + CloseSend() error + Context() context.Context + Error() error + Recv(_ interface{}) error + Request() Request + Response() Response + Send(_ interface{}) error +} + +type Wrapper func(Client) Client diff --git a/go/ql/test/library-tests/semmle/go/frameworks/GoMicro/vendor/go-micro.dev/v4/server/stub.go b/go/ql/test/library-tests/semmle/go/frameworks/GoMicro/vendor/go-micro.dev/v4/server/stub.go new file mode 100644 index 00000000000..8ca59456e75 --- /dev/null +++ b/go/ql/test/library-tests/semmle/go/frameworks/GoMicro/vendor/go-micro.dev/v4/server/stub.go @@ -0,0 +1,126 @@ +// Code generated by depstubber. DO NOT EDIT. +// This is a simple stub for go-micro.dev/v4/server, strictly for use in testing. + +// See the LICENSE file for information about the licensing of the original library. +// Source: go-micro.dev/v4/server (exports: Server; functions: Handle) + +// Package server is a stub of go-micro.dev/v4/server, generated by depstubber. +package server + +import ( + context "context" + tls "crypto/tls" + time "time" +) + +func Handle(_ Handler) error { + return nil +} + +type Handler interface { + Endpoints() []interface{} + Handler() interface{} + Name() string + Options() HandlerOptions +} + +type HandlerFunc func(context.Context, Request, interface{}) error + +type HandlerOption func(*HandlerOptions) + +type HandlerOptions struct { + Metadata map[string]map[string]string + Internal bool +} + +type HandlerWrapper func(HandlerFunc) HandlerFunc + +type Message interface { + Body() []byte + Codec() interface{} + ContentType() string + Header() map[string]string + Payload() interface{} + Topic() string +} + +type Option func(*Options) + +type Options struct { + Logger interface{} + Broker interface{} + Registry interface{} + Tracer interface{} + Transport interface{} + Context context.Context + Router Router + RegisterCheck func(context.Context) error + Metadata map[string]string + TLSConfig *tls.Config + Codecs map[string]interface{} + Name string + Id string + Version string + Advertise string + Address string + HdlrWrappers []HandlerWrapper + ListenOptions []interface{} + SubWrappers []SubscriberWrapper + RegisterInterval time.Duration + RegisterTTL time.Duration +} + +type Request interface { + Body() interface{} + Codec() interface{} + ContentType() string + Endpoint() string + Header() map[string]string + Method() string + Read() ([]byte, error) + Service() string + Stream() bool +} + +type Response interface { + Codec() interface{} + Write(_ []byte) error + WriteHeader(_ map[string]string) +} + +type Router interface { + ProcessMessage(_ context.Context, _ Message) error + ServeRequest(_ context.Context, _ Request, _ Response) error +} + +type Server interface { + Handle(_ Handler) error + Init(_ ...Option) error + NewHandler(_ interface{}, _ ...HandlerOption) Handler + NewSubscriber(_ string, _ interface{}, _ ...SubscriberOption) Subscriber + Options() Options + Start() error + Stop() error + String() string + Subscribe(_ Subscriber) error +} + +type Subscriber interface { + Endpoints() []interface{} + Options() SubscriberOptions + Subscriber() interface{} + Topic() string +} + +type SubscriberFunc func(context.Context, Message) error + +type SubscriberOption func(*SubscriberOptions) + +type SubscriberOptions struct { + Context context.Context + Queue string + AutoAck bool + Internal bool +} + +type SubscriberWrapper func(SubscriberFunc) SubscriberFunc diff --git a/go/ql/test/library-tests/semmle/go/frameworks/GoMicro/vendor/go-micro.dev/v4/stub.go b/go/ql/test/library-tests/semmle/go/frameworks/GoMicro/vendor/go-micro.dev/v4/stub.go new file mode 100644 index 00000000000..73fb230153c --- /dev/null +++ b/go/ql/test/library-tests/semmle/go/frameworks/GoMicro/vendor/go-micro.dev/v4/stub.go @@ -0,0 +1,69 @@ +// Code generated by depstubber. DO NOT EDIT. +// This is a simple stub for go-micro.dev/v4, strictly for use in testing. + +// See the LICENSE file for information about the licensing of the original library. +// Source: go-micro.dev/v4 (exports: Service,Option,Options; functions: NewService,Name,Handle,Server,Client) + +// Package go_pkg is a stub of go-micro.dev/v4, generated by depstubber. +package go_pkg + +import ( + context "context" + + "go-micro.dev/v4/client" + "go-micro.dev/v4/server" +) + +func Client(_ interface{}) Option { + return nil +} + +func Handle(_ interface{}) Option { + return nil +} + +func Name(_ string) Option { + return nil +} + +func NewService(_ ...Option) Service { + return nil +} + +type Option func(*Options) + +type Options struct { + Registry interface{} + Store interface{} + Auth interface{} + Cmd interface{} + Config interface{} + Client interface{} + Server interface{} + Context context.Context + Cache interface{} + Runtime interface{} + Profile interface{} + Transport interface{} + Logger interface{} + Broker interface{} + BeforeStart []func() error + AfterStart []func() error + AfterStop []func() error + BeforeStop []func() error + Signal bool +} + +func Server(_ interface{}) Option { + return nil +} + +type Service interface { + Client() client.Client + Init(_ ...Option) + Name() string + Options() Options + Run() error + Server() server.Server + String() string +} diff --git a/go/ql/test/library-tests/semmle/go/frameworks/GoMicro/vendor/google.golang.org/protobuf/LICENSE b/go/ql/test/library-tests/semmle/go/frameworks/GoMicro/vendor/google.golang.org/protobuf/LICENSE new file mode 100644 index 00000000000..49ea0f92882 --- /dev/null +++ b/go/ql/test/library-tests/semmle/go/frameworks/GoMicro/vendor/google.golang.org/protobuf/LICENSE @@ -0,0 +1,27 @@ +Copyright (c) 2018 The Go Authors. 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 of Google Inc. 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/go/ql/test/library-tests/semmle/go/frameworks/GoMicro/vendor/google.golang.org/protobuf/internal/impl/stub.go b/go/ql/test/library-tests/semmle/go/frameworks/GoMicro/vendor/google.golang.org/protobuf/internal/impl/stub.go new file mode 100644 index 00000000000..6d87b1ed810 --- /dev/null +++ b/go/ql/test/library-tests/semmle/go/frameworks/GoMicro/vendor/google.golang.org/protobuf/internal/impl/stub.go @@ -0,0 +1,132 @@ +// This is a simple stub for google.golang.org/protobuf/internal/impl, strictly for use in testing. + +// See the LICENSE file for information about the licensing of the original library. +// Source: google.golang.org/protobuf/internal/impl (exports: MessageState,Pointer; functions: ) + +// Package impl is a stub of google.golang.org/protobuf/internal/impl. +package impl + +import ( + "google.golang.org/protobuf/reflect/protoreflect" +) + +type MessageState struct { + NoUnkeyedLiterals interface{} + DoNotCompare interface{} + DoNotCopy interface{} +} + +type Pointer interface{} + +type MessageInfo struct { + Exporter interface{} +} + +func (*MessageInfo) MessageOf(_ interface{}) protoreflect.Message { return nil } + +type EnumInfo struct{} + +func (_ *EnumInfo) Descriptor() protoreflect.EnumDescriptor { return nil } +func (_ *EnumInfo) New(_ protoreflect.EnumNumber) protoreflect.Enum { return nil } + +type DescBuilder struct { + GoPackagePath string + RawDescriptor []byte + NumEnums int + NumMessages int + NumExtensions int + NumServices int +} + +type TypeBuilder struct { + File DescBuilder + GoTypes []interface{} + DependencyIndexes []int32 + EnumInfos []EnumInfo + MessageInfos []MessageInfo +} + +type BuilderOut struct { + File protoreflect.FileDescriptor +} + +func (tb TypeBuilder) Build() BuilderOut { + return BuilderOut{nil} +} + +func (ms *MessageState) LoadMessageInfo() *MessageInfo { return nil } +func (ms *MessageState) StoreMessageInfo(mi *MessageInfo) {} + +func (ms *MessageState) Clear(_ protoreflect.FieldDescriptor) {} +func (ms *MessageState) Descriptor() protoreflect.MessageDescriptor { return nil } +func (ms *MessageState) Get(_ protoreflect.FieldDescriptor) protoreflect.Value { + return protoreflect.Value{} +} +func (ms *MessageState) GetUnknown() protoreflect.RawFields { return nil } +func (ms *MessageState) Has(_ protoreflect.FieldDescriptor) bool { return false } +func (ms *MessageState) Interface() protoreflect.ProtoMessage { return nil } +func (ms *MessageState) IsValid() bool { return false } +func (ms *MessageState) Mutable(_ protoreflect.FieldDescriptor) protoreflect.Value { + return protoreflect.Value{} +} +func (ms *MessageState) New() protoreflect.Message { return nil } +func (ms *MessageState) NewField(_ protoreflect.FieldDescriptor) protoreflect.Value { + return protoreflect.Value{} +} +func (ms *MessageState) ProtoMethods() *struct { + NoUnkeyedLiterals interface{} + Flags uint64 + Size func(struct { + NoUnkeyedLiterals interface{} + Message protoreflect.Message + Flags byte + }) struct { + NoUnkeyedLiterals interface{} + Size int + } + Marshal func(struct { + NoUnkeyedLiterals interface{} + Message protoreflect.Message + Buf []byte + Flags byte + }) (struct { + NoUnkeyedLiterals interface{} + Buf []byte + }, error) + Unmarshal func(struct { + NoUnkeyedLiterals interface{} + Message protoreflect.Message + Buf []byte + Flags byte + Resolver interface { + FindExtensionByName(_ protoreflect.FullName) (protoreflect.ExtensionType, error) + FindExtensionByNumber(_ protoreflect.FullName, _ interface{}) (protoreflect.ExtensionType, error) + } + }) (struct { + NoUnkeyedLiterals interface{} + Flags byte + }, error) + Merge func(struct { + NoUnkeyedLiterals interface{} + Source protoreflect.Message + Destination protoreflect.Message + }) struct { + NoUnkeyedLiterals interface{} + Flags byte + } + CheckInitialized func(struct { + NoUnkeyedLiterals interface{} + Message protoreflect.Message + }) (struct { + NoUnkeyedLiterals interface{} + }, error) +} { + return nil +} +func (ms *MessageState) Range(_ func(protoreflect.FieldDescriptor, protoreflect.Value) bool) {} +func (ms *MessageState) Set(_ protoreflect.FieldDescriptor, _ protoreflect.Value) {} +func (ms *MessageState) SetUnknown(_ protoreflect.RawFields) {} +func (ms *MessageState) Type() protoreflect.MessageType { return nil } +func (ms *MessageState) WhichOneof(_ protoreflect.OneofDescriptor) protoreflect.FieldDescriptor { + return nil +} diff --git a/go/ql/test/library-tests/semmle/go/frameworks/GoMicro/vendor/google.golang.org/protobuf/proto/stub.go b/go/ql/test/library-tests/semmle/go/frameworks/GoMicro/vendor/google.golang.org/protobuf/proto/stub.go new file mode 100644 index 00000000000..979a31e7fbd --- /dev/null +++ b/go/ql/test/library-tests/semmle/go/frameworks/GoMicro/vendor/google.golang.org/protobuf/proto/stub.go @@ -0,0 +1,68 @@ +// This is a simple stub for github.com/golang/protobuf/proto, strictly for use in testing. + +// See the LICENSE file for information about the licensing of the original library. +// Source: github.com/golang/protobuf/proto (exports: Message; functions: Marshal,Unmarshal,ProtoPackageIsVersion4) + +// Package proto is a stub of github.com/golang/protobuf/proto. +package proto + +import ( + protoreflect "google.golang.org/protobuf/reflect/protoreflect" + protoiface "google.golang.org/protobuf/runtime/protoiface" +) + +func Marshal(_ interface{}) ([]byte, error) { + return nil, nil +} + +type Message = protoreflect.ProtoMessage + +var ProtoPackageIsVersion4 bool = false + +func Unmarshal(_ []byte, _ interface{}) error { + return nil +} + +type MarshalOptions struct { + AllowPartial bool + Deterministic bool + UseCachedSize bool +} + +func (_ MarshalOptions) Marshal(_ Message) ([]byte, error) { return nil, nil } +func (_ MarshalOptions) MarshalAppend(b []byte, m Message) ([]byte, error) { return nil, nil } +func (_ MarshalOptions) MarshalState(in protoiface.MarshalInput) (protoiface.MarshalOutput, error) { + return protoiface.MarshalOutput{nil}, nil +} + +type UnmarshalOptions struct { + // Merge merges the input into the destination message. + // The default behavior is to always reset the message before unmarshaling, + // unless Merge is specified. + Merge bool + + // AllowPartial accepts input for messages that will result in missing + // required fields. If AllowPartial is false (the default), Unmarshal will + // return an error if there are any missing required fields. + AllowPartial bool + + // If DiscardUnknown is set, unknown fields are ignored. + DiscardUnknown bool + + // Resolver is used for looking up types when unmarshaling extension fields. + // If nil, this defaults to using protoregistry.GlobalTypes. + Resolver interface { + FindExtensionByName(field protoreflect.FullName) (protoreflect.ExtensionType, error) + FindExtensionByNumber(message protoreflect.FullName, field protoreflect.FieldNumber) (protoreflect.ExtensionType, error) + } +} + +func (o UnmarshalOptions) Unmarshal(b []byte, m Message) error { + return nil +} + +func Clone(_ Message) Message { + return nil +} + +func Merge(_, _ Message) {} diff --git a/go/ql/test/library-tests/semmle/go/frameworks/GoMicro/vendor/google.golang.org/protobuf/reflect/protoreflect/stub.go b/go/ql/test/library-tests/semmle/go/frameworks/GoMicro/vendor/google.golang.org/protobuf/reflect/protoreflect/stub.go new file mode 100644 index 00000000000..0f2fd6e1094 --- /dev/null +++ b/go/ql/test/library-tests/semmle/go/frameworks/GoMicro/vendor/google.golang.org/protobuf/reflect/protoreflect/stub.go @@ -0,0 +1,683 @@ +// Code generated by depstubber. DO NOT EDIT. +// This is a simple stub for google.golang.org/protobuf/reflect/protoreflect, strictly for use in testing. + +// See the LICENSE file for information about the licensing of the original library. +// Source: google.golang.org/protobuf/reflect/protoreflect (exports: EnumDescriptor,EnumType,EnumNumber,Message,FileDescriptor; functions: ) + +// Package protoreflect is a stub of google.golang.org/protobuf/reflect/protoreflect, generated by depstubber. +package protoreflect + +import () + +type Cardinality int8 + +func (_ Cardinality) GoString() string { + return "" +} + +func (_ Cardinality) IsValid() bool { + return false +} + +func (_ Cardinality) String() string { + return "" +} + +type Descriptor interface { + FullName() FullName + Index() int + IsPlaceholder() bool + Name() Name + Options() ProtoMessage + Parent() Descriptor + ParentFile() FileDescriptor + ProtoInternal(_ interface{}) + Syntax() Syntax +} + +type Enum interface { + Descriptor() EnumDescriptor + Number() EnumNumber + Type() EnumType +} + +type EnumDescriptor interface { + FullName() FullName + Index() int + IsPlaceholder() bool + Name() Name + Options() ProtoMessage + Parent() Descriptor + ParentFile() FileDescriptor + ProtoInternal(_ interface{}) + ProtoType(_ EnumDescriptor) + ReservedNames() Names + ReservedRanges() EnumRanges + Syntax() Syntax + Values() EnumValueDescriptors +} + +type EnumDescriptors interface { + ByName(_ Name) EnumDescriptor + Get(_ int) EnumDescriptor + Len() int + ProtoInternal(_ interface{}) +} + +type EnumNumber int32 + +type EnumRanges interface { + Get(_ int) [2]EnumNumber + Has(_ EnumNumber) bool + Len() int + ProtoInternal(_ interface{}) +} + +type EnumType interface { + Descriptor() EnumDescriptor + New(_ EnumNumber) Enum +} + +type EnumValueDescriptor interface { + FullName() FullName + Index() int + IsPlaceholder() bool + Name() Name + Number() EnumNumber + Options() ProtoMessage + Parent() Descriptor + ParentFile() FileDescriptor + ProtoInternal(_ interface{}) + ProtoType(_ EnumValueDescriptor) + Syntax() Syntax +} + +type EnumValueDescriptors interface { + ByName(_ Name) EnumValueDescriptor + ByNumber(_ EnumNumber) EnumValueDescriptor + Get(_ int) EnumValueDescriptor + Len() int + ProtoInternal(_ interface{}) +} + +type ExtensionDescriptors interface { + ByName(_ Name) FieldDescriptor + Get(_ int) FieldDescriptor + Len() int + ProtoInternal(_ interface{}) +} + +type ExtensionType interface { + InterfaceOf(_ Value) interface{} + IsValidInterface(_ interface{}) bool + IsValidValue(_ Value) bool + New() Value + TypeDescriptor() ExtensionTypeDescriptor + ValueOf(_ interface{}) Value + Zero() Value +} + +type ExtensionTypeDescriptor interface { + Cardinality() Cardinality + ContainingMessage() MessageDescriptor + ContainingOneof() OneofDescriptor + Default() Value + DefaultEnumValue() EnumValueDescriptor + Descriptor() FieldDescriptor + Enum() EnumDescriptor + FullName() FullName + HasDefault() bool + HasJSONName() bool + HasOptionalKeyword() bool + HasPresence() bool + Index() int + IsExtension() bool + IsList() bool + IsMap() bool + IsPacked() bool + IsPlaceholder() bool + IsWeak() bool + JSONName() string + Kind() Kind + MapKey() FieldDescriptor + MapValue() FieldDescriptor + Message() MessageDescriptor + Name() Name + Number() interface{} + Options() ProtoMessage + Parent() Descriptor + ParentFile() FileDescriptor + ProtoInternal(_ interface{}) + ProtoType(_ FieldDescriptor) + Syntax() Syntax + Type() ExtensionType +} + +type FieldDescriptor interface { + Cardinality() Cardinality + ContainingMessage() MessageDescriptor + ContainingOneof() OneofDescriptor + Default() Value + DefaultEnumValue() EnumValueDescriptor + Enum() EnumDescriptor + FullName() FullName + HasDefault() bool + HasJSONName() bool + HasOptionalKeyword() bool + HasPresence() bool + Index() int + IsExtension() bool + IsList() bool + IsMap() bool + IsPacked() bool + IsPlaceholder() bool + IsWeak() bool + JSONName() string + Kind() Kind + MapKey() FieldDescriptor + MapValue() FieldDescriptor + Message() MessageDescriptor + Name() Name + Number() interface{} + Options() ProtoMessage + Parent() Descriptor + ParentFile() FileDescriptor + ProtoInternal(_ interface{}) + ProtoType(_ FieldDescriptor) + Syntax() Syntax +} + +type FieldDescriptors interface { + ByJSONName(_ string) FieldDescriptor + ByName(_ Name) FieldDescriptor + ByNumber(_ interface{}) FieldDescriptor + Get(_ int) FieldDescriptor + Len() int + ProtoInternal(_ interface{}) +} + +type FieldNumber int32 + +type FieldNumbers interface { + Get(_ int) interface{} + Has(_ interface{}) bool + Len() int + ProtoInternal(_ interface{}) +} + +type FieldRanges interface { + Get(_ int) [2]interface{} + Has(_ interface{}) bool + Len() int + ProtoInternal(_ interface{}) +} + +type FileDescriptor interface { + Enums() EnumDescriptors + Extensions() ExtensionDescriptors + FullName() FullName + Imports() FileImports + Index() int + IsPlaceholder() bool + Messages() MessageDescriptors + Name() Name + Options() ProtoMessage + Package() FullName + Parent() Descriptor + ParentFile() FileDescriptor + Path() string + ProtoInternal(_ interface{}) + ProtoType(_ FileDescriptor) + Services() ServiceDescriptors + SourceLocations() SourceLocations + Syntax() Syntax +} + +type FileImport struct { + FileDescriptor FileDescriptor + IsPublic bool + IsWeak bool +} + +func (_ FileImport) Enums() EnumDescriptors { + return nil +} + +func (_ FileImport) Extensions() ExtensionDescriptors { + return nil +} + +func (_ FileImport) FullName() FullName { + return "" +} + +func (_ FileImport) Imports() FileImports { + return nil +} + +func (_ FileImport) Index() int { + return 0 +} + +func (_ FileImport) IsPlaceholder() bool { + return false +} + +func (_ FileImport) Messages() MessageDescriptors { + return nil +} + +func (_ FileImport) Name() Name { + return "" +} + +func (_ FileImport) Options() ProtoMessage { + return nil +} + +func (_ FileImport) Package() FullName { + return "" +} + +func (_ FileImport) Parent() Descriptor { + return nil +} + +func (_ FileImport) ParentFile() FileDescriptor { + return nil +} + +func (_ FileImport) Path() string { + return "" +} + +func (_ FileImport) ProtoInternal(_ interface{}) {} + +func (_ FileImport) ProtoType(_ FileDescriptor) {} + +func (_ FileImport) Services() ServiceDescriptors { + return nil +} + +func (_ FileImport) SourceLocations() SourceLocations { + return nil +} + +func (_ FileImport) Syntax() Syntax { + return 0 +} + +type FileImports interface { + Get(_ int) FileImport + Len() int + ProtoInternal(_ interface{}) +} + +type FullName string + +func (_ FullName) Append(_ Name) FullName { + return "" +} + +func (_ FullName) IsValid() bool { + return false +} + +func (_ FullName) Name() Name { + return "" +} + +func (_ FullName) Parent() FullName { + return "" +} + +type Kind int8 + +func (_ Kind) GoString() string { + return "" +} + +func (_ Kind) IsValid() bool { + return false +} + +func (_ Kind) String() string { + return "" +} + +type List interface { + Append(_ Value) + AppendMutable() Value + Get(_ int) Value + IsValid() bool + Len() int + NewElement() Value + Set(_ int, _ Value) + Truncate(_ int) +} + +type Map interface { + Clear(_ MapKey) + Get(_ MapKey) Value + Has(_ MapKey) bool + IsValid() bool + Len() int + Mutable(_ MapKey) Value + NewValue() Value + Range(_ func(MapKey, Value) bool) + Set(_ MapKey, _ Value) +} + +type MapKey struct { + DoNotCompare interface{} +} + +func (_ MapKey) Bool() bool { + return false +} + +func (_ MapKey) Int() int64 { + return 0 +} + +func (_ MapKey) Interface() interface{} { + return nil +} + +func (_ MapKey) IsValid() bool { + return false +} + +func (_ MapKey) String() string { + return "" +} + +func (_ MapKey) Uint() uint64 { + return 0 +} + +func (_ MapKey) Value() Value { + return Value{} +} + +type Message interface { + Clear(_ FieldDescriptor) + Descriptor() MessageDescriptor + Get(_ FieldDescriptor) Value + GetUnknown() RawFields + Has(_ FieldDescriptor) bool + Interface() ProtoMessage + IsValid() bool + Mutable(_ FieldDescriptor) Value + New() Message + NewField(_ FieldDescriptor) Value + ProtoMethods() *struct { + NoUnkeyedLiterals interface{} + Flags uint64 + Size func(struct { + NoUnkeyedLiterals interface{} + Message Message + Flags byte + }) struct { + NoUnkeyedLiterals interface{} + Size int + } + Marshal func(struct { + NoUnkeyedLiterals interface{} + Message Message + Buf []byte + Flags byte + }) (struct { + NoUnkeyedLiterals interface{} + Buf []byte + }, error) + Unmarshal func(struct { + NoUnkeyedLiterals interface{} + Message Message + Buf []byte + Flags byte + Resolver interface { + FindExtensionByName(_ FullName) (ExtensionType, error) + FindExtensionByNumber(_ FullName, _ interface{}) (ExtensionType, error) + } + }) (struct { + NoUnkeyedLiterals interface{} + Flags byte + }, error) + Merge func(struct { + NoUnkeyedLiterals interface{} + Source Message + Destination Message + }) struct { + NoUnkeyedLiterals interface{} + Flags byte + } + CheckInitialized func(struct { + NoUnkeyedLiterals interface{} + Message Message + }) (struct { + NoUnkeyedLiterals interface{} + }, error) + } + Range(_ func(FieldDescriptor, Value) bool) + Set(_ FieldDescriptor, _ Value) + SetUnknown(_ RawFields) + Type() MessageType + WhichOneof(_ OneofDescriptor) FieldDescriptor +} + +type MessageDescriptor interface { + Enums() EnumDescriptors + ExtensionRangeOptions(_ int) ProtoMessage + ExtensionRanges() FieldRanges + Extensions() ExtensionDescriptors + Fields() FieldDescriptors + FullName() FullName + Index() int + IsMapEntry() bool + IsPlaceholder() bool + Messages() MessageDescriptors + Name() Name + Oneofs() OneofDescriptors + Options() ProtoMessage + Parent() Descriptor + ParentFile() FileDescriptor + ProtoInternal(_ interface{}) + ProtoType(_ MessageDescriptor) + RequiredNumbers() FieldNumbers + ReservedNames() Names + ReservedRanges() FieldRanges + Syntax() Syntax +} + +type MessageDescriptors interface { + ByName(_ Name) MessageDescriptor + Get(_ int) MessageDescriptor + Len() int + ProtoInternal(_ interface{}) +} + +type MessageType interface { + Descriptor() MessageDescriptor + New() Message + Zero() Message +} + +type MethodDescriptor interface { + FullName() FullName + Index() int + Input() MessageDescriptor + IsPlaceholder() bool + IsStreamingClient() bool + IsStreamingServer() bool + Name() Name + Options() ProtoMessage + Output() MessageDescriptor + Parent() Descriptor + ParentFile() FileDescriptor + ProtoInternal(_ interface{}) + ProtoType(_ MethodDescriptor) + Syntax() Syntax +} + +type MethodDescriptors interface { + ByName(_ Name) MethodDescriptor + Get(_ int) MethodDescriptor + Len() int + ProtoInternal(_ interface{}) +} + +type Name string + +func (_ Name) IsValid() bool { + return false +} + +type Names interface { + Get(_ int) Name + Has(_ Name) bool + Len() int + ProtoInternal(_ interface{}) +} + +type OneofDescriptor interface { + Fields() FieldDescriptors + FullName() FullName + Index() int + IsPlaceholder() bool + IsSynthetic() bool + Name() Name + Options() ProtoMessage + Parent() Descriptor + ParentFile() FileDescriptor + ProtoInternal(_ interface{}) + ProtoType(_ OneofDescriptor) + Syntax() Syntax +} + +type OneofDescriptors interface { + ByName(_ Name) OneofDescriptor + Get(_ int) OneofDescriptor + Len() int + ProtoInternal(_ interface{}) +} + +type ProtoMessage interface { + ProtoReflect() Message +} + +type RawFields []byte + +func (_ RawFields) IsValid() bool { + return false +} + +type ServiceDescriptor interface { + FullName() FullName + Index() int + IsPlaceholder() bool + Methods() MethodDescriptors + Name() Name + Options() ProtoMessage + Parent() Descriptor + ParentFile() FileDescriptor + ProtoInternal(_ interface{}) + ProtoType(_ ServiceDescriptor) + Syntax() Syntax +} + +type ServiceDescriptors interface { + ByName(_ Name) ServiceDescriptor + Get(_ int) ServiceDescriptor + Len() int + ProtoInternal(_ interface{}) +} + +type SourceLocation struct { + Path SourcePath + StartLine int + StartColumn int + EndLine int + EndColumn int + LeadingDetachedComments []string + LeadingComments string + TrailingComments string +} + +type SourceLocations interface { + Get(_ int) SourceLocation + Len() int + ProtoInternal(_ interface{}) +} + +type SourcePath []int32 + +type Syntax int8 + +func (_ Syntax) GoString() string { + return "" +} + +func (_ Syntax) IsValid() bool { + return false +} + +func (_ Syntax) String() string { + return "" +} + +type Value struct { + DoNotCompare interface{} +} + +func (_ Value) Bool() bool { + return false +} + +func (_ Value) Bytes() []byte { + return nil +} + +func (_ Value) Enum() EnumNumber { + return 0 +} + +func (_ Value) Float() float64 { + return 0 +} + +func (_ Value) Int() int64 { + return 0 +} + +func (_ Value) Interface() interface{} { + return nil +} + +func (_ Value) IsValid() bool { + return false +} + +func (_ Value) List() List { + return nil +} + +func (_ Value) Map() Map { + return nil +} + +func (_ Value) MapKey() MapKey { + return MapKey{} +} + +func (_ Value) Message() Message { + return nil +} + +func (_ Value) String() string { + return "" +} + +func (_ Value) Uint() uint64 { + return 0 +} diff --git a/go/ql/test/library-tests/semmle/go/frameworks/GoMicro/vendor/google.golang.org/protobuf/runtime/protoiface/stub.go b/go/ql/test/library-tests/semmle/go/frameworks/GoMicro/vendor/google.golang.org/protobuf/runtime/protoiface/stub.go new file mode 100644 index 00000000000..85e7d573e55 --- /dev/null +++ b/go/ql/test/library-tests/semmle/go/frameworks/GoMicro/vendor/google.golang.org/protobuf/runtime/protoiface/stub.go @@ -0,0 +1,29 @@ +// This is a simple stub for google.golang.org/protobuf/runtime/protoiface, strictly for use in testing. + +// See the LICENSE file for information about the licensing of the original library. +// Source: google.golang.org/protobuf/runtime/protoiface (exports: MessageV1; functions: ) + +// Package protoiface is a stub of google.golang.org/protobuf/runtime/protoiface. +package protoiface + +import ( + "google.golang.org/protobuf/reflect/protoreflect" +) + +type MessageV1 interface { + ProtoMessage() + Reset() + String() string +} + +type MarshalInputFlags = uint8 + +type MarshalInput struct { + Message protoreflect.Message + Buf []byte // output is appended to this buffer + Flags MarshalInputFlags +} + +type MarshalOutput struct { + Buf []byte // contains marshaled message +} diff --git a/go/ql/test/library-tests/semmle/go/frameworks/GoMicro/vendor/google.golang.org/protobuf/runtime/protoimpl/stub.go b/go/ql/test/library-tests/semmle/go/frameworks/GoMicro/vendor/google.golang.org/protobuf/runtime/protoimpl/stub.go new file mode 100644 index 00000000000..c6fb6b140d5 --- /dev/null +++ b/go/ql/test/library-tests/semmle/go/frameworks/GoMicro/vendor/google.golang.org/protobuf/runtime/protoimpl/stub.go @@ -0,0 +1,107 @@ +// This is a simple stub for google.golang.org/protobuf/runtime/protoimpl, strictly for use in testing. + +// See the LICENSE file for information about the licensing of the original library. +// Source: google.golang.org/protobuf/runtime/protoimpl (exports: MessageState,SizeCache,UnknownFields,Pointer,EnforceVersion; functions: MinVersion,MaxVersion,UnsafeEnabled,X) + +// Package protoimpl is a stub of google.golang.org/protobuf/runtime/protoimpl. +package protoimpl + +import ( + impl "google.golang.org/protobuf/internal/impl" +) + +type EnforceVersion uint + +const MaxVersion int = 20 + +type MessageState = impl.MessageState + +const MinVersion int = 20 + +type Pointer = impl.Pointer + +type SizeCache = int32 + +type UnknownFields = []byte + +var UnsafeEnabled bool = false + +// Export is a zero-length named type that exists only to export a set of +// functions that we do not want to appear in godoc. +type Export struct{} + +var X Export = Export{} + +func (Export) NewError(f string, x ...interface{}) error { + return nil +} + +type enum = interface{} + +func (Export) EnumOf(e enum) interface{} { + return nil +} + +func (Export) EnumDescriptorOf(e enum) interface{} { + return nil +} + +func (Export) EnumTypeOf(e enum) interface{} { + return nil +} + +func (Export) EnumStringOf(ed interface{}, n interface{}) string { + return "" +} + +type message = interface{} + +type legacyMessageWrapper struct{ m interface{} } + +func (m legacyMessageWrapper) Reset() {} +func (m legacyMessageWrapper) String() string { return "" } +func (m legacyMessageWrapper) ProtoMessage() {} + +func (Export) ProtoMessageV1Of(m message) interface{} { + return nil +} + +func (Export) protoMessageV2Of(m message) interface{} { + return nil +} + +func (Export) ProtoMessageV2Of(m message) interface{} { + return nil +} + +func (Export) MessageOf(m message) interface{} { + return nil +} + +func (Export) MessageDescriptorOf(m message) interface{} { + return nil +} + +func (Export) MessageTypeOf(m message) interface{} { + return nil +} + +func (Export) MessageStringOf(m interface{}) string { + return "" +} + +func (Export) MessageStateOf(p Pointer) *MessageState { + return nil +} + +func (Export) CompressGZIP(_ []byte) []byte { + return nil +} + +type EnumInfo = impl.EnumInfo + +type MessageInfo = impl.MessageInfo + +type TypeBuilder = impl.TypeBuilder + +type DescBuilder = impl.DescBuilder diff --git a/go/ql/test/library-tests/semmle/go/frameworks/GoMicro/vendor/modules.txt b/go/ql/test/library-tests/semmle/go/frameworks/GoMicro/vendor/modules.txt new file mode 100644 index 00000000000..1884e9622af --- /dev/null +++ b/go/ql/test/library-tests/semmle/go/frameworks/GoMicro/vendor/modules.txt @@ -0,0 +1,6 @@ +# go-micro.dev/v4 v4.10.2 +## explicit +go-micro.dev/v4 +# google.golang.org/protobuf v1.28.1 +## explicit +google.golang.org/protobuf diff --git a/go/ql/test/library-tests/semmle/go/frameworks/SQL/bun/bun.expected b/go/ql/test/library-tests/semmle/go/frameworks/SQL/bun/bun.expected new file mode 100644 index 00000000000..f9d12d37a28 --- /dev/null +++ b/go/ql/test/library-tests/semmle/go/frameworks/SQL/bun/bun.expected @@ -0,0 +1,21 @@ +| bun.go:25:22:25:30 | untrusted | github.com/uptrace/bun | NewRawQuery | +| bun.go:27:22:27:30 | untrusted | github.com/uptrace/bun.DB | ExecContext | +| bun.go:28:25:28:33 | untrusted | github.com/uptrace/bun.DB | PrepareContext | +| bun.go:29:23:29:31 | untrusted | github.com/uptrace/bun.DB | QueryContext | +| bun.go:30:26:30:34 | untrusted | github.com/uptrace/bun.DB | QueryRowContext | +| bun.go:32:10:32:18 | untrusted | github.com/uptrace/bun.DB | Exec | +| bun.go:33:12:33:20 | untrusted | github.com/uptrace/bun.DB | NewRaw | +| bun.go:34:13:34:21 | untrusted | github.com/uptrace/bun.DB | Prepare | +| bun.go:35:11:35:19 | untrusted | github.com/uptrace/bun.DB | Query | +| bun.go:36:14:36:22 | untrusted | github.com/uptrace/bun.DB | QueryRow | +| bun.go:37:9:37:17 | untrusted | github.com/uptrace/bun.DB | Raw | +| bun.go:39:28:39:36 | untrusted | github.com/uptrace/bun.SelectQuery | ColumnExpr | +| bun.go:40:28:40:36 | untrusted | github.com/uptrace/bun.SelectQuery | DistinctOn | +| bun.go:41:21:41:29 | untrusted | github.com/uptrace/bun.SelectQuery | For | +| bun.go:42:27:42:35 | untrusted | github.com/uptrace/bun.SelectQuery | GroupExpr | +| bun.go:43:24:43:32 | untrusted | github.com/uptrace/bun.SelectQuery | Having | +| bun.go:44:32:44:40 | untrusted | github.com/uptrace/bun.SelectQuery | ModelTableExpr | +| bun.go:45:27:45:35 | untrusted | github.com/uptrace/bun.SelectQuery | OrderExpr | +| bun.go:46:27:46:35 | untrusted | github.com/uptrace/bun.SelectQuery | TableExpr | +| bun.go:47:23:47:31 | untrusted | github.com/uptrace/bun.SelectQuery | Where | +| bun.go:48:25:48:33 | untrusted | github.com/uptrace/bun.SelectQuery | WhereOr | diff --git a/go/ql/test/library-tests/semmle/go/frameworks/SQL/bun/bun.go b/go/ql/test/library-tests/semmle/go/frameworks/SQL/bun/bun.go new file mode 100644 index 00000000000..8ce4e5b0826 --- /dev/null +++ b/go/ql/test/library-tests/semmle/go/frameworks/SQL/bun/bun.go @@ -0,0 +1,49 @@ +package main + +import ( + "context" + "database/sql" + + "github.com/uptrace/bun" + "github.com/uptrace/bun/dialect/sqlitedialect" + "github.com/uptrace/bun/driver/sqliteshim" +) + +func getUntrustedString() string { + return "trouble" +} + +func main() { + untrusted := getUntrustedString() + + ctx := context.Background() + sqlite, err := sql.Open(sqliteshim.ShimName, "file::memory:?cache=shared") + if err != nil { + panic(err) + } + db := bun.NewDB(sqlite, sqlitedialect.New()) + bun.NewRawQuery(db, untrusted) + + db.ExecContext(ctx, untrusted) + db.PrepareContext(ctx, untrusted) + db.QueryContext(ctx, untrusted) + db.QueryRowContext(ctx, untrusted) + + db.Exec(untrusted) + db.NewRaw(untrusted) + db.Prepare(untrusted) + db.Query(untrusted) + db.QueryRow(untrusted) + db.Raw(untrusted) + + db.NewSelect().ColumnExpr(untrusted) + db.NewSelect().DistinctOn(untrusted) + db.NewSelect().For(untrusted) + db.NewSelect().GroupExpr(untrusted) + db.NewSelect().Having(untrusted) + db.NewSelect().ModelTableExpr(untrusted) + db.NewSelect().OrderExpr(untrusted) + db.NewSelect().TableExpr(untrusted) + db.NewSelect().Where(untrusted) + db.NewSelect().WhereOr(untrusted) +} diff --git a/go/ql/test/library-tests/semmle/go/frameworks/SQL/bun/bun.ql b/go/ql/test/library-tests/semmle/go/frameworks/SQL/bun/bun.ql new file mode 100644 index 00000000000..ba7d0de1650 --- /dev/null +++ b/go/ql/test/library-tests/semmle/go/frameworks/SQL/bun/bun.ql @@ -0,0 +1,7 @@ +import go + +from SQL::QueryString qs, Function func, string a, string b +where + func.hasQualifiedName(a, b) and + qs = func.getACall().getSyntacticArgument(_) +select qs, a, b diff --git a/go/ql/test/library-tests/semmle/go/frameworks/SQL/bun/go.mod b/go/ql/test/library-tests/semmle/go/frameworks/SQL/bun/go.mod new file mode 100644 index 00000000000..590a2e9d99a --- /dev/null +++ b/go/ql/test/library-tests/semmle/go/frameworks/SQL/bun/go.mod @@ -0,0 +1,35 @@ +module pwntester/bun + +go 1.19 + +require ( + github.com/uptrace/bun v1.1.14 + github.com/uptrace/bun/dialect/sqlitedialect v1.1.14 + github.com/uptrace/bun/driver/sqliteshim v1.1.14 +) + +require ( + github.com/dustin/go-humanize v1.0.1 // indirect + github.com/google/uuid v1.3.0 // indirect + github.com/jinzhu/inflection v1.0.0 // indirect + github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 // indirect + github.com/mattn/go-isatty v0.0.19 // indirect + github.com/mattn/go-sqlite3 v1.14.16 // indirect + github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec // indirect + github.com/tmthrgd/go-hex v0.0.0-20190904060850-447a3041c3bc // indirect + github.com/vmihailenco/msgpack/v5 v5.3.5 // indirect + github.com/vmihailenco/tagparser/v2 v2.0.0 // indirect + golang.org/x/mod v0.10.0 // indirect + golang.org/x/sys v0.8.0 // indirect + golang.org/x/tools v0.9.1 // indirect + lukechampine.com/uint128 v1.3.0 // indirect + modernc.org/cc/v3 v3.40.0 // indirect + modernc.org/ccgo/v3 v3.16.13 // indirect + modernc.org/libc v1.22.6 // indirect + modernc.org/mathutil v1.5.0 // indirect + modernc.org/memory v1.5.0 // indirect + modernc.org/opt v0.1.3 // indirect + modernc.org/sqlite v1.22.1 // indirect + modernc.org/strutil v1.1.3 // indirect + modernc.org/token v1.1.0 // indirect +) diff --git a/go/ql/test/library-tests/semmle/go/frameworks/SQL/bun/vendor/github.com/uptrace/bun/dialect/sqlitedialect/stub.go b/go/ql/test/library-tests/semmle/go/frameworks/SQL/bun/vendor/github.com/uptrace/bun/dialect/sqlitedialect/stub.go new file mode 100644 index 00000000000..3e5e0553fc6 --- /dev/null +++ b/go/ql/test/library-tests/semmle/go/frameworks/SQL/bun/vendor/github.com/uptrace/bun/dialect/sqlitedialect/stub.go @@ -0,0 +1,73 @@ +// Code generated by depstubber. DO NOT EDIT. +// This is a simple stub for github.com/uptrace/bun/dialect/sqlitedialect, strictly for use in testing. + +// See the LICENSE file for information about the licensing of the original library. +// Source: github.com/uptrace/bun/dialect/sqlitedialect (exports: Dialect; functions: New) + +// Package sqlitedialect is a stub of github.com/uptrace/bun/dialect/sqlitedialect, generated by depstubber. +package sqlitedialect + +import ( + sql "database/sql" + time "time" +) + +type Dialect struct { + BaseDialect interface{} +} + +func (_ Dialect) AppendBool(_ []byte, _ bool) []byte { + return nil +} + +func (_ Dialect) AppendJSON(_ []byte, _ []byte) []byte { + return nil +} + +func (_ Dialect) AppendString(_ []byte, _ string) []byte { + return nil +} + +func (_ Dialect) AppendTime(_ []byte, _ time.Time) []byte { + return nil +} + +func (_ Dialect) AppendUint32(_ []byte, _ uint32) []byte { + return nil +} + +func (_ Dialect) AppendUint64(_ []byte, _ uint64) []byte { + return nil +} + +func (_ *Dialect) AppendBytes(_ []byte, _ []byte) []byte { + return nil +} + +func (_ *Dialect) DefaultVarcharLen() int { + return 0 +} + +func (_ *Dialect) Features() interface{} { + return nil +} + +func (_ *Dialect) IdentQuote() byte { + return 0 +} + +func (_ *Dialect) Init(_ *sql.DB) {} + +func (_ *Dialect) Name() interface{} { + return nil +} + +func (_ *Dialect) OnTable(_ interface{}) {} + +func (_ *Dialect) Tables() interface{} { + return nil +} + +func New() *Dialect { + return nil +} diff --git a/go/ql/test/library-tests/semmle/go/frameworks/SQL/bun/vendor/github.com/uptrace/bun/driver/sqliteshim/stub.go b/go/ql/test/library-tests/semmle/go/frameworks/SQL/bun/vendor/github.com/uptrace/bun/driver/sqliteshim/stub.go new file mode 100644 index 00000000000..de5faeef6df --- /dev/null +++ b/go/ql/test/library-tests/semmle/go/frameworks/SQL/bun/vendor/github.com/uptrace/bun/driver/sqliteshim/stub.go @@ -0,0 +1,10 @@ +// Code generated by depstubber. DO NOT EDIT. +// This is a simple stub for github.com/uptrace/bun/driver/sqliteshim, strictly for use in testing. + +// See the LICENSE file for information about the licensing of the original library. +// Source: github.com/uptrace/bun/driver/sqliteshim (exports: ; functions: ShimName) + +// Package sqliteshim is a stub of github.com/uptrace/bun/driver/sqliteshim, generated by depstubber. +package sqliteshim + +var ShimName string = "" diff --git a/go/ql/test/library-tests/semmle/go/frameworks/SQL/bun/vendor/github.com/uptrace/bun/stub.go b/go/ql/test/library-tests/semmle/go/frameworks/SQL/bun/vendor/github.com/uptrace/bun/stub.go new file mode 100644 index 00000000000..59341539564 --- /dev/null +++ b/go/ql/test/library-tests/semmle/go/frameworks/SQL/bun/vendor/github.com/uptrace/bun/stub.go @@ -0,0 +1,2714 @@ +// Code generated by depstubber. DO NOT EDIT. +// This is a simple stub for github.com/uptrace/bun, strictly for use in testing. + +// See the LICENSE file for information about the licensing of the original library. +// Source: github.com/uptrace/bun (exports: DB; functions: NewDB,NewRawQuery) + +// Package bun is a stub of github.com/uptrace/bun, generated by depstubber. +package bun + +import ( + context "context" + sql "database/sql" + driver "database/sql/driver" + reflect "reflect" + time "time" +) + +type AddColumnQuery struct{} + +func (_ *AddColumnQuery) AppendNamedArg(_ interface{}, _ []byte, _ string) ([]byte, bool) { + return nil, false +} + +func (_ *AddColumnQuery) AppendQuery(_ interface{}, _ []byte) ([]byte, error) { + return nil, nil +} + +func (_ *AddColumnQuery) Apply(_ func(*AddColumnQuery) *AddColumnQuery) *AddColumnQuery { + return nil +} + +func (_ *AddColumnQuery) ColumnExpr(_ string, _ ...interface{}) *AddColumnQuery { + return nil +} + +func (_ *AddColumnQuery) Conn(_ IConn) *AddColumnQuery { + return nil +} + +func (_ *AddColumnQuery) DB() *DB { + return nil +} + +func (_ *AddColumnQuery) Dialect() interface{} { + return nil +} + +func (_ *AddColumnQuery) Err(_ error) *AddColumnQuery { + return nil +} + +func (_ *AddColumnQuery) Exec(_ context.Context, _ ...interface{}) (sql.Result, error) { + return nil, nil +} + +func (_ *AddColumnQuery) GetConn() IConn { + return nil +} + +func (_ *AddColumnQuery) GetModel() interface{} { + return nil +} + +func (_ *AddColumnQuery) GetTableName() string { + return "" +} + +func (_ *AddColumnQuery) IfNotExists() *AddColumnQuery { + return nil +} + +func (_ *AddColumnQuery) Model(_ interface{}) *AddColumnQuery { + return nil +} + +func (_ *AddColumnQuery) ModelTableExpr(_ string, _ ...interface{}) *AddColumnQuery { + return nil +} + +func (_ *AddColumnQuery) NewAddColumn() *AddColumnQuery { + return nil +} + +func (_ *AddColumnQuery) NewCreateIndex() *CreateIndexQuery { + return nil +} + +func (_ *AddColumnQuery) NewCreateTable() *CreateTableQuery { + return nil +} + +func (_ *AddColumnQuery) NewDelete() *DeleteQuery { + return nil +} + +func (_ *AddColumnQuery) NewDropColumn() *DropColumnQuery { + return nil +} + +func (_ *AddColumnQuery) NewDropIndex() *DropIndexQuery { + return nil +} + +func (_ *AddColumnQuery) NewDropTable() *DropTableQuery { + return nil +} + +func (_ *AddColumnQuery) NewInsert() *InsertQuery { + return nil +} + +func (_ *AddColumnQuery) NewRaw(_ string, _ ...interface{}) *RawQuery { + return nil +} + +func (_ *AddColumnQuery) NewSelect() *SelectQuery { + return nil +} + +func (_ *AddColumnQuery) NewTruncateTable() *TruncateTableQuery { + return nil +} + +func (_ *AddColumnQuery) NewUpdate() *UpdateQuery { + return nil +} + +func (_ *AddColumnQuery) NewValues(_ interface{}) *ValuesQuery { + return nil +} + +func (_ *AddColumnQuery) Operation() string { + return "" +} + +func (_ *AddColumnQuery) Table(_ ...string) *AddColumnQuery { + return nil +} + +func (_ *AddColumnQuery) TableExpr(_ string, _ ...interface{}) *AddColumnQuery { + return nil +} + +type Conn struct { + Conn *sql.Conn +} + +func (_ Conn) BeginTx(_ context.Context, _ *sql.TxOptions) (Tx, error) { + return Tx{}, nil +} + +func (_ Conn) Close() error { + return nil +} + +func (_ Conn) Dialect() interface{} { + return nil +} + +func (_ Conn) ExecContext(_ context.Context, _ string, _ ...interface{}) (sql.Result, error) { + return nil, nil +} + +func (_ Conn) NewAddColumn() *AddColumnQuery { + return nil +} + +func (_ Conn) NewCreateIndex() *CreateIndexQuery { + return nil +} + +func (_ Conn) NewCreateTable() *CreateTableQuery { + return nil +} + +func (_ Conn) NewDelete() *DeleteQuery { + return nil +} + +func (_ Conn) NewDropColumn() *DropColumnQuery { + return nil +} + +func (_ Conn) NewDropIndex() *DropIndexQuery { + return nil +} + +func (_ Conn) NewDropTable() *DropTableQuery { + return nil +} + +func (_ Conn) NewInsert() *InsertQuery { + return nil +} + +func (_ Conn) NewMerge() *MergeQuery { + return nil +} + +func (_ Conn) NewRaw(_ string, _ ...interface{}) *RawQuery { + return nil +} + +func (_ Conn) NewSelect() *SelectQuery { + return nil +} + +func (_ Conn) NewTruncateTable() *TruncateTableQuery { + return nil +} + +func (_ Conn) NewUpdate() *UpdateQuery { + return nil +} + +func (_ Conn) NewValues(_ interface{}) *ValuesQuery { + return nil +} + +func (_ Conn) PingContext(_ context.Context) error { + return nil +} + +func (_ Conn) PrepareContext(_ context.Context, _ string) (*sql.Stmt, error) { + return nil, nil +} + +func (_ Conn) QueryContext(_ context.Context, _ string, _ ...interface{}) (*sql.Rows, error) { + return nil, nil +} + +func (_ Conn) QueryRowContext(_ context.Context, _ string, _ ...interface{}) *sql.Row { + return nil +} + +func (_ Conn) Raw(_ func(interface{}) error) error { + return nil +} + +func (_ Conn) RunInTx(_ context.Context, _ *sql.TxOptions, _ func(context.Context, Tx) error) error { + return nil +} + +type CreateIndexQuery struct{} + +func (_ *CreateIndexQuery) AppendNamedArg(_ interface{}, _ []byte, _ string) ([]byte, bool) { + return nil, false +} + +func (_ *CreateIndexQuery) AppendQuery(_ interface{}, _ []byte) ([]byte, error) { + return nil, nil +} + +func (_ *CreateIndexQuery) Column(_ ...string) *CreateIndexQuery { + return nil +} + +func (_ *CreateIndexQuery) ColumnExpr(_ string, _ ...interface{}) *CreateIndexQuery { + return nil +} + +func (_ *CreateIndexQuery) Concurrently() *CreateIndexQuery { + return nil +} + +func (_ *CreateIndexQuery) Conn(_ IConn) *CreateIndexQuery { + return nil +} + +func (_ *CreateIndexQuery) DB() *DB { + return nil +} + +func (_ *CreateIndexQuery) Dialect() interface{} { + return nil +} + +func (_ *CreateIndexQuery) Err(_ error) *CreateIndexQuery { + return nil +} + +func (_ *CreateIndexQuery) ExcludeColumn(_ ...string) *CreateIndexQuery { + return nil +} + +func (_ *CreateIndexQuery) Exec(_ context.Context, _ ...interface{}) (sql.Result, error) { + return nil, nil +} + +func (_ *CreateIndexQuery) GetConn() IConn { + return nil +} + +func (_ *CreateIndexQuery) GetModel() interface{} { + return nil +} + +func (_ *CreateIndexQuery) GetTableName() string { + return "" +} + +func (_ *CreateIndexQuery) IfNotExists() *CreateIndexQuery { + return nil +} + +func (_ *CreateIndexQuery) Include(_ ...string) *CreateIndexQuery { + return nil +} + +func (_ *CreateIndexQuery) IncludeExpr(_ string, _ ...interface{}) *CreateIndexQuery { + return nil +} + +func (_ *CreateIndexQuery) Index(_ string) *CreateIndexQuery { + return nil +} + +func (_ *CreateIndexQuery) IndexExpr(_ string, _ ...interface{}) *CreateIndexQuery { + return nil +} + +func (_ *CreateIndexQuery) Model(_ interface{}) *CreateIndexQuery { + return nil +} + +func (_ *CreateIndexQuery) ModelTableExpr(_ string, _ ...interface{}) *CreateIndexQuery { + return nil +} + +func (_ *CreateIndexQuery) NewAddColumn() *AddColumnQuery { + return nil +} + +func (_ *CreateIndexQuery) NewCreateIndex() *CreateIndexQuery { + return nil +} + +func (_ *CreateIndexQuery) NewCreateTable() *CreateTableQuery { + return nil +} + +func (_ *CreateIndexQuery) NewDelete() *DeleteQuery { + return nil +} + +func (_ *CreateIndexQuery) NewDropColumn() *DropColumnQuery { + return nil +} + +func (_ *CreateIndexQuery) NewDropIndex() *DropIndexQuery { + return nil +} + +func (_ *CreateIndexQuery) NewDropTable() *DropTableQuery { + return nil +} + +func (_ *CreateIndexQuery) NewInsert() *InsertQuery { + return nil +} + +func (_ *CreateIndexQuery) NewRaw(_ string, _ ...interface{}) *RawQuery { + return nil +} + +func (_ *CreateIndexQuery) NewSelect() *SelectQuery { + return nil +} + +func (_ *CreateIndexQuery) NewTruncateTable() *TruncateTableQuery { + return nil +} + +func (_ *CreateIndexQuery) NewUpdate() *UpdateQuery { + return nil +} + +func (_ *CreateIndexQuery) NewValues(_ interface{}) *ValuesQuery { + return nil +} + +func (_ *CreateIndexQuery) Operation() string { + return "" +} + +func (_ *CreateIndexQuery) Table(_ ...string) *CreateIndexQuery { + return nil +} + +func (_ *CreateIndexQuery) TableExpr(_ string, _ ...interface{}) *CreateIndexQuery { + return nil +} + +func (_ *CreateIndexQuery) Unique() *CreateIndexQuery { + return nil +} + +func (_ *CreateIndexQuery) Using(_ string, _ ...interface{}) *CreateIndexQuery { + return nil +} + +func (_ *CreateIndexQuery) Where(_ string, _ ...interface{}) *CreateIndexQuery { + return nil +} + +func (_ *CreateIndexQuery) WhereOr(_ string, _ ...interface{}) *CreateIndexQuery { + return nil +} + +type CreateTableQuery struct{} + +func (_ *CreateTableQuery) AppendNamedArg(_ interface{}, _ []byte, _ string) ([]byte, bool) { + return nil, false +} + +func (_ *CreateTableQuery) AppendQuery(_ interface{}, _ []byte) ([]byte, error) { + return nil, nil +} + +func (_ *CreateTableQuery) ColumnExpr(_ string, _ ...interface{}) *CreateTableQuery { + return nil +} + +func (_ *CreateTableQuery) Conn(_ IConn) *CreateTableQuery { + return nil +} + +func (_ *CreateTableQuery) DB() *DB { + return nil +} + +func (_ *CreateTableQuery) Dialect() interface{} { + return nil +} + +func (_ *CreateTableQuery) Err(_ error) *CreateTableQuery { + return nil +} + +func (_ *CreateTableQuery) Exec(_ context.Context, _ ...interface{}) (sql.Result, error) { + return nil, nil +} + +func (_ *CreateTableQuery) ForeignKey(_ string, _ ...interface{}) *CreateTableQuery { + return nil +} + +func (_ *CreateTableQuery) GetConn() IConn { + return nil +} + +func (_ *CreateTableQuery) GetModel() interface{} { + return nil +} + +func (_ *CreateTableQuery) GetTableName() string { + return "" +} + +func (_ *CreateTableQuery) IfNotExists() *CreateTableQuery { + return nil +} + +func (_ *CreateTableQuery) Model(_ interface{}) *CreateTableQuery { + return nil +} + +func (_ *CreateTableQuery) ModelTableExpr(_ string, _ ...interface{}) *CreateTableQuery { + return nil +} + +func (_ *CreateTableQuery) NewAddColumn() *AddColumnQuery { + return nil +} + +func (_ *CreateTableQuery) NewCreateIndex() *CreateIndexQuery { + return nil +} + +func (_ *CreateTableQuery) NewCreateTable() *CreateTableQuery { + return nil +} + +func (_ *CreateTableQuery) NewDelete() *DeleteQuery { + return nil +} + +func (_ *CreateTableQuery) NewDropColumn() *DropColumnQuery { + return nil +} + +func (_ *CreateTableQuery) NewDropIndex() *DropIndexQuery { + return nil +} + +func (_ *CreateTableQuery) NewDropTable() *DropTableQuery { + return nil +} + +func (_ *CreateTableQuery) NewInsert() *InsertQuery { + return nil +} + +func (_ *CreateTableQuery) NewRaw(_ string, _ ...interface{}) *RawQuery { + return nil +} + +func (_ *CreateTableQuery) NewSelect() *SelectQuery { + return nil +} + +func (_ *CreateTableQuery) NewTruncateTable() *TruncateTableQuery { + return nil +} + +func (_ *CreateTableQuery) NewUpdate() *UpdateQuery { + return nil +} + +func (_ *CreateTableQuery) NewValues(_ interface{}) *ValuesQuery { + return nil +} + +func (_ *CreateTableQuery) Operation() string { + return "" +} + +func (_ *CreateTableQuery) PartitionBy(_ string, _ ...interface{}) *CreateTableQuery { + return nil +} + +func (_ *CreateTableQuery) Table(_ ...string) *CreateTableQuery { + return nil +} + +func (_ *CreateTableQuery) TableExpr(_ string, _ ...interface{}) *CreateTableQuery { + return nil +} + +func (_ *CreateTableQuery) TableSpace(_ string) *CreateTableQuery { + return nil +} + +func (_ *CreateTableQuery) Temp() *CreateTableQuery { + return nil +} + +func (_ *CreateTableQuery) Varchar(_ int) *CreateTableQuery { + return nil +} + +func (_ *CreateTableQuery) WithForeignKeys() *CreateTableQuery { + return nil +} + +type DB struct { + DB *sql.DB +} + +func (_ DB) Close() error { + return nil +} + +func (_ DB) Driver() driver.Driver { + return nil +} + +func (_ DB) Ping() error { + return nil +} + +func (_ DB) PingContext(_ context.Context) error { + return nil +} + +func (_ DB) SetConnMaxIdleTime(_ time.Duration) {} + +func (_ DB) SetConnMaxLifetime(_ time.Duration) {} + +func (_ DB) SetMaxIdleConns(_ int) {} + +func (_ DB) SetMaxOpenConns(_ int) {} + +func (_ DB) Stats() sql.DBStats { + return sql.DBStats{} +} + +func (_ *DB) AddQueryHook(_ QueryHook) {} + +func (_ *DB) Begin() (Tx, error) { + return Tx{}, nil +} + +func (_ *DB) BeginTx(_ context.Context, _ *sql.TxOptions) (Tx, error) { + return Tx{}, nil +} + +func (_ *DB) Conn(_ context.Context) (Conn, error) { + return Conn{}, nil +} + +func (_ *DB) DBStats() DBStats { + return DBStats{} +} + +func (_ *DB) Dialect() interface{} { + return nil +} + +func (_ *DB) Exec(_ string, _ ...interface{}) (sql.Result, error) { + return nil, nil +} + +func (_ *DB) ExecContext(_ context.Context, _ string, _ ...interface{}) (sql.Result, error) { + return nil, nil +} + +func (_ *DB) Formatter() interface{} { + return nil +} + +func (_ *DB) HasFeature(_ interface{}) bool { + return false +} + +func (_ *DB) NewAddColumn() *AddColumnQuery { + return nil +} + +func (_ *DB) NewCreateIndex() *CreateIndexQuery { + return nil +} + +func (_ *DB) NewCreateTable() *CreateTableQuery { + return nil +} + +func (_ *DB) NewDelete() *DeleteQuery { + return nil +} + +func (_ *DB) NewDropColumn() *DropColumnQuery { + return nil +} + +func (_ *DB) NewDropIndex() *DropIndexQuery { + return nil +} + +func (_ *DB) NewDropTable() *DropTableQuery { + return nil +} + +func (_ *DB) NewInsert() *InsertQuery { + return nil +} + +func (_ *DB) NewMerge() *MergeQuery { + return nil +} + +func (_ *DB) NewRaw(_ string, _ ...interface{}) *RawQuery { + return nil +} + +func (_ *DB) NewSelect() *SelectQuery { + return nil +} + +func (_ *DB) NewTruncateTable() *TruncateTableQuery { + return nil +} + +func (_ *DB) NewUpdate() *UpdateQuery { + return nil +} + +func (_ *DB) NewValues(_ interface{}) *ValuesQuery { + return nil +} + +func (_ *DB) Prepare(_ string) (Stmt, error) { + return Stmt{}, nil +} + +func (_ *DB) PrepareContext(_ context.Context, _ string) (Stmt, error) { + return Stmt{}, nil +} + +func (_ *DB) Query(_ string, _ ...interface{}) (*sql.Rows, error) { + return nil, nil +} + +func (_ *DB) QueryContext(_ context.Context, _ string, _ ...interface{}) (*sql.Rows, error) { + return nil, nil +} + +func (_ *DB) QueryRow(_ string, _ ...interface{}) *sql.Row { + return nil +} + +func (_ *DB) QueryRowContext(_ context.Context, _ string, _ ...interface{}) *sql.Row { + return nil +} + +func (_ *DB) Raw(_ string, _ ...interface{}) *RawQuery { + return nil +} + +func (_ *DB) RegisterModel(_ ...interface{}) {} + +func (_ *DB) ResetModel(_ context.Context, _ ...interface{}) error { + return nil +} + +func (_ *DB) RunInTx(_ context.Context, _ *sql.TxOptions, _ func(context.Context, Tx) error) error { + return nil +} + +func (_ *DB) ScanRow(_ context.Context, _ *sql.Rows, _ ...interface{}) error { + return nil +} + +func (_ *DB) ScanRows(_ context.Context, _ *sql.Rows, _ ...interface{}) error { + return nil +} + +func (_ *DB) String() string { + return "" +} + +func (_ *DB) Table(_ reflect.Type) interface{} { + return nil +} + +func (_ *DB) UpdateFQN(_ string, _ string) interface{} { + return nil +} + +func (_ *DB) WithNamedArg(_ string, _ interface{}) *DB { + return nil +} + +type DBOption func(*DB) + +type DBStats struct { + Queries uint32 + Errors uint32 +} + +type DeleteQuery struct{} + +func (_ *DeleteQuery) AppendNamedArg(_ interface{}, _ []byte, _ string) ([]byte, bool) { + return nil, false +} + +func (_ *DeleteQuery) AppendQuery(_ interface{}, _ []byte) ([]byte, error) { + return nil, nil +} + +func (_ *DeleteQuery) Apply(_ func(*DeleteQuery) *DeleteQuery) *DeleteQuery { + return nil +} + +func (_ *DeleteQuery) ApplyQueryBuilder(_ func(QueryBuilder) QueryBuilder) *DeleteQuery { + return nil +} + +func (_ *DeleteQuery) Conn(_ IConn) *DeleteQuery { + return nil +} + +func (_ *DeleteQuery) DB() *DB { + return nil +} + +func (_ *DeleteQuery) Dialect() interface{} { + return nil +} + +func (_ *DeleteQuery) Err(_ error) *DeleteQuery { + return nil +} + +func (_ *DeleteQuery) Exec(_ context.Context, _ ...interface{}) (sql.Result, error) { + return nil, nil +} + +func (_ *DeleteQuery) ForceDelete() *DeleteQuery { + return nil +} + +func (_ *DeleteQuery) GetConn() IConn { + return nil +} + +func (_ *DeleteQuery) GetModel() interface{} { + return nil +} + +func (_ *DeleteQuery) GetTableName() string { + return "" +} + +func (_ *DeleteQuery) Model(_ interface{}) *DeleteQuery { + return nil +} + +func (_ *DeleteQuery) ModelTableExpr(_ string, _ ...interface{}) *DeleteQuery { + return nil +} + +func (_ *DeleteQuery) NewAddColumn() *AddColumnQuery { + return nil +} + +func (_ *DeleteQuery) NewCreateIndex() *CreateIndexQuery { + return nil +} + +func (_ *DeleteQuery) NewCreateTable() *CreateTableQuery { + return nil +} + +func (_ *DeleteQuery) NewDelete() *DeleteQuery { + return nil +} + +func (_ *DeleteQuery) NewDropColumn() *DropColumnQuery { + return nil +} + +func (_ *DeleteQuery) NewDropIndex() *DropIndexQuery { + return nil +} + +func (_ *DeleteQuery) NewDropTable() *DropTableQuery { + return nil +} + +func (_ *DeleteQuery) NewInsert() *InsertQuery { + return nil +} + +func (_ *DeleteQuery) NewRaw(_ string, _ ...interface{}) *RawQuery { + return nil +} + +func (_ *DeleteQuery) NewSelect() *SelectQuery { + return nil +} + +func (_ *DeleteQuery) NewTruncateTable() *TruncateTableQuery { + return nil +} + +func (_ *DeleteQuery) NewUpdate() *UpdateQuery { + return nil +} + +func (_ *DeleteQuery) NewValues(_ interface{}) *ValuesQuery { + return nil +} + +func (_ *DeleteQuery) Operation() string { + return "" +} + +func (_ *DeleteQuery) QueryBuilder() QueryBuilder { + return nil +} + +func (_ *DeleteQuery) Returning(_ string, _ ...interface{}) *DeleteQuery { + return nil +} + +func (_ *DeleteQuery) Scan(_ context.Context, _ ...interface{}) error { + return nil +} + +func (_ *DeleteQuery) String() string { + return "" +} + +func (_ *DeleteQuery) Table(_ ...string) *DeleteQuery { + return nil +} + +func (_ *DeleteQuery) TableExpr(_ string, _ ...interface{}) *DeleteQuery { + return nil +} + +func (_ *DeleteQuery) Where(_ string, _ ...interface{}) *DeleteQuery { + return nil +} + +func (_ *DeleteQuery) WhereAllWithDeleted() *DeleteQuery { + return nil +} + +func (_ *DeleteQuery) WhereDeleted() *DeleteQuery { + return nil +} + +func (_ *DeleteQuery) WhereGroup(_ string, _ func(*DeleteQuery) *DeleteQuery) *DeleteQuery { + return nil +} + +func (_ *DeleteQuery) WhereOr(_ string, _ ...interface{}) *DeleteQuery { + return nil +} + +func (_ *DeleteQuery) WherePK(_ ...string) *DeleteQuery { + return nil +} + +func (_ *DeleteQuery) With(_ string, _ interface{}) *DeleteQuery { + return nil +} + +func (_ *DeleteQuery) WithRecursive(_ string, _ interface{}) *DeleteQuery { + return nil +} + +type DropColumnQuery struct{} + +func (_ *DropColumnQuery) AppendNamedArg(_ interface{}, _ []byte, _ string) ([]byte, bool) { + return nil, false +} + +func (_ *DropColumnQuery) AppendQuery(_ interface{}, _ []byte) ([]byte, error) { + return nil, nil +} + +func (_ *DropColumnQuery) Apply(_ func(*DropColumnQuery) *DropColumnQuery) *DropColumnQuery { + return nil +} + +func (_ *DropColumnQuery) Column(_ ...string) *DropColumnQuery { + return nil +} + +func (_ *DropColumnQuery) ColumnExpr(_ string, _ ...interface{}) *DropColumnQuery { + return nil +} + +func (_ *DropColumnQuery) Conn(_ IConn) *DropColumnQuery { + return nil +} + +func (_ *DropColumnQuery) DB() *DB { + return nil +} + +func (_ *DropColumnQuery) Dialect() interface{} { + return nil +} + +func (_ *DropColumnQuery) Err(_ error) *DropColumnQuery { + return nil +} + +func (_ *DropColumnQuery) Exec(_ context.Context, _ ...interface{}) (sql.Result, error) { + return nil, nil +} + +func (_ *DropColumnQuery) GetConn() IConn { + return nil +} + +func (_ *DropColumnQuery) GetModel() interface{} { + return nil +} + +func (_ *DropColumnQuery) GetTableName() string { + return "" +} + +func (_ *DropColumnQuery) Model(_ interface{}) *DropColumnQuery { + return nil +} + +func (_ *DropColumnQuery) ModelTableExpr(_ string, _ ...interface{}) *DropColumnQuery { + return nil +} + +func (_ *DropColumnQuery) NewAddColumn() *AddColumnQuery { + return nil +} + +func (_ *DropColumnQuery) NewCreateIndex() *CreateIndexQuery { + return nil +} + +func (_ *DropColumnQuery) NewCreateTable() *CreateTableQuery { + return nil +} + +func (_ *DropColumnQuery) NewDelete() *DeleteQuery { + return nil +} + +func (_ *DropColumnQuery) NewDropColumn() *DropColumnQuery { + return nil +} + +func (_ *DropColumnQuery) NewDropIndex() *DropIndexQuery { + return nil +} + +func (_ *DropColumnQuery) NewDropTable() *DropTableQuery { + return nil +} + +func (_ *DropColumnQuery) NewInsert() *InsertQuery { + return nil +} + +func (_ *DropColumnQuery) NewRaw(_ string, _ ...interface{}) *RawQuery { + return nil +} + +func (_ *DropColumnQuery) NewSelect() *SelectQuery { + return nil +} + +func (_ *DropColumnQuery) NewTruncateTable() *TruncateTableQuery { + return nil +} + +func (_ *DropColumnQuery) NewUpdate() *UpdateQuery { + return nil +} + +func (_ *DropColumnQuery) NewValues(_ interface{}) *ValuesQuery { + return nil +} + +func (_ *DropColumnQuery) Operation() string { + return "" +} + +func (_ *DropColumnQuery) Table(_ ...string) *DropColumnQuery { + return nil +} + +func (_ *DropColumnQuery) TableExpr(_ string, _ ...interface{}) *DropColumnQuery { + return nil +} + +type DropIndexQuery struct{} + +func (_ *DropIndexQuery) AppendNamedArg(_ interface{}, _ []byte, _ string) ([]byte, bool) { + return nil, false +} + +func (_ *DropIndexQuery) AppendQuery(_ interface{}, _ []byte) ([]byte, error) { + return nil, nil +} + +func (_ *DropIndexQuery) Cascade() *DropIndexQuery { + return nil +} + +func (_ *DropIndexQuery) Concurrently() *DropIndexQuery { + return nil +} + +func (_ *DropIndexQuery) Conn(_ IConn) *DropIndexQuery { + return nil +} + +func (_ *DropIndexQuery) DB() *DB { + return nil +} + +func (_ *DropIndexQuery) Dialect() interface{} { + return nil +} + +func (_ *DropIndexQuery) Err(_ error) *DropIndexQuery { + return nil +} + +func (_ *DropIndexQuery) Exec(_ context.Context, _ ...interface{}) (sql.Result, error) { + return nil, nil +} + +func (_ *DropIndexQuery) GetConn() IConn { + return nil +} + +func (_ *DropIndexQuery) GetModel() interface{} { + return nil +} + +func (_ *DropIndexQuery) GetTableName() string { + return "" +} + +func (_ *DropIndexQuery) IfExists() *DropIndexQuery { + return nil +} + +func (_ *DropIndexQuery) Index(_ string, _ ...interface{}) *DropIndexQuery { + return nil +} + +func (_ *DropIndexQuery) Model(_ interface{}) *DropIndexQuery { + return nil +} + +func (_ *DropIndexQuery) NewAddColumn() *AddColumnQuery { + return nil +} + +func (_ *DropIndexQuery) NewCreateIndex() *CreateIndexQuery { + return nil +} + +func (_ *DropIndexQuery) NewCreateTable() *CreateTableQuery { + return nil +} + +func (_ *DropIndexQuery) NewDelete() *DeleteQuery { + return nil +} + +func (_ *DropIndexQuery) NewDropColumn() *DropColumnQuery { + return nil +} + +func (_ *DropIndexQuery) NewDropIndex() *DropIndexQuery { + return nil +} + +func (_ *DropIndexQuery) NewDropTable() *DropTableQuery { + return nil +} + +func (_ *DropIndexQuery) NewInsert() *InsertQuery { + return nil +} + +func (_ *DropIndexQuery) NewRaw(_ string, _ ...interface{}) *RawQuery { + return nil +} + +func (_ *DropIndexQuery) NewSelect() *SelectQuery { + return nil +} + +func (_ *DropIndexQuery) NewTruncateTable() *TruncateTableQuery { + return nil +} + +func (_ *DropIndexQuery) NewUpdate() *UpdateQuery { + return nil +} + +func (_ *DropIndexQuery) NewValues(_ interface{}) *ValuesQuery { + return nil +} + +func (_ *DropIndexQuery) Operation() string { + return "" +} + +func (_ *DropIndexQuery) Restrict() *DropIndexQuery { + return nil +} + +type DropTableQuery struct{} + +func (_ *DropTableQuery) AppendNamedArg(_ interface{}, _ []byte, _ string) ([]byte, bool) { + return nil, false +} + +func (_ *DropTableQuery) AppendQuery(_ interface{}, _ []byte) ([]byte, error) { + return nil, nil +} + +func (_ *DropTableQuery) Cascade() *DropTableQuery { + return nil +} + +func (_ *DropTableQuery) Conn(_ IConn) *DropTableQuery { + return nil +} + +func (_ *DropTableQuery) DB() *DB { + return nil +} + +func (_ *DropTableQuery) Dialect() interface{} { + return nil +} + +func (_ *DropTableQuery) Err(_ error) *DropTableQuery { + return nil +} + +func (_ *DropTableQuery) Exec(_ context.Context, _ ...interface{}) (sql.Result, error) { + return nil, nil +} + +func (_ *DropTableQuery) GetConn() IConn { + return nil +} + +func (_ *DropTableQuery) GetModel() interface{} { + return nil +} + +func (_ *DropTableQuery) GetTableName() string { + return "" +} + +func (_ *DropTableQuery) IfExists() *DropTableQuery { + return nil +} + +func (_ *DropTableQuery) Model(_ interface{}) *DropTableQuery { + return nil +} + +func (_ *DropTableQuery) ModelTableExpr(_ string, _ ...interface{}) *DropTableQuery { + return nil +} + +func (_ *DropTableQuery) NewAddColumn() *AddColumnQuery { + return nil +} + +func (_ *DropTableQuery) NewCreateIndex() *CreateIndexQuery { + return nil +} + +func (_ *DropTableQuery) NewCreateTable() *CreateTableQuery { + return nil +} + +func (_ *DropTableQuery) NewDelete() *DeleteQuery { + return nil +} + +func (_ *DropTableQuery) NewDropColumn() *DropColumnQuery { + return nil +} + +func (_ *DropTableQuery) NewDropIndex() *DropIndexQuery { + return nil +} + +func (_ *DropTableQuery) NewDropTable() *DropTableQuery { + return nil +} + +func (_ *DropTableQuery) NewInsert() *InsertQuery { + return nil +} + +func (_ *DropTableQuery) NewRaw(_ string, _ ...interface{}) *RawQuery { + return nil +} + +func (_ *DropTableQuery) NewSelect() *SelectQuery { + return nil +} + +func (_ *DropTableQuery) NewTruncateTable() *TruncateTableQuery { + return nil +} + +func (_ *DropTableQuery) NewUpdate() *UpdateQuery { + return nil +} + +func (_ *DropTableQuery) NewValues(_ interface{}) *ValuesQuery { + return nil +} + +func (_ *DropTableQuery) Operation() string { + return "" +} + +func (_ *DropTableQuery) Restrict() *DropTableQuery { + return nil +} + +func (_ *DropTableQuery) Table(_ ...string) *DropTableQuery { + return nil +} + +func (_ *DropTableQuery) TableExpr(_ string, _ ...interface{}) *DropTableQuery { + return nil +} + +type IConn interface { + ExecContext(_ context.Context, _ string, _ ...interface{}) (sql.Result, error) + QueryContext(_ context.Context, _ string, _ ...interface{}) (*sql.Rows, error) + QueryRowContext(_ context.Context, _ string, _ ...interface{}) *sql.Row +} + +type InsertQuery struct{} + +func (_ *InsertQuery) AppendNamedArg(_ interface{}, _ []byte, _ string) ([]byte, bool) { + return nil, false +} + +func (_ *InsertQuery) AppendQuery(_ interface{}, _ []byte) ([]byte, error) { + return nil, nil +} + +func (_ *InsertQuery) Apply(_ func(*InsertQuery) *InsertQuery) *InsertQuery { + return nil +} + +func (_ *InsertQuery) Column(_ ...string) *InsertQuery { + return nil +} + +func (_ *InsertQuery) ColumnExpr(_ string, _ ...interface{}) *InsertQuery { + return nil +} + +func (_ *InsertQuery) Conn(_ IConn) *InsertQuery { + return nil +} + +func (_ *InsertQuery) DB() *DB { + return nil +} + +func (_ *InsertQuery) Dialect() interface{} { + return nil +} + +func (_ *InsertQuery) Err(_ error) *InsertQuery { + return nil +} + +func (_ *InsertQuery) ExcludeColumn(_ ...string) *InsertQuery { + return nil +} + +func (_ *InsertQuery) Exec(_ context.Context, _ ...interface{}) (sql.Result, error) { + return nil, nil +} + +func (_ *InsertQuery) GetConn() IConn { + return nil +} + +func (_ *InsertQuery) GetModel() interface{} { + return nil +} + +func (_ *InsertQuery) GetTableName() string { + return "" +} + +func (_ *InsertQuery) Ignore() *InsertQuery { + return nil +} + +func (_ *InsertQuery) Model(_ interface{}) *InsertQuery { + return nil +} + +func (_ *InsertQuery) ModelTableExpr(_ string, _ ...interface{}) *InsertQuery { + return nil +} + +func (_ *InsertQuery) NewAddColumn() *AddColumnQuery { + return nil +} + +func (_ *InsertQuery) NewCreateIndex() *CreateIndexQuery { + return nil +} + +func (_ *InsertQuery) NewCreateTable() *CreateTableQuery { + return nil +} + +func (_ *InsertQuery) NewDelete() *DeleteQuery { + return nil +} + +func (_ *InsertQuery) NewDropColumn() *DropColumnQuery { + return nil +} + +func (_ *InsertQuery) NewDropIndex() *DropIndexQuery { + return nil +} + +func (_ *InsertQuery) NewDropTable() *DropTableQuery { + return nil +} + +func (_ *InsertQuery) NewInsert() *InsertQuery { + return nil +} + +func (_ *InsertQuery) NewRaw(_ string, _ ...interface{}) *RawQuery { + return nil +} + +func (_ *InsertQuery) NewSelect() *SelectQuery { + return nil +} + +func (_ *InsertQuery) NewTruncateTable() *TruncateTableQuery { + return nil +} + +func (_ *InsertQuery) NewUpdate() *UpdateQuery { + return nil +} + +func (_ *InsertQuery) NewValues(_ interface{}) *ValuesQuery { + return nil +} + +func (_ *InsertQuery) On(_ string, _ ...interface{}) *InsertQuery { + return nil +} + +func (_ *InsertQuery) Operation() string { + return "" +} + +func (_ *InsertQuery) Replace() *InsertQuery { + return nil +} + +func (_ *InsertQuery) Returning(_ string, _ ...interface{}) *InsertQuery { + return nil +} + +func (_ *InsertQuery) Scan(_ context.Context, _ ...interface{}) error { + return nil +} + +func (_ *InsertQuery) Set(_ string, _ ...interface{}) *InsertQuery { + return nil +} + +func (_ *InsertQuery) String() string { + return "" +} + +func (_ *InsertQuery) Table(_ ...string) *InsertQuery { + return nil +} + +func (_ *InsertQuery) TableExpr(_ string, _ ...interface{}) *InsertQuery { + return nil +} + +func (_ *InsertQuery) Value(_ string, _ string, _ ...interface{}) *InsertQuery { + return nil +} + +func (_ *InsertQuery) Where(_ string, _ ...interface{}) *InsertQuery { + return nil +} + +func (_ *InsertQuery) WhereOr(_ string, _ ...interface{}) *InsertQuery { + return nil +} + +func (_ *InsertQuery) With(_ string, _ interface{}) *InsertQuery { + return nil +} + +func (_ *InsertQuery) WithRecursive(_ string, _ interface{}) *InsertQuery { + return nil +} + +type MergeQuery struct{} + +func (_ *MergeQuery) AppendNamedArg(_ interface{}, _ []byte, _ string) ([]byte, bool) { + return nil, false +} + +func (_ *MergeQuery) AppendQuery(_ interface{}, _ []byte) ([]byte, error) { + return nil, nil +} + +func (_ *MergeQuery) Apply(_ func(*MergeQuery) *MergeQuery) *MergeQuery { + return nil +} + +func (_ *MergeQuery) Conn(_ IConn) *MergeQuery { + return nil +} + +func (_ *MergeQuery) DB() *DB { + return nil +} + +func (_ *MergeQuery) Dialect() interface{} { + return nil +} + +func (_ *MergeQuery) Err(_ error) *MergeQuery { + return nil +} + +func (_ *MergeQuery) Exec(_ context.Context, _ ...interface{}) (sql.Result, error) { + return nil, nil +} + +func (_ *MergeQuery) GetConn() IConn { + return nil +} + +func (_ *MergeQuery) GetModel() interface{} { + return nil +} + +func (_ *MergeQuery) GetTableName() string { + return "" +} + +func (_ *MergeQuery) Model(_ interface{}) *MergeQuery { + return nil +} + +func (_ *MergeQuery) ModelTableExpr(_ string, _ ...interface{}) *MergeQuery { + return nil +} + +func (_ *MergeQuery) NewAddColumn() *AddColumnQuery { + return nil +} + +func (_ *MergeQuery) NewCreateIndex() *CreateIndexQuery { + return nil +} + +func (_ *MergeQuery) NewCreateTable() *CreateTableQuery { + return nil +} + +func (_ *MergeQuery) NewDelete() *DeleteQuery { + return nil +} + +func (_ *MergeQuery) NewDropColumn() *DropColumnQuery { + return nil +} + +func (_ *MergeQuery) NewDropIndex() *DropIndexQuery { + return nil +} + +func (_ *MergeQuery) NewDropTable() *DropTableQuery { + return nil +} + +func (_ *MergeQuery) NewInsert() *InsertQuery { + return nil +} + +func (_ *MergeQuery) NewRaw(_ string, _ ...interface{}) *RawQuery { + return nil +} + +func (_ *MergeQuery) NewSelect() *SelectQuery { + return nil +} + +func (_ *MergeQuery) NewTruncateTable() *TruncateTableQuery { + return nil +} + +func (_ *MergeQuery) NewUpdate() *UpdateQuery { + return nil +} + +func (_ *MergeQuery) NewValues(_ interface{}) *ValuesQuery { + return nil +} + +func (_ *MergeQuery) On(_ string, _ ...interface{}) *MergeQuery { + return nil +} + +func (_ *MergeQuery) Operation() string { + return "" +} + +func (_ *MergeQuery) Returning(_ string, _ ...interface{}) *MergeQuery { + return nil +} + +func (_ *MergeQuery) Scan(_ context.Context, _ ...interface{}) error { + return nil +} + +func (_ *MergeQuery) String() string { + return "" +} + +func (_ *MergeQuery) Table(_ ...string) *MergeQuery { + return nil +} + +func (_ *MergeQuery) TableExpr(_ string, _ ...interface{}) *MergeQuery { + return nil +} + +func (_ *MergeQuery) Using(_ string, _ ...interface{}) *MergeQuery { + return nil +} + +func (_ *MergeQuery) When(_ string, _ ...interface{}) *MergeQuery { + return nil +} + +func (_ *MergeQuery) WhenDelete(_ string) *MergeQuery { + return nil +} + +func (_ *MergeQuery) WhenInsert(_ string, _ func(*InsertQuery) *InsertQuery) *MergeQuery { + return nil +} + +func (_ *MergeQuery) WhenUpdate(_ string, _ func(*UpdateQuery) *UpdateQuery) *MergeQuery { + return nil +} + +func (_ *MergeQuery) With(_ string, _ interface{}) *MergeQuery { + return nil +} + +func (_ *MergeQuery) WithRecursive(_ string, _ interface{}) *MergeQuery { + return nil +} + +func NewDB(_ *sql.DB, _ interface{}, _ ...DBOption) *DB { + return nil +} + +func NewRawQuery(_ *DB, _ string, _ ...interface{}) *RawQuery { + return nil +} + +type QueryBuilder interface { + AppendQuery(_ interface{}, _ []byte) ([]byte, error) + GetModel() interface{} + GetTableName() string + Operation() string + Unwrap() interface{} + Where(_ string, _ ...interface{}) QueryBuilder + WhereAllWithDeleted() QueryBuilder + WhereDeleted() QueryBuilder + WhereGroup(_ string, _ func(QueryBuilder) QueryBuilder) QueryBuilder + WhereOr(_ string, _ ...interface{}) QueryBuilder + WherePK(_ ...string) QueryBuilder +} + +type QueryEvent struct { + DB *DB + QueryAppender interface{} + IQuery interface{} + Query string + QueryTemplate string + QueryArgs []interface{} + Model interface{} + StartTime time.Time + Result sql.Result + Err error + Stash map[interface{}]interface{} +} + +func (_ *QueryEvent) Operation() string { + return "" +} + +type QueryHook interface { + AfterQuery(_ context.Context, _ *QueryEvent) + BeforeQuery(_ context.Context, _ *QueryEvent) context.Context +} + +type RawQuery struct{} + +func (_ *RawQuery) AppendNamedArg(_ interface{}, _ []byte, _ string) ([]byte, bool) { + return nil, false +} + +func (_ *RawQuery) AppendQuery(_ interface{}, _ []byte) ([]byte, error) { + return nil, nil +} + +func (_ *RawQuery) Conn(_ IConn) *RawQuery { + return nil +} + +func (_ *RawQuery) DB() *DB { + return nil +} + +func (_ *RawQuery) Dialect() interface{} { + return nil +} + +func (_ *RawQuery) Err(_ error) *RawQuery { + return nil +} + +func (_ *RawQuery) Exec(_ context.Context, _ ...interface{}) (sql.Result, error) { + return nil, nil +} + +func (_ *RawQuery) GetConn() IConn { + return nil +} + +func (_ *RawQuery) GetModel() interface{} { + return nil +} + +func (_ *RawQuery) GetTableName() string { + return "" +} + +func (_ *RawQuery) NewAddColumn() *AddColumnQuery { + return nil +} + +func (_ *RawQuery) NewCreateIndex() *CreateIndexQuery { + return nil +} + +func (_ *RawQuery) NewCreateTable() *CreateTableQuery { + return nil +} + +func (_ *RawQuery) NewDelete() *DeleteQuery { + return nil +} + +func (_ *RawQuery) NewDropColumn() *DropColumnQuery { + return nil +} + +func (_ *RawQuery) NewDropIndex() *DropIndexQuery { + return nil +} + +func (_ *RawQuery) NewDropTable() *DropTableQuery { + return nil +} + +func (_ *RawQuery) NewInsert() *InsertQuery { + return nil +} + +func (_ *RawQuery) NewRaw(_ string, _ ...interface{}) *RawQuery { + return nil +} + +func (_ *RawQuery) NewSelect() *SelectQuery { + return nil +} + +func (_ *RawQuery) NewTruncateTable() *TruncateTableQuery { + return nil +} + +func (_ *RawQuery) NewUpdate() *UpdateQuery { + return nil +} + +func (_ *RawQuery) NewValues(_ interface{}) *ValuesQuery { + return nil +} + +func (_ *RawQuery) Operation() string { + return "" +} + +func (_ *RawQuery) Scan(_ context.Context, _ ...interface{}) error { + return nil +} + +type SelectQuery struct{} + +func (_ *SelectQuery) AppendNamedArg(_ interface{}, _ []byte, _ string) ([]byte, bool) { + return nil, false +} + +func (_ *SelectQuery) AppendQuery(_ interface{}, _ []byte) ([]byte, error) { + return nil, nil +} + +func (_ *SelectQuery) Apply(_ func(*SelectQuery) *SelectQuery) *SelectQuery { + return nil +} + +func (_ *SelectQuery) ApplyQueryBuilder(_ func(QueryBuilder) QueryBuilder) *SelectQuery { + return nil +} + +func (_ *SelectQuery) Column(_ ...string) *SelectQuery { + return nil +} + +func (_ *SelectQuery) ColumnExpr(_ string, _ ...interface{}) *SelectQuery { + return nil +} + +func (_ *SelectQuery) Conn(_ IConn) *SelectQuery { + return nil +} + +func (_ *SelectQuery) Count(_ context.Context) (int, error) { + return 0, nil +} + +func (_ *SelectQuery) DB() *DB { + return nil +} + +func (_ *SelectQuery) Dialect() interface{} { + return nil +} + +func (_ *SelectQuery) Distinct() *SelectQuery { + return nil +} + +func (_ *SelectQuery) DistinctOn(_ string, _ ...interface{}) *SelectQuery { + return nil +} + +func (_ *SelectQuery) Err(_ error) *SelectQuery { + return nil +} + +func (_ *SelectQuery) Except(_ *SelectQuery) *SelectQuery { + return nil +} + +func (_ *SelectQuery) ExceptAll(_ *SelectQuery) *SelectQuery { + return nil +} + +func (_ *SelectQuery) ExcludeColumn(_ ...string) *SelectQuery { + return nil +} + +func (_ *SelectQuery) Exec(_ context.Context, _ ...interface{}) (sql.Result, error) { + return nil, nil +} + +func (_ *SelectQuery) Exists(_ context.Context) (bool, error) { + return false, nil +} + +func (_ *SelectQuery) For(_ string, _ ...interface{}) *SelectQuery { + return nil +} + +func (_ *SelectQuery) ForceIndex(_ ...string) *SelectQuery { + return nil +} + +func (_ *SelectQuery) ForceIndexForGroupBy(_ ...string) *SelectQuery { + return nil +} + +func (_ *SelectQuery) ForceIndexForJoin(_ ...string) *SelectQuery { + return nil +} + +func (_ *SelectQuery) ForceIndexForOrderBy(_ ...string) *SelectQuery { + return nil +} + +func (_ *SelectQuery) GetConn() IConn { + return nil +} + +func (_ *SelectQuery) GetModel() interface{} { + return nil +} + +func (_ *SelectQuery) GetTableName() string { + return "" +} + +func (_ *SelectQuery) Group(_ ...string) *SelectQuery { + return nil +} + +func (_ *SelectQuery) GroupExpr(_ string, _ ...interface{}) *SelectQuery { + return nil +} + +func (_ *SelectQuery) Having(_ string, _ ...interface{}) *SelectQuery { + return nil +} + +func (_ *SelectQuery) IgnoreIndex(_ ...string) *SelectQuery { + return nil +} + +func (_ *SelectQuery) IgnoreIndexForGroupBy(_ ...string) *SelectQuery { + return nil +} + +func (_ *SelectQuery) IgnoreIndexForJoin(_ ...string) *SelectQuery { + return nil +} + +func (_ *SelectQuery) IgnoreIndexForOrderBy(_ ...string) *SelectQuery { + return nil +} + +func (_ *SelectQuery) Intersect(_ *SelectQuery) *SelectQuery { + return nil +} + +func (_ *SelectQuery) IntersectAll(_ *SelectQuery) *SelectQuery { + return nil +} + +func (_ *SelectQuery) Join(_ string, _ ...interface{}) *SelectQuery { + return nil +} + +func (_ *SelectQuery) JoinOn(_ string, _ ...interface{}) *SelectQuery { + return nil +} + +func (_ *SelectQuery) JoinOnOr(_ string, _ ...interface{}) *SelectQuery { + return nil +} + +func (_ *SelectQuery) Limit(_ int) *SelectQuery { + return nil +} + +func (_ *SelectQuery) Model(_ interface{}) *SelectQuery { + return nil +} + +func (_ *SelectQuery) ModelTableExpr(_ string, _ ...interface{}) *SelectQuery { + return nil +} + +func (_ *SelectQuery) NewAddColumn() *AddColumnQuery { + return nil +} + +func (_ *SelectQuery) NewCreateIndex() *CreateIndexQuery { + return nil +} + +func (_ *SelectQuery) NewCreateTable() *CreateTableQuery { + return nil +} + +func (_ *SelectQuery) NewDelete() *DeleteQuery { + return nil +} + +func (_ *SelectQuery) NewDropColumn() *DropColumnQuery { + return nil +} + +func (_ *SelectQuery) NewDropIndex() *DropIndexQuery { + return nil +} + +func (_ *SelectQuery) NewDropTable() *DropTableQuery { + return nil +} + +func (_ *SelectQuery) NewInsert() *InsertQuery { + return nil +} + +func (_ *SelectQuery) NewRaw(_ string, _ ...interface{}) *RawQuery { + return nil +} + +func (_ *SelectQuery) NewSelect() *SelectQuery { + return nil +} + +func (_ *SelectQuery) NewTruncateTable() *TruncateTableQuery { + return nil +} + +func (_ *SelectQuery) NewUpdate() *UpdateQuery { + return nil +} + +func (_ *SelectQuery) NewValues(_ interface{}) *ValuesQuery { + return nil +} + +func (_ *SelectQuery) Offset(_ int) *SelectQuery { + return nil +} + +func (_ *SelectQuery) Operation() string { + return "" +} + +func (_ *SelectQuery) Order(_ ...string) *SelectQuery { + return nil +} + +func (_ *SelectQuery) OrderExpr(_ string, _ ...interface{}) *SelectQuery { + return nil +} + +func (_ *SelectQuery) QueryBuilder() QueryBuilder { + return nil +} + +func (_ *SelectQuery) Relation(_ string, _ ...func(*SelectQuery) *SelectQuery) *SelectQuery { + return nil +} + +func (_ *SelectQuery) Rows(_ context.Context) (*sql.Rows, error) { + return nil, nil +} + +func (_ *SelectQuery) Scan(_ context.Context, _ ...interface{}) error { + return nil +} + +func (_ *SelectQuery) ScanAndCount(_ context.Context, _ ...interface{}) (int, error) { + return 0, nil +} + +func (_ *SelectQuery) String() string { + return "" +} + +func (_ *SelectQuery) Table(_ ...string) *SelectQuery { + return nil +} + +func (_ *SelectQuery) TableExpr(_ string, _ ...interface{}) *SelectQuery { + return nil +} + +func (_ *SelectQuery) Union(_ *SelectQuery) *SelectQuery { + return nil +} + +func (_ *SelectQuery) UnionAll(_ *SelectQuery) *SelectQuery { + return nil +} + +func (_ *SelectQuery) UseIndex(_ ...string) *SelectQuery { + return nil +} + +func (_ *SelectQuery) UseIndexForGroupBy(_ ...string) *SelectQuery { + return nil +} + +func (_ *SelectQuery) UseIndexForJoin(_ ...string) *SelectQuery { + return nil +} + +func (_ *SelectQuery) UseIndexForOrderBy(_ ...string) *SelectQuery { + return nil +} + +func (_ *SelectQuery) Where(_ string, _ ...interface{}) *SelectQuery { + return nil +} + +func (_ *SelectQuery) WhereAllWithDeleted() *SelectQuery { + return nil +} + +func (_ *SelectQuery) WhereDeleted() *SelectQuery { + return nil +} + +func (_ *SelectQuery) WhereGroup(_ string, _ func(*SelectQuery) *SelectQuery) *SelectQuery { + return nil +} + +func (_ *SelectQuery) WhereOr(_ string, _ ...interface{}) *SelectQuery { + return nil +} + +func (_ *SelectQuery) WherePK(_ ...string) *SelectQuery { + return nil +} + +func (_ *SelectQuery) With(_ string, _ interface{}) *SelectQuery { + return nil +} + +func (_ *SelectQuery) WithRecursive(_ string, _ interface{}) *SelectQuery { + return nil +} + +type Stmt struct { + Stmt *sql.Stmt +} + +func (_ Stmt) Close() error { + return nil +} + +func (_ Stmt) Exec(_ ...interface{}) (sql.Result, error) { + return nil, nil +} + +func (_ Stmt) ExecContext(_ context.Context, _ ...interface{}) (sql.Result, error) { + return nil, nil +} + +func (_ Stmt) Query(_ ...interface{}) (*sql.Rows, error) { + return nil, nil +} + +func (_ Stmt) QueryContext(_ context.Context, _ ...interface{}) (*sql.Rows, error) { + return nil, nil +} + +func (_ Stmt) QueryRow(_ ...interface{}) *sql.Row { + return nil +} + +func (_ Stmt) QueryRowContext(_ context.Context, _ ...interface{}) *sql.Row { + return nil +} + +type TruncateTableQuery struct{} + +func (_ *TruncateTableQuery) AppendNamedArg(_ interface{}, _ []byte, _ string) ([]byte, bool) { + return nil, false +} + +func (_ *TruncateTableQuery) AppendQuery(_ interface{}, _ []byte) ([]byte, error) { + return nil, nil +} + +func (_ *TruncateTableQuery) Cascade() *TruncateTableQuery { + return nil +} + +func (_ *TruncateTableQuery) Conn(_ IConn) *TruncateTableQuery { + return nil +} + +func (_ *TruncateTableQuery) ContinueIdentity() *TruncateTableQuery { + return nil +} + +func (_ *TruncateTableQuery) DB() *DB { + return nil +} + +func (_ *TruncateTableQuery) Dialect() interface{} { + return nil +} + +func (_ *TruncateTableQuery) Err(_ error) *TruncateTableQuery { + return nil +} + +func (_ *TruncateTableQuery) Exec(_ context.Context, _ ...interface{}) (sql.Result, error) { + return nil, nil +} + +func (_ *TruncateTableQuery) GetConn() IConn { + return nil +} + +func (_ *TruncateTableQuery) GetModel() interface{} { + return nil +} + +func (_ *TruncateTableQuery) GetTableName() string { + return "" +} + +func (_ *TruncateTableQuery) Model(_ interface{}) *TruncateTableQuery { + return nil +} + +func (_ *TruncateTableQuery) NewAddColumn() *AddColumnQuery { + return nil +} + +func (_ *TruncateTableQuery) NewCreateIndex() *CreateIndexQuery { + return nil +} + +func (_ *TruncateTableQuery) NewCreateTable() *CreateTableQuery { + return nil +} + +func (_ *TruncateTableQuery) NewDelete() *DeleteQuery { + return nil +} + +func (_ *TruncateTableQuery) NewDropColumn() *DropColumnQuery { + return nil +} + +func (_ *TruncateTableQuery) NewDropIndex() *DropIndexQuery { + return nil +} + +func (_ *TruncateTableQuery) NewDropTable() *DropTableQuery { + return nil +} + +func (_ *TruncateTableQuery) NewInsert() *InsertQuery { + return nil +} + +func (_ *TruncateTableQuery) NewRaw(_ string, _ ...interface{}) *RawQuery { + return nil +} + +func (_ *TruncateTableQuery) NewSelect() *SelectQuery { + return nil +} + +func (_ *TruncateTableQuery) NewTruncateTable() *TruncateTableQuery { + return nil +} + +func (_ *TruncateTableQuery) NewUpdate() *UpdateQuery { + return nil +} + +func (_ *TruncateTableQuery) NewValues(_ interface{}) *ValuesQuery { + return nil +} + +func (_ *TruncateTableQuery) Operation() string { + return "" +} + +func (_ *TruncateTableQuery) Restrict() *TruncateTableQuery { + return nil +} + +func (_ *TruncateTableQuery) Table(_ ...string) *TruncateTableQuery { + return nil +} + +func (_ *TruncateTableQuery) TableExpr(_ string, _ ...interface{}) *TruncateTableQuery { + return nil +} + +type Tx struct { + Tx *sql.Tx +} + +func (_ Tx) Begin() (Tx, error) { + return Tx{}, nil +} + +func (_ Tx) BeginTx(_ context.Context, _ *sql.TxOptions) (Tx, error) { + return Tx{}, nil +} + +func (_ Tx) Commit() error { + return nil +} + +func (_ Tx) Dialect() interface{} { + return nil +} + +func (_ Tx) Exec(_ string, _ ...interface{}) (sql.Result, error) { + return nil, nil +} + +func (_ Tx) ExecContext(_ context.Context, _ string, _ ...interface{}) (sql.Result, error) { + return nil, nil +} + +func (_ Tx) NewAddColumn() *AddColumnQuery { + return nil +} + +func (_ Tx) NewCreateIndex() *CreateIndexQuery { + return nil +} + +func (_ Tx) NewCreateTable() *CreateTableQuery { + return nil +} + +func (_ Tx) NewDelete() *DeleteQuery { + return nil +} + +func (_ Tx) NewDropColumn() *DropColumnQuery { + return nil +} + +func (_ Tx) NewDropIndex() *DropIndexQuery { + return nil +} + +func (_ Tx) NewDropTable() *DropTableQuery { + return nil +} + +func (_ Tx) NewInsert() *InsertQuery { + return nil +} + +func (_ Tx) NewMerge() *MergeQuery { + return nil +} + +func (_ Tx) NewRaw(_ string, _ ...interface{}) *RawQuery { + return nil +} + +func (_ Tx) NewSelect() *SelectQuery { + return nil +} + +func (_ Tx) NewTruncateTable() *TruncateTableQuery { + return nil +} + +func (_ Tx) NewUpdate() *UpdateQuery { + return nil +} + +func (_ Tx) NewValues(_ interface{}) *ValuesQuery { + return nil +} + +func (_ Tx) Prepare(_ string) (*sql.Stmt, error) { + return nil, nil +} + +func (_ Tx) PrepareContext(_ context.Context, _ string) (*sql.Stmt, error) { + return nil, nil +} + +func (_ Tx) Query(_ string, _ ...interface{}) (*sql.Rows, error) { + return nil, nil +} + +func (_ Tx) QueryContext(_ context.Context, _ string, _ ...interface{}) (*sql.Rows, error) { + return nil, nil +} + +func (_ Tx) QueryRow(_ string, _ ...interface{}) *sql.Row { + return nil +} + +func (_ Tx) QueryRowContext(_ context.Context, _ string, _ ...interface{}) *sql.Row { + return nil +} + +func (_ Tx) Rollback() error { + return nil +} + +func (_ Tx) RunInTx(_ context.Context, _ *sql.TxOptions, _ func(context.Context, Tx) error) error { + return nil +} + +func (_ Tx) Stmt(_ *sql.Stmt) *sql.Stmt { + return nil +} + +func (_ Tx) StmtContext(_ context.Context, _ *sql.Stmt) *sql.Stmt { + return nil +} + +type UpdateQuery struct{} + +func (_ *UpdateQuery) AppendNamedArg(_ interface{}, _ []byte, _ string) ([]byte, bool) { + return nil, false +} + +func (_ *UpdateQuery) AppendQuery(_ interface{}, _ []byte) ([]byte, error) { + return nil, nil +} + +func (_ *UpdateQuery) Apply(_ func(*UpdateQuery) *UpdateQuery) *UpdateQuery { + return nil +} + +func (_ *UpdateQuery) ApplyQueryBuilder(_ func(QueryBuilder) QueryBuilder) *UpdateQuery { + return nil +} + +func (_ *UpdateQuery) Bulk() *UpdateQuery { + return nil +} + +func (_ *UpdateQuery) Column(_ ...string) *UpdateQuery { + return nil +} + +func (_ *UpdateQuery) Conn(_ IConn) *UpdateQuery { + return nil +} + +func (_ *UpdateQuery) DB() *DB { + return nil +} + +func (_ *UpdateQuery) Dialect() interface{} { + return nil +} + +func (_ *UpdateQuery) Err(_ error) *UpdateQuery { + return nil +} + +func (_ *UpdateQuery) ExcludeColumn(_ ...string) *UpdateQuery { + return nil +} + +func (_ *UpdateQuery) Exec(_ context.Context, _ ...interface{}) (sql.Result, error) { + return nil, nil +} + +func (_ *UpdateQuery) FQN(_ string) interface{} { + return nil +} + +func (_ *UpdateQuery) ForceIndex(_ ...string) *UpdateQuery { + return nil +} + +func (_ *UpdateQuery) GetConn() IConn { + return nil +} + +func (_ *UpdateQuery) GetModel() interface{} { + return nil +} + +func (_ *UpdateQuery) GetTableName() string { + return "" +} + +func (_ *UpdateQuery) IgnoreIndex(_ ...string) *UpdateQuery { + return nil +} + +func (_ *UpdateQuery) Model(_ interface{}) *UpdateQuery { + return nil +} + +func (_ *UpdateQuery) ModelTableExpr(_ string, _ ...interface{}) *UpdateQuery { + return nil +} + +func (_ *UpdateQuery) NewAddColumn() *AddColumnQuery { + return nil +} + +func (_ *UpdateQuery) NewCreateIndex() *CreateIndexQuery { + return nil +} + +func (_ *UpdateQuery) NewCreateTable() *CreateTableQuery { + return nil +} + +func (_ *UpdateQuery) NewDelete() *DeleteQuery { + return nil +} + +func (_ *UpdateQuery) NewDropColumn() *DropColumnQuery { + return nil +} + +func (_ *UpdateQuery) NewDropIndex() *DropIndexQuery { + return nil +} + +func (_ *UpdateQuery) NewDropTable() *DropTableQuery { + return nil +} + +func (_ *UpdateQuery) NewInsert() *InsertQuery { + return nil +} + +func (_ *UpdateQuery) NewRaw(_ string, _ ...interface{}) *RawQuery { + return nil +} + +func (_ *UpdateQuery) NewSelect() *SelectQuery { + return nil +} + +func (_ *UpdateQuery) NewTruncateTable() *TruncateTableQuery { + return nil +} + +func (_ *UpdateQuery) NewUpdate() *UpdateQuery { + return nil +} + +func (_ *UpdateQuery) NewValues(_ interface{}) *ValuesQuery { + return nil +} + +func (_ *UpdateQuery) OmitZero() *UpdateQuery { + return nil +} + +func (_ *UpdateQuery) Operation() string { + return "" +} + +func (_ *UpdateQuery) QueryBuilder() QueryBuilder { + return nil +} + +func (_ *UpdateQuery) Returning(_ string, _ ...interface{}) *UpdateQuery { + return nil +} + +func (_ *UpdateQuery) Scan(_ context.Context, _ ...interface{}) error { + return nil +} + +func (_ *UpdateQuery) Set(_ string, _ ...interface{}) *UpdateQuery { + return nil +} + +func (_ *UpdateQuery) SetColumn(_ string, _ string, _ ...interface{}) *UpdateQuery { + return nil +} + +func (_ *UpdateQuery) String() string { + return "" +} + +func (_ *UpdateQuery) Table(_ ...string) *UpdateQuery { + return nil +} + +func (_ *UpdateQuery) TableExpr(_ string, _ ...interface{}) *UpdateQuery { + return nil +} + +func (_ *UpdateQuery) UseIndex(_ ...string) *UpdateQuery { + return nil +} + +func (_ *UpdateQuery) Value(_ string, _ string, _ ...interface{}) *UpdateQuery { + return nil +} + +func (_ *UpdateQuery) Where(_ string, _ ...interface{}) *UpdateQuery { + return nil +} + +func (_ *UpdateQuery) WhereAllWithDeleted() *UpdateQuery { + return nil +} + +func (_ *UpdateQuery) WhereDeleted() *UpdateQuery { + return nil +} + +func (_ *UpdateQuery) WhereGroup(_ string, _ func(*UpdateQuery) *UpdateQuery) *UpdateQuery { + return nil +} + +func (_ *UpdateQuery) WhereOr(_ string, _ ...interface{}) *UpdateQuery { + return nil +} + +func (_ *UpdateQuery) WherePK(_ ...string) *UpdateQuery { + return nil +} + +func (_ *UpdateQuery) With(_ string, _ interface{}) *UpdateQuery { + return nil +} + +func (_ *UpdateQuery) WithRecursive(_ string, _ interface{}) *UpdateQuery { + return nil +} + +type ValuesQuery struct{} + +func (_ *ValuesQuery) AppendColumns(_ interface{}, _ []byte) ([]byte, error) { + return nil, nil +} + +func (_ *ValuesQuery) AppendNamedArg(_ interface{}, _ []byte, _ string) ([]byte, bool) { + return nil, false +} + +func (_ *ValuesQuery) AppendQuery(_ interface{}, _ []byte) ([]byte, error) { + return nil, nil +} + +func (_ *ValuesQuery) Column(_ ...string) *ValuesQuery { + return nil +} + +func (_ *ValuesQuery) Conn(_ IConn) *ValuesQuery { + return nil +} + +func (_ *ValuesQuery) DB() *DB { + return nil +} + +func (_ *ValuesQuery) Dialect() interface{} { + return nil +} + +func (_ *ValuesQuery) Err(_ error) *ValuesQuery { + return nil +} + +func (_ *ValuesQuery) GetConn() IConn { + return nil +} + +func (_ *ValuesQuery) GetModel() interface{} { + return nil +} + +func (_ *ValuesQuery) GetTableName() string { + return "" +} + +func (_ *ValuesQuery) NewAddColumn() *AddColumnQuery { + return nil +} + +func (_ *ValuesQuery) NewCreateIndex() *CreateIndexQuery { + return nil +} + +func (_ *ValuesQuery) NewCreateTable() *CreateTableQuery { + return nil +} + +func (_ *ValuesQuery) NewDelete() *DeleteQuery { + return nil +} + +func (_ *ValuesQuery) NewDropColumn() *DropColumnQuery { + return nil +} + +func (_ *ValuesQuery) NewDropIndex() *DropIndexQuery { + return nil +} + +func (_ *ValuesQuery) NewDropTable() *DropTableQuery { + return nil +} + +func (_ *ValuesQuery) NewInsert() *InsertQuery { + return nil +} + +func (_ *ValuesQuery) NewRaw(_ string, _ ...interface{}) *RawQuery { + return nil +} + +func (_ *ValuesQuery) NewSelect() *SelectQuery { + return nil +} + +func (_ *ValuesQuery) NewTruncateTable() *TruncateTableQuery { + return nil +} + +func (_ *ValuesQuery) NewUpdate() *UpdateQuery { + return nil +} + +func (_ *ValuesQuery) NewValues(_ interface{}) *ValuesQuery { + return nil +} + +func (_ *ValuesQuery) Operation() string { + return "" +} + +func (_ *ValuesQuery) Value(_ string, _ string, _ ...interface{}) *ValuesQuery { + return nil +} + +func (_ *ValuesQuery) WithOrder() *ValuesQuery { + return nil +} diff --git a/go/ql/test/library-tests/semmle/go/frameworks/SQL/bun/vendor/modules.txt b/go/ql/test/library-tests/semmle/go/frameworks/SQL/bun/vendor/modules.txt new file mode 100644 index 00000000000..03ac276ce98 --- /dev/null +++ b/go/ql/test/library-tests/semmle/go/frameworks/SQL/bun/vendor/modules.txt @@ -0,0 +1,78 @@ +# github.com/uptrace/bun v1.1.14 +## explicit +github.com/uptrace/bun +# github.com/uptrace/bun/dialect/sqlitedialect v1.1.14 +## explicit +github.com/uptrace/bun/dialect/sqlitedialect +# github.com/uptrace/bun/driver/sqliteshim v1.1.14 +## explicit +github.com/uptrace/bun/driver/sqliteshim +# github.com/dustin/go-humanize v1.0.1 +## explicit +github.com/dustin/go-humanize +# github.com/google/uuid v1.3.0 +## explicit +github.com/google/uuid +# github.com/jinzhu/inflection v1.0.0 +## explicit +github.com/jinzhu/inflection +# github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 +## explicit +github.com/kballard/go-shellquote +# github.com/mattn/go-isatty v0.0.19 +## explicit +github.com/mattn/go-isatty +# github.com/mattn/go-sqlite3 v1.14.16 +## explicit +github.com/mattn/go-sqlite3 +# github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec +## explicit +github.com/remyoudompheng/bigfft +# github.com/tmthrgd/go-hex v0.0.0-20190904060850-447a3041c3bc +## explicit +github.com/tmthrgd/go-hex +# github.com/vmihailenco/msgpack/v5 v5.3.5 +## explicit +github.com/vmihailenco/msgpack/v5 +# github.com/vmihailenco/tagparser/v2 v2.0.0 +## explicit +github.com/vmihailenco/tagparser/v2 +# golang.org/x/mod v0.10.0 +## explicit +golang.org/x/mod +# golang.org/x/sys v0.8.0 +## explicit +golang.org/x/sys +# golang.org/x/tools v0.9.1 +## explicit +golang.org/x/tools +# lukechampine.com/uint128 v1.3.0 +## explicit +lukechampine.com/uint128 +# modernc.org/cc/v3 v3.40.0 +## explicit +modernc.org/cc/v3 +# modernc.org/ccgo/v3 v3.16.13 +## explicit +modernc.org/ccgo/v3 +# modernc.org/libc v1.22.6 +## explicit +modernc.org/libc +# modernc.org/mathutil v1.5.0 +## explicit +modernc.org/mathutil +# modernc.org/memory v1.5.0 +## explicit +modernc.org/memory +# modernc.org/opt v0.1.3 +## explicit +modernc.org/opt +# modernc.org/sqlite v1.22.1 +## explicit +modernc.org/sqlite +# modernc.org/strutil v1.1.3 +## explicit +modernc.org/strutil +# modernc.org/token v1.1.0 +## explicit +modernc.org/token diff --git a/go/ql/test/library-tests/semmle/go/frameworks/SQL/go.mod b/go/ql/test/library-tests/semmle/go/frameworks/SQL/go.mod index 69db5c96c41..d6d79cd4a53 100644 --- a/go/ql/test/library-tests/semmle/go/frameworks/SQL/go.mod +++ b/go/ql/test/library-tests/semmle/go/frameworks/SQL/go.mod @@ -8,7 +8,10 @@ require ( github.com/go-pg/pg/v9 v9.1.3 github.com/go-sql-driver/mysql v1.6.0 // indirect github.com/go-xorm/xorm v0.7.9 + github.com/kr/text v0.2.0 // indirect github.com/lib/pq v1.10.2 // indirect - github.com/mattn/go-sqlite3 v1.14.7 // indirect + github.com/uptrace/bun v1.1.14 + github.com/uptrace/bun/dialect/sqlitedialect v1.1.14 + github.com/uptrace/bun/driver/sqliteshim v1.1.14 xorm.io/xorm v1.1.0 ) diff --git a/go/ql/test/library-tests/semmle/go/frameworks/TaintSteps/TaintStep.expected b/go/ql/test/library-tests/semmle/go/frameworks/TaintSteps/TaintStep.expected index da82753fd19..bc354e68d29 100644 --- a/go/ql/test/library-tests/semmle/go/frameworks/TaintSteps/TaintStep.expected +++ b/go/ql/test/library-tests/semmle/go/frameworks/TaintSteps/TaintStep.expected @@ -5,861 +5,861 @@ | crypto.go:11:18:11:57 | call to Open | crypto.go:11:2:11:57 | ... := ...[0] | | crypto.go:11:18:11:57 | call to Open | crypto.go:11:2:11:57 | ... := ...[1] | | crypto.go:11:42:11:51 | ciphertext | crypto.go:11:2:11:57 | ... := ...[0] | -| file://:0:0:0:0 | [summary param] 0 in Abs | file://:0:0:0:0 | [summary] to write: return (return[0]) in Abs | -| file://:0:0:0:0 | [summary param] 0 in Add | file://:0:0:0:0 | [summary] to write: argument -1 in Add | -| file://:0:0:0:0 | [summary param] 0 in Add | file://:0:0:0:0 | [summary] to write: argument -1 in Add | -| file://:0:0:0:0 | [summary param] 0 in AddCookie | file://:0:0:0:0 | [summary] to write: argument -1 in AddCookie | -| file://:0:0:0:0 | [summary param] 0 in AppendQuote | file://:0:0:0:0 | [summary] to write: return (return[0]) in AppendQuote | -| file://:0:0:0:0 | [summary param] 0 in AppendQuoteToASCII | file://:0:0:0:0 | [summary] to write: return (return[0]) in AppendQuoteToASCII | -| file://:0:0:0:0 | [summary param] 0 in AppendQuoteToGraphic | file://:0:0:0:0 | [summary] to write: return (return[0]) in AppendQuoteToGraphic | -| file://:0:0:0:0 | [summary param] 0 in AppendSlice | file://:0:0:0:0 | [summary] to write: return (return[0]) in AppendSlice | -| file://:0:0:0:0 | [summary param] 0 in As | file://:0:0:0:0 | [summary] to write: argument 1 in As | -| file://:0:0:0:0 | [summary param] 0 in Base | file://:0:0:0:0 | [summary] to write: return (return[0]) in Base | -| file://:0:0:0:0 | [summary param] 0 in Base | file://:0:0:0:0 | [summary] to write: return (return[0]) in Base | -| file://:0:0:0:0 | [summary param] 0 in BytePtrFromString | file://:0:0:0:0 | [summary] to write: return (return[0]) in BytePtrFromString | -| file://:0:0:0:0 | [summary param] 0 in ByteSliceFromString | file://:0:0:0:0 | [summary] to write: return (return[0]) in ByteSliceFromString | -| file://:0:0:0:0 | [summary param] 0 in CanonicalHeaderKey | file://:0:0:0:0 | [summary] to write: return (return[0]) in CanonicalHeaderKey | -| file://:0:0:0:0 | [summary param] 0 in CanonicalMIMEHeaderKey | file://:0:0:0:0 | [summary] to write: return (return[0]) in CanonicalMIMEHeaderKey | -| file://:0:0:0:0 | [summary param] 0 in Clean | file://:0:0:0:0 | [summary] to write: return (return[0]) in Clean | -| file://:0:0:0:0 | [summary param] 0 in Clean | file://:0:0:0:0 | [summary] to write: return (return[0]) in Clean | -| file://:0:0:0:0 | [summary param] 0 in Client | file://:0:0:0:0 | [summary] to write: return (return[0]) in Client | -| file://:0:0:0:0 | [summary param] 0 in Clone | file://:0:0:0:0 | [summary] to write: return (return[0]) in Clone | -| file://:0:0:0:0 | [summary param] 0 in Cut | file://:0:0:0:0 | [summary] to write: return (return[0]) in Cut | -| file://:0:0:0:0 | [summary param] 0 in Cut | file://:0:0:0:0 | [summary] to write: return (return[1]) in Cut | -| file://:0:0:0:0 | [summary param] 0 in CutPrefix | file://:0:0:0:0 | [summary] to write: return (return[0]) in CutPrefix | -| file://:0:0:0:0 | [summary param] 0 in CutSuffix | file://:0:0:0:0 | [summary] to write: return (return[0]) in CutSuffix | -| file://:0:0:0:0 | [summary param] 0 in Decode | file://:0:0:0:0 | [summary] to write: return (return[0]) in Decode | -| file://:0:0:0:0 | [summary param] 0 in Decode | file://:0:0:0:0 | [summary] to write: return (return[0]) in Decode | -| file://:0:0:0:0 | [summary param] 0 in Decode | file://:0:0:0:0 | [summary] to write: return (return[1]) in Decode | -| file://:0:0:0:0 | [summary param] 0 in DecodeHeader | file://:0:0:0:0 | [summary] to write: return (return[0]) in DecodeHeader | -| file://:0:0:0:0 | [summary param] 0 in DecodeString | file://:0:0:0:0 | [summary] to write: return (return[0]) in DecodeString | -| file://:0:0:0:0 | [summary param] 0 in DecodeString | file://:0:0:0:0 | [summary] to write: return (return[0]) in DecodeString | -| file://:0:0:0:0 | [summary param] 0 in DecryptPEMBlock | file://:0:0:0:0 | [summary] to write: return (return[0]) in DecryptPEMBlock | -| file://:0:0:0:0 | [summary param] 0 in Dir | file://:0:0:0:0 | [summary] to write: return (return[0]) in Dir | -| file://:0:0:0:0 | [summary param] 0 in Dir | file://:0:0:0:0 | [summary] to write: return (return[0]) in Dir | -| file://:0:0:0:0 | [summary param] 0 in Encode | file://:0:0:0:0 | [summary] to write: argument -1 in Encode | -| file://:0:0:0:0 | [summary param] 0 in EncodeToMemory | file://:0:0:0:0 | [summary] to write: return (return[0]) in EncodeToMemory | -| file://:0:0:0:0 | [summary param] 0 in EvalSymlinks | file://:0:0:0:0 | [summary] to write: return (return[0]) in EvalSymlinks | -| file://:0:0:0:0 | [summary param] 0 in Expand | file://:0:0:0:0 | [summary] to write: return (return[0]) in Expand | -| file://:0:0:0:0 | [summary param] 0 in ExpandEnv | file://:0:0:0:0 | [summary] to write: return (return[0]) in ExpandEnv | -| file://:0:0:0:0 | [summary param] 0 in Ext | file://:0:0:0:0 | [summary] to write: return (return[0]) in Ext | -| file://:0:0:0:0 | [summary param] 0 in Ext | file://:0:0:0:0 | [summary] to write: return (return[0]) in Ext | -| file://:0:0:0:0 | [summary param] 0 in Fields | file://:0:0:0:0 | [summary] to write: return (return[0]) in Fields | -| file://:0:0:0:0 | [summary param] 0 in Fields | file://:0:0:0:0 | [summary] to write: return (return[0]) in Fields | -| file://:0:0:0:0 | [summary param] 0 in FieldsFunc | file://:0:0:0:0 | [summary] to write: return (return[0]) in FieldsFunc | -| file://:0:0:0:0 | [summary param] 0 in FieldsFunc | file://:0:0:0:0 | [summary] to write: return (return[0]) in FieldsFunc | -| file://:0:0:0:0 | [summary param] 0 in FileConn | file://:0:0:0:0 | [summary] to write: return (return[0]) in FileConn | -| file://:0:0:0:0 | [summary param] 0 in FileInfoToDirEntry | file://:0:0:0:0 | [summary] to write: return (return[0]) in FileInfoToDirEntry | -| file://:0:0:0:0 | [summary param] 0 in FilePacketConn | file://:0:0:0:0 | [summary] to write: return (return[0]) in FilePacketConn | -| file://:0:0:0:0 | [summary param] 0 in FormatMediaType | file://:0:0:0:0 | [summary] to write: return (return[0]) in FormatMediaType | -| file://:0:0:0:0 | [summary param] 0 in FromSlash | file://:0:0:0:0 | [summary] to write: return (return[0]) in FromSlash | -| file://:0:0:0:0 | [summary param] 0 in Glob | file://:0:0:0:0 | [summary] to write: return (return[0]) in Glob | -| file://:0:0:0:0 | [summary param] 0 in Glob | file://:0:0:0:0 | [summary] to write: return (return[0]) in Glob | -| file://:0:0:0:0 | [summary param] 0 in Indirect | file://:0:0:0:0 | [summary] to write: return (return[0]) in Indirect | -| file://:0:0:0:0 | [summary param] 0 in InsertAfter | file://:0:0:0:0 | [summary] to write: argument -1 in InsertAfter | -| file://:0:0:0:0 | [summary param] 0 in InsertAfter | file://:0:0:0:0 | [summary] to write: return (return[0]) in InsertAfter | -| file://:0:0:0:0 | [summary param] 0 in InsertBefore | file://:0:0:0:0 | [summary] to write: argument -1 in InsertBefore | -| file://:0:0:0:0 | [summary param] 0 in InsertBefore | file://:0:0:0:0 | [summary] to write: return (return[0]) in InsertBefore | -| file://:0:0:0:0 | [summary param] 0 in Join | file://:0:0:0:0 | [summary] to write: return (return[0]) in Join | -| file://:0:0:0:0 | [summary param] 0 in Join | file://:0:0:0:0 | [summary] to write: return (return[0]) in Join | -| file://:0:0:0:0 | [summary param] 0 in JoinHostPort | file://:0:0:0:0 | [summary] to write: return (return[0]) in JoinHostPort | -| file://:0:0:0:0 | [summary param] 0 in LimitReader | file://:0:0:0:0 | [summary] to write: return (return[0]) in LimitReader | -| file://:0:0:0:0 | [summary param] 0 in LoadOrStore | file://:0:0:0:0 | [summary] to write: argument -1 in LoadOrStore | -| file://:0:0:0:0 | [summary param] 0 in LoadOrStore | file://:0:0:0:0 | [summary] to write: return (return[0]) in LoadOrStore | -| file://:0:0:0:0 | [summary param] 0 in LoadPointer | file://:0:0:0:0 | [summary] to write: return (return[0]) in LoadPointer | -| file://:0:0:0:0 | [summary param] 0 in LoadUintptr | file://:0:0:0:0 | [summary] to write: return (return[0]) in LoadUintptr | -| file://:0:0:0:0 | [summary param] 0 in Marshal | file://:0:0:0:0 | [summary] to write: return (return[0]) in Marshal | -| file://:0:0:0:0 | [summary param] 0 in Marshal | file://:0:0:0:0 | [summary] to write: return (return[0]) in Marshal | -| file://:0:0:0:0 | [summary param] 0 in MarshalIndent | file://:0:0:0:0 | [summary] to write: return (return[0]) in MarshalIndent | -| file://:0:0:0:0 | [summary param] 0 in MarshalWithParams | file://:0:0:0:0 | [summary] to write: return (return[0]) in MarshalWithParams | -| file://:0:0:0:0 | [summary param] 0 in MoveAfter | file://:0:0:0:0 | [summary] to write: argument -1 in MoveAfter | -| file://:0:0:0:0 | [summary param] 0 in MoveBefore | file://:0:0:0:0 | [summary] to write: argument -1 in MoveBefore | -| file://:0:0:0:0 | [summary param] 0 in MoveToBack | file://:0:0:0:0 | [summary] to write: argument -1 in MoveToBack | -| file://:0:0:0:0 | [summary param] 0 in MoveToFront | file://:0:0:0:0 | [summary] to write: argument -1 in MoveToFront | -| file://:0:0:0:0 | [summary param] 0 in New | file://:0:0:0:0 | [summary] to write: return (return[0]) in New | -| file://:0:0:0:0 | [summary param] 0 in NewBuffer | file://:0:0:0:0 | [summary] to write: return (return[0]) in NewBuffer | -| file://:0:0:0:0 | [summary param] 0 in NewBufferString | file://:0:0:0:0 | [summary] to write: return (return[0]) in NewBufferString | -| file://:0:0:0:0 | [summary param] 0 in NewConn | file://:0:0:0:0 | [summary] to write: return (return[0]) in NewConn | -| file://:0:0:0:0 | [summary param] 0 in NewDecoder | file://:0:0:0:0 | [summary] to write: return (return[0]) in NewDecoder | -| file://:0:0:0:0 | [summary param] 0 in NewDecoder | file://:0:0:0:0 | [summary] to write: return (return[0]) in NewDecoder | -| file://:0:0:0:0 | [summary param] 0 in NewFile | file://:0:0:0:0 | [summary] to write: return (return[0]) in NewFile | -| file://:0:0:0:0 | [summary param] 0 in NewListener | file://:0:0:0:0 | [summary] to write: return (return[0]) in NewListener | -| file://:0:0:0:0 | [summary param] 0 in NewReadWriter | file://:0:0:0:0 | [summary] to write: return (return[0]) in NewReadWriter | -| file://:0:0:0:0 | [summary param] 0 in NewReader | file://:0:0:0:0 | [summary] to write: return (return[0]) in NewReader | -| file://:0:0:0:0 | [summary param] 0 in NewReader | file://:0:0:0:0 | [summary] to write: return (return[0]) in NewReader | -| file://:0:0:0:0 | [summary param] 0 in NewReader | file://:0:0:0:0 | [summary] to write: return (return[0]) in NewReader | -| file://:0:0:0:0 | [summary param] 0 in NewReader | file://:0:0:0:0 | [summary] to write: return (return[0]) in NewReader | -| file://:0:0:0:0 | [summary param] 0 in NewReader | file://:0:0:0:0 | [summary] to write: return (return[0]) in NewReader | -| file://:0:0:0:0 | [summary param] 0 in NewReader | file://:0:0:0:0 | [summary] to write: return (return[0]) in NewReader | -| file://:0:0:0:0 | [summary param] 0 in NewReader | file://:0:0:0:0 | [summary] to write: return (return[0]) in NewReader | -| file://:0:0:0:0 | [summary param] 0 in NewReader | file://:0:0:0:0 | [summary] to write: return (return[0]) in NewReader | -| file://:0:0:0:0 | [summary param] 0 in NewReaderDict | file://:0:0:0:0 | [summary] to write: return (return[0]) in NewReaderDict | -| file://:0:0:0:0 | [summary param] 0 in NewReaderSize | file://:0:0:0:0 | [summary] to write: return (return[0]) in NewReaderSize | -| file://:0:0:0:0 | [summary param] 0 in NewScanner | file://:0:0:0:0 | [summary] to write: return (return[0]) in NewScanner | -| file://:0:0:0:0 | [summary param] 0 in NewSectionReader | file://:0:0:0:0 | [summary] to write: return (return[0]) in NewSectionReader | -| file://:0:0:0:0 | [summary param] 0 in NopCloser | file://:0:0:0:0 | [summary] to write: return (return[0]) in NopCloser | -| file://:0:0:0:0 | [summary param] 0 in NopCloser | file://:0:0:0:0 | [summary] to write: return (return[0]) in NopCloser | -| file://:0:0:0:0 | [summary param] 0 in Parse | file://:0:0:0:0 | [summary] to write: return (return[0]) in Parse | -| file://:0:0:0:0 | [summary param] 0 in Parse | file://:0:0:0:0 | [summary] to write: return (return[0]) in Parse | -| file://:0:0:0:0 | [summary param] 0 in ParseMediaType | file://:0:0:0:0 | [summary] to write: return (return[0]) in ParseMediaType | -| file://:0:0:0:0 | [summary param] 0 in ParseMediaType | file://:0:0:0:0 | [summary] to write: return (return[1]) in ParseMediaType | -| file://:0:0:0:0 | [summary param] 0 in ParseQuery | file://:0:0:0:0 | [summary] to write: return (return[0]) in ParseQuery | -| file://:0:0:0:0 | [summary param] 0 in ParseRequestURI | file://:0:0:0:0 | [summary] to write: return (return[0]) in ParseRequestURI | -| file://:0:0:0:0 | [summary param] 0 in PathEscape | file://:0:0:0:0 | [summary] to write: return (return[0]) in PathEscape | -| file://:0:0:0:0 | [summary param] 0 in PathUnescape | file://:0:0:0:0 | [summary] to write: return (return[0]) in PathUnescape | -| file://:0:0:0:0 | [summary param] 0 in PushBack | file://:0:0:0:0 | [summary] to write: argument -1 in PushBack | -| file://:0:0:0:0 | [summary param] 0 in PushBack | file://:0:0:0:0 | [summary] to write: return (return[0]) in PushBack | -| file://:0:0:0:0 | [summary param] 0 in PushBackList | file://:0:0:0:0 | [summary] to write: argument -1 in PushBackList | -| file://:0:0:0:0 | [summary param] 0 in PushFront | file://:0:0:0:0 | [summary] to write: argument -1 in PushFront | -| file://:0:0:0:0 | [summary param] 0 in PushFront | file://:0:0:0:0 | [summary] to write: return (return[0]) in PushFront | -| file://:0:0:0:0 | [summary param] 0 in PushFrontList | file://:0:0:0:0 | [summary] to write: argument -1 in PushFrontList | -| file://:0:0:0:0 | [summary param] 0 in Put | file://:0:0:0:0 | [summary] to write: argument -1 in Put | -| file://:0:0:0:0 | [summary param] 0 in QueryEscape | file://:0:0:0:0 | [summary] to write: return (return[0]) in QueryEscape | -| file://:0:0:0:0 | [summary param] 0 in QueryUnescape | file://:0:0:0:0 | [summary] to write: return (return[0]) in QueryUnescape | -| file://:0:0:0:0 | [summary param] 0 in Quote | file://:0:0:0:0 | [summary] to write: return (return[0]) in Quote | -| file://:0:0:0:0 | [summary param] 0 in QuoteToASCII | file://:0:0:0:0 | [summary] to write: return (return[0]) in QuoteToASCII | -| file://:0:0:0:0 | [summary param] 0 in QuoteToGraphic | file://:0:0:0:0 | [summary] to write: return (return[0]) in QuoteToGraphic | -| file://:0:0:0:0 | [summary param] 0 in QuotedPrefix | file://:0:0:0:0 | [summary] to write: return (return[0]) in QuotedPrefix | -| file://:0:0:0:0 | [summary param] 0 in Read | file://:0:0:0:0 | [summary] to write: argument 2 in Read | -| file://:0:0:0:0 | [summary param] 0 in ReadAll | file://:0:0:0:0 | [summary] to write: return (return[0]) in ReadAll | -| file://:0:0:0:0 | [summary param] 0 in ReadAll | file://:0:0:0:0 | [summary] to write: return (return[0]) in ReadAll | -| file://:0:0:0:0 | [summary param] 0 in ReadAtLeast | file://:0:0:0:0 | [summary] to write: argument 1 in ReadAtLeast | -| file://:0:0:0:0 | [summary param] 0 in ReadDir | file://:0:0:0:0 | [summary] to write: return (return[0]) in ReadDir | -| file://:0:0:0:0 | [summary param] 0 in ReadFile | file://:0:0:0:0 | [summary] to write: return (return[0]) in ReadFile | -| file://:0:0:0:0 | [summary param] 0 in ReadFrom | file://:0:0:0:0 | [summary] to write: argument -1 in ReadFrom | -| file://:0:0:0:0 | [summary param] 0 in ReadFrom | file://:0:0:0:0 | [summary] to write: argument -1 in ReadFrom | -| file://:0:0:0:0 | [summary param] 0 in ReadFrom | file://:0:0:0:0 | [summary] to write: argument -1 in ReadFrom | -| file://:0:0:0:0 | [summary param] 0 in ReadFrom | file://:0:0:0:0 | [summary] to write: argument -1 in ReadFrom | -| file://:0:0:0:0 | [summary param] 0 in ReadFrom | file://:0:0:0:0 | [summary] to write: argument -1 in ReadFrom | -| file://:0:0:0:0 | [summary param] 0 in ReadFrom | file://:0:0:0:0 | [summary] to write: argument -1 in ReadFrom | -| file://:0:0:0:0 | [summary param] 0 in ReadFrom | file://:0:0:0:0 | [summary] to write: argument -1 in ReadFrom | -| file://:0:0:0:0 | [summary param] 0 in ReadFrom | file://:0:0:0:0 | [summary] to write: argument -1 in ReadFrom | -| file://:0:0:0:0 | [summary param] 0 in ReadFull | file://:0:0:0:0 | [summary] to write: argument 1 in ReadFull | -| file://:0:0:0:0 | [summary param] 0 in ReadRequest | file://:0:0:0:0 | [summary] to write: return (return[0]) in ReadRequest | -| file://:0:0:0:0 | [summary param] 0 in ReadResponse | file://:0:0:0:0 | [summary] to write: return (return[0]) in ReadResponse | -| file://:0:0:0:0 | [summary param] 0 in Rel | file://:0:0:0:0 | [summary] to write: return (return[0]) in Rel | -| file://:0:0:0:0 | [summary param] 0 in Remove | file://:0:0:0:0 | [summary] to write: return (return[0]) in Remove | -| file://:0:0:0:0 | [summary param] 0 in Repeat | file://:0:0:0:0 | [summary] to write: return (return[0]) in Repeat | -| file://:0:0:0:0 | [summary param] 0 in Repeat | file://:0:0:0:0 | [summary] to write: return (return[0]) in Repeat | -| file://:0:0:0:0 | [summary param] 0 in Replace | file://:0:0:0:0 | [summary] to write: return (return[0]) in Replace | -| file://:0:0:0:0 | [summary param] 0 in Replace | file://:0:0:0:0 | [summary] to write: return (return[0]) in Replace | -| file://:0:0:0:0 | [summary param] 0 in Replace | file://:0:0:0:0 | [summary] to write: return (return[0]) in Replace | -| file://:0:0:0:0 | [summary param] 0 in ReplaceAll | file://:0:0:0:0 | [summary] to write: return (return[0]) in ReplaceAll | -| file://:0:0:0:0 | [summary param] 0 in ReplaceAll | file://:0:0:0:0 | [summary] to write: return (return[0]) in ReplaceAll | -| file://:0:0:0:0 | [summary param] 0 in Reset | file://:0:0:0:0 | [summary] to write: argument -1 in Reset | -| file://:0:0:0:0 | [summary param] 0 in Reset | file://:0:0:0:0 | [summary] to write: argument -1 in Reset | -| file://:0:0:0:0 | [summary param] 0 in Reset | file://:0:0:0:0 | [summary] to write: argument -1 in Reset | -| file://:0:0:0:0 | [summary param] 0 in Reset | file://:0:0:0:0 | [summary] to write: argument -1 in Reset | -| file://:0:0:0:0 | [summary param] 0 in Reset | file://:0:0:0:0 | [summary] to write: argument -1 in Reset | -| file://:0:0:0:0 | [summary param] 0 in Reset | file://:0:0:0:0 | [summary] to write: argument -1 in Reset | -| file://:0:0:0:0 | [summary param] 0 in ResolveReference | file://:0:0:0:0 | [summary] to write: return (return[0]) in ResolveReference | -| file://:0:0:0:0 | [summary param] 0 in Reverse | file://:0:0:0:0 | [summary] to write: return (return[0]) in Reverse | -| file://:0:0:0:0 | [summary param] 0 in Runes | file://:0:0:0:0 | [summary] to write: return (return[0]) in Runes | -| file://:0:0:0:0 | [summary param] 0 in ScanBytes | file://:0:0:0:0 | [summary] to write: return (return[1]) in ScanBytes | -| file://:0:0:0:0 | [summary param] 0 in ScanLines | file://:0:0:0:0 | [summary] to write: return (return[1]) in ScanLines | -| file://:0:0:0:0 | [summary param] 0 in ScanRunes | file://:0:0:0:0 | [summary] to write: return (return[1]) in ScanRunes | -| file://:0:0:0:0 | [summary param] 0 in ScanWords | file://:0:0:0:0 | [summary] to write: return (return[1]) in ScanWords | -| file://:0:0:0:0 | [summary param] 0 in Send | file://:0:0:0:0 | [summary] to write: argument -1 in Send | -| file://:0:0:0:0 | [summary param] 0 in Server | file://:0:0:0:0 | [summary] to write: return (return[0]) in Server | -| file://:0:0:0:0 | [summary param] 0 in Set | file://:0:0:0:0 | [summary] to write: argument -1 in Set | -| file://:0:0:0:0 | [summary param] 0 in Set | file://:0:0:0:0 | [summary] to write: argument -1 in Set | -| file://:0:0:0:0 | [summary param] 0 in Set | file://:0:0:0:0 | [summary] to write: argument -1 in Set | -| file://:0:0:0:0 | [summary param] 0 in SetBytes | file://:0:0:0:0 | [summary] to write: argument -1 in SetBytes | -| file://:0:0:0:0 | [summary param] 0 in SetIndent | file://:0:0:0:0 | [summary] to write: argument -1 in SetIndent | -| file://:0:0:0:0 | [summary param] 0 in SetMapIndex | file://:0:0:0:0 | [summary] to write: argument -1 in SetMapIndex | -| file://:0:0:0:0 | [summary param] 0 in SetPointer | file://:0:0:0:0 | [summary] to write: argument -1 in SetPointer | -| file://:0:0:0:0 | [summary param] 0 in SetPrefix | file://:0:0:0:0 | [summary] to write: argument -1 in SetPrefix | -| file://:0:0:0:0 | [summary param] 0 in SetString | file://:0:0:0:0 | [summary] to write: argument -1 in SetString | -| file://:0:0:0:0 | [summary param] 0 in Split | file://:0:0:0:0 | [summary] to write: return (return[0]) in Split | -| file://:0:0:0:0 | [summary param] 0 in Split | file://:0:0:0:0 | [summary] to write: return (return[0]) in Split | -| file://:0:0:0:0 | [summary param] 0 in Split | file://:0:0:0:0 | [summary] to write: return (return[0]) in Split | -| file://:0:0:0:0 | [summary param] 0 in Split | file://:0:0:0:0 | [summary] to write: return (return[0]) in Split | -| file://:0:0:0:0 | [summary param] 0 in Split | file://:0:0:0:0 | [summary] to write: return (return[1]) in Split | -| file://:0:0:0:0 | [summary param] 0 in Split | file://:0:0:0:0 | [summary] to write: return (return[1]) in Split | -| file://:0:0:0:0 | [summary param] 0 in SplitAfter | file://:0:0:0:0 | [summary] to write: return (return[0]) in SplitAfter | -| file://:0:0:0:0 | [summary param] 0 in SplitAfter | file://:0:0:0:0 | [summary] to write: return (return[0]) in SplitAfter | -| file://:0:0:0:0 | [summary param] 0 in SplitAfterN | file://:0:0:0:0 | [summary] to write: return (return[0]) in SplitAfterN | -| file://:0:0:0:0 | [summary param] 0 in SplitAfterN | file://:0:0:0:0 | [summary] to write: return (return[0]) in SplitAfterN | -| file://:0:0:0:0 | [summary param] 0 in SplitHostPort | file://:0:0:0:0 | [summary] to write: return (return[0]) in SplitHostPort | -| file://:0:0:0:0 | [summary param] 0 in SplitHostPort | file://:0:0:0:0 | [summary] to write: return (return[1]) in SplitHostPort | -| file://:0:0:0:0 | [summary param] 0 in SplitList | file://:0:0:0:0 | [summary] to write: return (return[0]) in SplitList | -| file://:0:0:0:0 | [summary param] 0 in SplitN | file://:0:0:0:0 | [summary] to write: return (return[0]) in SplitN | -| file://:0:0:0:0 | [summary param] 0 in SplitN | file://:0:0:0:0 | [summary] to write: return (return[0]) in SplitN | -| file://:0:0:0:0 | [summary param] 0 in Store | file://:0:0:0:0 | [summary] to write: argument -1 in Store | -| file://:0:0:0:0 | [summary param] 0 in Store | file://:0:0:0:0 | [summary] to write: argument -1 in Store | -| file://:0:0:0:0 | [summary param] 0 in Store | file://:0:0:0:0 | [summary] to write: argument -1 in Store | -| file://:0:0:0:0 | [summary param] 0 in Store | file://:0:0:0:0 | [summary] to write: argument -1 in Store | -| file://:0:0:0:0 | [summary param] 0 in StringBytePtr | file://:0:0:0:0 | [summary] to write: return (return[0]) in StringBytePtr | -| file://:0:0:0:0 | [summary param] 0 in StringByteSlice | file://:0:0:0:0 | [summary] to write: return (return[0]) in StringByteSlice | -| file://:0:0:0:0 | [summary param] 0 in Sub | file://:0:0:0:0 | [summary] to write: return (return[0]) in Sub | -| file://:0:0:0:0 | [summary param] 0 in Swap | file://:0:0:0:0 | [summary] to write: argument -1 in Swap | -| file://:0:0:0:0 | [summary param] 0 in Swap | file://:0:0:0:0 | [summary] to write: argument -1 in Swap | -| file://:0:0:0:0 | [summary param] 0 in Swap | file://:0:0:0:0 | [summary] to write: argument -1 in Swap | -| file://:0:0:0:0 | [summary param] 0 in Swap | file://:0:0:0:0 | [summary] to write: argument -1 in Swap | -| file://:0:0:0:0 | [summary param] 0 in SwapPointer | file://:0:0:0:0 | [summary] to write: return (return[0]) in SwapPointer | -| file://:0:0:0:0 | [summary param] 0 in SwapUintptr | file://:0:0:0:0 | [summary] to write: return (return[0]) in SwapUintptr | -| file://:0:0:0:0 | [summary param] 0 in TeeReader | file://:0:0:0:0 | [summary] to write: argument 1 in TeeReader | -| file://:0:0:0:0 | [summary param] 0 in TeeReader | file://:0:0:0:0 | [summary] to write: return (return[0]) in TeeReader | -| file://:0:0:0:0 | [summary param] 0 in Title | file://:0:0:0:0 | [summary] to write: return (return[0]) in Title | -| file://:0:0:0:0 | [summary param] 0 in Title | file://:0:0:0:0 | [summary] to write: return (return[0]) in Title | -| file://:0:0:0:0 | [summary param] 0 in ToLower | file://:0:0:0:0 | [summary] to write: return (return[0]) in ToLower | -| file://:0:0:0:0 | [summary param] 0 in ToLower | file://:0:0:0:0 | [summary] to write: return (return[0]) in ToLower | -| file://:0:0:0:0 | [summary param] 0 in ToSlash | file://:0:0:0:0 | [summary] to write: return (return[0]) in ToSlash | -| file://:0:0:0:0 | [summary param] 0 in ToTitle | file://:0:0:0:0 | [summary] to write: return (return[0]) in ToTitle | -| file://:0:0:0:0 | [summary param] 0 in ToTitle | file://:0:0:0:0 | [summary] to write: return (return[0]) in ToTitle | -| file://:0:0:0:0 | [summary param] 0 in ToUpper | file://:0:0:0:0 | [summary] to write: return (return[0]) in ToUpper | -| file://:0:0:0:0 | [summary param] 0 in ToUpper | file://:0:0:0:0 | [summary] to write: return (return[0]) in ToUpper | -| file://:0:0:0:0 | [summary param] 0 in ToValidUTF8 | file://:0:0:0:0 | [summary] to write: return (return[0]) in ToValidUTF8 | -| file://:0:0:0:0 | [summary param] 0 in ToValidUTF8 | file://:0:0:0:0 | [summary] to write: return (return[0]) in ToValidUTF8 | -| file://:0:0:0:0 | [summary param] 0 in Trim | file://:0:0:0:0 | [summary] to write: return (return[0]) in Trim | -| file://:0:0:0:0 | [summary param] 0 in Trim | file://:0:0:0:0 | [summary] to write: return (return[0]) in Trim | -| file://:0:0:0:0 | [summary param] 0 in TrimBytes | file://:0:0:0:0 | [summary] to write: return (return[0]) in TrimBytes | -| file://:0:0:0:0 | [summary param] 0 in TrimFunc | file://:0:0:0:0 | [summary] to write: return (return[0]) in TrimFunc | -| file://:0:0:0:0 | [summary param] 0 in TrimFunc | file://:0:0:0:0 | [summary] to write: return (return[0]) in TrimFunc | -| file://:0:0:0:0 | [summary param] 0 in TrimLeft | file://:0:0:0:0 | [summary] to write: return (return[0]) in TrimLeft | -| file://:0:0:0:0 | [summary param] 0 in TrimLeft | file://:0:0:0:0 | [summary] to write: return (return[0]) in TrimLeft | -| file://:0:0:0:0 | [summary param] 0 in TrimLeftFunc | file://:0:0:0:0 | [summary] to write: return (return[0]) in TrimLeftFunc | -| file://:0:0:0:0 | [summary param] 0 in TrimLeftFunc | file://:0:0:0:0 | [summary] to write: return (return[0]) in TrimLeftFunc | -| file://:0:0:0:0 | [summary param] 0 in TrimPrefix | file://:0:0:0:0 | [summary] to write: return (return[0]) in TrimPrefix | -| file://:0:0:0:0 | [summary param] 0 in TrimPrefix | file://:0:0:0:0 | [summary] to write: return (return[0]) in TrimPrefix | -| file://:0:0:0:0 | [summary param] 0 in TrimRight | file://:0:0:0:0 | [summary] to write: return (return[0]) in TrimRight | -| file://:0:0:0:0 | [summary param] 0 in TrimRight | file://:0:0:0:0 | [summary] to write: return (return[0]) in TrimRight | -| file://:0:0:0:0 | [summary param] 0 in TrimRightFunc | file://:0:0:0:0 | [summary] to write: return (return[0]) in TrimRightFunc | -| file://:0:0:0:0 | [summary param] 0 in TrimRightFunc | file://:0:0:0:0 | [summary] to write: return (return[0]) in TrimRightFunc | -| file://:0:0:0:0 | [summary param] 0 in TrimSpace | file://:0:0:0:0 | [summary] to write: return (return[0]) in TrimSpace | -| file://:0:0:0:0 | [summary param] 0 in TrimSpace | file://:0:0:0:0 | [summary] to write: return (return[0]) in TrimSpace | -| file://:0:0:0:0 | [summary param] 0 in TrimString | file://:0:0:0:0 | [summary] to write: return (return[0]) in TrimString | -| file://:0:0:0:0 | [summary param] 0 in TrimSuffix | file://:0:0:0:0 | [summary] to write: return (return[0]) in TrimSuffix | -| file://:0:0:0:0 | [summary param] 0 in TrimSuffix | file://:0:0:0:0 | [summary] to write: return (return[0]) in TrimSuffix | -| file://:0:0:0:0 | [summary param] 0 in TrySend | file://:0:0:0:0 | [summary] to write: argument -1 in TrySend | -| file://:0:0:0:0 | [summary param] 0 in Unmarshal | file://:0:0:0:0 | [summary] to write: argument 1 in Unmarshal | -| file://:0:0:0:0 | [summary param] 0 in Unmarshal | file://:0:0:0:0 | [summary] to write: argument 1 in Unmarshal | -| file://:0:0:0:0 | [summary param] 0 in Unmarshal | file://:0:0:0:0 | [summary] to write: return (return[0]) in Unmarshal | -| file://:0:0:0:0 | [summary param] 0 in UnmarshalBinary | file://:0:0:0:0 | [summary] to write: argument -1 in UnmarshalBinary | -| file://:0:0:0:0 | [summary param] 0 in UnmarshalBinary | file://:0:0:0:0 | [summary] to write: argument -1 in UnmarshalBinary | -| file://:0:0:0:0 | [summary param] 0 in UnmarshalBinary | file://:0:0:0:0 | [summary] to write: argument -1 in UnmarshalBinary | -| file://:0:0:0:0 | [summary param] 0 in UnmarshalBinary | file://:0:0:0:0 | [summary] to write: argument -1 in UnmarshalBinary | -| file://:0:0:0:0 | [summary param] 0 in UnmarshalBinary | file://:0:0:0:0 | [summary] to write: argument -1 in UnmarshalBinary | -| file://:0:0:0:0 | [summary param] 0 in UnmarshalBinary | file://:0:0:0:0 | [summary] to write: argument -1 in UnmarshalBinary | -| file://:0:0:0:0 | [summary param] 0 in UnmarshalBinary | file://:0:0:0:0 | [summary] to write: argument -1 in UnmarshalBinary | -| file://:0:0:0:0 | [summary param] 0 in UnmarshalBinary | file://:0:0:0:0 | [summary] to write: argument -1 in UnmarshalBinary | -| file://:0:0:0:0 | [summary param] 0 in UnmarshalBinary | file://:0:0:0:0 | [summary] to write: argument -1 in UnmarshalBinary | -| file://:0:0:0:0 | [summary param] 0 in UnmarshalBinary | file://:0:0:0:0 | [summary] to write: argument -1 in UnmarshalBinary | -| file://:0:0:0:0 | [summary param] 0 in UnmarshalBinary | file://:0:0:0:0 | [summary] to write: argument -1 in UnmarshalBinary | -| file://:0:0:0:0 | [summary param] 0 in UnmarshalBinary | file://:0:0:0:0 | [summary] to write: argument -1 in UnmarshalBinary | -| file://:0:0:0:0 | [summary param] 0 in UnmarshalJSON | file://:0:0:0:0 | [summary] to write: argument -1 in UnmarshalJSON | -| file://:0:0:0:0 | [summary param] 0 in UnmarshalJSON | file://:0:0:0:0 | [summary] to write: argument -1 in UnmarshalJSON | -| file://:0:0:0:0 | [summary param] 0 in UnmarshalJSON | file://:0:0:0:0 | [summary] to write: argument -1 in UnmarshalJSON | -| file://:0:0:0:0 | [summary param] 0 in UnmarshalJSON | file://:0:0:0:0 | [summary] to write: argument -1 in UnmarshalJSON | -| file://:0:0:0:0 | [summary param] 0 in UnmarshalText | file://:0:0:0:0 | [summary] to write: argument -1 in UnmarshalText | -| file://:0:0:0:0 | [summary param] 0 in UnmarshalText | file://:0:0:0:0 | [summary] to write: argument -1 in UnmarshalText | -| file://:0:0:0:0 | [summary param] 0 in UnmarshalText | file://:0:0:0:0 | [summary] to write: argument -1 in UnmarshalText | -| file://:0:0:0:0 | [summary param] 0 in UnmarshalText | file://:0:0:0:0 | [summary] to write: argument -1 in UnmarshalText | -| file://:0:0:0:0 | [summary param] 0 in UnmarshalText | file://:0:0:0:0 | [summary] to write: argument -1 in UnmarshalText | -| file://:0:0:0:0 | [summary param] 0 in UnmarshalText | file://:0:0:0:0 | [summary] to write: argument -1 in UnmarshalText | -| file://:0:0:0:0 | [summary param] 0 in UnmarshalText | file://:0:0:0:0 | [summary] to write: argument -1 in UnmarshalText | -| file://:0:0:0:0 | [summary param] 0 in UnmarshalText | file://:0:0:0:0 | [summary] to write: argument -1 in UnmarshalText | -| file://:0:0:0:0 | [summary param] 0 in UnmarshalText | file://:0:0:0:0 | [summary] to write: argument -1 in UnmarshalText | -| file://:0:0:0:0 | [summary param] 0 in UnmarshalWithParams | file://:0:0:0:0 | [summary] to write: argument 1 in UnmarshalWithParams | -| file://:0:0:0:0 | [summary param] 0 in UnmarshalWithParams | file://:0:0:0:0 | [summary] to write: return (return[0]) in UnmarshalWithParams | -| file://:0:0:0:0 | [summary param] 0 in Unquote | file://:0:0:0:0 | [summary] to write: return (return[0]) in Unquote | -| file://:0:0:0:0 | [summary param] 0 in UnquoteChar | file://:0:0:0:0 | [summary] to write: return (return[2]) in UnquoteChar | -| file://:0:0:0:0 | [summary param] 0 in Unwrap | file://:0:0:0:0 | [summary] to write: return (return[0]) in Unwrap | -| file://:0:0:0:0 | [summary param] 0 in User | file://:0:0:0:0 | [summary] to write: return (return[0]) in User | -| file://:0:0:0:0 | [summary param] 0 in UserPassword | file://:0:0:0:0 | [summary] to write: return (return[0]) in UserPassword | -| file://:0:0:0:0 | [summary param] 0 in ValueOf | file://:0:0:0:0 | [summary] to write: return (return[0]) in ValueOf | -| file://:0:0:0:0 | [summary param] 0 in VolumeName | file://:0:0:0:0 | [summary] to write: return (return[0]) in VolumeName | -| file://:0:0:0:0 | [summary param] 0 in WithCancel | file://:0:0:0:0 | [summary] to write: return (return[0]) in WithCancel | -| file://:0:0:0:0 | [summary param] 0 in WithDeadline | file://:0:0:0:0 | [summary] to write: return (return[0]) in WithDeadline | -| file://:0:0:0:0 | [summary param] 0 in WithTimeout | file://:0:0:0:0 | [summary] to write: return (return[0]) in WithTimeout | -| file://:0:0:0:0 | [summary param] 0 in WithValue | file://:0:0:0:0 | [summary] to write: return (return[0]) in WithValue | -| file://:0:0:0:0 | [summary param] 0 in Write | file://:0:0:0:0 | [summary] to write: argument -1 in Write | -| file://:0:0:0:0 | [summary param] 0 in Write | file://:0:0:0:0 | [summary] to write: argument -1 in Write | -| file://:0:0:0:0 | [summary param] 0 in Write | file://:0:0:0:0 | [summary] to write: argument -1 in Write | -| file://:0:0:0:0 | [summary param] 0 in Write | file://:0:0:0:0 | [summary] to write: argument -1 in Write | -| file://:0:0:0:0 | [summary param] 0 in Write | file://:0:0:0:0 | [summary] to write: argument -1 in Write | -| file://:0:0:0:0 | [summary param] 0 in Write | file://:0:0:0:0 | [summary] to write: argument -1 in Write | -| file://:0:0:0:0 | [summary param] 0 in Write | file://:0:0:0:0 | [summary] to write: argument -1 in Write | -| file://:0:0:0:0 | [summary param] 0 in Write | file://:0:0:0:0 | [summary] to write: argument -1 in Write | -| file://:0:0:0:0 | [summary param] 0 in Write | file://:0:0:0:0 | [summary] to write: argument -1 in Write | -| file://:0:0:0:0 | [summary param] 0 in Write | file://:0:0:0:0 | [summary] to write: argument -1 in Write | -| file://:0:0:0:0 | [summary param] 0 in Write | file://:0:0:0:0 | [summary] to write: argument -1 in Write | -| file://:0:0:0:0 | [summary param] 0 in Write | file://:0:0:0:0 | [summary] to write: argument -1 in Write | -| file://:0:0:0:0 | [summary param] 0 in Write | file://:0:0:0:0 | [summary] to write: argument -1 in Write | -| file://:0:0:0:0 | [summary param] 0 in Write | file://:0:0:0:0 | [summary] to write: argument -1 in Write | -| file://:0:0:0:0 | [summary param] 0 in Write | file://:0:0:0:0 | [summary] to write: argument -1 in Write | -| file://:0:0:0:0 | [summary param] 0 in Write | file://:0:0:0:0 | [summary] to write: argument -1 in Write | -| file://:0:0:0:0 | [summary param] 0 in Write | file://:0:0:0:0 | [summary] to write: argument -1 in Write | -| file://:0:0:0:0 | [summary param] 0 in Write | file://:0:0:0:0 | [summary] to write: argument -1 in Write | -| file://:0:0:0:0 | [summary param] 0 in Write | file://:0:0:0:0 | [summary] to write: argument -1 in Write | -| file://:0:0:0:0 | [summary param] 0 in Write | file://:0:0:0:0 | [summary] to write: argument -1 in Write | -| file://:0:0:0:0 | [summary param] 0 in Write | file://:0:0:0:0 | [summary] to write: argument -1 in Write | -| file://:0:0:0:0 | [summary param] 0 in Write | file://:0:0:0:0 | [summary] to write: argument -1 in Write | -| file://:0:0:0:0 | [summary param] 0 in Write | file://:0:0:0:0 | [summary] to write: argument -1 in Write | -| file://:0:0:0:0 | [summary param] 0 in Write | file://:0:0:0:0 | [summary] to write: argument -1 in Write | -| file://:0:0:0:0 | [summary param] 0 in Write | file://:0:0:0:0 | [summary] to write: argument -1 in Write | -| file://:0:0:0:0 | [summary param] 0 in Write | file://:0:0:0:0 | [summary] to write: argument -1 in Write | -| file://:0:0:0:0 | [summary param] 0 in Write | file://:0:0:0:0 | [summary] to write: argument -1 in Write | -| file://:0:0:0:0 | [summary param] 0 in Write | file://:0:0:0:0 | [summary] to write: argument -1 in Write | -| file://:0:0:0:0 | [summary param] 0 in Write | file://:0:0:0:0 | [summary] to write: argument -1 in Write | -| file://:0:0:0:0 | [summary param] 0 in Write | file://:0:0:0:0 | [summary] to write: argument -1 in Write | -| file://:0:0:0:0 | [summary param] 0 in Write | file://:0:0:0:0 | [summary] to write: argument -1 in Write | -| file://:0:0:0:0 | [summary param] 0 in Write | file://:0:0:0:0 | [summary] to write: argument -1 in Write | -| file://:0:0:0:0 | [summary param] 0 in Write | file://:0:0:0:0 | [summary] to write: argument -1 in Write | -| file://:0:0:0:0 | [summary param] 0 in Write | file://:0:0:0:0 | [summary] to write: argument -1 in Write | -| file://:0:0:0:0 | [summary param] 0 in Write | file://:0:0:0:0 | [summary] to write: argument -1 in Write | -| file://:0:0:0:0 | [summary param] 0 in Write | file://:0:0:0:0 | [summary] to write: argument -1 in Write | -| file://:0:0:0:0 | [summary param] 0 in Write | file://:0:0:0:0 | [summary] to write: argument -1 in Write | -| file://:0:0:0:0 | [summary param] 0 in Write | file://:0:0:0:0 | [summary] to write: argument -1 in Write | -| file://:0:0:0:0 | [summary param] 0 in Write | file://:0:0:0:0 | [summary] to write: argument -1 in Write | -| file://:0:0:0:0 | [summary param] 0 in Write | file://:0:0:0:0 | [summary] to write: argument -1 in Write | -| file://:0:0:0:0 | [summary param] 0 in Write | file://:0:0:0:0 | [summary] to write: argument -1 in Write | -| file://:0:0:0:0 | [summary param] 0 in Write | file://:0:0:0:0 | [summary] to write: argument -1 in Write | -| file://:0:0:0:0 | [summary param] 0 in Write | file://:0:0:0:0 | [summary] to write: argument -1 in Write | -| file://:0:0:0:0 | [summary param] 0 in Write | file://:0:0:0:0 | [summary] to write: argument -1 in Write | -| file://:0:0:0:0 | [summary param] 0 in Write | file://:0:0:0:0 | [summary] to write: argument -1 in Write | -| file://:0:0:0:0 | [summary param] 0 in Write | file://:0:0:0:0 | [summary] to write: argument -1 in Write | -| file://:0:0:0:0 | [summary param] 0 in Write | file://:0:0:0:0 | [summary] to write: argument -1 in Write | -| file://:0:0:0:0 | [summary param] 0 in Write | file://:0:0:0:0 | [summary] to write: argument -1 in Write | -| file://:0:0:0:0 | [summary param] 0 in Write | file://:0:0:0:0 | [summary] to write: argument -1 in Write | -| file://:0:0:0:0 | [summary param] 0 in Write | file://:0:0:0:0 | [summary] to write: argument -1 in Write | -| file://:0:0:0:0 | [summary param] 0 in Write | file://:0:0:0:0 | [summary] to write: argument -1 in Write | -| file://:0:0:0:0 | [summary param] 0 in Write | file://:0:0:0:0 | [summary] to write: argument -1 in Write | -| file://:0:0:0:0 | [summary param] 0 in Write | file://:0:0:0:0 | [summary] to write: argument -1 in Write | -| file://:0:0:0:0 | [summary param] 0 in Write | file://:0:0:0:0 | [summary] to write: argument -1 in Write | -| file://:0:0:0:0 | [summary param] 0 in Write | file://:0:0:0:0 | [summary] to write: argument -1 in Write | -| file://:0:0:0:0 | [summary param] 0 in Write | file://:0:0:0:0 | [summary] to write: argument -1 in Write | -| file://:0:0:0:0 | [summary param] 0 in Write | file://:0:0:0:0 | [summary] to write: argument -1 in Write | -| file://:0:0:0:0 | [summary param] 0 in Write | file://:0:0:0:0 | [summary] to write: argument -1 in Write | -| file://:0:0:0:0 | [summary param] 0 in Write | file://:0:0:0:0 | [summary] to write: argument -1 in Write | -| file://:0:0:0:0 | [summary param] 0 in Write | file://:0:0:0:0 | [summary] to write: argument -1 in Write | -| file://:0:0:0:0 | [summary param] 0 in Write | file://:0:0:0:0 | [summary] to write: argument -1 in Write | -| file://:0:0:0:0 | [summary param] 0 in Write | file://:0:0:0:0 | [summary] to write: argument -1 in Write | -| file://:0:0:0:0 | [summary param] 0 in Write | file://:0:0:0:0 | [summary] to write: argument -1 in Write | -| file://:0:0:0:0 | [summary param] 0 in Write | file://:0:0:0:0 | [summary] to write: argument -1 in Write | -| file://:0:0:0:0 | [summary param] 0 in Write | file://:0:0:0:0 | [summary] to write: argument -1 in Write | -| file://:0:0:0:0 | [summary param] 0 in WriteAt | file://:0:0:0:0 | [summary] to write: argument -1 in WriteAt | -| file://:0:0:0:0 | [summary param] 0 in WriteAt | file://:0:0:0:0 | [summary] to write: argument -1 in WriteAt | -| file://:0:0:0:0 | [summary param] 0 in WriteAt | file://:0:0:0:0 | [summary] to write: argument -1 in WriteAt | -| file://:0:0:0:0 | [summary param] 0 in WriteField | file://:0:0:0:0 | [summary] to write: argument -1 in WriteField | -| file://:0:0:0:0 | [summary param] 0 in WriteMsgIP | file://:0:0:0:0 | [summary] to write: argument -1 in WriteMsgIP | -| file://:0:0:0:0 | [summary param] 0 in WriteMsgUDP | file://:0:0:0:0 | [summary] to write: argument -1 in WriteMsgUDP | -| file://:0:0:0:0 | [summary param] 0 in WriteMsgUnix | file://:0:0:0:0 | [summary] to write: argument -1 in WriteMsgUnix | -| file://:0:0:0:0 | [summary param] 0 in WriteString | file://:0:0:0:0 | [summary] to write: argument -1 in WriteString | -| file://:0:0:0:0 | [summary param] 0 in WriteString | file://:0:0:0:0 | [summary] to write: argument -1 in WriteString | -| file://:0:0:0:0 | [summary param] 0 in WriteString | file://:0:0:0:0 | [summary] to write: argument -1 in WriteString | -| file://:0:0:0:0 | [summary param] 0 in WriteString | file://:0:0:0:0 | [summary] to write: argument -1 in WriteString | -| file://:0:0:0:0 | [summary param] 0 in WriteString | file://:0:0:0:0 | [summary] to write: argument -1 in WriteString | -| file://:0:0:0:0 | [summary param] 0 in WriteString | file://:0:0:0:0 | [summary] to write: argument -1 in WriteString | -| file://:0:0:0:0 | [summary param] 0 in WriteString | file://:0:0:0:0 | [summary] to write: argument -1 in WriteString | -| file://:0:0:0:0 | [summary param] 0 in WriteString | file://:0:0:0:0 | [summary] to write: argument -1 in WriteString | -| file://:0:0:0:0 | [summary param] 0 in WriteString | file://:0:0:0:0 | [summary] to write: argument -1 in WriteString | -| file://:0:0:0:0 | [summary param] 0 in WriteString | file://:0:0:0:0 | [summary] to write: argument -1 in WriteString | -| file://:0:0:0:0 | [summary param] 0 in WriteString | file://:0:0:0:0 | [summary] to write: argument -1 in WriteString | -| file://:0:0:0:0 | [summary param] 0 in WriteString | file://:0:0:0:0 | [summary] to write: argument -1 in WriteString | -| file://:0:0:0:0 | [summary param] 0 in WriteString | file://:0:0:0:0 | [summary] to write: argument -1 in WriteString | -| file://:0:0:0:0 | [summary param] 0 in WriteString | file://:0:0:0:0 | [summary] to write: argument -1 in WriteString | -| file://:0:0:0:0 | [summary param] 0 in WriteTo | file://:0:0:0:0 | [summary] to write: argument -1 in WriteTo | -| file://:0:0:0:0 | [summary param] 0 in WriteTo | file://:0:0:0:0 | [summary] to write: argument -1 in WriteTo | -| file://:0:0:0:0 | [summary param] 0 in WriteTo | file://:0:0:0:0 | [summary] to write: argument -1 in WriteTo | -| file://:0:0:0:0 | [summary param] 0 in WriteTo | file://:0:0:0:0 | [summary] to write: argument -1 in WriteTo | -| file://:0:0:0:0 | [summary param] 0 in WriteToIP | file://:0:0:0:0 | [summary] to write: argument -1 in WriteToIP | -| file://:0:0:0:0 | [summary param] 0 in WriteToUDP | file://:0:0:0:0 | [summary] to write: argument -1 in WriteToUDP | -| file://:0:0:0:0 | [summary param] 0 in WriteToUnix | file://:0:0:0:0 | [summary] to write: argument -1 in WriteToUnix | -| file://:0:0:0:0 | [summary param] 1 in Add | file://:0:0:0:0 | [summary] to write: argument -1 in Add | -| file://:0:0:0:0 | [summary param] 1 in Add | file://:0:0:0:0 | [summary] to write: argument -1 in Add | -| file://:0:0:0:0 | [summary param] 1 in AddUintptr | file://:0:0:0:0 | [summary] to write: argument 0 in AddUintptr | -| file://:0:0:0:0 | [summary param] 1 in AddUintptr | file://:0:0:0:0 | [summary] to write: return (return[0]) in AddUintptr | -| file://:0:0:0:0 | [summary param] 1 in AppendQuote | file://:0:0:0:0 | [summary] to write: return (return[0]) in AppendQuote | -| file://:0:0:0:0 | [summary param] 1 in AppendQuoteToASCII | file://:0:0:0:0 | [summary] to write: return (return[0]) in AppendQuoteToASCII | -| file://:0:0:0:0 | [summary param] 1 in AppendQuoteToGraphic | file://:0:0:0:0 | [summary] to write: return (return[0]) in AppendQuoteToGraphic | -| file://:0:0:0:0 | [summary param] 1 in AppendSlice | file://:0:0:0:0 | [summary] to write: return (return[0]) in AppendSlice | -| file://:0:0:0:0 | [summary param] 1 in Compact | file://:0:0:0:0 | [summary] to write: argument 0 in Compact | -| file://:0:0:0:0 | [summary param] 1 in Copy | file://:0:0:0:0 | [summary] to write: argument 0 in Copy | -| file://:0:0:0:0 | [summary param] 1 in Copy | file://:0:0:0:0 | [summary] to write: argument 0 in Copy | -| file://:0:0:0:0 | [summary param] 1 in CopyBuffer | file://:0:0:0:0 | [summary] to write: argument 0 in CopyBuffer | -| file://:0:0:0:0 | [summary param] 1 in CopyN | file://:0:0:0:0 | [summary] to write: argument 0 in CopyN | -| file://:0:0:0:0 | [summary param] 1 in Decode | file://:0:0:0:0 | [summary] to write: argument 0 in Decode | -| file://:0:0:0:0 | [summary param] 1 in Decode | file://:0:0:0:0 | [summary] to write: argument 0 in Decode | -| file://:0:0:0:0 | [summary param] 1 in Decrypt | file://:0:0:0:0 | [summary] to write: argument 0 in Decrypt | -| file://:0:0:0:0 | [summary param] 1 in Decrypt | file://:0:0:0:0 | [summary] to write: argument 0 in Decrypt | -| file://:0:0:0:0 | [summary param] 1 in Decrypt | file://:0:0:0:0 | [summary] to write: argument 0 in Decrypt | -| file://:0:0:0:0 | [summary param] 1 in Decrypt | file://:0:0:0:0 | [summary] to write: argument 0 in Decrypt | -| file://:0:0:0:0 | [summary param] 1 in Decrypt | file://:0:0:0:0 | [summary] to write: argument 0 in Decrypt | -| file://:0:0:0:0 | [summary param] 1 in Decrypt | file://:0:0:0:0 | [summary] to write: return (return[0]) in Decrypt | -| file://:0:0:0:0 | [summary param] 1 in Decrypt | file://:0:0:0:0 | [summary] to write: return (return[0]) in Decrypt | -| file://:0:0:0:0 | [summary param] 1 in Encode | file://:0:0:0:0 | [summary] to write: argument 0 in Encode | -| file://:0:0:0:0 | [summary param] 1 in Encode | file://:0:0:0:0 | [summary] to write: return (return[0]) in Encode | -| file://:0:0:0:0 | [summary param] 1 in Error | file://:0:0:0:0 | [summary] to write: argument 0 in Error | -| file://:0:0:0:0 | [summary param] 1 in FormatMediaType | file://:0:0:0:0 | [summary] to write: return (return[0]) in FormatMediaType | -| file://:0:0:0:0 | [summary param] 1 in HTMLEscape | file://:0:0:0:0 | [summary] to write: argument 0 in HTMLEscape | -| file://:0:0:0:0 | [summary param] 1 in Indent | file://:0:0:0:0 | [summary] to write: argument 0 in Indent | -| file://:0:0:0:0 | [summary param] 1 in Join | file://:0:0:0:0 | [summary] to write: return (return[0]) in Join | -| file://:0:0:0:0 | [summary param] 1 in Join | file://:0:0:0:0 | [summary] to write: return (return[0]) in Join | -| file://:0:0:0:0 | [summary param] 1 in JoinHostPort | file://:0:0:0:0 | [summary] to write: return (return[0]) in JoinHostPort | -| file://:0:0:0:0 | [summary param] 1 in LoadOrStore | file://:0:0:0:0 | [summary] to write: argument -1 in LoadOrStore | -| file://:0:0:0:0 | [summary param] 1 in LoadOrStore | file://:0:0:0:0 | [summary] to write: return (return[0]) in LoadOrStore | -| file://:0:0:0:0 | [summary param] 1 in Map | file://:0:0:0:0 | [summary] to write: return (return[0]) in Map | -| file://:0:0:0:0 | [summary param] 1 in Map | file://:0:0:0:0 | [summary] to write: return (return[0]) in Map | -| file://:0:0:0:0 | [summary param] 1 in MarshalIndent | file://:0:0:0:0 | [summary] to write: return (return[0]) in MarshalIndent | -| file://:0:0:0:0 | [summary param] 1 in MarshalWithParams | file://:0:0:0:0 | [summary] to write: return (return[0]) in MarshalWithParams | -| file://:0:0:0:0 | [summary param] 1 in MaxBytesReader | file://:0:0:0:0 | [summary] to write: return (return[0]) in MaxBytesReader | -| file://:0:0:0:0 | [summary param] 1 in NewDecoder | file://:0:0:0:0 | [summary] to write: return (return[0]) in NewDecoder | -| file://:0:0:0:0 | [summary param] 1 in NewRequest | file://:0:0:0:0 | [summary] to write: return (return[0]) in NewRequest | -| file://:0:0:0:0 | [summary param] 1 in Rel | file://:0:0:0:0 | [summary] to write: return (return[0]) in Rel | -| file://:0:0:0:0 | [summary param] 1 in Set | file://:0:0:0:0 | [summary] to write: argument -1 in Set | -| file://:0:0:0:0 | [summary param] 1 in Set | file://:0:0:0:0 | [summary] to write: argument -1 in Set | -| file://:0:0:0:0 | [summary param] 1 in SetCookie | file://:0:0:0:0 | [summary] to write: argument 0 in SetCookie | -| file://:0:0:0:0 | [summary param] 1 in SetIndent | file://:0:0:0:0 | [summary] to write: argument -1 in SetIndent | -| file://:0:0:0:0 | [summary param] 1 in SetMapIndex | file://:0:0:0:0 | [summary] to write: argument -1 in SetMapIndex | -| file://:0:0:0:0 | [summary param] 1 in Store | file://:0:0:0:0 | [summary] to write: argument -1 in Store | -| file://:0:0:0:0 | [summary param] 1 in StorePointer | file://:0:0:0:0 | [summary] to write: argument 0 in StorePointer | -| file://:0:0:0:0 | [summary param] 1 in StoreUintptr | file://:0:0:0:0 | [summary] to write: argument 0 in StoreUintptr | -| file://:0:0:0:0 | [summary param] 1 in Swap | file://:0:0:0:0 | [summary] to write: argument -1 in Swap | -| file://:0:0:0:0 | [summary param] 1 in SwapPointer | file://:0:0:0:0 | [summary] to write: argument 0 in SwapPointer | -| file://:0:0:0:0 | [summary param] 1 in SwapUintptr | file://:0:0:0:0 | [summary] to write: argument 0 in SwapUintptr | -| file://:0:0:0:0 | [summary param] 1 in ToLowerSpecial | file://:0:0:0:0 | [summary] to write: return (return[0]) in ToLowerSpecial | -| file://:0:0:0:0 | [summary param] 1 in ToLowerSpecial | file://:0:0:0:0 | [summary] to write: return (return[0]) in ToLowerSpecial | -| file://:0:0:0:0 | [summary param] 1 in ToTitleSpecial | file://:0:0:0:0 | [summary] to write: return (return[0]) in ToTitleSpecial | -| file://:0:0:0:0 | [summary param] 1 in ToTitleSpecial | file://:0:0:0:0 | [summary] to write: return (return[0]) in ToTitleSpecial | -| file://:0:0:0:0 | [summary param] 1 in ToUpperSpecial | file://:0:0:0:0 | [summary] to write: return (return[0]) in ToUpperSpecial | -| file://:0:0:0:0 | [summary param] 1 in ToUpperSpecial | file://:0:0:0:0 | [summary] to write: return (return[0]) in ToUpperSpecial | -| file://:0:0:0:0 | [summary param] 1 in ToValidUTF8 | file://:0:0:0:0 | [summary] to write: return (return[0]) in ToValidUTF8 | -| file://:0:0:0:0 | [summary param] 1 in ToValidUTF8 | file://:0:0:0:0 | [summary] to write: return (return[0]) in ToValidUTF8 | -| file://:0:0:0:0 | [summary param] 1 in UserPassword | file://:0:0:0:0 | [summary] to write: return (return[0]) in UserPassword | -| file://:0:0:0:0 | [summary param] 1 in WithValue | file://:0:0:0:0 | [summary] to write: return (return[0]) in WithValue | -| file://:0:0:0:0 | [summary param] 1 in WriteField | file://:0:0:0:0 | [summary] to write: argument -1 in WriteField | -| file://:0:0:0:0 | [summary param] 1 in WriteMsgIP | file://:0:0:0:0 | [summary] to write: argument -1 in WriteMsgIP | -| file://:0:0:0:0 | [summary param] 1 in WriteMsgUDP | file://:0:0:0:0 | [summary] to write: argument -1 in WriteMsgUDP | -| file://:0:0:0:0 | [summary param] 1 in WriteMsgUnix | file://:0:0:0:0 | [summary] to write: argument -1 in WriteMsgUnix | -| file://:0:0:0:0 | [summary param] 1 in WriteString | file://:0:0:0:0 | [summary] to write: argument 0 in WriteString | -| file://:0:0:0:0 | [summary param] 1 in WriteString | file://:0:0:0:0 | [summary] to write: argument 0 in WriteString | -| file://:0:0:0:0 | [summary param] 2 in CompareAndSwap | file://:0:0:0:0 | [summary] to write: argument -1 in CompareAndSwap | -| file://:0:0:0:0 | [summary param] 2 in CompareAndSwapPointer | file://:0:0:0:0 | [summary] to write: argument 0 in CompareAndSwapPointer | -| file://:0:0:0:0 | [summary param] 2 in CompareAndSwapUintptr | file://:0:0:0:0 | [summary] to write: argument 0 in CompareAndSwapUintptr | -| file://:0:0:0:0 | [summary param] 2 in DecryptPKCS1v15 | file://:0:0:0:0 | [summary] to write: return (return[0]) in DecryptPKCS1v15 | -| file://:0:0:0:0 | [summary param] 2 in Indent | file://:0:0:0:0 | [summary] to write: argument 0 in Indent | -| file://:0:0:0:0 | [summary param] 2 in MarshalIndent | file://:0:0:0:0 | [summary] to write: return (return[0]) in MarshalIndent | -| file://:0:0:0:0 | [summary param] 2 in NewRequestWithContext | file://:0:0:0:0 | [summary] to write: return (return[0]) in NewRequestWithContext | -| file://:0:0:0:0 | [summary param] 2 in Open | file://:0:0:0:0 | [summary] to write: argument 0 in Open | -| file://:0:0:0:0 | [summary param] 2 in Open | file://:0:0:0:0 | [summary] to write: argument 0 in Open | -| file://:0:0:0:0 | [summary param] 2 in Open | file://:0:0:0:0 | [summary] to write: argument 0 in Open | -| file://:0:0:0:0 | [summary param] 2 in Open | file://:0:0:0:0 | [summary] to write: argument 0 in Open | -| file://:0:0:0:0 | [summary param] 2 in Open | file://:0:0:0:0 | [summary] to write: argument 0 in Open | -| file://:0:0:0:0 | [summary param] 2 in Open | file://:0:0:0:0 | [summary] to write: argument 0 in Open | -| file://:0:0:0:0 | [summary param] 2 in Open | file://:0:0:0:0 | [summary] to write: argument 0 in Open | -| file://:0:0:0:0 | [summary param] 2 in Open | file://:0:0:0:0 | [summary] to write: return (return[0]) in Open | -| file://:0:0:0:0 | [summary param] 2 in Open | file://:0:0:0:0 | [summary] to write: return (return[0]) in Open | -| file://:0:0:0:0 | [summary param] 2 in Open | file://:0:0:0:0 | [summary] to write: return (return[0]) in Open | -| file://:0:0:0:0 | [summary param] 2 in Open | file://:0:0:0:0 | [summary] to write: return (return[0]) in Open | -| file://:0:0:0:0 | [summary param] 2 in Open | file://:0:0:0:0 | [summary] to write: return (return[0]) in Open | -| file://:0:0:0:0 | [summary param] 2 in Open | file://:0:0:0:0 | [summary] to write: return (return[0]) in Open | -| file://:0:0:0:0 | [summary param] 2 in Open | file://:0:0:0:0 | [summary] to write: return (return[0]) in Open | -| file://:0:0:0:0 | [summary param] 2 in Replace | file://:0:0:0:0 | [summary] to write: return (return[0]) in Replace | -| file://:0:0:0:0 | [summary param] 2 in Replace | file://:0:0:0:0 | [summary] to write: return (return[0]) in Replace | -| file://:0:0:0:0 | [summary param] 2 in ReplaceAll | file://:0:0:0:0 | [summary] to write: return (return[0]) in ReplaceAll | -| file://:0:0:0:0 | [summary param] 2 in ReplaceAll | file://:0:0:0:0 | [summary] to write: return (return[0]) in ReplaceAll | -| file://:0:0:0:0 | [summary param] 2 in UnmarshalWithParams | file://:0:0:0:0 | [summary] to write: argument 1 in UnmarshalWithParams | -| file://:0:0:0:0 | [summary param] 2 in UnmarshalWithParams | file://:0:0:0:0 | [summary] to write: return (return[0]) in UnmarshalWithParams | -| file://:0:0:0:0 | [summary param] 2 in WithValue | file://:0:0:0:0 | [summary] to write: return (return[0]) in WithValue | -| file://:0:0:0:0 | [summary param] 2 in Write | file://:0:0:0:0 | [summary] to write: argument 0 in Write | -| file://:0:0:0:0 | [summary param] 3 in DecryptOAEP | file://:0:0:0:0 | [summary] to write: return (return[0]) in DecryptOAEP | -| file://:0:0:0:0 | [summary param] 3 in Indent | file://:0:0:0:0 | [summary] to write: argument 0 in Indent | -| file://:0:0:0:0 | [summary param] -1 in Addr | file://:0:0:0:0 | [summary] to write: return (return[0]) in Addr | -| file://:0:0:0:0 | [summary param] -1 in Back | file://:0:0:0:0 | [summary] to write: return (return[0]) in Back | -| file://:0:0:0:0 | [summary param] -1 in Buffered | file://:0:0:0:0 | [summary] to write: return (return[0]) in Buffered | -| file://:0:0:0:0 | [summary param] -1 in Bytes | file://:0:0:0:0 | [summary] to write: return (return[0]) in Bytes | -| file://:0:0:0:0 | [summary param] -1 in Bytes | file://:0:0:0:0 | [summary] to write: return (return[0]) in Bytes | -| file://:0:0:0:0 | [summary param] -1 in Bytes | file://:0:0:0:0 | [summary] to write: return (return[0]) in Bytes | -| file://:0:0:0:0 | [summary param] -1 in Clone | file://:0:0:0:0 | [summary] to write: return (return[0]) in Clone | -| file://:0:0:0:0 | [summary param] -1 in Clone | file://:0:0:0:0 | [summary] to write: return (return[0]) in Clone | -| file://:0:0:0:0 | [summary param] -1 in Clone | file://:0:0:0:0 | [summary] to write: return (return[0]) in Clone | -| file://:0:0:0:0 | [summary param] -1 in Convert | file://:0:0:0:0 | [summary] to write: return (return[0]) in Convert | -| file://:0:0:0:0 | [summary param] -1 in Decode | file://:0:0:0:0 | [summary] to write: argument 0 in Decode | -| file://:0:0:0:0 | [summary param] -1 in DotReader | file://:0:0:0:0 | [summary] to write: return (return[0]) in DotReader | -| file://:0:0:0:0 | [summary param] -1 in Elem | file://:0:0:0:0 | [summary] to write: return (return[0]) in Elem | -| file://:0:0:0:0 | [summary param] -1 in Encode | file://:0:0:0:0 | [summary] to write: return (return[0]) in Encode | -| file://:0:0:0:0 | [summary param] -1 in EscapedPath | file://:0:0:0:0 | [summary] to write: return (return[0]) in EscapedPath | -| file://:0:0:0:0 | [summary param] -1 in Fd | file://:0:0:0:0 | [summary] to write: return (return[0]) in Fd | -| file://:0:0:0:0 | [summary param] -1 in Field | file://:0:0:0:0 | [summary] to write: return (return[0]) in Field | -| file://:0:0:0:0 | [summary param] -1 in FieldByIndex | file://:0:0:0:0 | [summary] to write: return (return[0]) in FieldByIndex | -| file://:0:0:0:0 | [summary param] -1 in FieldByName | file://:0:0:0:0 | [summary] to write: return (return[0]) in FieldByName | -| file://:0:0:0:0 | [summary param] -1 in FieldByNameFunc | file://:0:0:0:0 | [summary] to write: return (return[0]) in FieldByNameFunc | -| file://:0:0:0:0 | [summary param] -1 in File | file://:0:0:0:0 | [summary] to write: return (return[0]) in File | -| file://:0:0:0:0 | [summary param] -1 in File | file://:0:0:0:0 | [summary] to write: return (return[0]) in File | -| file://:0:0:0:0 | [summary param] -1 in FileName | file://:0:0:0:0 | [summary] to write: return (return[0]) in FileName | -| file://:0:0:0:0 | [summary param] -1 in FormName | file://:0:0:0:0 | [summary] to write: return (return[0]) in FormName | -| file://:0:0:0:0 | [summary param] -1 in Front | file://:0:0:0:0 | [summary] to write: return (return[0]) in Front | -| file://:0:0:0:0 | [summary param] -1 in Get | file://:0:0:0:0 | [summary] to write: return (return[0]) in Get | -| file://:0:0:0:0 | [summary param] -1 in Get | file://:0:0:0:0 | [summary] to write: return (return[0]) in Get | -| file://:0:0:0:0 | [summary param] -1 in Get | file://:0:0:0:0 | [summary] to write: return (return[0]) in Get | -| file://:0:0:0:0 | [summary param] -1 in Get | file://:0:0:0:0 | [summary] to write: return (return[0]) in Get | -| file://:0:0:0:0 | [summary param] -1 in Get | file://:0:0:0:0 | [summary] to write: return (return[0]) in Get | -| file://:0:0:0:0 | [summary param] -1 in Glob | file://:0:0:0:0 | [summary] to write: return (return[0]) in Glob | -| file://:0:0:0:0 | [summary param] -1 in Glob | file://:0:0:0:0 | [summary] to write: return (return[0]) in Glob | -| file://:0:0:0:0 | [summary param] -1 in GoString | file://:0:0:0:0 | [summary] to write: return (return[0]) in GoString | -| file://:0:0:0:0 | [summary param] -1 in GoString | file://:0:0:0:0 | [summary] to write: return (return[0]) in GoString | -| file://:0:0:0:0 | [summary param] -1 in GoString | file://:0:0:0:0 | [summary] to write: return (return[0]) in GoString | -| file://:0:0:0:0 | [summary param] -1 in GoString | file://:0:0:0:0 | [summary] to write: return (return[0]) in GoString | -| file://:0:0:0:0 | [summary param] -1 in GoString | file://:0:0:0:0 | [summary] to write: return (return[0]) in GoString | -| file://:0:0:0:0 | [summary param] -1 in GoString | file://:0:0:0:0 | [summary] to write: return (return[0]) in GoString | -| file://:0:0:0:0 | [summary param] -1 in GoString | file://:0:0:0:0 | [summary] to write: return (return[0]) in GoString | -| file://:0:0:0:0 | [summary param] -1 in GoString | file://:0:0:0:0 | [summary] to write: return (return[0]) in GoString | -| file://:0:0:0:0 | [summary param] -1 in GoString | file://:0:0:0:0 | [summary] to write: return (return[0]) in GoString | -| file://:0:0:0:0 | [summary param] -1 in GoString | file://:0:0:0:0 | [summary] to write: return (return[0]) in GoString | -| file://:0:0:0:0 | [summary param] -1 in GoString | file://:0:0:0:0 | [summary] to write: return (return[0]) in GoString | -| file://:0:0:0:0 | [summary param] -1 in GoString | file://:0:0:0:0 | [summary] to write: return (return[0]) in GoString | -| file://:0:0:0:0 | [summary param] -1 in GoString | file://:0:0:0:0 | [summary] to write: return (return[0]) in GoString | -| file://:0:0:0:0 | [summary param] -1 in GoString | file://:0:0:0:0 | [summary] to write: return (return[0]) in GoString | -| file://:0:0:0:0 | [summary param] -1 in GoString | file://:0:0:0:0 | [summary] to write: return (return[0]) in GoString | -| file://:0:0:0:0 | [summary param] -1 in GoString | file://:0:0:0:0 | [summary] to write: return (return[0]) in GoString | -| file://:0:0:0:0 | [summary param] -1 in GoString | file://:0:0:0:0 | [summary] to write: return (return[0]) in GoString | -| file://:0:0:0:0 | [summary param] -1 in GoString | file://:0:0:0:0 | [summary] to write: return (return[0]) in GoString | -| file://:0:0:0:0 | [summary param] -1 in GoString | file://:0:0:0:0 | [summary] to write: return (return[0]) in GoString | -| file://:0:0:0:0 | [summary param] -1 in GoString | file://:0:0:0:0 | [summary] to write: return (return[0]) in GoString | -| file://:0:0:0:0 | [summary param] -1 in GoString | file://:0:0:0:0 | [summary] to write: return (return[0]) in GoString | -| file://:0:0:0:0 | [summary param] -1 in GoString | file://:0:0:0:0 | [summary] to write: return (return[0]) in GoString | -| file://:0:0:0:0 | [summary param] -1 in GoString | file://:0:0:0:0 | [summary] to write: return (return[0]) in GoString | -| file://:0:0:0:0 | [summary param] -1 in GoString | file://:0:0:0:0 | [summary] to write: return (return[0]) in GoString | -| file://:0:0:0:0 | [summary param] -1 in GoString | file://:0:0:0:0 | [summary] to write: return (return[0]) in GoString | -| file://:0:0:0:0 | [summary param] -1 in GoString | file://:0:0:0:0 | [summary] to write: return (return[0]) in GoString | -| file://:0:0:0:0 | [summary param] -1 in GoString | file://:0:0:0:0 | [summary] to write: return (return[0]) in GoString | -| file://:0:0:0:0 | [summary param] -1 in Hijack | file://:0:0:0:0 | [summary] to write: return (return[0]) in Hijack | -| file://:0:0:0:0 | [summary param] -1 in Hijack | file://:0:0:0:0 | [summary] to write: return (return[0]) in Hijack | -| file://:0:0:0:0 | [summary param] -1 in Hijack | file://:0:0:0:0 | [summary] to write: return (return[0]) in Hijack | -| file://:0:0:0:0 | [summary param] -1 in Hijack | file://:0:0:0:0 | [summary] to write: return (return[1]) in Hijack | -| file://:0:0:0:0 | [summary param] -1 in Hijack | file://:0:0:0:0 | [summary] to write: return (return[1]) in Hijack | -| file://:0:0:0:0 | [summary param] -1 in Hijack | file://:0:0:0:0 | [summary] to write: return (return[1]) in Hijack | -| file://:0:0:0:0 | [summary param] -1 in Hostname | file://:0:0:0:0 | [summary] to write: return (return[0]) in Hostname | -| file://:0:0:0:0 | [summary param] -1 in Index | file://:0:0:0:0 | [summary] to write: return (return[0]) in Index | -| file://:0:0:0:0 | [summary param] -1 in Info | file://:0:0:0:0 | [summary] to write: return (return[0]) in Info | -| file://:0:0:0:0 | [summary param] -1 in Info | file://:0:0:0:0 | [summary] to write: return (return[0]) in Info | -| file://:0:0:0:0 | [summary param] -1 in Info | file://:0:0:0:0 | [summary] to write: return (return[0]) in Info | -| file://:0:0:0:0 | [summary param] -1 in Info | file://:0:0:0:0 | [summary] to write: return (return[0]) in Info | -| file://:0:0:0:0 | [summary param] -1 in Info | file://:0:0:0:0 | [summary] to write: return (return[0]) in Info | -| file://:0:0:0:0 | [summary param] -1 in Init | file://:0:0:0:0 | [summary] to write: return (return[0]) in Init | -| file://:0:0:0:0 | [summary param] -1 in Interface | file://:0:0:0:0 | [summary] to write: return (return[0]) in Interface | -| file://:0:0:0:0 | [summary param] -1 in InterfaceData | file://:0:0:0:0 | [summary] to write: return (return[0]) in InterfaceData | -| file://:0:0:0:0 | [summary param] -1 in Key | file://:0:0:0:0 | [summary] to write: return (return[0]) in Key | -| file://:0:0:0:0 | [summary param] -1 in Load | file://:0:0:0:0 | [summary] to write: return (return[0]) in Load | -| file://:0:0:0:0 | [summary param] -1 in Load | file://:0:0:0:0 | [summary] to write: return (return[0]) in Load | -| file://:0:0:0:0 | [summary param] -1 in Load | file://:0:0:0:0 | [summary] to write: return (return[0]) in Load | -| file://:0:0:0:0 | [summary param] -1 in Load | file://:0:0:0:0 | [summary] to write: return (return[0]) in Load | -| file://:0:0:0:0 | [summary param] -1 in LoadOrStore | file://:0:0:0:0 | [summary] to write: return (return[0]) in LoadOrStore | -| file://:0:0:0:0 | [summary param] -1 in Lookup | file://:0:0:0:0 | [summary] to write: return (return[0]) in Lookup | -| file://:0:0:0:0 | [summary param] -1 in MapIndex | file://:0:0:0:0 | [summary] to write: return (return[0]) in MapIndex | -| file://:0:0:0:0 | [summary param] -1 in MapKeys | file://:0:0:0:0 | [summary] to write: return (return[0]) in MapKeys | -| file://:0:0:0:0 | [summary param] -1 in MapRange | file://:0:0:0:0 | [summary] to write: return (return[0]) in MapRange | -| file://:0:0:0:0 | [summary param] -1 in MarshalBinary | file://:0:0:0:0 | [summary] to write: return (return[0]) in MarshalBinary | -| file://:0:0:0:0 | [summary param] -1 in MarshalBinary | file://:0:0:0:0 | [summary] to write: return (return[0]) in MarshalBinary | -| file://:0:0:0:0 | [summary param] -1 in MarshalBinary | file://:0:0:0:0 | [summary] to write: return (return[0]) in MarshalBinary | -| file://:0:0:0:0 | [summary param] -1 in MarshalBinary | file://:0:0:0:0 | [summary] to write: return (return[0]) in MarshalBinary | -| file://:0:0:0:0 | [summary param] -1 in MarshalBinary | file://:0:0:0:0 | [summary] to write: return (return[0]) in MarshalBinary | -| file://:0:0:0:0 | [summary param] -1 in MarshalBinary | file://:0:0:0:0 | [summary] to write: return (return[0]) in MarshalBinary | -| file://:0:0:0:0 | [summary param] -1 in MarshalBinary | file://:0:0:0:0 | [summary] to write: return (return[0]) in MarshalBinary | -| file://:0:0:0:0 | [summary param] -1 in MarshalBinary | file://:0:0:0:0 | [summary] to write: return (return[0]) in MarshalBinary | -| file://:0:0:0:0 | [summary param] -1 in MarshalBinary | file://:0:0:0:0 | [summary] to write: return (return[0]) in MarshalBinary | -| file://:0:0:0:0 | [summary param] -1 in MarshalBinary | file://:0:0:0:0 | [summary] to write: return (return[0]) in MarshalBinary | -| file://:0:0:0:0 | [summary param] -1 in MarshalBinary | file://:0:0:0:0 | [summary] to write: return (return[0]) in MarshalBinary | -| file://:0:0:0:0 | [summary param] -1 in MarshalBinary | file://:0:0:0:0 | [summary] to write: return (return[0]) in MarshalBinary | -| file://:0:0:0:0 | [summary param] -1 in MarshalJSON | file://:0:0:0:0 | [summary] to write: return (return[0]) in MarshalJSON | -| file://:0:0:0:0 | [summary param] -1 in MarshalJSON | file://:0:0:0:0 | [summary] to write: return (return[0]) in MarshalJSON | -| file://:0:0:0:0 | [summary param] -1 in MarshalJSON | file://:0:0:0:0 | [summary] to write: return (return[0]) in MarshalJSON | -| file://:0:0:0:0 | [summary param] -1 in MarshalJSON | file://:0:0:0:0 | [summary] to write: return (return[0]) in MarshalJSON | -| file://:0:0:0:0 | [summary param] -1 in MarshalText | file://:0:0:0:0 | [summary] to write: return (return[0]) in MarshalText | -| file://:0:0:0:0 | [summary param] -1 in MarshalText | file://:0:0:0:0 | [summary] to write: return (return[0]) in MarshalText | -| file://:0:0:0:0 | [summary param] -1 in MarshalText | file://:0:0:0:0 | [summary] to write: return (return[0]) in MarshalText | -| file://:0:0:0:0 | [summary param] -1 in MarshalText | file://:0:0:0:0 | [summary] to write: return (return[0]) in MarshalText | -| file://:0:0:0:0 | [summary param] -1 in MarshalText | file://:0:0:0:0 | [summary] to write: return (return[0]) in MarshalText | -| file://:0:0:0:0 | [summary param] -1 in MarshalText | file://:0:0:0:0 | [summary] to write: return (return[0]) in MarshalText | -| file://:0:0:0:0 | [summary param] -1 in MarshalText | file://:0:0:0:0 | [summary] to write: return (return[0]) in MarshalText | -| file://:0:0:0:0 | [summary param] -1 in MarshalText | file://:0:0:0:0 | [summary] to write: return (return[0]) in MarshalText | -| file://:0:0:0:0 | [summary param] -1 in MarshalText | file://:0:0:0:0 | [summary] to write: return (return[0]) in MarshalText | -| file://:0:0:0:0 | [summary param] -1 in Method | file://:0:0:0:0 | [summary] to write: return (return[0]) in Method | -| file://:0:0:0:0 | [summary param] -1 in MethodByName | file://:0:0:0:0 | [summary] to write: return (return[0]) in MethodByName | -| file://:0:0:0:0 | [summary param] -1 in Name | file://:0:0:0:0 | [summary] to write: return (return[0]) in Name | -| file://:0:0:0:0 | [summary param] -1 in Name | file://:0:0:0:0 | [summary] to write: return (return[0]) in Name | -| file://:0:0:0:0 | [summary param] -1 in Name | file://:0:0:0:0 | [summary] to write: return (return[0]) in Name | -| file://:0:0:0:0 | [summary param] -1 in Name | file://:0:0:0:0 | [summary] to write: return (return[0]) in Name | -| file://:0:0:0:0 | [summary param] -1 in Name | file://:0:0:0:0 | [summary] to write: return (return[0]) in Name | -| file://:0:0:0:0 | [summary param] -1 in Next | file://:0:0:0:0 | [summary] to write: return (return[0]) in Next | -| file://:0:0:0:0 | [summary param] -1 in Next | file://:0:0:0:0 | [summary] to write: return (return[0]) in Next | -| file://:0:0:0:0 | [summary param] -1 in NextPart | file://:0:0:0:0 | [summary] to write: return (return[0]) in NextPart | -| file://:0:0:0:0 | [summary param] -1 in NextRawPart | file://:0:0:0:0 | [summary] to write: return (return[0]) in NextRawPart | -| file://:0:0:0:0 | [summary param] -1 in Open | file://:0:0:0:0 | [summary] to write: return (return[0]) in Open | -| file://:0:0:0:0 | [summary param] -1 in Open | file://:0:0:0:0 | [summary] to write: return (return[0]) in Open | -| file://:0:0:0:0 | [summary param] -1 in Open | file://:0:0:0:0 | [summary] to write: return (return[0]) in Open | -| file://:0:0:0:0 | [summary param] -1 in Open | file://:0:0:0:0 | [summary] to write: return (return[0]) in Open | -| file://:0:0:0:0 | [summary param] -1 in Open | file://:0:0:0:0 | [summary] to write: return (return[0]) in Open | -| file://:0:0:0:0 | [summary param] -1 in Parse | file://:0:0:0:0 | [summary] to write: return (return[0]) in Parse | -| file://:0:0:0:0 | [summary param] -1 in Password | file://:0:0:0:0 | [summary] to write: return (return[0]) in Password | -| file://:0:0:0:0 | [summary param] -1 in Peek | file://:0:0:0:0 | [summary] to write: return (return[0]) in Peek | -| file://:0:0:0:0 | [summary param] -1 in Pointer | file://:0:0:0:0 | [summary] to write: return (return[0]) in Pointer | -| file://:0:0:0:0 | [summary param] -1 in Port | file://:0:0:0:0 | [summary] to write: return (return[0]) in Port | -| file://:0:0:0:0 | [summary param] -1 in Prev | file://:0:0:0:0 | [summary] to write: return (return[0]) in Prev | -| file://:0:0:0:0 | [summary param] -1 in Query | file://:0:0:0:0 | [summary] to write: return (return[0]) in Query | -| file://:0:0:0:0 | [summary param] -1 in Read | file://:0:0:0:0 | [summary] to write: argument 0 in Read | -| file://:0:0:0:0 | [summary param] -1 in Read | file://:0:0:0:0 | [summary] to write: argument 0 in Read | -| file://:0:0:0:0 | [summary param] -1 in Read | file://:0:0:0:0 | [summary] to write: argument 0 in Read | -| file://:0:0:0:0 | [summary param] -1 in Read | file://:0:0:0:0 | [summary] to write: argument 0 in Read | -| file://:0:0:0:0 | [summary param] -1 in Read | file://:0:0:0:0 | [summary] to write: argument 0 in Read | -| file://:0:0:0:0 | [summary param] -1 in Read | file://:0:0:0:0 | [summary] to write: argument 0 in Read | -| file://:0:0:0:0 | [summary param] -1 in Read | file://:0:0:0:0 | [summary] to write: argument 0 in Read | -| file://:0:0:0:0 | [summary param] -1 in Read | file://:0:0:0:0 | [summary] to write: argument 0 in Read | -| file://:0:0:0:0 | [summary param] -1 in Read | file://:0:0:0:0 | [summary] to write: argument 0 in Read | -| file://:0:0:0:0 | [summary param] -1 in Read | file://:0:0:0:0 | [summary] to write: argument 0 in Read | -| file://:0:0:0:0 | [summary param] -1 in Read | file://:0:0:0:0 | [summary] to write: argument 0 in Read | -| file://:0:0:0:0 | [summary param] -1 in Read | file://:0:0:0:0 | [summary] to write: argument 0 in Read | -| file://:0:0:0:0 | [summary param] -1 in Read | file://:0:0:0:0 | [summary] to write: argument 0 in Read | -| file://:0:0:0:0 | [summary param] -1 in Read | file://:0:0:0:0 | [summary] to write: argument 0 in Read | -| file://:0:0:0:0 | [summary param] -1 in Read | file://:0:0:0:0 | [summary] to write: argument 0 in Read | -| file://:0:0:0:0 | [summary param] -1 in Read | file://:0:0:0:0 | [summary] to write: argument 0 in Read | -| file://:0:0:0:0 | [summary param] -1 in Read | file://:0:0:0:0 | [summary] to write: argument 0 in Read | -| file://:0:0:0:0 | [summary param] -1 in Read | file://:0:0:0:0 | [summary] to write: argument 0 in Read | -| file://:0:0:0:0 | [summary param] -1 in Read | file://:0:0:0:0 | [summary] to write: argument 0 in Read | -| file://:0:0:0:0 | [summary param] -1 in Read | file://:0:0:0:0 | [summary] to write: argument 0 in Read | -| file://:0:0:0:0 | [summary param] -1 in Read | file://:0:0:0:0 | [summary] to write: argument 0 in Read | -| file://:0:0:0:0 | [summary param] -1 in Read | file://:0:0:0:0 | [summary] to write: argument 0 in Read | -| file://:0:0:0:0 | [summary param] -1 in Read | file://:0:0:0:0 | [summary] to write: argument 0 in Read | -| file://:0:0:0:0 | [summary param] -1 in Read | file://:0:0:0:0 | [summary] to write: argument 0 in Read | -| file://:0:0:0:0 | [summary param] -1 in Read | file://:0:0:0:0 | [summary] to write: argument 0 in Read | -| file://:0:0:0:0 | [summary param] -1 in Read | file://:0:0:0:0 | [summary] to write: argument 0 in Read | -| file://:0:0:0:0 | [summary param] -1 in Read | file://:0:0:0:0 | [summary] to write: argument 0 in Read | -| file://:0:0:0:0 | [summary param] -1 in Read | file://:0:0:0:0 | [summary] to write: argument 0 in Read | -| file://:0:0:0:0 | [summary param] -1 in Read | file://:0:0:0:0 | [summary] to write: argument 0 in Read | -| file://:0:0:0:0 | [summary param] -1 in Read | file://:0:0:0:0 | [summary] to write: argument 0 in Read | -| file://:0:0:0:0 | [summary param] -1 in Read | file://:0:0:0:0 | [summary] to write: argument 0 in Read | -| file://:0:0:0:0 | [summary param] -1 in Read | file://:0:0:0:0 | [summary] to write: argument 0 in Read | -| file://:0:0:0:0 | [summary param] -1 in Read | file://:0:0:0:0 | [summary] to write: argument 0 in Read | -| file://:0:0:0:0 | [summary param] -1 in Read | file://:0:0:0:0 | [summary] to write: argument 0 in Read | -| file://:0:0:0:0 | [summary param] -1 in Read | file://:0:0:0:0 | [summary] to write: argument 0 in Read | -| file://:0:0:0:0 | [summary param] -1 in Read | file://:0:0:0:0 | [summary] to write: argument 0 in Read | -| file://:0:0:0:0 | [summary param] -1 in Read | file://:0:0:0:0 | [summary] to write: argument 0 in Read | -| file://:0:0:0:0 | [summary param] -1 in Read | file://:0:0:0:0 | [summary] to write: argument 0 in Read | -| file://:0:0:0:0 | [summary param] -1 in Read | file://:0:0:0:0 | [summary] to write: argument 0 in Read | -| file://:0:0:0:0 | [summary param] -1 in Read | file://:0:0:0:0 | [summary] to write: argument 0 in Read | -| file://:0:0:0:0 | [summary param] -1 in Read | file://:0:0:0:0 | [summary] to write: argument 0 in Read | -| file://:0:0:0:0 | [summary param] -1 in Read | file://:0:0:0:0 | [summary] to write: argument 0 in Read | -| file://:0:0:0:0 | [summary param] -1 in Read | file://:0:0:0:0 | [summary] to write: argument 0 in Read | -| file://:0:0:0:0 | [summary param] -1 in Read | file://:0:0:0:0 | [summary] to write: argument 0 in Read | -| file://:0:0:0:0 | [summary param] -1 in Read | file://:0:0:0:0 | [summary] to write: argument 0 in Read | -| file://:0:0:0:0 | [summary param] -1 in Read | file://:0:0:0:0 | [summary] to write: argument 0 in Read | -| file://:0:0:0:0 | [summary param] -1 in Read | file://:0:0:0:0 | [summary] to write: argument 0 in Read | -| file://:0:0:0:0 | [summary param] -1 in Read | file://:0:0:0:0 | [summary] to write: argument 0 in Read | -| file://:0:0:0:0 | [summary param] -1 in Read | file://:0:0:0:0 | [summary] to write: argument 0 in Read | -| file://:0:0:0:0 | [summary param] -1 in Read | file://:0:0:0:0 | [summary] to write: argument 0 in Read | -| file://:0:0:0:0 | [summary param] -1 in Read | file://:0:0:0:0 | [summary] to write: argument 0 in Read | -| file://:0:0:0:0 | [summary param] -1 in Read | file://:0:0:0:0 | [summary] to write: argument 0 in Read | -| file://:0:0:0:0 | [summary param] -1 in Read | file://:0:0:0:0 | [summary] to write: argument 0 in Read | -| file://:0:0:0:0 | [summary param] -1 in Read | file://:0:0:0:0 | [summary] to write: argument 0 in Read | -| file://:0:0:0:0 | [summary param] -1 in Read | file://:0:0:0:0 | [summary] to write: argument 0 in Read | -| file://:0:0:0:0 | [summary param] -1 in Read | file://:0:0:0:0 | [summary] to write: argument 0 in Read | -| file://:0:0:0:0 | [summary param] -1 in Read | file://:0:0:0:0 | [summary] to write: argument 0 in Read | -| file://:0:0:0:0 | [summary param] -1 in Read | file://:0:0:0:0 | [summary] to write: argument 0 in Read | -| file://:0:0:0:0 | [summary param] -1 in Read | file://:0:0:0:0 | [summary] to write: argument 0 in Read | -| file://:0:0:0:0 | [summary param] -1 in Read | file://:0:0:0:0 | [summary] to write: argument 0 in Read | -| file://:0:0:0:0 | [summary param] -1 in Read | file://:0:0:0:0 | [summary] to write: argument 0 in Read | -| file://:0:0:0:0 | [summary param] -1 in Read | file://:0:0:0:0 | [summary] to write: argument 0 in Read | -| file://:0:0:0:0 | [summary param] -1 in Read | file://:0:0:0:0 | [summary] to write: argument 0 in Read | -| file://:0:0:0:0 | [summary param] -1 in Read | file://:0:0:0:0 | [summary] to write: argument 0 in Read | -| file://:0:0:0:0 | [summary param] -1 in Read | file://:0:0:0:0 | [summary] to write: argument 0 in Read | -| file://:0:0:0:0 | [summary param] -1 in Read | file://:0:0:0:0 | [summary] to write: argument 0 in Read | -| file://:0:0:0:0 | [summary param] -1 in Read | file://:0:0:0:0 | [summary] to write: argument 0 in Read | -| file://:0:0:0:0 | [summary param] -1 in Read | file://:0:0:0:0 | [summary] to write: argument 0 in Read | -| file://:0:0:0:0 | [summary param] -1 in Read | file://:0:0:0:0 | [summary] to write: argument 0 in Read | -| file://:0:0:0:0 | [summary param] -1 in Read | file://:0:0:0:0 | [summary] to write: argument 0 in Read | -| file://:0:0:0:0 | [summary param] -1 in Read | file://:0:0:0:0 | [summary] to write: argument 0 in Read | -| file://:0:0:0:0 | [summary param] -1 in Read | file://:0:0:0:0 | [summary] to write: argument 0 in Read | -| file://:0:0:0:0 | [summary param] -1 in Read | file://:0:0:0:0 | [summary] to write: argument 0 in Read | -| file://:0:0:0:0 | [summary param] -1 in ReadAt | file://:0:0:0:0 | [summary] to write: argument 0 in ReadAt | -| file://:0:0:0:0 | [summary param] -1 in ReadAt | file://:0:0:0:0 | [summary] to write: argument 0 in ReadAt | -| file://:0:0:0:0 | [summary param] -1 in ReadAt | file://:0:0:0:0 | [summary] to write: argument 0 in ReadAt | -| file://:0:0:0:0 | [summary param] -1 in ReadAt | file://:0:0:0:0 | [summary] to write: argument 0 in ReadAt | -| file://:0:0:0:0 | [summary param] -1 in ReadAt | file://:0:0:0:0 | [summary] to write: argument 0 in ReadAt | -| file://:0:0:0:0 | [summary param] -1 in ReadBytes | file://:0:0:0:0 | [summary] to write: return (return[0]) in ReadBytes | -| file://:0:0:0:0 | [summary param] -1 in ReadBytes | file://:0:0:0:0 | [summary] to write: return (return[0]) in ReadBytes | -| file://:0:0:0:0 | [summary param] -1 in ReadCodeLine | file://:0:0:0:0 | [summary] to write: return (return[1]) in ReadCodeLine | -| file://:0:0:0:0 | [summary param] -1 in ReadContinuedLine | file://:0:0:0:0 | [summary] to write: return (return[0]) in ReadContinuedLine | -| file://:0:0:0:0 | [summary param] -1 in ReadContinuedLineBytes | file://:0:0:0:0 | [summary] to write: return (return[0]) in ReadContinuedLineBytes | -| file://:0:0:0:0 | [summary param] -1 in ReadDir | file://:0:0:0:0 | [summary] to write: return (return[0]) in ReadDir | -| file://:0:0:0:0 | [summary param] -1 in ReadDir | file://:0:0:0:0 | [summary] to write: return (return[0]) in ReadDir | -| file://:0:0:0:0 | [summary param] -1 in ReadDir | file://:0:0:0:0 | [summary] to write: return (return[0]) in ReadDir | -| file://:0:0:0:0 | [summary param] -1 in ReadDotBytes | file://:0:0:0:0 | [summary] to write: return (return[0]) in ReadDotBytes | -| file://:0:0:0:0 | [summary param] -1 in ReadDotLines | file://:0:0:0:0 | [summary] to write: return (return[0]) in ReadDotLines | -| file://:0:0:0:0 | [summary param] -1 in ReadFile | file://:0:0:0:0 | [summary] to write: return (return[0]) in ReadFile | -| file://:0:0:0:0 | [summary param] -1 in ReadFile | file://:0:0:0:0 | [summary] to write: return (return[0]) in ReadFile | -| file://:0:0:0:0 | [summary param] -1 in ReadFile | file://:0:0:0:0 | [summary] to write: return (return[0]) in ReadFile | -| file://:0:0:0:0 | [summary param] -1 in ReadForm | file://:0:0:0:0 | [summary] to write: return (return[0]) in ReadForm | -| file://:0:0:0:0 | [summary param] -1 in ReadFrom | file://:0:0:0:0 | [summary] to write: argument 0 in ReadFrom | -| file://:0:0:0:0 | [summary param] -1 in ReadFrom | file://:0:0:0:0 | [summary] to write: argument 0 in ReadFrom | -| file://:0:0:0:0 | [summary param] -1 in ReadFrom | file://:0:0:0:0 | [summary] to write: argument 0 in ReadFrom | -| file://:0:0:0:0 | [summary param] -1 in ReadFrom | file://:0:0:0:0 | [summary] to write: argument 0 in ReadFrom | -| file://:0:0:0:0 | [summary param] -1 in ReadFromIP | file://:0:0:0:0 | [summary] to write: argument 0 in ReadFromIP | -| file://:0:0:0:0 | [summary param] -1 in ReadFromUDP | file://:0:0:0:0 | [summary] to write: argument 0 in ReadFromUDP | -| file://:0:0:0:0 | [summary param] -1 in ReadFromUnix | file://:0:0:0:0 | [summary] to write: argument 0 in ReadFromUnix | -| file://:0:0:0:0 | [summary param] -1 in ReadLine | file://:0:0:0:0 | [summary] to write: return (return[0]) in ReadLine | -| file://:0:0:0:0 | [summary param] -1 in ReadLine | file://:0:0:0:0 | [summary] to write: return (return[0]) in ReadLine | -| file://:0:0:0:0 | [summary param] -1 in ReadLineBytes | file://:0:0:0:0 | [summary] to write: return (return[0]) in ReadLineBytes | -| file://:0:0:0:0 | [summary param] -1 in ReadMIMEHeader | file://:0:0:0:0 | [summary] to write: return (return[0]) in ReadMIMEHeader | -| file://:0:0:0:0 | [summary param] -1 in ReadMsgIP | file://:0:0:0:0 | [summary] to write: argument 0 in ReadMsgIP | -| file://:0:0:0:0 | [summary param] -1 in ReadMsgIP | file://:0:0:0:0 | [summary] to write: argument 1 in ReadMsgIP | -| file://:0:0:0:0 | [summary param] -1 in ReadMsgUDP | file://:0:0:0:0 | [summary] to write: argument 0 in ReadMsgUDP | -| file://:0:0:0:0 | [summary param] -1 in ReadMsgUDP | file://:0:0:0:0 | [summary] to write: argument 1 in ReadMsgUDP | -| file://:0:0:0:0 | [summary param] -1 in ReadMsgUnix | file://:0:0:0:0 | [summary] to write: argument 0 in ReadMsgUnix | -| file://:0:0:0:0 | [summary param] -1 in ReadMsgUnix | file://:0:0:0:0 | [summary] to write: argument 1 in ReadMsgUnix | -| file://:0:0:0:0 | [summary param] -1 in ReadResponse | file://:0:0:0:0 | [summary] to write: return (return[1]) in ReadResponse | -| file://:0:0:0:0 | [summary param] -1 in ReadSlice | file://:0:0:0:0 | [summary] to write: return (return[0]) in ReadSlice | -| file://:0:0:0:0 | [summary param] -1 in ReadString | file://:0:0:0:0 | [summary] to write: return (return[0]) in ReadString | -| file://:0:0:0:0 | [summary param] -1 in ReadString | file://:0:0:0:0 | [summary] to write: return (return[0]) in ReadString | -| file://:0:0:0:0 | [summary param] -1 in Recv | file://:0:0:0:0 | [summary] to write: return (return[0]) in Recv | -| file://:0:0:0:0 | [summary param] -1 in RequestURI | file://:0:0:0:0 | [summary] to write: return (return[0]) in RequestURI | -| file://:0:0:0:0 | [summary param] -1 in Reset | file://:0:0:0:0 | [summary] to write: argument 0 in Reset | -| file://:0:0:0:0 | [summary param] -1 in Reset | file://:0:0:0:0 | [summary] to write: argument 0 in Reset | -| file://:0:0:0:0 | [summary param] -1 in Reset | file://:0:0:0:0 | [summary] to write: argument 0 in Reset | -| file://:0:0:0:0 | [summary param] -1 in ResolveReference | file://:0:0:0:0 | [summary] to write: return (return[0]) in ResolveReference | -| file://:0:0:0:0 | [summary param] -1 in SetOutput | file://:0:0:0:0 | [summary] to write: argument 0 in SetOutput | -| file://:0:0:0:0 | [summary param] -1 in Slice | file://:0:0:0:0 | [summary] to write: return (return[0]) in Slice | -| file://:0:0:0:0 | [summary param] -1 in Slice3 | file://:0:0:0:0 | [summary] to write: return (return[0]) in Slice3 | -| file://:0:0:0:0 | [summary param] -1 in String | file://:0:0:0:0 | [summary] to write: return (return[0]) in String | -| file://:0:0:0:0 | [summary param] -1 in String | file://:0:0:0:0 | [summary] to write: return (return[0]) in String | -| file://:0:0:0:0 | [summary param] -1 in String | file://:0:0:0:0 | [summary] to write: return (return[0]) in String | -| file://:0:0:0:0 | [summary param] -1 in String | file://:0:0:0:0 | [summary] to write: return (return[0]) in String | -| file://:0:0:0:0 | [summary param] -1 in String | file://:0:0:0:0 | [summary] to write: return (return[0]) in String | -| file://:0:0:0:0 | [summary param] -1 in String | file://:0:0:0:0 | [summary] to write: return (return[0]) in String | -| file://:0:0:0:0 | [summary param] -1 in String | file://:0:0:0:0 | [summary] to write: return (return[0]) in String | -| file://:0:0:0:0 | [summary param] -1 in String | file://:0:0:0:0 | [summary] to write: return (return[0]) in String | -| file://:0:0:0:0 | [summary param] -1 in String | file://:0:0:0:0 | [summary] to write: return (return[0]) in String | -| file://:0:0:0:0 | [summary param] -1 in String | file://:0:0:0:0 | [summary] to write: return (return[0]) in String | -| file://:0:0:0:0 | [summary param] -1 in String | file://:0:0:0:0 | [summary] to write: return (return[0]) in String | -| file://:0:0:0:0 | [summary param] -1 in String | file://:0:0:0:0 | [summary] to write: return (return[0]) in String | -| file://:0:0:0:0 | [summary param] -1 in String | file://:0:0:0:0 | [summary] to write: return (return[0]) in String | -| file://:0:0:0:0 | [summary param] -1 in String | file://:0:0:0:0 | [summary] to write: return (return[0]) in String | -| file://:0:0:0:0 | [summary param] -1 in String | file://:0:0:0:0 | [summary] to write: return (return[0]) in String | -| file://:0:0:0:0 | [summary param] -1 in String | file://:0:0:0:0 | [summary] to write: return (return[0]) in String | -| file://:0:0:0:0 | [summary param] -1 in String | file://:0:0:0:0 | [summary] to write: return (return[0]) in String | -| file://:0:0:0:0 | [summary param] -1 in String | file://:0:0:0:0 | [summary] to write: return (return[0]) in String | -| file://:0:0:0:0 | [summary param] -1 in String | file://:0:0:0:0 | [summary] to write: return (return[0]) in String | -| file://:0:0:0:0 | [summary param] -1 in String | file://:0:0:0:0 | [summary] to write: return (return[0]) in String | -| file://:0:0:0:0 | [summary param] -1 in String | file://:0:0:0:0 | [summary] to write: return (return[0]) in String | -| file://:0:0:0:0 | [summary param] -1 in String | file://:0:0:0:0 | [summary] to write: return (return[0]) in String | -| file://:0:0:0:0 | [summary param] -1 in String | file://:0:0:0:0 | [summary] to write: return (return[0]) in String | -| file://:0:0:0:0 | [summary param] -1 in String | file://:0:0:0:0 | [summary] to write: return (return[0]) in String | -| file://:0:0:0:0 | [summary param] -1 in String | file://:0:0:0:0 | [summary] to write: return (return[0]) in String | -| file://:0:0:0:0 | [summary param] -1 in String | file://:0:0:0:0 | [summary] to write: return (return[0]) in String | -| file://:0:0:0:0 | [summary param] -1 in String | file://:0:0:0:0 | [summary] to write: return (return[0]) in String | -| file://:0:0:0:0 | [summary param] -1 in String | file://:0:0:0:0 | [summary] to write: return (return[0]) in String | -| file://:0:0:0:0 | [summary param] -1 in String | file://:0:0:0:0 | [summary] to write: return (return[0]) in String | -| file://:0:0:0:0 | [summary param] -1 in String | file://:0:0:0:0 | [summary] to write: return (return[0]) in String | -| file://:0:0:0:0 | [summary param] -1 in String | file://:0:0:0:0 | [summary] to write: return (return[0]) in String | -| file://:0:0:0:0 | [summary param] -1 in String | file://:0:0:0:0 | [summary] to write: return (return[0]) in String | -| file://:0:0:0:0 | [summary param] -1 in String | file://:0:0:0:0 | [summary] to write: return (return[0]) in String | -| file://:0:0:0:0 | [summary param] -1 in String | file://:0:0:0:0 | [summary] to write: return (return[0]) in String | -| file://:0:0:0:0 | [summary param] -1 in String | file://:0:0:0:0 | [summary] to write: return (return[0]) in String | -| file://:0:0:0:0 | [summary param] -1 in String | file://:0:0:0:0 | [summary] to write: return (return[0]) in String | -| file://:0:0:0:0 | [summary param] -1 in String | file://:0:0:0:0 | [summary] to write: return (return[0]) in String | -| file://:0:0:0:0 | [summary param] -1 in String | file://:0:0:0:0 | [summary] to write: return (return[0]) in String | -| file://:0:0:0:0 | [summary param] -1 in String | file://:0:0:0:0 | [summary] to write: return (return[0]) in String | -| file://:0:0:0:0 | [summary param] -1 in String | file://:0:0:0:0 | [summary] to write: return (return[0]) in String | -| file://:0:0:0:0 | [summary param] -1 in String | file://:0:0:0:0 | [summary] to write: return (return[0]) in String | -| file://:0:0:0:0 | [summary param] -1 in String | file://:0:0:0:0 | [summary] to write: return (return[0]) in String | -| file://:0:0:0:0 | [summary param] -1 in String | file://:0:0:0:0 | [summary] to write: return (return[0]) in String | -| file://:0:0:0:0 | [summary param] -1 in String | file://:0:0:0:0 | [summary] to write: return (return[0]) in String | -| file://:0:0:0:0 | [summary param] -1 in String | file://:0:0:0:0 | [summary] to write: return (return[0]) in String | -| file://:0:0:0:0 | [summary param] -1 in String | file://:0:0:0:0 | [summary] to write: return (return[0]) in String | -| file://:0:0:0:0 | [summary param] -1 in String | file://:0:0:0:0 | [summary] to write: return (return[0]) in String | -| file://:0:0:0:0 | [summary param] -1 in String | file://:0:0:0:0 | [summary] to write: return (return[0]) in String | -| file://:0:0:0:0 | [summary param] -1 in String | file://:0:0:0:0 | [summary] to write: return (return[0]) in String | -| file://:0:0:0:0 | [summary param] -1 in String | file://:0:0:0:0 | [summary] to write: return (return[0]) in String | -| file://:0:0:0:0 | [summary param] -1 in String | file://:0:0:0:0 | [summary] to write: return (return[0]) in String | -| file://:0:0:0:0 | [summary param] -1 in String | file://:0:0:0:0 | [summary] to write: return (return[0]) in String | -| file://:0:0:0:0 | [summary param] -1 in String | file://:0:0:0:0 | [summary] to write: return (return[0]) in String | -| file://:0:0:0:0 | [summary param] -1 in String | file://:0:0:0:0 | [summary] to write: return (return[0]) in String | -| file://:0:0:0:0 | [summary param] -1 in String | file://:0:0:0:0 | [summary] to write: return (return[0]) in String | -| file://:0:0:0:0 | [summary param] -1 in String | file://:0:0:0:0 | [summary] to write: return (return[0]) in String | -| file://:0:0:0:0 | [summary param] -1 in String | file://:0:0:0:0 | [summary] to write: return (return[0]) in String | -| file://:0:0:0:0 | [summary param] -1 in String | file://:0:0:0:0 | [summary] to write: return (return[0]) in String | -| file://:0:0:0:0 | [summary param] -1 in String | file://:0:0:0:0 | [summary] to write: return (return[0]) in String | -| file://:0:0:0:0 | [summary param] -1 in String | file://:0:0:0:0 | [summary] to write: return (return[0]) in String | -| file://:0:0:0:0 | [summary param] -1 in String | file://:0:0:0:0 | [summary] to write: return (return[0]) in String | -| file://:0:0:0:0 | [summary param] -1 in String | file://:0:0:0:0 | [summary] to write: return (return[0]) in String | -| file://:0:0:0:0 | [summary param] -1 in String | file://:0:0:0:0 | [summary] to write: return (return[0]) in String | -| file://:0:0:0:0 | [summary param] -1 in String | file://:0:0:0:0 | [summary] to write: return (return[0]) in String | -| file://:0:0:0:0 | [summary param] -1 in String | file://:0:0:0:0 | [summary] to write: return (return[0]) in String | -| file://:0:0:0:0 | [summary param] -1 in String | file://:0:0:0:0 | [summary] to write: return (return[0]) in String | -| file://:0:0:0:0 | [summary param] -1 in String | file://:0:0:0:0 | [summary] to write: return (return[0]) in String | -| file://:0:0:0:0 | [summary param] -1 in String | file://:0:0:0:0 | [summary] to write: return (return[0]) in String | -| file://:0:0:0:0 | [summary param] -1 in String | file://:0:0:0:0 | [summary] to write: return (return[0]) in String | -| file://:0:0:0:0 | [summary param] -1 in String | file://:0:0:0:0 | [summary] to write: return (return[0]) in String | -| file://:0:0:0:0 | [summary param] -1 in String | file://:0:0:0:0 | [summary] to write: return (return[0]) in String | -| file://:0:0:0:0 | [summary param] -1 in String | file://:0:0:0:0 | [summary] to write: return (return[0]) in String | -| file://:0:0:0:0 | [summary param] -1 in String | file://:0:0:0:0 | [summary] to write: return (return[0]) in String | -| file://:0:0:0:0 | [summary param] -1 in String | file://:0:0:0:0 | [summary] to write: return (return[0]) in String | -| file://:0:0:0:0 | [summary param] -1 in String | file://:0:0:0:0 | [summary] to write: return (return[0]) in String | -| file://:0:0:0:0 | [summary param] -1 in String | file://:0:0:0:0 | [summary] to write: return (return[0]) in String | -| file://:0:0:0:0 | [summary param] -1 in String | file://:0:0:0:0 | [summary] to write: return (return[0]) in String | -| file://:0:0:0:0 | [summary param] -1 in String | file://:0:0:0:0 | [summary] to write: return (return[0]) in String | -| file://:0:0:0:0 | [summary param] -1 in String | file://:0:0:0:0 | [summary] to write: return (return[0]) in String | -| file://:0:0:0:0 | [summary param] -1 in String | file://:0:0:0:0 | [summary] to write: return (return[0]) in String | -| file://:0:0:0:0 | [summary param] -1 in String | file://:0:0:0:0 | [summary] to write: return (return[0]) in String | -| file://:0:0:0:0 | [summary param] -1 in String | file://:0:0:0:0 | [summary] to write: return (return[0]) in String | -| file://:0:0:0:0 | [summary param] -1 in String | file://:0:0:0:0 | [summary] to write: return (return[0]) in String | -| file://:0:0:0:0 | [summary param] -1 in String | file://:0:0:0:0 | [summary] to write: return (return[0]) in String | -| file://:0:0:0:0 | [summary param] -1 in String | file://:0:0:0:0 | [summary] to write: return (return[0]) in String | -| file://:0:0:0:0 | [summary param] -1 in String | file://:0:0:0:0 | [summary] to write: return (return[0]) in String | -| file://:0:0:0:0 | [summary param] -1 in String | file://:0:0:0:0 | [summary] to write: return (return[0]) in String | -| file://:0:0:0:0 | [summary param] -1 in String | file://:0:0:0:0 | [summary] to write: return (return[0]) in String | -| file://:0:0:0:0 | [summary param] -1 in String | file://:0:0:0:0 | [summary] to write: return (return[0]) in String | -| file://:0:0:0:0 | [summary param] -1 in String | file://:0:0:0:0 | [summary] to write: return (return[0]) in String | -| file://:0:0:0:0 | [summary param] -1 in String | file://:0:0:0:0 | [summary] to write: return (return[0]) in String | -| file://:0:0:0:0 | [summary param] -1 in String | file://:0:0:0:0 | [summary] to write: return (return[0]) in String | -| file://:0:0:0:0 | [summary param] -1 in String | file://:0:0:0:0 | [summary] to write: return (return[0]) in String | -| file://:0:0:0:0 | [summary param] -1 in String | file://:0:0:0:0 | [summary] to write: return (return[0]) in String | -| file://:0:0:0:0 | [summary param] -1 in String | file://:0:0:0:0 | [summary] to write: return (return[0]) in String | -| file://:0:0:0:0 | [summary param] -1 in String | file://:0:0:0:0 | [summary] to write: return (return[0]) in String | -| file://:0:0:0:0 | [summary param] -1 in Sub | file://:0:0:0:0 | [summary] to write: return (return[0]) in Sub | -| file://:0:0:0:0 | [summary param] -1 in Sub | file://:0:0:0:0 | [summary] to write: return (return[0]) in Sub | -| file://:0:0:0:0 | [summary param] -1 in Swap | file://:0:0:0:0 | [summary] to write: return (return[0]) in Swap | -| file://:0:0:0:0 | [summary param] -1 in Swap | file://:0:0:0:0 | [summary] to write: return (return[0]) in Swap | -| file://:0:0:0:0 | [summary param] -1 in Swap | file://:0:0:0:0 | [summary] to write: return (return[0]) in Swap | -| file://:0:0:0:0 | [summary param] -1 in Swap | file://:0:0:0:0 | [summary] to write: return (return[0]) in Swap | -| file://:0:0:0:0 | [summary param] -1 in SyscallConn | file://:0:0:0:0 | [summary] to write: return (return[0]) in SyscallConn | -| file://:0:0:0:0 | [summary param] -1 in SyscallConn | file://:0:0:0:0 | [summary] to write: return (return[0]) in SyscallConn | -| file://:0:0:0:0 | [summary param] -1 in SyscallConn | file://:0:0:0:0 | [summary] to write: return (return[0]) in SyscallConn | -| file://:0:0:0:0 | [summary param] -1 in SyscallConn | file://:0:0:0:0 | [summary] to write: return (return[0]) in SyscallConn | -| file://:0:0:0:0 | [summary param] -1 in SyscallConn | file://:0:0:0:0 | [summary] to write: return (return[0]) in SyscallConn | -| file://:0:0:0:0 | [summary param] -1 in SyscallConn | file://:0:0:0:0 | [summary] to write: return (return[0]) in SyscallConn | -| file://:0:0:0:0 | [summary param] -1 in SyscallConn | file://:0:0:0:0 | [summary] to write: return (return[0]) in SyscallConn | -| file://:0:0:0:0 | [summary param] -1 in SyscallConn | file://:0:0:0:0 | [summary] to write: return (return[0]) in SyscallConn | -| file://:0:0:0:0 | [summary param] -1 in Text | file://:0:0:0:0 | [summary] to write: return (return[0]) in Text | -| file://:0:0:0:0 | [summary param] -1 in Token | file://:0:0:0:0 | [summary] to write: return (return[0]) in Token | -| file://:0:0:0:0 | [summary param] -1 in Token | file://:0:0:0:0 | [summary] to write: return (return[0]) in Token | -| file://:0:0:0:0 | [summary param] -1 in Token | file://:0:0:0:0 | [summary] to write: return (return[0]) in Token | -| file://:0:0:0:0 | [summary param] -1 in TryRecv | file://:0:0:0:0 | [summary] to write: return (return[0]) in TryRecv | -| file://:0:0:0:0 | [summary param] -1 in UnsafeAddr | file://:0:0:0:0 | [summary] to write: return (return[0]) in UnsafeAddr | -| file://:0:0:0:0 | [summary param] -1 in Username | file://:0:0:0:0 | [summary] to write: return (return[0]) in Username | -| file://:0:0:0:0 | [summary param] -1 in Value | file://:0:0:0:0 | [summary] to write: return (return[0]) in Value | -| file://:0:0:0:0 | [summary param] -1 in Value | file://:0:0:0:0 | [summary] to write: return (return[0]) in Value | -| file://:0:0:0:0 | [summary param] -1 in Value | file://:0:0:0:0 | [summary] to write: return (return[0]) in Value | -| file://:0:0:0:0 | [summary param] -1 in Value | file://:0:0:0:0 | [summary] to write: return (return[0]) in Value | -| file://:0:0:0:0 | [summary param] -1 in Value | file://:0:0:0:0 | [summary] to write: return (return[0]) in Value | -| file://:0:0:0:0 | [summary param] -1 in Value | file://:0:0:0:0 | [summary] to write: return (return[0]) in Value | -| file://:0:0:0:0 | [summary param] -1 in Values | file://:0:0:0:0 | [summary] to write: return (return[0]) in Values | -| file://:0:0:0:0 | [summary param] -1 in Values | file://:0:0:0:0 | [summary] to write: return (return[0]) in Values | -| file://:0:0:0:0 | [summary param] -1 in Write | file://:0:0:0:0 | [summary] to write: argument 0 in Write | -| file://:0:0:0:0 | [summary param] -1 in Write | file://:0:0:0:0 | [summary] to write: argument 0 in Write | -| file://:0:0:0:0 | [summary param] -1 in Write | file://:0:0:0:0 | [summary] to write: argument 0 in Write | -| file://:0:0:0:0 | [summary param] -1 in WriteProxy | file://:0:0:0:0 | [summary] to write: argument 0 in WriteProxy | -| file://:0:0:0:0 | [summary param] -1 in WriteSubset | file://:0:0:0:0 | [summary] to write: argument 0 in WriteSubset | -| file://:0:0:0:0 | [summary param] -1 in WriteTo | file://:0:0:0:0 | [summary] to write: argument 0 in WriteTo | -| file://:0:0:0:0 | [summary param] -1 in WriteTo | file://:0:0:0:0 | [summary] to write: argument 0 in WriteTo | -| file://:0:0:0:0 | [summary param] -1 in WriteTo | file://:0:0:0:0 | [summary] to write: argument 0 in WriteTo | -| file://:0:0:0:0 | [summary param] -1 in WriteTo | file://:0:0:0:0 | [summary] to write: argument 0 in WriteTo | -| file://:0:0:0:0 | [summary param] -1 in WriteTo | file://:0:0:0:0 | [summary] to write: argument 0 in WriteTo | -| file://:0:0:0:0 | [summary param] -1 in WriteTo | file://:0:0:0:0 | [summary] to write: argument 0 in WriteTo | -| file://:0:0:0:0 | [summary param] -1 in WriteTo | file://:0:0:0:0 | [summary] to write: argument 0 in WriteTo | -| file://:0:0:0:0 | [summary param] -1 in WriteTo | file://:0:0:0:0 | [summary] to write: argument 0 in WriteTo | -| file://:0:0:0:0 | [summary param] -1 in WriteTo | file://:0:0:0:0 | [summary] to write: argument 0 in WriteTo | -| file://:0:0:0:0 | [summary param] -1 in Writer | file://:0:0:0:0 | [summary] to write: return (return[0]) in Writer | +| file://:0:0:0:0 | [summary param] 0 in Abs | file://:0:0:0:0 | [summary] to write: ReturnValue in Abs | +| file://:0:0:0:0 | [summary param] 0 in Add | file://:0:0:0:0 | [summary] to write: Argument[-1] in Add | +| file://:0:0:0:0 | [summary param] 0 in Add | file://:0:0:0:0 | [summary] to write: Argument[-1] in Add | +| file://:0:0:0:0 | [summary param] 0 in AddCookie | file://:0:0:0:0 | [summary] to write: Argument[-1] in AddCookie | +| file://:0:0:0:0 | [summary param] 0 in AppendQuote | file://:0:0:0:0 | [summary] to write: ReturnValue in AppendQuote | +| file://:0:0:0:0 | [summary param] 0 in AppendQuoteToASCII | file://:0:0:0:0 | [summary] to write: ReturnValue in AppendQuoteToASCII | +| file://:0:0:0:0 | [summary param] 0 in AppendQuoteToGraphic | file://:0:0:0:0 | [summary] to write: ReturnValue in AppendQuoteToGraphic | +| file://:0:0:0:0 | [summary param] 0 in AppendSlice | file://:0:0:0:0 | [summary] to write: ReturnValue in AppendSlice | +| file://:0:0:0:0 | [summary param] 0 in As | file://:0:0:0:0 | [summary] to write: Argument[1] in As | +| file://:0:0:0:0 | [summary param] 0 in Base | file://:0:0:0:0 | [summary] to write: ReturnValue in Base | +| file://:0:0:0:0 | [summary param] 0 in Base | file://:0:0:0:0 | [summary] to write: ReturnValue in Base | +| file://:0:0:0:0 | [summary param] 0 in BytePtrFromString | file://:0:0:0:0 | [summary] to write: ReturnValue in BytePtrFromString | +| file://:0:0:0:0 | [summary param] 0 in ByteSliceFromString | file://:0:0:0:0 | [summary] to write: ReturnValue in ByteSliceFromString | +| file://:0:0:0:0 | [summary param] 0 in CanonicalHeaderKey | file://:0:0:0:0 | [summary] to write: ReturnValue in CanonicalHeaderKey | +| file://:0:0:0:0 | [summary param] 0 in CanonicalMIMEHeaderKey | file://:0:0:0:0 | [summary] to write: ReturnValue in CanonicalMIMEHeaderKey | +| file://:0:0:0:0 | [summary param] 0 in Clean | file://:0:0:0:0 | [summary] to write: ReturnValue in Clean | +| file://:0:0:0:0 | [summary param] 0 in Clean | file://:0:0:0:0 | [summary] to write: ReturnValue in Clean | +| file://:0:0:0:0 | [summary param] 0 in Client | file://:0:0:0:0 | [summary] to write: ReturnValue in Client | +| file://:0:0:0:0 | [summary param] 0 in Clone | file://:0:0:0:0 | [summary] to write: ReturnValue in Clone | +| file://:0:0:0:0 | [summary param] 0 in Cut | file://:0:0:0:0 | [summary] to write: ReturnValue in Cut | +| file://:0:0:0:0 | [summary param] 0 in Cut | file://:0:0:0:0 | [summary] to write: ReturnValue[1] in Cut | +| file://:0:0:0:0 | [summary param] 0 in CutPrefix | file://:0:0:0:0 | [summary] to write: ReturnValue in CutPrefix | +| file://:0:0:0:0 | [summary param] 0 in CutSuffix | file://:0:0:0:0 | [summary] to write: ReturnValue in CutSuffix | +| file://:0:0:0:0 | [summary param] 0 in Decode | file://:0:0:0:0 | [summary] to write: ReturnValue in Decode | +| file://:0:0:0:0 | [summary param] 0 in Decode | file://:0:0:0:0 | [summary] to write: ReturnValue in Decode | +| file://:0:0:0:0 | [summary param] 0 in Decode | file://:0:0:0:0 | [summary] to write: ReturnValue[1] in Decode | +| file://:0:0:0:0 | [summary param] 0 in DecodeHeader | file://:0:0:0:0 | [summary] to write: ReturnValue in DecodeHeader | +| file://:0:0:0:0 | [summary param] 0 in DecodeString | file://:0:0:0:0 | [summary] to write: ReturnValue in DecodeString | +| file://:0:0:0:0 | [summary param] 0 in DecodeString | file://:0:0:0:0 | [summary] to write: ReturnValue in DecodeString | +| file://:0:0:0:0 | [summary param] 0 in DecryptPEMBlock | file://:0:0:0:0 | [summary] to write: ReturnValue in DecryptPEMBlock | +| file://:0:0:0:0 | [summary param] 0 in Dir | file://:0:0:0:0 | [summary] to write: ReturnValue in Dir | +| file://:0:0:0:0 | [summary param] 0 in Dir | file://:0:0:0:0 | [summary] to write: ReturnValue in Dir | +| file://:0:0:0:0 | [summary param] 0 in Encode | file://:0:0:0:0 | [summary] to write: Argument[-1] in Encode | +| file://:0:0:0:0 | [summary param] 0 in EncodeToMemory | file://:0:0:0:0 | [summary] to write: ReturnValue in EncodeToMemory | +| file://:0:0:0:0 | [summary param] 0 in EvalSymlinks | file://:0:0:0:0 | [summary] to write: ReturnValue in EvalSymlinks | +| file://:0:0:0:0 | [summary param] 0 in Expand | file://:0:0:0:0 | [summary] to write: ReturnValue in Expand | +| file://:0:0:0:0 | [summary param] 0 in ExpandEnv | file://:0:0:0:0 | [summary] to write: ReturnValue in ExpandEnv | +| file://:0:0:0:0 | [summary param] 0 in Ext | file://:0:0:0:0 | [summary] to write: ReturnValue in Ext | +| file://:0:0:0:0 | [summary param] 0 in Ext | file://:0:0:0:0 | [summary] to write: ReturnValue in Ext | +| file://:0:0:0:0 | [summary param] 0 in Fields | file://:0:0:0:0 | [summary] to write: ReturnValue in Fields | +| file://:0:0:0:0 | [summary param] 0 in Fields | file://:0:0:0:0 | [summary] to write: ReturnValue in Fields | +| file://:0:0:0:0 | [summary param] 0 in FieldsFunc | file://:0:0:0:0 | [summary] to write: ReturnValue in FieldsFunc | +| file://:0:0:0:0 | [summary param] 0 in FieldsFunc | file://:0:0:0:0 | [summary] to write: ReturnValue in FieldsFunc | +| file://:0:0:0:0 | [summary param] 0 in FileConn | file://:0:0:0:0 | [summary] to write: ReturnValue in FileConn | +| file://:0:0:0:0 | [summary param] 0 in FileInfoToDirEntry | file://:0:0:0:0 | [summary] to write: ReturnValue in FileInfoToDirEntry | +| file://:0:0:0:0 | [summary param] 0 in FilePacketConn | file://:0:0:0:0 | [summary] to write: ReturnValue in FilePacketConn | +| file://:0:0:0:0 | [summary param] 0 in FormatMediaType | file://:0:0:0:0 | [summary] to write: ReturnValue in FormatMediaType | +| file://:0:0:0:0 | [summary param] 0 in FromSlash | file://:0:0:0:0 | [summary] to write: ReturnValue in FromSlash | +| file://:0:0:0:0 | [summary param] 0 in Glob | file://:0:0:0:0 | [summary] to write: ReturnValue in Glob | +| file://:0:0:0:0 | [summary param] 0 in Glob | file://:0:0:0:0 | [summary] to write: ReturnValue in Glob | +| file://:0:0:0:0 | [summary param] 0 in Indirect | file://:0:0:0:0 | [summary] to write: ReturnValue in Indirect | +| file://:0:0:0:0 | [summary param] 0 in InsertAfter | file://:0:0:0:0 | [summary] to write: Argument[-1] in InsertAfter | +| file://:0:0:0:0 | [summary param] 0 in InsertAfter | file://:0:0:0:0 | [summary] to write: ReturnValue in InsertAfter | +| file://:0:0:0:0 | [summary param] 0 in InsertBefore | file://:0:0:0:0 | [summary] to write: Argument[-1] in InsertBefore | +| file://:0:0:0:0 | [summary param] 0 in InsertBefore | file://:0:0:0:0 | [summary] to write: ReturnValue in InsertBefore | +| file://:0:0:0:0 | [summary param] 0 in Join | file://:0:0:0:0 | [summary] to write: ReturnValue in Join | +| file://:0:0:0:0 | [summary param] 0 in Join | file://:0:0:0:0 | [summary] to write: ReturnValue in Join | +| file://:0:0:0:0 | [summary param] 0 in JoinHostPort | file://:0:0:0:0 | [summary] to write: ReturnValue in JoinHostPort | +| file://:0:0:0:0 | [summary param] 0 in LimitReader | file://:0:0:0:0 | [summary] to write: ReturnValue in LimitReader | +| file://:0:0:0:0 | [summary param] 0 in LoadOrStore | file://:0:0:0:0 | [summary] to write: Argument[-1] in LoadOrStore | +| file://:0:0:0:0 | [summary param] 0 in LoadOrStore | file://:0:0:0:0 | [summary] to write: ReturnValue in LoadOrStore | +| file://:0:0:0:0 | [summary param] 0 in LoadPointer | file://:0:0:0:0 | [summary] to write: ReturnValue in LoadPointer | +| file://:0:0:0:0 | [summary param] 0 in LoadUintptr | file://:0:0:0:0 | [summary] to write: ReturnValue in LoadUintptr | +| file://:0:0:0:0 | [summary param] 0 in Marshal | file://:0:0:0:0 | [summary] to write: ReturnValue in Marshal | +| file://:0:0:0:0 | [summary param] 0 in Marshal | file://:0:0:0:0 | [summary] to write: ReturnValue in Marshal | +| file://:0:0:0:0 | [summary param] 0 in MarshalIndent | file://:0:0:0:0 | [summary] to write: ReturnValue in MarshalIndent | +| file://:0:0:0:0 | [summary param] 0 in MarshalWithParams | file://:0:0:0:0 | [summary] to write: ReturnValue in MarshalWithParams | +| file://:0:0:0:0 | [summary param] 0 in MoveAfter | file://:0:0:0:0 | [summary] to write: Argument[-1] in MoveAfter | +| file://:0:0:0:0 | [summary param] 0 in MoveBefore | file://:0:0:0:0 | [summary] to write: Argument[-1] in MoveBefore | +| file://:0:0:0:0 | [summary param] 0 in MoveToBack | file://:0:0:0:0 | [summary] to write: Argument[-1] in MoveToBack | +| file://:0:0:0:0 | [summary param] 0 in MoveToFront | file://:0:0:0:0 | [summary] to write: Argument[-1] in MoveToFront | +| file://:0:0:0:0 | [summary param] 0 in New | file://:0:0:0:0 | [summary] to write: ReturnValue in New | +| file://:0:0:0:0 | [summary param] 0 in NewBuffer | file://:0:0:0:0 | [summary] to write: ReturnValue in NewBuffer | +| file://:0:0:0:0 | [summary param] 0 in NewBufferString | file://:0:0:0:0 | [summary] to write: ReturnValue in NewBufferString | +| file://:0:0:0:0 | [summary param] 0 in NewConn | file://:0:0:0:0 | [summary] to write: ReturnValue in NewConn | +| file://:0:0:0:0 | [summary param] 0 in NewDecoder | file://:0:0:0:0 | [summary] to write: ReturnValue in NewDecoder | +| file://:0:0:0:0 | [summary param] 0 in NewDecoder | file://:0:0:0:0 | [summary] to write: ReturnValue in NewDecoder | +| file://:0:0:0:0 | [summary param] 0 in NewFile | file://:0:0:0:0 | [summary] to write: ReturnValue in NewFile | +| file://:0:0:0:0 | [summary param] 0 in NewListener | file://:0:0:0:0 | [summary] to write: ReturnValue in NewListener | +| file://:0:0:0:0 | [summary param] 0 in NewReadWriter | file://:0:0:0:0 | [summary] to write: ReturnValue in NewReadWriter | +| file://:0:0:0:0 | [summary param] 0 in NewReader | file://:0:0:0:0 | [summary] to write: ReturnValue in NewReader | +| file://:0:0:0:0 | [summary param] 0 in NewReader | file://:0:0:0:0 | [summary] to write: ReturnValue in NewReader | +| file://:0:0:0:0 | [summary param] 0 in NewReader | file://:0:0:0:0 | [summary] to write: ReturnValue in NewReader | +| file://:0:0:0:0 | [summary param] 0 in NewReader | file://:0:0:0:0 | [summary] to write: ReturnValue in NewReader | +| file://:0:0:0:0 | [summary param] 0 in NewReader | file://:0:0:0:0 | [summary] to write: ReturnValue in NewReader | +| file://:0:0:0:0 | [summary param] 0 in NewReader | file://:0:0:0:0 | [summary] to write: ReturnValue in NewReader | +| file://:0:0:0:0 | [summary param] 0 in NewReader | file://:0:0:0:0 | [summary] to write: ReturnValue in NewReader | +| file://:0:0:0:0 | [summary param] 0 in NewReader | file://:0:0:0:0 | [summary] to write: ReturnValue in NewReader | +| file://:0:0:0:0 | [summary param] 0 in NewReaderDict | file://:0:0:0:0 | [summary] to write: ReturnValue in NewReaderDict | +| file://:0:0:0:0 | [summary param] 0 in NewReaderSize | file://:0:0:0:0 | [summary] to write: ReturnValue in NewReaderSize | +| file://:0:0:0:0 | [summary param] 0 in NewScanner | file://:0:0:0:0 | [summary] to write: ReturnValue in NewScanner | +| file://:0:0:0:0 | [summary param] 0 in NewSectionReader | file://:0:0:0:0 | [summary] to write: ReturnValue in NewSectionReader | +| file://:0:0:0:0 | [summary param] 0 in NopCloser | file://:0:0:0:0 | [summary] to write: ReturnValue in NopCloser | +| file://:0:0:0:0 | [summary param] 0 in NopCloser | file://:0:0:0:0 | [summary] to write: ReturnValue in NopCloser | +| file://:0:0:0:0 | [summary param] 0 in Parse | file://:0:0:0:0 | [summary] to write: ReturnValue in Parse | +| file://:0:0:0:0 | [summary param] 0 in Parse | file://:0:0:0:0 | [summary] to write: ReturnValue in Parse | +| file://:0:0:0:0 | [summary param] 0 in ParseMediaType | file://:0:0:0:0 | [summary] to write: ReturnValue in ParseMediaType | +| file://:0:0:0:0 | [summary param] 0 in ParseMediaType | file://:0:0:0:0 | [summary] to write: ReturnValue[1] in ParseMediaType | +| file://:0:0:0:0 | [summary param] 0 in ParseQuery | file://:0:0:0:0 | [summary] to write: ReturnValue in ParseQuery | +| file://:0:0:0:0 | [summary param] 0 in ParseRequestURI | file://:0:0:0:0 | [summary] to write: ReturnValue in ParseRequestURI | +| file://:0:0:0:0 | [summary param] 0 in PathEscape | file://:0:0:0:0 | [summary] to write: ReturnValue in PathEscape | +| file://:0:0:0:0 | [summary param] 0 in PathUnescape | file://:0:0:0:0 | [summary] to write: ReturnValue in PathUnescape | +| file://:0:0:0:0 | [summary param] 0 in PushBack | file://:0:0:0:0 | [summary] to write: Argument[-1] in PushBack | +| file://:0:0:0:0 | [summary param] 0 in PushBack | file://:0:0:0:0 | [summary] to write: ReturnValue in PushBack | +| file://:0:0:0:0 | [summary param] 0 in PushBackList | file://:0:0:0:0 | [summary] to write: Argument[-1] in PushBackList | +| file://:0:0:0:0 | [summary param] 0 in PushFront | file://:0:0:0:0 | [summary] to write: Argument[-1] in PushFront | +| file://:0:0:0:0 | [summary param] 0 in PushFront | file://:0:0:0:0 | [summary] to write: ReturnValue in PushFront | +| file://:0:0:0:0 | [summary param] 0 in PushFrontList | file://:0:0:0:0 | [summary] to write: Argument[-1] in PushFrontList | +| file://:0:0:0:0 | [summary param] 0 in Put | file://:0:0:0:0 | [summary] to write: Argument[-1] in Put | +| file://:0:0:0:0 | [summary param] 0 in QueryEscape | file://:0:0:0:0 | [summary] to write: ReturnValue in QueryEscape | +| file://:0:0:0:0 | [summary param] 0 in QueryUnescape | file://:0:0:0:0 | [summary] to write: ReturnValue in QueryUnescape | +| file://:0:0:0:0 | [summary param] 0 in Quote | file://:0:0:0:0 | [summary] to write: ReturnValue in Quote | +| file://:0:0:0:0 | [summary param] 0 in QuoteToASCII | file://:0:0:0:0 | [summary] to write: ReturnValue in QuoteToASCII | +| file://:0:0:0:0 | [summary param] 0 in QuoteToGraphic | file://:0:0:0:0 | [summary] to write: ReturnValue in QuoteToGraphic | +| file://:0:0:0:0 | [summary param] 0 in QuotedPrefix | file://:0:0:0:0 | [summary] to write: ReturnValue in QuotedPrefix | +| file://:0:0:0:0 | [summary param] 0 in Read | file://:0:0:0:0 | [summary] to write: Argument[2] in Read | +| file://:0:0:0:0 | [summary param] 0 in ReadAll | file://:0:0:0:0 | [summary] to write: ReturnValue in ReadAll | +| file://:0:0:0:0 | [summary param] 0 in ReadAll | file://:0:0:0:0 | [summary] to write: ReturnValue in ReadAll | +| file://:0:0:0:0 | [summary param] 0 in ReadAtLeast | file://:0:0:0:0 | [summary] to write: Argument[1] in ReadAtLeast | +| file://:0:0:0:0 | [summary param] 0 in ReadDir | file://:0:0:0:0 | [summary] to write: ReturnValue in ReadDir | +| file://:0:0:0:0 | [summary param] 0 in ReadFile | file://:0:0:0:0 | [summary] to write: ReturnValue in ReadFile | +| file://:0:0:0:0 | [summary param] 0 in ReadFrom | file://:0:0:0:0 | [summary] to write: Argument[-1] in ReadFrom | +| file://:0:0:0:0 | [summary param] 0 in ReadFrom | file://:0:0:0:0 | [summary] to write: Argument[-1] in ReadFrom | +| file://:0:0:0:0 | [summary param] 0 in ReadFrom | file://:0:0:0:0 | [summary] to write: Argument[-1] in ReadFrom | +| file://:0:0:0:0 | [summary param] 0 in ReadFrom | file://:0:0:0:0 | [summary] to write: Argument[-1] in ReadFrom | +| file://:0:0:0:0 | [summary param] 0 in ReadFrom | file://:0:0:0:0 | [summary] to write: Argument[-1] in ReadFrom | +| file://:0:0:0:0 | [summary param] 0 in ReadFrom | file://:0:0:0:0 | [summary] to write: Argument[-1] in ReadFrom | +| file://:0:0:0:0 | [summary param] 0 in ReadFrom | file://:0:0:0:0 | [summary] to write: Argument[-1] in ReadFrom | +| file://:0:0:0:0 | [summary param] 0 in ReadFrom | file://:0:0:0:0 | [summary] to write: Argument[-1] in ReadFrom | +| file://:0:0:0:0 | [summary param] 0 in ReadFull | file://:0:0:0:0 | [summary] to write: Argument[1] in ReadFull | +| file://:0:0:0:0 | [summary param] 0 in ReadRequest | file://:0:0:0:0 | [summary] to write: ReturnValue in ReadRequest | +| file://:0:0:0:0 | [summary param] 0 in ReadResponse | file://:0:0:0:0 | [summary] to write: ReturnValue in ReadResponse | +| file://:0:0:0:0 | [summary param] 0 in Rel | file://:0:0:0:0 | [summary] to write: ReturnValue in Rel | +| file://:0:0:0:0 | [summary param] 0 in Remove | file://:0:0:0:0 | [summary] to write: ReturnValue in Remove | +| file://:0:0:0:0 | [summary param] 0 in Repeat | file://:0:0:0:0 | [summary] to write: ReturnValue in Repeat | +| file://:0:0:0:0 | [summary param] 0 in Repeat | file://:0:0:0:0 | [summary] to write: ReturnValue in Repeat | +| file://:0:0:0:0 | [summary param] 0 in Replace | file://:0:0:0:0 | [summary] to write: ReturnValue in Replace | +| file://:0:0:0:0 | [summary param] 0 in Replace | file://:0:0:0:0 | [summary] to write: ReturnValue in Replace | +| file://:0:0:0:0 | [summary param] 0 in Replace | file://:0:0:0:0 | [summary] to write: ReturnValue in Replace | +| file://:0:0:0:0 | [summary param] 0 in ReplaceAll | file://:0:0:0:0 | [summary] to write: ReturnValue in ReplaceAll | +| file://:0:0:0:0 | [summary param] 0 in ReplaceAll | file://:0:0:0:0 | [summary] to write: ReturnValue in ReplaceAll | +| file://:0:0:0:0 | [summary param] 0 in Reset | file://:0:0:0:0 | [summary] to write: Argument[-1] in Reset | +| file://:0:0:0:0 | [summary param] 0 in Reset | file://:0:0:0:0 | [summary] to write: Argument[-1] in Reset | +| file://:0:0:0:0 | [summary param] 0 in Reset | file://:0:0:0:0 | [summary] to write: Argument[-1] in Reset | +| file://:0:0:0:0 | [summary param] 0 in Reset | file://:0:0:0:0 | [summary] to write: Argument[-1] in Reset | +| file://:0:0:0:0 | [summary param] 0 in Reset | file://:0:0:0:0 | [summary] to write: Argument[-1] in Reset | +| file://:0:0:0:0 | [summary param] 0 in Reset | file://:0:0:0:0 | [summary] to write: Argument[-1] in Reset | +| file://:0:0:0:0 | [summary param] 0 in ResolveReference | file://:0:0:0:0 | [summary] to write: ReturnValue in ResolveReference | +| file://:0:0:0:0 | [summary param] 0 in Reverse | file://:0:0:0:0 | [summary] to write: ReturnValue in Reverse | +| file://:0:0:0:0 | [summary param] 0 in Runes | file://:0:0:0:0 | [summary] to write: ReturnValue in Runes | +| file://:0:0:0:0 | [summary param] 0 in ScanBytes | file://:0:0:0:0 | [summary] to write: ReturnValue[1] in ScanBytes | +| file://:0:0:0:0 | [summary param] 0 in ScanLines | file://:0:0:0:0 | [summary] to write: ReturnValue[1] in ScanLines | +| file://:0:0:0:0 | [summary param] 0 in ScanRunes | file://:0:0:0:0 | [summary] to write: ReturnValue[1] in ScanRunes | +| file://:0:0:0:0 | [summary param] 0 in ScanWords | file://:0:0:0:0 | [summary] to write: ReturnValue[1] in ScanWords | +| file://:0:0:0:0 | [summary param] 0 in Send | file://:0:0:0:0 | [summary] to write: Argument[-1] in Send | +| file://:0:0:0:0 | [summary param] 0 in Server | file://:0:0:0:0 | [summary] to write: ReturnValue in Server | +| file://:0:0:0:0 | [summary param] 0 in Set | file://:0:0:0:0 | [summary] to write: Argument[-1] in Set | +| file://:0:0:0:0 | [summary param] 0 in Set | file://:0:0:0:0 | [summary] to write: Argument[-1] in Set | +| file://:0:0:0:0 | [summary param] 0 in Set | file://:0:0:0:0 | [summary] to write: Argument[-1] in Set | +| file://:0:0:0:0 | [summary param] 0 in SetBytes | file://:0:0:0:0 | [summary] to write: Argument[-1] in SetBytes | +| file://:0:0:0:0 | [summary param] 0 in SetIndent | file://:0:0:0:0 | [summary] to write: Argument[-1] in SetIndent | +| file://:0:0:0:0 | [summary param] 0 in SetMapIndex | file://:0:0:0:0 | [summary] to write: Argument[-1] in SetMapIndex | +| file://:0:0:0:0 | [summary param] 0 in SetPointer | file://:0:0:0:0 | [summary] to write: Argument[-1] in SetPointer | +| file://:0:0:0:0 | [summary param] 0 in SetPrefix | file://:0:0:0:0 | [summary] to write: Argument[-1] in SetPrefix | +| file://:0:0:0:0 | [summary param] 0 in SetString | file://:0:0:0:0 | [summary] to write: Argument[-1] in SetString | +| file://:0:0:0:0 | [summary param] 0 in Split | file://:0:0:0:0 | [summary] to write: ReturnValue in Split | +| file://:0:0:0:0 | [summary param] 0 in Split | file://:0:0:0:0 | [summary] to write: ReturnValue in Split | +| file://:0:0:0:0 | [summary param] 0 in Split | file://:0:0:0:0 | [summary] to write: ReturnValue in Split | +| file://:0:0:0:0 | [summary param] 0 in Split | file://:0:0:0:0 | [summary] to write: ReturnValue in Split | +| file://:0:0:0:0 | [summary param] 0 in Split | file://:0:0:0:0 | [summary] to write: ReturnValue[1] in Split | +| file://:0:0:0:0 | [summary param] 0 in Split | file://:0:0:0:0 | [summary] to write: ReturnValue[1] in Split | +| file://:0:0:0:0 | [summary param] 0 in SplitAfter | file://:0:0:0:0 | [summary] to write: ReturnValue in SplitAfter | +| file://:0:0:0:0 | [summary param] 0 in SplitAfter | file://:0:0:0:0 | [summary] to write: ReturnValue in SplitAfter | +| file://:0:0:0:0 | [summary param] 0 in SplitAfterN | file://:0:0:0:0 | [summary] to write: ReturnValue in SplitAfterN | +| file://:0:0:0:0 | [summary param] 0 in SplitAfterN | file://:0:0:0:0 | [summary] to write: ReturnValue in SplitAfterN | +| file://:0:0:0:0 | [summary param] 0 in SplitHostPort | file://:0:0:0:0 | [summary] to write: ReturnValue in SplitHostPort | +| file://:0:0:0:0 | [summary param] 0 in SplitHostPort | file://:0:0:0:0 | [summary] to write: ReturnValue[1] in SplitHostPort | +| file://:0:0:0:0 | [summary param] 0 in SplitList | file://:0:0:0:0 | [summary] to write: ReturnValue in SplitList | +| file://:0:0:0:0 | [summary param] 0 in SplitN | file://:0:0:0:0 | [summary] to write: ReturnValue in SplitN | +| file://:0:0:0:0 | [summary param] 0 in SplitN | file://:0:0:0:0 | [summary] to write: ReturnValue in SplitN | +| file://:0:0:0:0 | [summary param] 0 in Store | file://:0:0:0:0 | [summary] to write: Argument[-1] in Store | +| file://:0:0:0:0 | [summary param] 0 in Store | file://:0:0:0:0 | [summary] to write: Argument[-1] in Store | +| file://:0:0:0:0 | [summary param] 0 in Store | file://:0:0:0:0 | [summary] to write: Argument[-1] in Store | +| file://:0:0:0:0 | [summary param] 0 in Store | file://:0:0:0:0 | [summary] to write: Argument[-1] in Store | +| file://:0:0:0:0 | [summary param] 0 in StringBytePtr | file://:0:0:0:0 | [summary] to write: ReturnValue in StringBytePtr | +| file://:0:0:0:0 | [summary param] 0 in StringByteSlice | file://:0:0:0:0 | [summary] to write: ReturnValue in StringByteSlice | +| file://:0:0:0:0 | [summary param] 0 in Sub | file://:0:0:0:0 | [summary] to write: ReturnValue in Sub | +| file://:0:0:0:0 | [summary param] 0 in Swap | file://:0:0:0:0 | [summary] to write: Argument[-1] in Swap | +| file://:0:0:0:0 | [summary param] 0 in Swap | file://:0:0:0:0 | [summary] to write: Argument[-1] in Swap | +| file://:0:0:0:0 | [summary param] 0 in Swap | file://:0:0:0:0 | [summary] to write: Argument[-1] in Swap | +| file://:0:0:0:0 | [summary param] 0 in Swap | file://:0:0:0:0 | [summary] to write: Argument[-1] in Swap | +| file://:0:0:0:0 | [summary param] 0 in SwapPointer | file://:0:0:0:0 | [summary] to write: ReturnValue in SwapPointer | +| file://:0:0:0:0 | [summary param] 0 in SwapUintptr | file://:0:0:0:0 | [summary] to write: ReturnValue in SwapUintptr | +| file://:0:0:0:0 | [summary param] 0 in TeeReader | file://:0:0:0:0 | [summary] to write: Argument[1] in TeeReader | +| file://:0:0:0:0 | [summary param] 0 in TeeReader | file://:0:0:0:0 | [summary] to write: ReturnValue in TeeReader | +| file://:0:0:0:0 | [summary param] 0 in Title | file://:0:0:0:0 | [summary] to write: ReturnValue in Title | +| file://:0:0:0:0 | [summary param] 0 in Title | file://:0:0:0:0 | [summary] to write: ReturnValue in Title | +| file://:0:0:0:0 | [summary param] 0 in ToLower | file://:0:0:0:0 | [summary] to write: ReturnValue in ToLower | +| file://:0:0:0:0 | [summary param] 0 in ToLower | file://:0:0:0:0 | [summary] to write: ReturnValue in ToLower | +| file://:0:0:0:0 | [summary param] 0 in ToSlash | file://:0:0:0:0 | [summary] to write: ReturnValue in ToSlash | +| file://:0:0:0:0 | [summary param] 0 in ToTitle | file://:0:0:0:0 | [summary] to write: ReturnValue in ToTitle | +| file://:0:0:0:0 | [summary param] 0 in ToTitle | file://:0:0:0:0 | [summary] to write: ReturnValue in ToTitle | +| file://:0:0:0:0 | [summary param] 0 in ToUpper | file://:0:0:0:0 | [summary] to write: ReturnValue in ToUpper | +| file://:0:0:0:0 | [summary param] 0 in ToUpper | file://:0:0:0:0 | [summary] to write: ReturnValue in ToUpper | +| file://:0:0:0:0 | [summary param] 0 in ToValidUTF8 | file://:0:0:0:0 | [summary] to write: ReturnValue in ToValidUTF8 | +| file://:0:0:0:0 | [summary param] 0 in ToValidUTF8 | file://:0:0:0:0 | [summary] to write: ReturnValue in ToValidUTF8 | +| file://:0:0:0:0 | [summary param] 0 in Trim | file://:0:0:0:0 | [summary] to write: ReturnValue in Trim | +| file://:0:0:0:0 | [summary param] 0 in Trim | file://:0:0:0:0 | [summary] to write: ReturnValue in Trim | +| file://:0:0:0:0 | [summary param] 0 in TrimBytes | file://:0:0:0:0 | [summary] to write: ReturnValue in TrimBytes | +| file://:0:0:0:0 | [summary param] 0 in TrimFunc | file://:0:0:0:0 | [summary] to write: ReturnValue in TrimFunc | +| file://:0:0:0:0 | [summary param] 0 in TrimFunc | file://:0:0:0:0 | [summary] to write: ReturnValue in TrimFunc | +| file://:0:0:0:0 | [summary param] 0 in TrimLeft | file://:0:0:0:0 | [summary] to write: ReturnValue in TrimLeft | +| file://:0:0:0:0 | [summary param] 0 in TrimLeft | file://:0:0:0:0 | [summary] to write: ReturnValue in TrimLeft | +| file://:0:0:0:0 | [summary param] 0 in TrimLeftFunc | file://:0:0:0:0 | [summary] to write: ReturnValue in TrimLeftFunc | +| file://:0:0:0:0 | [summary param] 0 in TrimLeftFunc | file://:0:0:0:0 | [summary] to write: ReturnValue in TrimLeftFunc | +| file://:0:0:0:0 | [summary param] 0 in TrimPrefix | file://:0:0:0:0 | [summary] to write: ReturnValue in TrimPrefix | +| file://:0:0:0:0 | [summary param] 0 in TrimPrefix | file://:0:0:0:0 | [summary] to write: ReturnValue in TrimPrefix | +| file://:0:0:0:0 | [summary param] 0 in TrimRight | file://:0:0:0:0 | [summary] to write: ReturnValue in TrimRight | +| file://:0:0:0:0 | [summary param] 0 in TrimRight | file://:0:0:0:0 | [summary] to write: ReturnValue in TrimRight | +| file://:0:0:0:0 | [summary param] 0 in TrimRightFunc | file://:0:0:0:0 | [summary] to write: ReturnValue in TrimRightFunc | +| file://:0:0:0:0 | [summary param] 0 in TrimRightFunc | file://:0:0:0:0 | [summary] to write: ReturnValue in TrimRightFunc | +| file://:0:0:0:0 | [summary param] 0 in TrimSpace | file://:0:0:0:0 | [summary] to write: ReturnValue in TrimSpace | +| file://:0:0:0:0 | [summary param] 0 in TrimSpace | file://:0:0:0:0 | [summary] to write: ReturnValue in TrimSpace | +| file://:0:0:0:0 | [summary param] 0 in TrimString | file://:0:0:0:0 | [summary] to write: ReturnValue in TrimString | +| file://:0:0:0:0 | [summary param] 0 in TrimSuffix | file://:0:0:0:0 | [summary] to write: ReturnValue in TrimSuffix | +| file://:0:0:0:0 | [summary param] 0 in TrimSuffix | file://:0:0:0:0 | [summary] to write: ReturnValue in TrimSuffix | +| file://:0:0:0:0 | [summary param] 0 in TrySend | file://:0:0:0:0 | [summary] to write: Argument[-1] in TrySend | +| file://:0:0:0:0 | [summary param] 0 in Unmarshal | file://:0:0:0:0 | [summary] to write: Argument[1] in Unmarshal | +| file://:0:0:0:0 | [summary param] 0 in Unmarshal | file://:0:0:0:0 | [summary] to write: Argument[1] in Unmarshal | +| file://:0:0:0:0 | [summary param] 0 in Unmarshal | file://:0:0:0:0 | [summary] to write: ReturnValue in Unmarshal | +| file://:0:0:0:0 | [summary param] 0 in UnmarshalBinary | file://:0:0:0:0 | [summary] to write: Argument[-1] in UnmarshalBinary | +| file://:0:0:0:0 | [summary param] 0 in UnmarshalBinary | file://:0:0:0:0 | [summary] to write: Argument[-1] in UnmarshalBinary | +| file://:0:0:0:0 | [summary param] 0 in UnmarshalBinary | file://:0:0:0:0 | [summary] to write: Argument[-1] in UnmarshalBinary | +| file://:0:0:0:0 | [summary param] 0 in UnmarshalBinary | file://:0:0:0:0 | [summary] to write: Argument[-1] in UnmarshalBinary | +| file://:0:0:0:0 | [summary param] 0 in UnmarshalBinary | file://:0:0:0:0 | [summary] to write: Argument[-1] in UnmarshalBinary | +| file://:0:0:0:0 | [summary param] 0 in UnmarshalBinary | file://:0:0:0:0 | [summary] to write: Argument[-1] in UnmarshalBinary | +| file://:0:0:0:0 | [summary param] 0 in UnmarshalBinary | file://:0:0:0:0 | [summary] to write: Argument[-1] in UnmarshalBinary | +| file://:0:0:0:0 | [summary param] 0 in UnmarshalBinary | file://:0:0:0:0 | [summary] to write: Argument[-1] in UnmarshalBinary | +| file://:0:0:0:0 | [summary param] 0 in UnmarshalBinary | file://:0:0:0:0 | [summary] to write: Argument[-1] in UnmarshalBinary | +| file://:0:0:0:0 | [summary param] 0 in UnmarshalBinary | file://:0:0:0:0 | [summary] to write: Argument[-1] in UnmarshalBinary | +| file://:0:0:0:0 | [summary param] 0 in UnmarshalBinary | file://:0:0:0:0 | [summary] to write: Argument[-1] in UnmarshalBinary | +| file://:0:0:0:0 | [summary param] 0 in UnmarshalBinary | file://:0:0:0:0 | [summary] to write: Argument[-1] in UnmarshalBinary | +| file://:0:0:0:0 | [summary param] 0 in UnmarshalJSON | file://:0:0:0:0 | [summary] to write: Argument[-1] in UnmarshalJSON | +| file://:0:0:0:0 | [summary param] 0 in UnmarshalJSON | file://:0:0:0:0 | [summary] to write: Argument[-1] in UnmarshalJSON | +| file://:0:0:0:0 | [summary param] 0 in UnmarshalJSON | file://:0:0:0:0 | [summary] to write: Argument[-1] in UnmarshalJSON | +| file://:0:0:0:0 | [summary param] 0 in UnmarshalJSON | file://:0:0:0:0 | [summary] to write: Argument[-1] in UnmarshalJSON | +| file://:0:0:0:0 | [summary param] 0 in UnmarshalText | file://:0:0:0:0 | [summary] to write: Argument[-1] in UnmarshalText | +| file://:0:0:0:0 | [summary param] 0 in UnmarshalText | file://:0:0:0:0 | [summary] to write: Argument[-1] in UnmarshalText | +| file://:0:0:0:0 | [summary param] 0 in UnmarshalText | file://:0:0:0:0 | [summary] to write: Argument[-1] in UnmarshalText | +| file://:0:0:0:0 | [summary param] 0 in UnmarshalText | file://:0:0:0:0 | [summary] to write: Argument[-1] in UnmarshalText | +| file://:0:0:0:0 | [summary param] 0 in UnmarshalText | file://:0:0:0:0 | [summary] to write: Argument[-1] in UnmarshalText | +| file://:0:0:0:0 | [summary param] 0 in UnmarshalText | file://:0:0:0:0 | [summary] to write: Argument[-1] in UnmarshalText | +| file://:0:0:0:0 | [summary param] 0 in UnmarshalText | file://:0:0:0:0 | [summary] to write: Argument[-1] in UnmarshalText | +| file://:0:0:0:0 | [summary param] 0 in UnmarshalText | file://:0:0:0:0 | [summary] to write: Argument[-1] in UnmarshalText | +| file://:0:0:0:0 | [summary param] 0 in UnmarshalText | file://:0:0:0:0 | [summary] to write: Argument[-1] in UnmarshalText | +| file://:0:0:0:0 | [summary param] 0 in UnmarshalWithParams | file://:0:0:0:0 | [summary] to write: Argument[1] in UnmarshalWithParams | +| file://:0:0:0:0 | [summary param] 0 in UnmarshalWithParams | file://:0:0:0:0 | [summary] to write: ReturnValue in UnmarshalWithParams | +| file://:0:0:0:0 | [summary param] 0 in Unquote | file://:0:0:0:0 | [summary] to write: ReturnValue in Unquote | +| file://:0:0:0:0 | [summary param] 0 in UnquoteChar | file://:0:0:0:0 | [summary] to write: ReturnValue[2] in UnquoteChar | +| file://:0:0:0:0 | [summary param] 0 in Unwrap | file://:0:0:0:0 | [summary] to write: ReturnValue in Unwrap | +| file://:0:0:0:0 | [summary param] 0 in User | file://:0:0:0:0 | [summary] to write: ReturnValue in User | +| file://:0:0:0:0 | [summary param] 0 in UserPassword | file://:0:0:0:0 | [summary] to write: ReturnValue in UserPassword | +| file://:0:0:0:0 | [summary param] 0 in ValueOf | file://:0:0:0:0 | [summary] to write: ReturnValue in ValueOf | +| file://:0:0:0:0 | [summary param] 0 in VolumeName | file://:0:0:0:0 | [summary] to write: ReturnValue in VolumeName | +| file://:0:0:0:0 | [summary param] 0 in WithCancel | file://:0:0:0:0 | [summary] to write: ReturnValue in WithCancel | +| file://:0:0:0:0 | [summary param] 0 in WithDeadline | file://:0:0:0:0 | [summary] to write: ReturnValue in WithDeadline | +| file://:0:0:0:0 | [summary param] 0 in WithTimeout | file://:0:0:0:0 | [summary] to write: ReturnValue in WithTimeout | +| file://:0:0:0:0 | [summary param] 0 in WithValue | file://:0:0:0:0 | [summary] to write: ReturnValue in WithValue | +| file://:0:0:0:0 | [summary param] 0 in Write | file://:0:0:0:0 | [summary] to write: Argument[-1] in Write | +| file://:0:0:0:0 | [summary param] 0 in Write | file://:0:0:0:0 | [summary] to write: Argument[-1] in Write | +| file://:0:0:0:0 | [summary param] 0 in Write | file://:0:0:0:0 | [summary] to write: Argument[-1] in Write | +| file://:0:0:0:0 | [summary param] 0 in Write | file://:0:0:0:0 | [summary] to write: Argument[-1] in Write | +| file://:0:0:0:0 | [summary param] 0 in Write | file://:0:0:0:0 | [summary] to write: Argument[-1] in Write | +| file://:0:0:0:0 | [summary param] 0 in Write | file://:0:0:0:0 | [summary] to write: Argument[-1] in Write | +| file://:0:0:0:0 | [summary param] 0 in Write | file://:0:0:0:0 | [summary] to write: Argument[-1] in Write | +| file://:0:0:0:0 | [summary param] 0 in Write | file://:0:0:0:0 | [summary] to write: Argument[-1] in Write | +| file://:0:0:0:0 | [summary param] 0 in Write | file://:0:0:0:0 | [summary] to write: Argument[-1] in Write | +| file://:0:0:0:0 | [summary param] 0 in Write | file://:0:0:0:0 | [summary] to write: Argument[-1] in Write | +| file://:0:0:0:0 | [summary param] 0 in Write | file://:0:0:0:0 | [summary] to write: Argument[-1] in Write | +| file://:0:0:0:0 | [summary param] 0 in Write | file://:0:0:0:0 | [summary] to write: Argument[-1] in Write | +| file://:0:0:0:0 | [summary param] 0 in Write | file://:0:0:0:0 | [summary] to write: Argument[-1] in Write | +| file://:0:0:0:0 | [summary param] 0 in Write | file://:0:0:0:0 | [summary] to write: Argument[-1] in Write | +| file://:0:0:0:0 | [summary param] 0 in Write | file://:0:0:0:0 | [summary] to write: Argument[-1] in Write | +| file://:0:0:0:0 | [summary param] 0 in Write | file://:0:0:0:0 | [summary] to write: Argument[-1] in Write | +| file://:0:0:0:0 | [summary param] 0 in Write | file://:0:0:0:0 | [summary] to write: Argument[-1] in Write | +| file://:0:0:0:0 | [summary param] 0 in Write | file://:0:0:0:0 | [summary] to write: Argument[-1] in Write | +| file://:0:0:0:0 | [summary param] 0 in Write | file://:0:0:0:0 | [summary] to write: Argument[-1] in Write | +| file://:0:0:0:0 | [summary param] 0 in Write | file://:0:0:0:0 | [summary] to write: Argument[-1] in Write | +| file://:0:0:0:0 | [summary param] 0 in Write | file://:0:0:0:0 | [summary] to write: Argument[-1] in Write | +| file://:0:0:0:0 | [summary param] 0 in Write | file://:0:0:0:0 | [summary] to write: Argument[-1] in Write | +| file://:0:0:0:0 | [summary param] 0 in Write | file://:0:0:0:0 | [summary] to write: Argument[-1] in Write | +| file://:0:0:0:0 | [summary param] 0 in Write | file://:0:0:0:0 | [summary] to write: Argument[-1] in Write | +| file://:0:0:0:0 | [summary param] 0 in Write | file://:0:0:0:0 | [summary] to write: Argument[-1] in Write | +| file://:0:0:0:0 | [summary param] 0 in Write | file://:0:0:0:0 | [summary] to write: Argument[-1] in Write | +| file://:0:0:0:0 | [summary param] 0 in Write | file://:0:0:0:0 | [summary] to write: Argument[-1] in Write | +| file://:0:0:0:0 | [summary param] 0 in Write | file://:0:0:0:0 | [summary] to write: Argument[-1] in Write | +| file://:0:0:0:0 | [summary param] 0 in Write | file://:0:0:0:0 | [summary] to write: Argument[-1] in Write | +| file://:0:0:0:0 | [summary param] 0 in Write | file://:0:0:0:0 | [summary] to write: Argument[-1] in Write | +| file://:0:0:0:0 | [summary param] 0 in Write | file://:0:0:0:0 | [summary] to write: Argument[-1] in Write | +| file://:0:0:0:0 | [summary param] 0 in Write | file://:0:0:0:0 | [summary] to write: Argument[-1] in Write | +| file://:0:0:0:0 | [summary param] 0 in Write | file://:0:0:0:0 | [summary] to write: Argument[-1] in Write | +| file://:0:0:0:0 | [summary param] 0 in Write | file://:0:0:0:0 | [summary] to write: Argument[-1] in Write | +| file://:0:0:0:0 | [summary param] 0 in Write | file://:0:0:0:0 | [summary] to write: Argument[-1] in Write | +| file://:0:0:0:0 | [summary param] 0 in Write | file://:0:0:0:0 | [summary] to write: Argument[-1] in Write | +| file://:0:0:0:0 | [summary param] 0 in Write | file://:0:0:0:0 | [summary] to write: Argument[-1] in Write | +| file://:0:0:0:0 | [summary param] 0 in Write | file://:0:0:0:0 | [summary] to write: Argument[-1] in Write | +| file://:0:0:0:0 | [summary param] 0 in Write | file://:0:0:0:0 | [summary] to write: Argument[-1] in Write | +| file://:0:0:0:0 | [summary param] 0 in Write | file://:0:0:0:0 | [summary] to write: Argument[-1] in Write | +| file://:0:0:0:0 | [summary param] 0 in Write | file://:0:0:0:0 | [summary] to write: Argument[-1] in Write | +| file://:0:0:0:0 | [summary param] 0 in Write | file://:0:0:0:0 | [summary] to write: Argument[-1] in Write | +| file://:0:0:0:0 | [summary param] 0 in Write | file://:0:0:0:0 | [summary] to write: Argument[-1] in Write | +| file://:0:0:0:0 | [summary param] 0 in Write | file://:0:0:0:0 | [summary] to write: Argument[-1] in Write | +| file://:0:0:0:0 | [summary param] 0 in Write | file://:0:0:0:0 | [summary] to write: Argument[-1] in Write | +| file://:0:0:0:0 | [summary param] 0 in Write | file://:0:0:0:0 | [summary] to write: Argument[-1] in Write | +| file://:0:0:0:0 | [summary param] 0 in Write | file://:0:0:0:0 | [summary] to write: Argument[-1] in Write | +| file://:0:0:0:0 | [summary param] 0 in Write | file://:0:0:0:0 | [summary] to write: Argument[-1] in Write | +| file://:0:0:0:0 | [summary param] 0 in Write | file://:0:0:0:0 | [summary] to write: Argument[-1] in Write | +| file://:0:0:0:0 | [summary param] 0 in Write | file://:0:0:0:0 | [summary] to write: Argument[-1] in Write | +| file://:0:0:0:0 | [summary param] 0 in Write | file://:0:0:0:0 | [summary] to write: Argument[-1] in Write | +| file://:0:0:0:0 | [summary param] 0 in Write | file://:0:0:0:0 | [summary] to write: Argument[-1] in Write | +| file://:0:0:0:0 | [summary param] 0 in Write | file://:0:0:0:0 | [summary] to write: Argument[-1] in Write | +| file://:0:0:0:0 | [summary param] 0 in Write | file://:0:0:0:0 | [summary] to write: Argument[-1] in Write | +| file://:0:0:0:0 | [summary param] 0 in Write | file://:0:0:0:0 | [summary] to write: Argument[-1] in Write | +| file://:0:0:0:0 | [summary param] 0 in Write | file://:0:0:0:0 | [summary] to write: Argument[-1] in Write | +| file://:0:0:0:0 | [summary param] 0 in Write | file://:0:0:0:0 | [summary] to write: Argument[-1] in Write | +| file://:0:0:0:0 | [summary param] 0 in Write | file://:0:0:0:0 | [summary] to write: Argument[-1] in Write | +| file://:0:0:0:0 | [summary param] 0 in Write | file://:0:0:0:0 | [summary] to write: Argument[-1] in Write | +| file://:0:0:0:0 | [summary param] 0 in Write | file://:0:0:0:0 | [summary] to write: Argument[-1] in Write | +| file://:0:0:0:0 | [summary param] 0 in Write | file://:0:0:0:0 | [summary] to write: Argument[-1] in Write | +| file://:0:0:0:0 | [summary param] 0 in Write | file://:0:0:0:0 | [summary] to write: Argument[-1] in Write | +| file://:0:0:0:0 | [summary param] 0 in Write | file://:0:0:0:0 | [summary] to write: Argument[-1] in Write | +| file://:0:0:0:0 | [summary param] 0 in Write | file://:0:0:0:0 | [summary] to write: Argument[-1] in Write | +| file://:0:0:0:0 | [summary param] 0 in Write | file://:0:0:0:0 | [summary] to write: Argument[-1] in Write | +| file://:0:0:0:0 | [summary param] 0 in WriteAt | file://:0:0:0:0 | [summary] to write: Argument[-1] in WriteAt | +| file://:0:0:0:0 | [summary param] 0 in WriteAt | file://:0:0:0:0 | [summary] to write: Argument[-1] in WriteAt | +| file://:0:0:0:0 | [summary param] 0 in WriteAt | file://:0:0:0:0 | [summary] to write: Argument[-1] in WriteAt | +| file://:0:0:0:0 | [summary param] 0 in WriteField | file://:0:0:0:0 | [summary] to write: Argument[-1] in WriteField | +| file://:0:0:0:0 | [summary param] 0 in WriteMsgIP | file://:0:0:0:0 | [summary] to write: Argument[-1] in WriteMsgIP | +| file://:0:0:0:0 | [summary param] 0 in WriteMsgUDP | file://:0:0:0:0 | [summary] to write: Argument[-1] in WriteMsgUDP | +| file://:0:0:0:0 | [summary param] 0 in WriteMsgUnix | file://:0:0:0:0 | [summary] to write: Argument[-1] in WriteMsgUnix | +| file://:0:0:0:0 | [summary param] 0 in WriteString | file://:0:0:0:0 | [summary] to write: Argument[-1] in WriteString | +| file://:0:0:0:0 | [summary param] 0 in WriteString | file://:0:0:0:0 | [summary] to write: Argument[-1] in WriteString | +| file://:0:0:0:0 | [summary param] 0 in WriteString | file://:0:0:0:0 | [summary] to write: Argument[-1] in WriteString | +| file://:0:0:0:0 | [summary param] 0 in WriteString | file://:0:0:0:0 | [summary] to write: Argument[-1] in WriteString | +| file://:0:0:0:0 | [summary param] 0 in WriteString | file://:0:0:0:0 | [summary] to write: Argument[-1] in WriteString | +| file://:0:0:0:0 | [summary param] 0 in WriteString | file://:0:0:0:0 | [summary] to write: Argument[-1] in WriteString | +| file://:0:0:0:0 | [summary param] 0 in WriteString | file://:0:0:0:0 | [summary] to write: Argument[-1] in WriteString | +| file://:0:0:0:0 | [summary param] 0 in WriteString | file://:0:0:0:0 | [summary] to write: Argument[-1] in WriteString | +| file://:0:0:0:0 | [summary param] 0 in WriteString | file://:0:0:0:0 | [summary] to write: Argument[-1] in WriteString | +| file://:0:0:0:0 | [summary param] 0 in WriteString | file://:0:0:0:0 | [summary] to write: Argument[-1] in WriteString | +| file://:0:0:0:0 | [summary param] 0 in WriteString | file://:0:0:0:0 | [summary] to write: Argument[-1] in WriteString | +| file://:0:0:0:0 | [summary param] 0 in WriteString | file://:0:0:0:0 | [summary] to write: Argument[-1] in WriteString | +| file://:0:0:0:0 | [summary param] 0 in WriteString | file://:0:0:0:0 | [summary] to write: Argument[-1] in WriteString | +| file://:0:0:0:0 | [summary param] 0 in WriteString | file://:0:0:0:0 | [summary] to write: Argument[-1] in WriteString | +| file://:0:0:0:0 | [summary param] 0 in WriteTo | file://:0:0:0:0 | [summary] to write: Argument[-1] in WriteTo | +| file://:0:0:0:0 | [summary param] 0 in WriteTo | file://:0:0:0:0 | [summary] to write: Argument[-1] in WriteTo | +| file://:0:0:0:0 | [summary param] 0 in WriteTo | file://:0:0:0:0 | [summary] to write: Argument[-1] in WriteTo | +| file://:0:0:0:0 | [summary param] 0 in WriteTo | file://:0:0:0:0 | [summary] to write: Argument[-1] in WriteTo | +| file://:0:0:0:0 | [summary param] 0 in WriteToIP | file://:0:0:0:0 | [summary] to write: Argument[-1] in WriteToIP | +| file://:0:0:0:0 | [summary param] 0 in WriteToUDP | file://:0:0:0:0 | [summary] to write: Argument[-1] in WriteToUDP | +| file://:0:0:0:0 | [summary param] 0 in WriteToUnix | file://:0:0:0:0 | [summary] to write: Argument[-1] in WriteToUnix | +| file://:0:0:0:0 | [summary param] 1 in Add | file://:0:0:0:0 | [summary] to write: Argument[-1] in Add | +| file://:0:0:0:0 | [summary param] 1 in Add | file://:0:0:0:0 | [summary] to write: Argument[-1] in Add | +| file://:0:0:0:0 | [summary param] 1 in AddUintptr | file://:0:0:0:0 | [summary] to write: Argument[0] in AddUintptr | +| file://:0:0:0:0 | [summary param] 1 in AddUintptr | file://:0:0:0:0 | [summary] to write: ReturnValue in AddUintptr | +| file://:0:0:0:0 | [summary param] 1 in AppendQuote | file://:0:0:0:0 | [summary] to write: ReturnValue in AppendQuote | +| file://:0:0:0:0 | [summary param] 1 in AppendQuoteToASCII | file://:0:0:0:0 | [summary] to write: ReturnValue in AppendQuoteToASCII | +| file://:0:0:0:0 | [summary param] 1 in AppendQuoteToGraphic | file://:0:0:0:0 | [summary] to write: ReturnValue in AppendQuoteToGraphic | +| file://:0:0:0:0 | [summary param] 1 in AppendSlice | file://:0:0:0:0 | [summary] to write: ReturnValue in AppendSlice | +| file://:0:0:0:0 | [summary param] 1 in Compact | file://:0:0:0:0 | [summary] to write: Argument[0] in Compact | +| file://:0:0:0:0 | [summary param] 1 in Copy | file://:0:0:0:0 | [summary] to write: Argument[0] in Copy | +| file://:0:0:0:0 | [summary param] 1 in Copy | file://:0:0:0:0 | [summary] to write: Argument[0] in Copy | +| file://:0:0:0:0 | [summary param] 1 in CopyBuffer | file://:0:0:0:0 | [summary] to write: Argument[0] in CopyBuffer | +| file://:0:0:0:0 | [summary param] 1 in CopyN | file://:0:0:0:0 | [summary] to write: Argument[0] in CopyN | +| file://:0:0:0:0 | [summary param] 1 in Decode | file://:0:0:0:0 | [summary] to write: Argument[0] in Decode | +| file://:0:0:0:0 | [summary param] 1 in Decode | file://:0:0:0:0 | [summary] to write: Argument[0] in Decode | +| file://:0:0:0:0 | [summary param] 1 in Decrypt | file://:0:0:0:0 | [summary] to write: Argument[0] in Decrypt | +| file://:0:0:0:0 | [summary param] 1 in Decrypt | file://:0:0:0:0 | [summary] to write: Argument[0] in Decrypt | +| file://:0:0:0:0 | [summary param] 1 in Decrypt | file://:0:0:0:0 | [summary] to write: Argument[0] in Decrypt | +| file://:0:0:0:0 | [summary param] 1 in Decrypt | file://:0:0:0:0 | [summary] to write: Argument[0] in Decrypt | +| file://:0:0:0:0 | [summary param] 1 in Decrypt | file://:0:0:0:0 | [summary] to write: Argument[0] in Decrypt | +| file://:0:0:0:0 | [summary param] 1 in Decrypt | file://:0:0:0:0 | [summary] to write: ReturnValue in Decrypt | +| file://:0:0:0:0 | [summary param] 1 in Decrypt | file://:0:0:0:0 | [summary] to write: ReturnValue in Decrypt | +| file://:0:0:0:0 | [summary param] 1 in Encode | file://:0:0:0:0 | [summary] to write: Argument[0] in Encode | +| file://:0:0:0:0 | [summary param] 1 in Encode | file://:0:0:0:0 | [summary] to write: ReturnValue in Encode | +| file://:0:0:0:0 | [summary param] 1 in Error | file://:0:0:0:0 | [summary] to write: Argument[0] in Error | +| file://:0:0:0:0 | [summary param] 1 in FormatMediaType | file://:0:0:0:0 | [summary] to write: ReturnValue in FormatMediaType | +| file://:0:0:0:0 | [summary param] 1 in HTMLEscape | file://:0:0:0:0 | [summary] to write: Argument[0] in HTMLEscape | +| file://:0:0:0:0 | [summary param] 1 in Indent | file://:0:0:0:0 | [summary] to write: Argument[0] in Indent | +| file://:0:0:0:0 | [summary param] 1 in Join | file://:0:0:0:0 | [summary] to write: ReturnValue in Join | +| file://:0:0:0:0 | [summary param] 1 in Join | file://:0:0:0:0 | [summary] to write: ReturnValue in Join | +| file://:0:0:0:0 | [summary param] 1 in JoinHostPort | file://:0:0:0:0 | [summary] to write: ReturnValue in JoinHostPort | +| file://:0:0:0:0 | [summary param] 1 in LoadOrStore | file://:0:0:0:0 | [summary] to write: Argument[-1] in LoadOrStore | +| file://:0:0:0:0 | [summary param] 1 in LoadOrStore | file://:0:0:0:0 | [summary] to write: ReturnValue in LoadOrStore | +| file://:0:0:0:0 | [summary param] 1 in Map | file://:0:0:0:0 | [summary] to write: ReturnValue in Map | +| file://:0:0:0:0 | [summary param] 1 in Map | file://:0:0:0:0 | [summary] to write: ReturnValue in Map | +| file://:0:0:0:0 | [summary param] 1 in MarshalIndent | file://:0:0:0:0 | [summary] to write: ReturnValue in MarshalIndent | +| file://:0:0:0:0 | [summary param] 1 in MarshalWithParams | file://:0:0:0:0 | [summary] to write: ReturnValue in MarshalWithParams | +| file://:0:0:0:0 | [summary param] 1 in MaxBytesReader | file://:0:0:0:0 | [summary] to write: ReturnValue in MaxBytesReader | +| file://:0:0:0:0 | [summary param] 1 in NewDecoder | file://:0:0:0:0 | [summary] to write: ReturnValue in NewDecoder | +| file://:0:0:0:0 | [summary param] 1 in NewRequest | file://:0:0:0:0 | [summary] to write: ReturnValue in NewRequest | +| file://:0:0:0:0 | [summary param] 1 in Rel | file://:0:0:0:0 | [summary] to write: ReturnValue in Rel | +| file://:0:0:0:0 | [summary param] 1 in Set | file://:0:0:0:0 | [summary] to write: Argument[-1] in Set | +| file://:0:0:0:0 | [summary param] 1 in Set | file://:0:0:0:0 | [summary] to write: Argument[-1] in Set | +| file://:0:0:0:0 | [summary param] 1 in SetCookie | file://:0:0:0:0 | [summary] to write: Argument[0] in SetCookie | +| file://:0:0:0:0 | [summary param] 1 in SetIndent | file://:0:0:0:0 | [summary] to write: Argument[-1] in SetIndent | +| file://:0:0:0:0 | [summary param] 1 in SetMapIndex | file://:0:0:0:0 | [summary] to write: Argument[-1] in SetMapIndex | +| file://:0:0:0:0 | [summary param] 1 in Store | file://:0:0:0:0 | [summary] to write: Argument[-1] in Store | +| file://:0:0:0:0 | [summary param] 1 in StorePointer | file://:0:0:0:0 | [summary] to write: Argument[0] in StorePointer | +| file://:0:0:0:0 | [summary param] 1 in StoreUintptr | file://:0:0:0:0 | [summary] to write: Argument[0] in StoreUintptr | +| file://:0:0:0:0 | [summary param] 1 in Swap | file://:0:0:0:0 | [summary] to write: Argument[-1] in Swap | +| file://:0:0:0:0 | [summary param] 1 in SwapPointer | file://:0:0:0:0 | [summary] to write: Argument[0] in SwapPointer | +| file://:0:0:0:0 | [summary param] 1 in SwapUintptr | file://:0:0:0:0 | [summary] to write: Argument[0] in SwapUintptr | +| file://:0:0:0:0 | [summary param] 1 in ToLowerSpecial | file://:0:0:0:0 | [summary] to write: ReturnValue in ToLowerSpecial | +| file://:0:0:0:0 | [summary param] 1 in ToLowerSpecial | file://:0:0:0:0 | [summary] to write: ReturnValue in ToLowerSpecial | +| file://:0:0:0:0 | [summary param] 1 in ToTitleSpecial | file://:0:0:0:0 | [summary] to write: ReturnValue in ToTitleSpecial | +| file://:0:0:0:0 | [summary param] 1 in ToTitleSpecial | file://:0:0:0:0 | [summary] to write: ReturnValue in ToTitleSpecial | +| file://:0:0:0:0 | [summary param] 1 in ToUpperSpecial | file://:0:0:0:0 | [summary] to write: ReturnValue in ToUpperSpecial | +| file://:0:0:0:0 | [summary param] 1 in ToUpperSpecial | file://:0:0:0:0 | [summary] to write: ReturnValue in ToUpperSpecial | +| file://:0:0:0:0 | [summary param] 1 in ToValidUTF8 | file://:0:0:0:0 | [summary] to write: ReturnValue in ToValidUTF8 | +| file://:0:0:0:0 | [summary param] 1 in ToValidUTF8 | file://:0:0:0:0 | [summary] to write: ReturnValue in ToValidUTF8 | +| file://:0:0:0:0 | [summary param] 1 in UserPassword | file://:0:0:0:0 | [summary] to write: ReturnValue in UserPassword | +| file://:0:0:0:0 | [summary param] 1 in WithValue | file://:0:0:0:0 | [summary] to write: ReturnValue in WithValue | +| file://:0:0:0:0 | [summary param] 1 in WriteField | file://:0:0:0:0 | [summary] to write: Argument[-1] in WriteField | +| file://:0:0:0:0 | [summary param] 1 in WriteMsgIP | file://:0:0:0:0 | [summary] to write: Argument[-1] in WriteMsgIP | +| file://:0:0:0:0 | [summary param] 1 in WriteMsgUDP | file://:0:0:0:0 | [summary] to write: Argument[-1] in WriteMsgUDP | +| file://:0:0:0:0 | [summary param] 1 in WriteMsgUnix | file://:0:0:0:0 | [summary] to write: Argument[-1] in WriteMsgUnix | +| file://:0:0:0:0 | [summary param] 1 in WriteString | file://:0:0:0:0 | [summary] to write: Argument[0] in WriteString | +| file://:0:0:0:0 | [summary param] 1 in WriteString | file://:0:0:0:0 | [summary] to write: Argument[0] in WriteString | +| file://:0:0:0:0 | [summary param] 2 in CompareAndSwap | file://:0:0:0:0 | [summary] to write: Argument[-1] in CompareAndSwap | +| file://:0:0:0:0 | [summary param] 2 in CompareAndSwapPointer | file://:0:0:0:0 | [summary] to write: Argument[0] in CompareAndSwapPointer | +| file://:0:0:0:0 | [summary param] 2 in CompareAndSwapUintptr | file://:0:0:0:0 | [summary] to write: Argument[0] in CompareAndSwapUintptr | +| file://:0:0:0:0 | [summary param] 2 in DecryptPKCS1v15 | file://:0:0:0:0 | [summary] to write: ReturnValue in DecryptPKCS1v15 | +| file://:0:0:0:0 | [summary param] 2 in Indent | file://:0:0:0:0 | [summary] to write: Argument[0] in Indent | +| file://:0:0:0:0 | [summary param] 2 in MarshalIndent | file://:0:0:0:0 | [summary] to write: ReturnValue in MarshalIndent | +| file://:0:0:0:0 | [summary param] 2 in NewRequestWithContext | file://:0:0:0:0 | [summary] to write: ReturnValue in NewRequestWithContext | +| file://:0:0:0:0 | [summary param] 2 in Open | file://:0:0:0:0 | [summary] to write: Argument[0] in Open | +| file://:0:0:0:0 | [summary param] 2 in Open | file://:0:0:0:0 | [summary] to write: Argument[0] in Open | +| file://:0:0:0:0 | [summary param] 2 in Open | file://:0:0:0:0 | [summary] to write: Argument[0] in Open | +| file://:0:0:0:0 | [summary param] 2 in Open | file://:0:0:0:0 | [summary] to write: Argument[0] in Open | +| file://:0:0:0:0 | [summary param] 2 in Open | file://:0:0:0:0 | [summary] to write: Argument[0] in Open | +| file://:0:0:0:0 | [summary param] 2 in Open | file://:0:0:0:0 | [summary] to write: Argument[0] in Open | +| file://:0:0:0:0 | [summary param] 2 in Open | file://:0:0:0:0 | [summary] to write: Argument[0] in Open | +| file://:0:0:0:0 | [summary param] 2 in Open | file://:0:0:0:0 | [summary] to write: ReturnValue in Open | +| file://:0:0:0:0 | [summary param] 2 in Open | file://:0:0:0:0 | [summary] to write: ReturnValue in Open | +| file://:0:0:0:0 | [summary param] 2 in Open | file://:0:0:0:0 | [summary] to write: ReturnValue in Open | +| file://:0:0:0:0 | [summary param] 2 in Open | file://:0:0:0:0 | [summary] to write: ReturnValue in Open | +| file://:0:0:0:0 | [summary param] 2 in Open | file://:0:0:0:0 | [summary] to write: ReturnValue in Open | +| file://:0:0:0:0 | [summary param] 2 in Open | file://:0:0:0:0 | [summary] to write: ReturnValue in Open | +| file://:0:0:0:0 | [summary param] 2 in Open | file://:0:0:0:0 | [summary] to write: ReturnValue in Open | +| file://:0:0:0:0 | [summary param] 2 in Replace | file://:0:0:0:0 | [summary] to write: ReturnValue in Replace | +| file://:0:0:0:0 | [summary param] 2 in Replace | file://:0:0:0:0 | [summary] to write: ReturnValue in Replace | +| file://:0:0:0:0 | [summary param] 2 in ReplaceAll | file://:0:0:0:0 | [summary] to write: ReturnValue in ReplaceAll | +| file://:0:0:0:0 | [summary param] 2 in ReplaceAll | file://:0:0:0:0 | [summary] to write: ReturnValue in ReplaceAll | +| file://:0:0:0:0 | [summary param] 2 in UnmarshalWithParams | file://:0:0:0:0 | [summary] to write: Argument[1] in UnmarshalWithParams | +| file://:0:0:0:0 | [summary param] 2 in UnmarshalWithParams | file://:0:0:0:0 | [summary] to write: ReturnValue in UnmarshalWithParams | +| file://:0:0:0:0 | [summary param] 2 in WithValue | file://:0:0:0:0 | [summary] to write: ReturnValue in WithValue | +| file://:0:0:0:0 | [summary param] 2 in Write | file://:0:0:0:0 | [summary] to write: Argument[0] in Write | +| file://:0:0:0:0 | [summary param] 3 in DecryptOAEP | file://:0:0:0:0 | [summary] to write: ReturnValue in DecryptOAEP | +| file://:0:0:0:0 | [summary param] 3 in Indent | file://:0:0:0:0 | [summary] to write: Argument[0] in Indent | +| file://:0:0:0:0 | [summary param] -1 in Addr | file://:0:0:0:0 | [summary] to write: ReturnValue in Addr | +| file://:0:0:0:0 | [summary param] -1 in Back | file://:0:0:0:0 | [summary] to write: ReturnValue in Back | +| file://:0:0:0:0 | [summary param] -1 in Buffered | file://:0:0:0:0 | [summary] to write: ReturnValue in Buffered | +| file://:0:0:0:0 | [summary param] -1 in Bytes | file://:0:0:0:0 | [summary] to write: ReturnValue in Bytes | +| file://:0:0:0:0 | [summary param] -1 in Bytes | file://:0:0:0:0 | [summary] to write: ReturnValue in Bytes | +| file://:0:0:0:0 | [summary param] -1 in Bytes | file://:0:0:0:0 | [summary] to write: ReturnValue in Bytes | +| file://:0:0:0:0 | [summary param] -1 in Clone | file://:0:0:0:0 | [summary] to write: ReturnValue in Clone | +| file://:0:0:0:0 | [summary param] -1 in Clone | file://:0:0:0:0 | [summary] to write: ReturnValue in Clone | +| file://:0:0:0:0 | [summary param] -1 in Clone | file://:0:0:0:0 | [summary] to write: ReturnValue in Clone | +| file://:0:0:0:0 | [summary param] -1 in Convert | file://:0:0:0:0 | [summary] to write: ReturnValue in Convert | +| file://:0:0:0:0 | [summary param] -1 in Decode | file://:0:0:0:0 | [summary] to write: Argument[0] in Decode | +| file://:0:0:0:0 | [summary param] -1 in DotReader | file://:0:0:0:0 | [summary] to write: ReturnValue in DotReader | +| file://:0:0:0:0 | [summary param] -1 in Elem | file://:0:0:0:0 | [summary] to write: ReturnValue in Elem | +| file://:0:0:0:0 | [summary param] -1 in Encode | file://:0:0:0:0 | [summary] to write: ReturnValue in Encode | +| file://:0:0:0:0 | [summary param] -1 in EscapedPath | file://:0:0:0:0 | [summary] to write: ReturnValue in EscapedPath | +| file://:0:0:0:0 | [summary param] -1 in Fd | file://:0:0:0:0 | [summary] to write: ReturnValue in Fd | +| file://:0:0:0:0 | [summary param] -1 in Field | file://:0:0:0:0 | [summary] to write: ReturnValue in Field | +| file://:0:0:0:0 | [summary param] -1 in FieldByIndex | file://:0:0:0:0 | [summary] to write: ReturnValue in FieldByIndex | +| file://:0:0:0:0 | [summary param] -1 in FieldByName | file://:0:0:0:0 | [summary] to write: ReturnValue in FieldByName | +| file://:0:0:0:0 | [summary param] -1 in FieldByNameFunc | file://:0:0:0:0 | [summary] to write: ReturnValue in FieldByNameFunc | +| file://:0:0:0:0 | [summary param] -1 in File | file://:0:0:0:0 | [summary] to write: ReturnValue in File | +| file://:0:0:0:0 | [summary param] -1 in File | file://:0:0:0:0 | [summary] to write: ReturnValue in File | +| file://:0:0:0:0 | [summary param] -1 in FileName | file://:0:0:0:0 | [summary] to write: ReturnValue in FileName | +| file://:0:0:0:0 | [summary param] -1 in FormName | file://:0:0:0:0 | [summary] to write: ReturnValue in FormName | +| file://:0:0:0:0 | [summary param] -1 in Front | file://:0:0:0:0 | [summary] to write: ReturnValue in Front | +| file://:0:0:0:0 | [summary param] -1 in Get | file://:0:0:0:0 | [summary] to write: ReturnValue in Get | +| file://:0:0:0:0 | [summary param] -1 in Get | file://:0:0:0:0 | [summary] to write: ReturnValue in Get | +| file://:0:0:0:0 | [summary param] -1 in Get | file://:0:0:0:0 | [summary] to write: ReturnValue in Get | +| file://:0:0:0:0 | [summary param] -1 in Get | file://:0:0:0:0 | [summary] to write: ReturnValue in Get | +| file://:0:0:0:0 | [summary param] -1 in Get | file://:0:0:0:0 | [summary] to write: ReturnValue in Get | +| file://:0:0:0:0 | [summary param] -1 in Glob | file://:0:0:0:0 | [summary] to write: ReturnValue in Glob | +| file://:0:0:0:0 | [summary param] -1 in Glob | file://:0:0:0:0 | [summary] to write: ReturnValue in Glob | +| file://:0:0:0:0 | [summary param] -1 in GoString | file://:0:0:0:0 | [summary] to write: ReturnValue in GoString | +| file://:0:0:0:0 | [summary param] -1 in GoString | file://:0:0:0:0 | [summary] to write: ReturnValue in GoString | +| file://:0:0:0:0 | [summary param] -1 in GoString | file://:0:0:0:0 | [summary] to write: ReturnValue in GoString | +| file://:0:0:0:0 | [summary param] -1 in GoString | file://:0:0:0:0 | [summary] to write: ReturnValue in GoString | +| file://:0:0:0:0 | [summary param] -1 in GoString | file://:0:0:0:0 | [summary] to write: ReturnValue in GoString | +| file://:0:0:0:0 | [summary param] -1 in GoString | file://:0:0:0:0 | [summary] to write: ReturnValue in GoString | +| file://:0:0:0:0 | [summary param] -1 in GoString | file://:0:0:0:0 | [summary] to write: ReturnValue in GoString | +| file://:0:0:0:0 | [summary param] -1 in GoString | file://:0:0:0:0 | [summary] to write: ReturnValue in GoString | +| file://:0:0:0:0 | [summary param] -1 in GoString | file://:0:0:0:0 | [summary] to write: ReturnValue in GoString | +| file://:0:0:0:0 | [summary param] -1 in GoString | file://:0:0:0:0 | [summary] to write: ReturnValue in GoString | +| file://:0:0:0:0 | [summary param] -1 in GoString | file://:0:0:0:0 | [summary] to write: ReturnValue in GoString | +| file://:0:0:0:0 | [summary param] -1 in GoString | file://:0:0:0:0 | [summary] to write: ReturnValue in GoString | +| file://:0:0:0:0 | [summary param] -1 in GoString | file://:0:0:0:0 | [summary] to write: ReturnValue in GoString | +| file://:0:0:0:0 | [summary param] -1 in GoString | file://:0:0:0:0 | [summary] to write: ReturnValue in GoString | +| file://:0:0:0:0 | [summary param] -1 in GoString | file://:0:0:0:0 | [summary] to write: ReturnValue in GoString | +| file://:0:0:0:0 | [summary param] -1 in GoString | file://:0:0:0:0 | [summary] to write: ReturnValue in GoString | +| file://:0:0:0:0 | [summary param] -1 in GoString | file://:0:0:0:0 | [summary] to write: ReturnValue in GoString | +| file://:0:0:0:0 | [summary param] -1 in GoString | file://:0:0:0:0 | [summary] to write: ReturnValue in GoString | +| file://:0:0:0:0 | [summary param] -1 in GoString | file://:0:0:0:0 | [summary] to write: ReturnValue in GoString | +| file://:0:0:0:0 | [summary param] -1 in GoString | file://:0:0:0:0 | [summary] to write: ReturnValue in GoString | +| file://:0:0:0:0 | [summary param] -1 in GoString | file://:0:0:0:0 | [summary] to write: ReturnValue in GoString | +| file://:0:0:0:0 | [summary param] -1 in GoString | file://:0:0:0:0 | [summary] to write: ReturnValue in GoString | +| file://:0:0:0:0 | [summary param] -1 in GoString | file://:0:0:0:0 | [summary] to write: ReturnValue in GoString | +| file://:0:0:0:0 | [summary param] -1 in GoString | file://:0:0:0:0 | [summary] to write: ReturnValue in GoString | +| file://:0:0:0:0 | [summary param] -1 in GoString | file://:0:0:0:0 | [summary] to write: ReturnValue in GoString | +| file://:0:0:0:0 | [summary param] -1 in GoString | file://:0:0:0:0 | [summary] to write: ReturnValue in GoString | +| file://:0:0:0:0 | [summary param] -1 in GoString | file://:0:0:0:0 | [summary] to write: ReturnValue in GoString | +| file://:0:0:0:0 | [summary param] -1 in Hijack | file://:0:0:0:0 | [summary] to write: ReturnValue in Hijack | +| file://:0:0:0:0 | [summary param] -1 in Hijack | file://:0:0:0:0 | [summary] to write: ReturnValue in Hijack | +| file://:0:0:0:0 | [summary param] -1 in Hijack | file://:0:0:0:0 | [summary] to write: ReturnValue in Hijack | +| file://:0:0:0:0 | [summary param] -1 in Hijack | file://:0:0:0:0 | [summary] to write: ReturnValue[1] in Hijack | +| file://:0:0:0:0 | [summary param] -1 in Hijack | file://:0:0:0:0 | [summary] to write: ReturnValue[1] in Hijack | +| file://:0:0:0:0 | [summary param] -1 in Hijack | file://:0:0:0:0 | [summary] to write: ReturnValue[1] in Hijack | +| file://:0:0:0:0 | [summary param] -1 in Hostname | file://:0:0:0:0 | [summary] to write: ReturnValue in Hostname | +| file://:0:0:0:0 | [summary param] -1 in Index | file://:0:0:0:0 | [summary] to write: ReturnValue in Index | +| file://:0:0:0:0 | [summary param] -1 in Info | file://:0:0:0:0 | [summary] to write: ReturnValue in Info | +| file://:0:0:0:0 | [summary param] -1 in Info | file://:0:0:0:0 | [summary] to write: ReturnValue in Info | +| file://:0:0:0:0 | [summary param] -1 in Info | file://:0:0:0:0 | [summary] to write: ReturnValue in Info | +| file://:0:0:0:0 | [summary param] -1 in Info | file://:0:0:0:0 | [summary] to write: ReturnValue in Info | +| file://:0:0:0:0 | [summary param] -1 in Info | file://:0:0:0:0 | [summary] to write: ReturnValue in Info | +| file://:0:0:0:0 | [summary param] -1 in Init | file://:0:0:0:0 | [summary] to write: ReturnValue in Init | +| file://:0:0:0:0 | [summary param] -1 in Interface | file://:0:0:0:0 | [summary] to write: ReturnValue in Interface | +| file://:0:0:0:0 | [summary param] -1 in InterfaceData | file://:0:0:0:0 | [summary] to write: ReturnValue in InterfaceData | +| file://:0:0:0:0 | [summary param] -1 in Key | file://:0:0:0:0 | [summary] to write: ReturnValue in Key | +| file://:0:0:0:0 | [summary param] -1 in Load | file://:0:0:0:0 | [summary] to write: ReturnValue in Load | +| file://:0:0:0:0 | [summary param] -1 in Load | file://:0:0:0:0 | [summary] to write: ReturnValue in Load | +| file://:0:0:0:0 | [summary param] -1 in Load | file://:0:0:0:0 | [summary] to write: ReturnValue in Load | +| file://:0:0:0:0 | [summary param] -1 in Load | file://:0:0:0:0 | [summary] to write: ReturnValue in Load | +| file://:0:0:0:0 | [summary param] -1 in LoadOrStore | file://:0:0:0:0 | [summary] to write: ReturnValue in LoadOrStore | +| file://:0:0:0:0 | [summary param] -1 in Lookup | file://:0:0:0:0 | [summary] to write: ReturnValue in Lookup | +| file://:0:0:0:0 | [summary param] -1 in MapIndex | file://:0:0:0:0 | [summary] to write: ReturnValue in MapIndex | +| file://:0:0:0:0 | [summary param] -1 in MapKeys | file://:0:0:0:0 | [summary] to write: ReturnValue in MapKeys | +| file://:0:0:0:0 | [summary param] -1 in MapRange | file://:0:0:0:0 | [summary] to write: ReturnValue in MapRange | +| file://:0:0:0:0 | [summary param] -1 in MarshalBinary | file://:0:0:0:0 | [summary] to write: ReturnValue in MarshalBinary | +| file://:0:0:0:0 | [summary param] -1 in MarshalBinary | file://:0:0:0:0 | [summary] to write: ReturnValue in MarshalBinary | +| file://:0:0:0:0 | [summary param] -1 in MarshalBinary | file://:0:0:0:0 | [summary] to write: ReturnValue in MarshalBinary | +| file://:0:0:0:0 | [summary param] -1 in MarshalBinary | file://:0:0:0:0 | [summary] to write: ReturnValue in MarshalBinary | +| file://:0:0:0:0 | [summary param] -1 in MarshalBinary | file://:0:0:0:0 | [summary] to write: ReturnValue in MarshalBinary | +| file://:0:0:0:0 | [summary param] -1 in MarshalBinary | file://:0:0:0:0 | [summary] to write: ReturnValue in MarshalBinary | +| file://:0:0:0:0 | [summary param] -1 in MarshalBinary | file://:0:0:0:0 | [summary] to write: ReturnValue in MarshalBinary | +| file://:0:0:0:0 | [summary param] -1 in MarshalBinary | file://:0:0:0:0 | [summary] to write: ReturnValue in MarshalBinary | +| file://:0:0:0:0 | [summary param] -1 in MarshalBinary | file://:0:0:0:0 | [summary] to write: ReturnValue in MarshalBinary | +| file://:0:0:0:0 | [summary param] -1 in MarshalBinary | file://:0:0:0:0 | [summary] to write: ReturnValue in MarshalBinary | +| file://:0:0:0:0 | [summary param] -1 in MarshalBinary | file://:0:0:0:0 | [summary] to write: ReturnValue in MarshalBinary | +| file://:0:0:0:0 | [summary param] -1 in MarshalBinary | file://:0:0:0:0 | [summary] to write: ReturnValue in MarshalBinary | +| file://:0:0:0:0 | [summary param] -1 in MarshalJSON | file://:0:0:0:0 | [summary] to write: ReturnValue in MarshalJSON | +| file://:0:0:0:0 | [summary param] -1 in MarshalJSON | file://:0:0:0:0 | [summary] to write: ReturnValue in MarshalJSON | +| file://:0:0:0:0 | [summary param] -1 in MarshalJSON | file://:0:0:0:0 | [summary] to write: ReturnValue in MarshalJSON | +| file://:0:0:0:0 | [summary param] -1 in MarshalJSON | file://:0:0:0:0 | [summary] to write: ReturnValue in MarshalJSON | +| file://:0:0:0:0 | [summary param] -1 in MarshalText | file://:0:0:0:0 | [summary] to write: ReturnValue in MarshalText | +| file://:0:0:0:0 | [summary param] -1 in MarshalText | file://:0:0:0:0 | [summary] to write: ReturnValue in MarshalText | +| file://:0:0:0:0 | [summary param] -1 in MarshalText | file://:0:0:0:0 | [summary] to write: ReturnValue in MarshalText | +| file://:0:0:0:0 | [summary param] -1 in MarshalText | file://:0:0:0:0 | [summary] to write: ReturnValue in MarshalText | +| file://:0:0:0:0 | [summary param] -1 in MarshalText | file://:0:0:0:0 | [summary] to write: ReturnValue in MarshalText | +| file://:0:0:0:0 | [summary param] -1 in MarshalText | file://:0:0:0:0 | [summary] to write: ReturnValue in MarshalText | +| file://:0:0:0:0 | [summary param] -1 in MarshalText | file://:0:0:0:0 | [summary] to write: ReturnValue in MarshalText | +| file://:0:0:0:0 | [summary param] -1 in MarshalText | file://:0:0:0:0 | [summary] to write: ReturnValue in MarshalText | +| file://:0:0:0:0 | [summary param] -1 in MarshalText | file://:0:0:0:0 | [summary] to write: ReturnValue in MarshalText | +| file://:0:0:0:0 | [summary param] -1 in Method | file://:0:0:0:0 | [summary] to write: ReturnValue in Method | +| file://:0:0:0:0 | [summary param] -1 in MethodByName | file://:0:0:0:0 | [summary] to write: ReturnValue in MethodByName | +| file://:0:0:0:0 | [summary param] -1 in Name | file://:0:0:0:0 | [summary] to write: ReturnValue in Name | +| file://:0:0:0:0 | [summary param] -1 in Name | file://:0:0:0:0 | [summary] to write: ReturnValue in Name | +| file://:0:0:0:0 | [summary param] -1 in Name | file://:0:0:0:0 | [summary] to write: ReturnValue in Name | +| file://:0:0:0:0 | [summary param] -1 in Name | file://:0:0:0:0 | [summary] to write: ReturnValue in Name | +| file://:0:0:0:0 | [summary param] -1 in Name | file://:0:0:0:0 | [summary] to write: ReturnValue in Name | +| file://:0:0:0:0 | [summary param] -1 in Next | file://:0:0:0:0 | [summary] to write: ReturnValue in Next | +| file://:0:0:0:0 | [summary param] -1 in Next | file://:0:0:0:0 | [summary] to write: ReturnValue in Next | +| file://:0:0:0:0 | [summary param] -1 in NextPart | file://:0:0:0:0 | [summary] to write: ReturnValue in NextPart | +| file://:0:0:0:0 | [summary param] -1 in NextRawPart | file://:0:0:0:0 | [summary] to write: ReturnValue in NextRawPart | +| file://:0:0:0:0 | [summary param] -1 in Open | file://:0:0:0:0 | [summary] to write: ReturnValue in Open | +| file://:0:0:0:0 | [summary param] -1 in Open | file://:0:0:0:0 | [summary] to write: ReturnValue in Open | +| file://:0:0:0:0 | [summary param] -1 in Open | file://:0:0:0:0 | [summary] to write: ReturnValue in Open | +| file://:0:0:0:0 | [summary param] -1 in Open | file://:0:0:0:0 | [summary] to write: ReturnValue in Open | +| file://:0:0:0:0 | [summary param] -1 in Open | file://:0:0:0:0 | [summary] to write: ReturnValue in Open | +| file://:0:0:0:0 | [summary param] -1 in Parse | file://:0:0:0:0 | [summary] to write: ReturnValue in Parse | +| file://:0:0:0:0 | [summary param] -1 in Password | file://:0:0:0:0 | [summary] to write: ReturnValue in Password | +| file://:0:0:0:0 | [summary param] -1 in Peek | file://:0:0:0:0 | [summary] to write: ReturnValue in Peek | +| file://:0:0:0:0 | [summary param] -1 in Pointer | file://:0:0:0:0 | [summary] to write: ReturnValue in Pointer | +| file://:0:0:0:0 | [summary param] -1 in Port | file://:0:0:0:0 | [summary] to write: ReturnValue in Port | +| file://:0:0:0:0 | [summary param] -1 in Prev | file://:0:0:0:0 | [summary] to write: ReturnValue in Prev | +| file://:0:0:0:0 | [summary param] -1 in Query | file://:0:0:0:0 | [summary] to write: ReturnValue in Query | +| file://:0:0:0:0 | [summary param] -1 in Read | file://:0:0:0:0 | [summary] to write: Argument[0] in Read | +| file://:0:0:0:0 | [summary param] -1 in Read | file://:0:0:0:0 | [summary] to write: Argument[0] in Read | +| file://:0:0:0:0 | [summary param] -1 in Read | file://:0:0:0:0 | [summary] to write: Argument[0] in Read | +| file://:0:0:0:0 | [summary param] -1 in Read | file://:0:0:0:0 | [summary] to write: Argument[0] in Read | +| file://:0:0:0:0 | [summary param] -1 in Read | file://:0:0:0:0 | [summary] to write: Argument[0] in Read | +| file://:0:0:0:0 | [summary param] -1 in Read | file://:0:0:0:0 | [summary] to write: Argument[0] in Read | +| file://:0:0:0:0 | [summary param] -1 in Read | file://:0:0:0:0 | [summary] to write: Argument[0] in Read | +| file://:0:0:0:0 | [summary param] -1 in Read | file://:0:0:0:0 | [summary] to write: Argument[0] in Read | +| file://:0:0:0:0 | [summary param] -1 in Read | file://:0:0:0:0 | [summary] to write: Argument[0] in Read | +| file://:0:0:0:0 | [summary param] -1 in Read | file://:0:0:0:0 | [summary] to write: Argument[0] in Read | +| file://:0:0:0:0 | [summary param] -1 in Read | file://:0:0:0:0 | [summary] to write: Argument[0] in Read | +| file://:0:0:0:0 | [summary param] -1 in Read | file://:0:0:0:0 | [summary] to write: Argument[0] in Read | +| file://:0:0:0:0 | [summary param] -1 in Read | file://:0:0:0:0 | [summary] to write: Argument[0] in Read | +| file://:0:0:0:0 | [summary param] -1 in Read | file://:0:0:0:0 | [summary] to write: Argument[0] in Read | +| file://:0:0:0:0 | [summary param] -1 in Read | file://:0:0:0:0 | [summary] to write: Argument[0] in Read | +| file://:0:0:0:0 | [summary param] -1 in Read | file://:0:0:0:0 | [summary] to write: Argument[0] in Read | +| file://:0:0:0:0 | [summary param] -1 in Read | file://:0:0:0:0 | [summary] to write: Argument[0] in Read | +| file://:0:0:0:0 | [summary param] -1 in Read | file://:0:0:0:0 | [summary] to write: Argument[0] in Read | +| file://:0:0:0:0 | [summary param] -1 in Read | file://:0:0:0:0 | [summary] to write: Argument[0] in Read | +| file://:0:0:0:0 | [summary param] -1 in Read | file://:0:0:0:0 | [summary] to write: Argument[0] in Read | +| file://:0:0:0:0 | [summary param] -1 in Read | file://:0:0:0:0 | [summary] to write: Argument[0] in Read | +| file://:0:0:0:0 | [summary param] -1 in Read | file://:0:0:0:0 | [summary] to write: Argument[0] in Read | +| file://:0:0:0:0 | [summary param] -1 in Read | file://:0:0:0:0 | [summary] to write: Argument[0] in Read | +| file://:0:0:0:0 | [summary param] -1 in Read | file://:0:0:0:0 | [summary] to write: Argument[0] in Read | +| file://:0:0:0:0 | [summary param] -1 in Read | file://:0:0:0:0 | [summary] to write: Argument[0] in Read | +| file://:0:0:0:0 | [summary param] -1 in Read | file://:0:0:0:0 | [summary] to write: Argument[0] in Read | +| file://:0:0:0:0 | [summary param] -1 in Read | file://:0:0:0:0 | [summary] to write: Argument[0] in Read | +| file://:0:0:0:0 | [summary param] -1 in Read | file://:0:0:0:0 | [summary] to write: Argument[0] in Read | +| file://:0:0:0:0 | [summary param] -1 in Read | file://:0:0:0:0 | [summary] to write: Argument[0] in Read | +| file://:0:0:0:0 | [summary param] -1 in Read | file://:0:0:0:0 | [summary] to write: Argument[0] in Read | +| file://:0:0:0:0 | [summary param] -1 in Read | file://:0:0:0:0 | [summary] to write: Argument[0] in Read | +| file://:0:0:0:0 | [summary param] -1 in Read | file://:0:0:0:0 | [summary] to write: Argument[0] in Read | +| file://:0:0:0:0 | [summary param] -1 in Read | file://:0:0:0:0 | [summary] to write: Argument[0] in Read | +| file://:0:0:0:0 | [summary param] -1 in Read | file://:0:0:0:0 | [summary] to write: Argument[0] in Read | +| file://:0:0:0:0 | [summary param] -1 in Read | file://:0:0:0:0 | [summary] to write: Argument[0] in Read | +| file://:0:0:0:0 | [summary param] -1 in Read | file://:0:0:0:0 | [summary] to write: Argument[0] in Read | +| file://:0:0:0:0 | [summary param] -1 in Read | file://:0:0:0:0 | [summary] to write: Argument[0] in Read | +| file://:0:0:0:0 | [summary param] -1 in Read | file://:0:0:0:0 | [summary] to write: Argument[0] in Read | +| file://:0:0:0:0 | [summary param] -1 in Read | file://:0:0:0:0 | [summary] to write: Argument[0] in Read | +| file://:0:0:0:0 | [summary param] -1 in Read | file://:0:0:0:0 | [summary] to write: Argument[0] in Read | +| file://:0:0:0:0 | [summary param] -1 in Read | file://:0:0:0:0 | [summary] to write: Argument[0] in Read | +| file://:0:0:0:0 | [summary param] -1 in Read | file://:0:0:0:0 | [summary] to write: Argument[0] in Read | +| file://:0:0:0:0 | [summary param] -1 in Read | file://:0:0:0:0 | [summary] to write: Argument[0] in Read | +| file://:0:0:0:0 | [summary param] -1 in Read | file://:0:0:0:0 | [summary] to write: Argument[0] in Read | +| file://:0:0:0:0 | [summary param] -1 in Read | file://:0:0:0:0 | [summary] to write: Argument[0] in Read | +| file://:0:0:0:0 | [summary param] -1 in Read | file://:0:0:0:0 | [summary] to write: Argument[0] in Read | +| file://:0:0:0:0 | [summary param] -1 in Read | file://:0:0:0:0 | [summary] to write: Argument[0] in Read | +| file://:0:0:0:0 | [summary param] -1 in Read | file://:0:0:0:0 | [summary] to write: Argument[0] in Read | +| file://:0:0:0:0 | [summary param] -1 in Read | file://:0:0:0:0 | [summary] to write: Argument[0] in Read | +| file://:0:0:0:0 | [summary param] -1 in Read | file://:0:0:0:0 | [summary] to write: Argument[0] in Read | +| file://:0:0:0:0 | [summary param] -1 in Read | file://:0:0:0:0 | [summary] to write: Argument[0] in Read | +| file://:0:0:0:0 | [summary param] -1 in Read | file://:0:0:0:0 | [summary] to write: Argument[0] in Read | +| file://:0:0:0:0 | [summary param] -1 in Read | file://:0:0:0:0 | [summary] to write: Argument[0] in Read | +| file://:0:0:0:0 | [summary param] -1 in Read | file://:0:0:0:0 | [summary] to write: Argument[0] in Read | +| file://:0:0:0:0 | [summary param] -1 in Read | file://:0:0:0:0 | [summary] to write: Argument[0] in Read | +| file://:0:0:0:0 | [summary param] -1 in Read | file://:0:0:0:0 | [summary] to write: Argument[0] in Read | +| file://:0:0:0:0 | [summary param] -1 in Read | file://:0:0:0:0 | [summary] to write: Argument[0] in Read | +| file://:0:0:0:0 | [summary param] -1 in Read | file://:0:0:0:0 | [summary] to write: Argument[0] in Read | +| file://:0:0:0:0 | [summary param] -1 in Read | file://:0:0:0:0 | [summary] to write: Argument[0] in Read | +| file://:0:0:0:0 | [summary param] -1 in Read | file://:0:0:0:0 | [summary] to write: Argument[0] in Read | +| file://:0:0:0:0 | [summary param] -1 in Read | file://:0:0:0:0 | [summary] to write: Argument[0] in Read | +| file://:0:0:0:0 | [summary param] -1 in Read | file://:0:0:0:0 | [summary] to write: Argument[0] in Read | +| file://:0:0:0:0 | [summary param] -1 in Read | file://:0:0:0:0 | [summary] to write: Argument[0] in Read | +| file://:0:0:0:0 | [summary param] -1 in Read | file://:0:0:0:0 | [summary] to write: Argument[0] in Read | +| file://:0:0:0:0 | [summary param] -1 in Read | file://:0:0:0:0 | [summary] to write: Argument[0] in Read | +| file://:0:0:0:0 | [summary param] -1 in Read | file://:0:0:0:0 | [summary] to write: Argument[0] in Read | +| file://:0:0:0:0 | [summary param] -1 in Read | file://:0:0:0:0 | [summary] to write: Argument[0] in Read | +| file://:0:0:0:0 | [summary param] -1 in Read | file://:0:0:0:0 | [summary] to write: Argument[0] in Read | +| file://:0:0:0:0 | [summary param] -1 in Read | file://:0:0:0:0 | [summary] to write: Argument[0] in Read | +| file://:0:0:0:0 | [summary param] -1 in Read | file://:0:0:0:0 | [summary] to write: Argument[0] in Read | +| file://:0:0:0:0 | [summary param] -1 in Read | file://:0:0:0:0 | [summary] to write: Argument[0] in Read | +| file://:0:0:0:0 | [summary param] -1 in Read | file://:0:0:0:0 | [summary] to write: Argument[0] in Read | +| file://:0:0:0:0 | [summary param] -1 in Read | file://:0:0:0:0 | [summary] to write: Argument[0] in Read | +| file://:0:0:0:0 | [summary param] -1 in ReadAt | file://:0:0:0:0 | [summary] to write: Argument[0] in ReadAt | +| file://:0:0:0:0 | [summary param] -1 in ReadAt | file://:0:0:0:0 | [summary] to write: Argument[0] in ReadAt | +| file://:0:0:0:0 | [summary param] -1 in ReadAt | file://:0:0:0:0 | [summary] to write: Argument[0] in ReadAt | +| file://:0:0:0:0 | [summary param] -1 in ReadAt | file://:0:0:0:0 | [summary] to write: Argument[0] in ReadAt | +| file://:0:0:0:0 | [summary param] -1 in ReadAt | file://:0:0:0:0 | [summary] to write: Argument[0] in ReadAt | +| file://:0:0:0:0 | [summary param] -1 in ReadBytes | file://:0:0:0:0 | [summary] to write: ReturnValue in ReadBytes | +| file://:0:0:0:0 | [summary param] -1 in ReadBytes | file://:0:0:0:0 | [summary] to write: ReturnValue in ReadBytes | +| file://:0:0:0:0 | [summary param] -1 in ReadCodeLine | file://:0:0:0:0 | [summary] to write: ReturnValue[1] in ReadCodeLine | +| file://:0:0:0:0 | [summary param] -1 in ReadContinuedLine | file://:0:0:0:0 | [summary] to write: ReturnValue in ReadContinuedLine | +| file://:0:0:0:0 | [summary param] -1 in ReadContinuedLineBytes | file://:0:0:0:0 | [summary] to write: ReturnValue in ReadContinuedLineBytes | +| file://:0:0:0:0 | [summary param] -1 in ReadDir | file://:0:0:0:0 | [summary] to write: ReturnValue in ReadDir | +| file://:0:0:0:0 | [summary param] -1 in ReadDir | file://:0:0:0:0 | [summary] to write: ReturnValue in ReadDir | +| file://:0:0:0:0 | [summary param] -1 in ReadDir | file://:0:0:0:0 | [summary] to write: ReturnValue in ReadDir | +| file://:0:0:0:0 | [summary param] -1 in ReadDotBytes | file://:0:0:0:0 | [summary] to write: ReturnValue in ReadDotBytes | +| file://:0:0:0:0 | [summary param] -1 in ReadDotLines | file://:0:0:0:0 | [summary] to write: ReturnValue in ReadDotLines | +| file://:0:0:0:0 | [summary param] -1 in ReadFile | file://:0:0:0:0 | [summary] to write: ReturnValue in ReadFile | +| file://:0:0:0:0 | [summary param] -1 in ReadFile | file://:0:0:0:0 | [summary] to write: ReturnValue in ReadFile | +| file://:0:0:0:0 | [summary param] -1 in ReadFile | file://:0:0:0:0 | [summary] to write: ReturnValue in ReadFile | +| file://:0:0:0:0 | [summary param] -1 in ReadForm | file://:0:0:0:0 | [summary] to write: ReturnValue in ReadForm | +| file://:0:0:0:0 | [summary param] -1 in ReadFrom | file://:0:0:0:0 | [summary] to write: Argument[0] in ReadFrom | +| file://:0:0:0:0 | [summary param] -1 in ReadFrom | file://:0:0:0:0 | [summary] to write: Argument[0] in ReadFrom | +| file://:0:0:0:0 | [summary param] -1 in ReadFrom | file://:0:0:0:0 | [summary] to write: Argument[0] in ReadFrom | +| file://:0:0:0:0 | [summary param] -1 in ReadFrom | file://:0:0:0:0 | [summary] to write: Argument[0] in ReadFrom | +| file://:0:0:0:0 | [summary param] -1 in ReadFromIP | file://:0:0:0:0 | [summary] to write: Argument[0] in ReadFromIP | +| file://:0:0:0:0 | [summary param] -1 in ReadFromUDP | file://:0:0:0:0 | [summary] to write: Argument[0] in ReadFromUDP | +| file://:0:0:0:0 | [summary param] -1 in ReadFromUnix | file://:0:0:0:0 | [summary] to write: Argument[0] in ReadFromUnix | +| file://:0:0:0:0 | [summary param] -1 in ReadLine | file://:0:0:0:0 | [summary] to write: ReturnValue in ReadLine | +| file://:0:0:0:0 | [summary param] -1 in ReadLine | file://:0:0:0:0 | [summary] to write: ReturnValue in ReadLine | +| file://:0:0:0:0 | [summary param] -1 in ReadLineBytes | file://:0:0:0:0 | [summary] to write: ReturnValue in ReadLineBytes | +| file://:0:0:0:0 | [summary param] -1 in ReadMIMEHeader | file://:0:0:0:0 | [summary] to write: ReturnValue in ReadMIMEHeader | +| file://:0:0:0:0 | [summary param] -1 in ReadMsgIP | file://:0:0:0:0 | [summary] to write: Argument[0] in ReadMsgIP | +| file://:0:0:0:0 | [summary param] -1 in ReadMsgIP | file://:0:0:0:0 | [summary] to write: Argument[1] in ReadMsgIP | +| file://:0:0:0:0 | [summary param] -1 in ReadMsgUDP | file://:0:0:0:0 | [summary] to write: Argument[0] in ReadMsgUDP | +| file://:0:0:0:0 | [summary param] -1 in ReadMsgUDP | file://:0:0:0:0 | [summary] to write: Argument[1] in ReadMsgUDP | +| file://:0:0:0:0 | [summary param] -1 in ReadMsgUnix | file://:0:0:0:0 | [summary] to write: Argument[0] in ReadMsgUnix | +| file://:0:0:0:0 | [summary param] -1 in ReadMsgUnix | file://:0:0:0:0 | [summary] to write: Argument[1] in ReadMsgUnix | +| file://:0:0:0:0 | [summary param] -1 in ReadResponse | file://:0:0:0:0 | [summary] to write: ReturnValue[1] in ReadResponse | +| file://:0:0:0:0 | [summary param] -1 in ReadSlice | file://:0:0:0:0 | [summary] to write: ReturnValue in ReadSlice | +| file://:0:0:0:0 | [summary param] -1 in ReadString | file://:0:0:0:0 | [summary] to write: ReturnValue in ReadString | +| file://:0:0:0:0 | [summary param] -1 in ReadString | file://:0:0:0:0 | [summary] to write: ReturnValue in ReadString | +| file://:0:0:0:0 | [summary param] -1 in Recv | file://:0:0:0:0 | [summary] to write: ReturnValue in Recv | +| file://:0:0:0:0 | [summary param] -1 in RequestURI | file://:0:0:0:0 | [summary] to write: ReturnValue in RequestURI | +| file://:0:0:0:0 | [summary param] -1 in Reset | file://:0:0:0:0 | [summary] to write: Argument[0] in Reset | +| file://:0:0:0:0 | [summary param] -1 in Reset | file://:0:0:0:0 | [summary] to write: Argument[0] in Reset | +| file://:0:0:0:0 | [summary param] -1 in Reset | file://:0:0:0:0 | [summary] to write: Argument[0] in Reset | +| file://:0:0:0:0 | [summary param] -1 in ResolveReference | file://:0:0:0:0 | [summary] to write: ReturnValue in ResolveReference | +| file://:0:0:0:0 | [summary param] -1 in SetOutput | file://:0:0:0:0 | [summary] to write: Argument[0] in SetOutput | +| file://:0:0:0:0 | [summary param] -1 in Slice | file://:0:0:0:0 | [summary] to write: ReturnValue in Slice | +| file://:0:0:0:0 | [summary param] -1 in Slice3 | file://:0:0:0:0 | [summary] to write: ReturnValue in Slice3 | +| file://:0:0:0:0 | [summary param] -1 in String | file://:0:0:0:0 | [summary] to write: ReturnValue in String | +| file://:0:0:0:0 | [summary param] -1 in String | file://:0:0:0:0 | [summary] to write: ReturnValue in String | +| file://:0:0:0:0 | [summary param] -1 in String | file://:0:0:0:0 | [summary] to write: ReturnValue in String | +| file://:0:0:0:0 | [summary param] -1 in String | file://:0:0:0:0 | [summary] to write: ReturnValue in String | +| file://:0:0:0:0 | [summary param] -1 in String | file://:0:0:0:0 | [summary] to write: ReturnValue in String | +| file://:0:0:0:0 | [summary param] -1 in String | file://:0:0:0:0 | [summary] to write: ReturnValue in String | +| file://:0:0:0:0 | [summary param] -1 in String | file://:0:0:0:0 | [summary] to write: ReturnValue in String | +| file://:0:0:0:0 | [summary param] -1 in String | file://:0:0:0:0 | [summary] to write: ReturnValue in String | +| file://:0:0:0:0 | [summary param] -1 in String | file://:0:0:0:0 | [summary] to write: ReturnValue in String | +| file://:0:0:0:0 | [summary param] -1 in String | file://:0:0:0:0 | [summary] to write: ReturnValue in String | +| file://:0:0:0:0 | [summary param] -1 in String | file://:0:0:0:0 | [summary] to write: ReturnValue in String | +| file://:0:0:0:0 | [summary param] -1 in String | file://:0:0:0:0 | [summary] to write: ReturnValue in String | +| file://:0:0:0:0 | [summary param] -1 in String | file://:0:0:0:0 | [summary] to write: ReturnValue in String | +| file://:0:0:0:0 | [summary param] -1 in String | file://:0:0:0:0 | [summary] to write: ReturnValue in String | +| file://:0:0:0:0 | [summary param] -1 in String | file://:0:0:0:0 | [summary] to write: ReturnValue in String | +| file://:0:0:0:0 | [summary param] -1 in String | file://:0:0:0:0 | [summary] to write: ReturnValue in String | +| file://:0:0:0:0 | [summary param] -1 in String | file://:0:0:0:0 | [summary] to write: ReturnValue in String | +| file://:0:0:0:0 | [summary param] -1 in String | file://:0:0:0:0 | [summary] to write: ReturnValue in String | +| file://:0:0:0:0 | [summary param] -1 in String | file://:0:0:0:0 | [summary] to write: ReturnValue in String | +| file://:0:0:0:0 | [summary param] -1 in String | file://:0:0:0:0 | [summary] to write: ReturnValue in String | +| file://:0:0:0:0 | [summary param] -1 in String | file://:0:0:0:0 | [summary] to write: ReturnValue in String | +| file://:0:0:0:0 | [summary param] -1 in String | file://:0:0:0:0 | [summary] to write: ReturnValue in String | +| file://:0:0:0:0 | [summary param] -1 in String | file://:0:0:0:0 | [summary] to write: ReturnValue in String | +| file://:0:0:0:0 | [summary param] -1 in String | file://:0:0:0:0 | [summary] to write: ReturnValue in String | +| file://:0:0:0:0 | [summary param] -1 in String | file://:0:0:0:0 | [summary] to write: ReturnValue in String | +| file://:0:0:0:0 | [summary param] -1 in String | file://:0:0:0:0 | [summary] to write: ReturnValue in String | +| file://:0:0:0:0 | [summary param] -1 in String | file://:0:0:0:0 | [summary] to write: ReturnValue in String | +| file://:0:0:0:0 | [summary param] -1 in String | file://:0:0:0:0 | [summary] to write: ReturnValue in String | +| file://:0:0:0:0 | [summary param] -1 in String | file://:0:0:0:0 | [summary] to write: ReturnValue in String | +| file://:0:0:0:0 | [summary param] -1 in String | file://:0:0:0:0 | [summary] to write: ReturnValue in String | +| file://:0:0:0:0 | [summary param] -1 in String | file://:0:0:0:0 | [summary] to write: ReturnValue in String | +| file://:0:0:0:0 | [summary param] -1 in String | file://:0:0:0:0 | [summary] to write: ReturnValue in String | +| file://:0:0:0:0 | [summary param] -1 in String | file://:0:0:0:0 | [summary] to write: ReturnValue in String | +| file://:0:0:0:0 | [summary param] -1 in String | file://:0:0:0:0 | [summary] to write: ReturnValue in String | +| file://:0:0:0:0 | [summary param] -1 in String | file://:0:0:0:0 | [summary] to write: ReturnValue in String | +| file://:0:0:0:0 | [summary param] -1 in String | file://:0:0:0:0 | [summary] to write: ReturnValue in String | +| file://:0:0:0:0 | [summary param] -1 in String | file://:0:0:0:0 | [summary] to write: ReturnValue in String | +| file://:0:0:0:0 | [summary param] -1 in String | file://:0:0:0:0 | [summary] to write: ReturnValue in String | +| file://:0:0:0:0 | [summary param] -1 in String | file://:0:0:0:0 | [summary] to write: ReturnValue in String | +| file://:0:0:0:0 | [summary param] -1 in String | file://:0:0:0:0 | [summary] to write: ReturnValue in String | +| file://:0:0:0:0 | [summary param] -1 in String | file://:0:0:0:0 | [summary] to write: ReturnValue in String | +| file://:0:0:0:0 | [summary param] -1 in String | file://:0:0:0:0 | [summary] to write: ReturnValue in String | +| file://:0:0:0:0 | [summary param] -1 in String | file://:0:0:0:0 | [summary] to write: ReturnValue in String | +| file://:0:0:0:0 | [summary param] -1 in String | file://:0:0:0:0 | [summary] to write: ReturnValue in String | +| file://:0:0:0:0 | [summary param] -1 in String | file://:0:0:0:0 | [summary] to write: ReturnValue in String | +| file://:0:0:0:0 | [summary param] -1 in String | file://:0:0:0:0 | [summary] to write: ReturnValue in String | +| file://:0:0:0:0 | [summary param] -1 in String | file://:0:0:0:0 | [summary] to write: ReturnValue in String | +| file://:0:0:0:0 | [summary param] -1 in String | file://:0:0:0:0 | [summary] to write: ReturnValue in String | +| file://:0:0:0:0 | [summary param] -1 in String | file://:0:0:0:0 | [summary] to write: ReturnValue in String | +| file://:0:0:0:0 | [summary param] -1 in String | file://:0:0:0:0 | [summary] to write: ReturnValue in String | +| file://:0:0:0:0 | [summary param] -1 in String | file://:0:0:0:0 | [summary] to write: ReturnValue in String | +| file://:0:0:0:0 | [summary param] -1 in String | file://:0:0:0:0 | [summary] to write: ReturnValue in String | +| file://:0:0:0:0 | [summary param] -1 in String | file://:0:0:0:0 | [summary] to write: ReturnValue in String | +| file://:0:0:0:0 | [summary param] -1 in String | file://:0:0:0:0 | [summary] to write: ReturnValue in String | +| file://:0:0:0:0 | [summary param] -1 in String | file://:0:0:0:0 | [summary] to write: ReturnValue in String | +| file://:0:0:0:0 | [summary param] -1 in String | file://:0:0:0:0 | [summary] to write: ReturnValue in String | +| file://:0:0:0:0 | [summary param] -1 in String | file://:0:0:0:0 | [summary] to write: ReturnValue in String | +| file://:0:0:0:0 | [summary param] -1 in String | file://:0:0:0:0 | [summary] to write: ReturnValue in String | +| file://:0:0:0:0 | [summary param] -1 in String | file://:0:0:0:0 | [summary] to write: ReturnValue in String | +| file://:0:0:0:0 | [summary param] -1 in String | file://:0:0:0:0 | [summary] to write: ReturnValue in String | +| file://:0:0:0:0 | [summary param] -1 in String | file://:0:0:0:0 | [summary] to write: ReturnValue in String | +| file://:0:0:0:0 | [summary param] -1 in String | file://:0:0:0:0 | [summary] to write: ReturnValue in String | +| file://:0:0:0:0 | [summary param] -1 in String | file://:0:0:0:0 | [summary] to write: ReturnValue in String | +| file://:0:0:0:0 | [summary param] -1 in String | file://:0:0:0:0 | [summary] to write: ReturnValue in String | +| file://:0:0:0:0 | [summary param] -1 in String | file://:0:0:0:0 | [summary] to write: ReturnValue in String | +| file://:0:0:0:0 | [summary param] -1 in String | file://:0:0:0:0 | [summary] to write: ReturnValue in String | +| file://:0:0:0:0 | [summary param] -1 in String | file://:0:0:0:0 | [summary] to write: ReturnValue in String | +| file://:0:0:0:0 | [summary param] -1 in String | file://:0:0:0:0 | [summary] to write: ReturnValue in String | +| file://:0:0:0:0 | [summary param] -1 in String | file://:0:0:0:0 | [summary] to write: ReturnValue in String | +| file://:0:0:0:0 | [summary param] -1 in String | file://:0:0:0:0 | [summary] to write: ReturnValue in String | +| file://:0:0:0:0 | [summary param] -1 in String | file://:0:0:0:0 | [summary] to write: ReturnValue in String | +| file://:0:0:0:0 | [summary param] -1 in String | file://:0:0:0:0 | [summary] to write: ReturnValue in String | +| file://:0:0:0:0 | [summary param] -1 in String | file://:0:0:0:0 | [summary] to write: ReturnValue in String | +| file://:0:0:0:0 | [summary param] -1 in String | file://:0:0:0:0 | [summary] to write: ReturnValue in String | +| file://:0:0:0:0 | [summary param] -1 in String | file://:0:0:0:0 | [summary] to write: ReturnValue in String | +| file://:0:0:0:0 | [summary param] -1 in String | file://:0:0:0:0 | [summary] to write: ReturnValue in String | +| file://:0:0:0:0 | [summary param] -1 in String | file://:0:0:0:0 | [summary] to write: ReturnValue in String | +| file://:0:0:0:0 | [summary param] -1 in String | file://:0:0:0:0 | [summary] to write: ReturnValue in String | +| file://:0:0:0:0 | [summary param] -1 in String | file://:0:0:0:0 | [summary] to write: ReturnValue in String | +| file://:0:0:0:0 | [summary param] -1 in String | file://:0:0:0:0 | [summary] to write: ReturnValue in String | +| file://:0:0:0:0 | [summary param] -1 in String | file://:0:0:0:0 | [summary] to write: ReturnValue in String | +| file://:0:0:0:0 | [summary param] -1 in String | file://:0:0:0:0 | [summary] to write: ReturnValue in String | +| file://:0:0:0:0 | [summary param] -1 in String | file://:0:0:0:0 | [summary] to write: ReturnValue in String | +| file://:0:0:0:0 | [summary param] -1 in String | file://:0:0:0:0 | [summary] to write: ReturnValue in String | +| file://:0:0:0:0 | [summary param] -1 in String | file://:0:0:0:0 | [summary] to write: ReturnValue in String | +| file://:0:0:0:0 | [summary param] -1 in String | file://:0:0:0:0 | [summary] to write: ReturnValue in String | +| file://:0:0:0:0 | [summary param] -1 in String | file://:0:0:0:0 | [summary] to write: ReturnValue in String | +| file://:0:0:0:0 | [summary param] -1 in String | file://:0:0:0:0 | [summary] to write: ReturnValue in String | +| file://:0:0:0:0 | [summary param] -1 in String | file://:0:0:0:0 | [summary] to write: ReturnValue in String | +| file://:0:0:0:0 | [summary param] -1 in String | file://:0:0:0:0 | [summary] to write: ReturnValue in String | +| file://:0:0:0:0 | [summary param] -1 in String | file://:0:0:0:0 | [summary] to write: ReturnValue in String | +| file://:0:0:0:0 | [summary param] -1 in String | file://:0:0:0:0 | [summary] to write: ReturnValue in String | +| file://:0:0:0:0 | [summary param] -1 in String | file://:0:0:0:0 | [summary] to write: ReturnValue in String | +| file://:0:0:0:0 | [summary param] -1 in String | file://:0:0:0:0 | [summary] to write: ReturnValue in String | +| file://:0:0:0:0 | [summary param] -1 in String | file://:0:0:0:0 | [summary] to write: ReturnValue in String | +| file://:0:0:0:0 | [summary param] -1 in String | file://:0:0:0:0 | [summary] to write: ReturnValue in String | +| file://:0:0:0:0 | [summary param] -1 in Sub | file://:0:0:0:0 | [summary] to write: ReturnValue in Sub | +| file://:0:0:0:0 | [summary param] -1 in Sub | file://:0:0:0:0 | [summary] to write: ReturnValue in Sub | +| file://:0:0:0:0 | [summary param] -1 in Swap | file://:0:0:0:0 | [summary] to write: ReturnValue in Swap | +| file://:0:0:0:0 | [summary param] -1 in Swap | file://:0:0:0:0 | [summary] to write: ReturnValue in Swap | +| file://:0:0:0:0 | [summary param] -1 in Swap | file://:0:0:0:0 | [summary] to write: ReturnValue in Swap | +| file://:0:0:0:0 | [summary param] -1 in Swap | file://:0:0:0:0 | [summary] to write: ReturnValue in Swap | +| file://:0:0:0:0 | [summary param] -1 in SyscallConn | file://:0:0:0:0 | [summary] to write: ReturnValue in SyscallConn | +| file://:0:0:0:0 | [summary param] -1 in SyscallConn | file://:0:0:0:0 | [summary] to write: ReturnValue in SyscallConn | +| file://:0:0:0:0 | [summary param] -1 in SyscallConn | file://:0:0:0:0 | [summary] to write: ReturnValue in SyscallConn | +| file://:0:0:0:0 | [summary param] -1 in SyscallConn | file://:0:0:0:0 | [summary] to write: ReturnValue in SyscallConn | +| file://:0:0:0:0 | [summary param] -1 in SyscallConn | file://:0:0:0:0 | [summary] to write: ReturnValue in SyscallConn | +| file://:0:0:0:0 | [summary param] -1 in SyscallConn | file://:0:0:0:0 | [summary] to write: ReturnValue in SyscallConn | +| file://:0:0:0:0 | [summary param] -1 in SyscallConn | file://:0:0:0:0 | [summary] to write: ReturnValue in SyscallConn | +| file://:0:0:0:0 | [summary param] -1 in SyscallConn | file://:0:0:0:0 | [summary] to write: ReturnValue in SyscallConn | +| file://:0:0:0:0 | [summary param] -1 in Text | file://:0:0:0:0 | [summary] to write: ReturnValue in Text | +| file://:0:0:0:0 | [summary param] -1 in Token | file://:0:0:0:0 | [summary] to write: ReturnValue in Token | +| file://:0:0:0:0 | [summary param] -1 in Token | file://:0:0:0:0 | [summary] to write: ReturnValue in Token | +| file://:0:0:0:0 | [summary param] -1 in Token | file://:0:0:0:0 | [summary] to write: ReturnValue in Token | +| file://:0:0:0:0 | [summary param] -1 in TryRecv | file://:0:0:0:0 | [summary] to write: ReturnValue in TryRecv | +| file://:0:0:0:0 | [summary param] -1 in UnsafeAddr | file://:0:0:0:0 | [summary] to write: ReturnValue in UnsafeAddr | +| file://:0:0:0:0 | [summary param] -1 in Username | file://:0:0:0:0 | [summary] to write: ReturnValue in Username | +| file://:0:0:0:0 | [summary param] -1 in Value | file://:0:0:0:0 | [summary] to write: ReturnValue in Value | +| file://:0:0:0:0 | [summary param] -1 in Value | file://:0:0:0:0 | [summary] to write: ReturnValue in Value | +| file://:0:0:0:0 | [summary param] -1 in Value | file://:0:0:0:0 | [summary] to write: ReturnValue in Value | +| file://:0:0:0:0 | [summary param] -1 in Value | file://:0:0:0:0 | [summary] to write: ReturnValue in Value | +| file://:0:0:0:0 | [summary param] -1 in Value | file://:0:0:0:0 | [summary] to write: ReturnValue in Value | +| file://:0:0:0:0 | [summary param] -1 in Value | file://:0:0:0:0 | [summary] to write: ReturnValue in Value | +| file://:0:0:0:0 | [summary param] -1 in Values | file://:0:0:0:0 | [summary] to write: ReturnValue in Values | +| file://:0:0:0:0 | [summary param] -1 in Values | file://:0:0:0:0 | [summary] to write: ReturnValue in Values | +| file://:0:0:0:0 | [summary param] -1 in Write | file://:0:0:0:0 | [summary] to write: Argument[0] in Write | +| file://:0:0:0:0 | [summary param] -1 in Write | file://:0:0:0:0 | [summary] to write: Argument[0] in Write | +| file://:0:0:0:0 | [summary param] -1 in Write | file://:0:0:0:0 | [summary] to write: Argument[0] in Write | +| file://:0:0:0:0 | [summary param] -1 in WriteProxy | file://:0:0:0:0 | [summary] to write: Argument[0] in WriteProxy | +| file://:0:0:0:0 | [summary param] -1 in WriteSubset | file://:0:0:0:0 | [summary] to write: Argument[0] in WriteSubset | +| file://:0:0:0:0 | [summary param] -1 in WriteTo | file://:0:0:0:0 | [summary] to write: Argument[0] in WriteTo | +| file://:0:0:0:0 | [summary param] -1 in WriteTo | file://:0:0:0:0 | [summary] to write: Argument[0] in WriteTo | +| file://:0:0:0:0 | [summary param] -1 in WriteTo | file://:0:0:0:0 | [summary] to write: Argument[0] in WriteTo | +| file://:0:0:0:0 | [summary param] -1 in WriteTo | file://:0:0:0:0 | [summary] to write: Argument[0] in WriteTo | +| file://:0:0:0:0 | [summary param] -1 in WriteTo | file://:0:0:0:0 | [summary] to write: Argument[0] in WriteTo | +| file://:0:0:0:0 | [summary param] -1 in WriteTo | file://:0:0:0:0 | [summary] to write: Argument[0] in WriteTo | +| file://:0:0:0:0 | [summary param] -1 in WriteTo | file://:0:0:0:0 | [summary] to write: Argument[0] in WriteTo | +| file://:0:0:0:0 | [summary param] -1 in WriteTo | file://:0:0:0:0 | [summary] to write: Argument[0] in WriteTo | +| file://:0:0:0:0 | [summary param] -1 in WriteTo | file://:0:0:0:0 | [summary] to write: Argument[0] in WriteTo | +| file://:0:0:0:0 | [summary param] -1 in Writer | file://:0:0:0:0 | [summary] to write: ReturnValue in Writer | | io.go:14:31:14:43 | "some string" | io.go:14:13:14:44 | call to NewReader | | io.go:16:3:16:3 | definition of w | io.go:16:23:16:27 | &... | | io.go:16:3:16:3 | definition of w | io.go:16:30:16:34 | &... | diff --git a/java/documentation/library-coverage/coverage.csv b/java/documentation/library-coverage/coverage.csv index bf73577e7cc..1681b511d92 100644 --- a/java/documentation/library-coverage/coverage.csv +++ b/java/documentation/library-coverage/coverage.csv @@ -55,12 +55,12 @@ jakarta.ws.rs.container,,9,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,9,, 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,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.io,50,,45,,,22,,,,,,,,,,,,,,28,,,,,,,,,,,,,,,,,,,43,2 +java.lang,31,,93,,13,,,,,,,,,,,,8,,,5,,,4,,,1,,,,,,,,,,,,,57,36 +java.net,13,3,23,,,,,,,,,,,,,,,,,,,,,,,,,,13,,,,,,,,,3,23, +java.nio,53,,36,,,5,,,,,,,,,,,,,,47,,,,,,,,,1,,,,,,,,,,36, java.sql,13,,2,,,,,,,,,,,,,,,,,,,,,,,,,,4,,9,,,,,,,,2, -java.util,44,,484,,,,,,,,,,,,,,34,,,,,,,5,2,,1,2,,,,,,,,,,,44,440 +java.util,45,,485,,,1,,,,,,,,,,,34,,,,,,,5,2,,1,2,,,,,,,,,,,45,440 javafx.scene.web,1,,,,,,,,,,,,,,,,,,,,,,,,,,,,1,,,,,,,,,,, javax.faces.context,2,7,,,,,,,,2,,,,,,,,,,,,,,,,,,,,,,,,,,,,7,, javax.imageio.stream,,,1,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,1, @@ -80,11 +80,11 @@ javax.xml.transform,2,,6,,,,,,,,,,,,,,,,,1,,,,,,,,,,,,,,,1,,,,6, javax.xml.xpath,3,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,3,,,,,, jenkins,,,446,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,423,23 jodd.json,,,10,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,10 -kotlin,16,,1847,,,,,,,,,,,,,,,,,14,,,,,,,,,2,,,,,,,,,,1836,11 +kotlin,16,,1849,,,,,,,,,,,,,,,,,14,,,,,,,,,2,,,,,,,,,,1836,13 net.sf.json,2,,338,,,,,,,,,,,,,,,,,,,,,,,,,,2,,,,,,,,,,321,17 net.sf.saxon.s9api,5,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,5,,,,, ognl,6,,,,,,,,,,,,,,,,,,6,,,,,,,,,,,,,,,,,,,,, -okhttp3,4,,48,,,,,,,,,,,,,,,,,,,,,,,,,,4,,,,,,,,,,23,25 +okhttp3,4,,50,,,,,,,,,,,,,,,,,,,,,,,,,,4,,,,,,,,,,23,27 org.acegisecurity,,,49,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,49, org.antlr.runtime,1,,,,,,,,,,,,,,,,,,,1,,,,,,,,,,,,,,,,,,,, org.apache.commons.codec,,,6,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,6, @@ -98,7 +98,7 @@ 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.lang3,6,,425,,,,,,,,,,,,,,,,,,,6,,,,,,,,,,,,,,,,,294,131 org.apache.commons.logging,6,,,,,,,,,,,,,,,,6,,,,,,,,,,,,,,,,,,,,,,, org.apache.commons.net,9,12,,,,,,,,,,,,,,,,,,3,,,,,,,,,6,,,,,,,,,12,, org.apache.commons.ognl,6,,,,,,,,,,,,,,,,,,6,,,,,,,,,,,,,,,,,,,,, @@ -131,6 +131,7 @@ org.dom4j,20,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,20,,,,,, org.eclipse.jetty.client,1,,,,,,,,,,,,,,,,,,,,,,,,,,,,1,,,,,,,,,,, org.fusesource.leveldbjni,1,,,,,,,,,,,,,,,,,,,1,,,,,,,,,,,,,,,,,,,, org.geogebra.web.full.main,1,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,1,,,,,,, +org.gradle.api.file,,,2,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,2, org.hibernate,7,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,7,,,,,,,,, org.influxdb,1,,,,,,,,,,,,,,,,,,,,,,,,,,,,1,,,,,,,,,,, org.jboss.logging,324,,,,,,,,,,,,,,,,324,,,,,,,,,,,,,,,,,,,,,,, @@ -180,4 +181,4 @@ ratpack.func,,,35,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,35 ratpack.handling,,6,4,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,6,4, ratpack.http,,10,10,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,10,10, ratpack.util,,,35,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,35 -retrofit2,1,,,,,,,,,,,,,,,,,,,,,,,,,,,,1,,,,,,,,,,, +retrofit2,1,,1,,,,,,,,,,,,,,,,,,,,,,,,,,1,,,,,,,,,,1, diff --git a/java/documentation/library-coverage/coverage.rst b/java/documentation/library-coverage/coverage.rst index c8831af7a5b..d07f04c3897 100644 --- a/java/documentation/library-coverage/coverage.rst +++ b/java/documentation/library-coverage/coverage.rst @@ -11,17 +11,17 @@ Java framework & library support Android extensions,``androidx.*``,5,183,19,,,,,, `Apache Commons Collections `_,"``org.apache.commons.collections``, ``org.apache.commons.collections4``",,1600,,,,,,, `Apache Commons IO `_,``org.apache.commons.io``,,560,111,94,,,,,15 - `Apache Commons Lang `_,``org.apache.commons.lang3``,,424,6,,,,,, + `Apache Commons Lang `_,``org.apache.commons.lang3``,,425,6,,,,,, `Apache Commons Text `_,``org.apache.commons.text``,,272,,,,,,, `Apache HttpComponents `_,"``org.apache.hc.core5.*``, ``org.apache.http``",5,182,122,,3,,,,119 `Apache Log4j 2 `_,``org.apache.logging.log4j``,,8,359,,,,,, `Google Guava `_,``com.google.common.*``,,730,41,7,,,,, JBoss Logging,``org.jboss.logging``,,,324,,,,,, `JSON-java `_,``org.json``,,236,,,,,,, - Java Standard Library,``java.*``,3,683,197,76,,9,,,17 + Java Standard Library,``java.*``,3,688,205,80,,9,,,18 Java extensions,"``javax.*``, ``jakarta.*``",63,672,34,2,4,,1,1,2 - Kotlin Standard Library,``kotlin*``,,1847,16,14,,,,,2 + Kotlin Standard Library,``kotlin*``,,1849,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.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,13593,2059,286,16,122,33,1,390 + 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.gradle.api.file``, ``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,5237,577,89,6,18,18,,200 + Totals,,283,13606,2067,290,16,122,33,1,391 diff --git a/java/kotlin-extractor/kotlin_plugin_versions.py b/java/kotlin-extractor/kotlin_plugin_versions.py index bf1c211073a..ea487b4e6cc 100755 --- a/java/kotlin-extractor/kotlin_plugin_versions.py +++ b/java/kotlin-extractor/kotlin_plugin_versions.py @@ -22,7 +22,7 @@ def version_string_to_tuple(version): return tuple([int(m.group(i)) for i in range(1, 4)] + [m.group(4)]) # Version number used by CI. -ci_version = '1.8.10' +ci_version = '1.9.0' many_versions = [ '1.4.32', '1.5.0', '1.5.10', '1.5.20', '1.5.30', '1.6.0', '1.6.20', '1.7.0', '1.7.20', '1.8.0', '1.9.0-Beta' ] diff --git a/java/kotlin-extractor/src/main/kotlin/KotlinFileExtractor.kt b/java/kotlin-extractor/src/main/kotlin/KotlinFileExtractor.kt index ef7fafc913a..aa0a137173b 100644 --- a/java/kotlin-extractor/src/main/kotlin/KotlinFileExtractor.kt +++ b/java/kotlin-extractor/src/main/kotlin/KotlinFileExtractor.kt @@ -157,10 +157,21 @@ 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 } @@ -632,6 +643,10 @@ open class KotlinFileExtractor( logger.warnElement("Unrecognised class kind $kind", c) } + if (c.origin == IrDeclarationOrigin.FILE_CLASS) { + tw.writeFile_class(id) + } + if (c.isData) { tw.writeKtDataClasses(id) } diff --git a/java/ql/integration-tests/all-platforms/kotlin/file_classes/A.kt b/java/ql/integration-tests/all-platforms/kotlin/file_classes/A.kt new file mode 100644 index 00000000000..8ece2422c02 --- /dev/null +++ b/java/ql/integration-tests/all-platforms/kotlin/file_classes/A.kt @@ -0,0 +1,2 @@ +fun a() { +} diff --git a/java/ql/integration-tests/all-platforms/kotlin/file_classes/B.kt b/java/ql/integration-tests/all-platforms/kotlin/file_classes/B.kt new file mode 100644 index 00000000000..0f1fb9d6fd8 --- /dev/null +++ b/java/ql/integration-tests/all-platforms/kotlin/file_classes/B.kt @@ -0,0 +1,3 @@ +fun b() { + a() +} diff --git a/java/ql/integration-tests/all-platforms/kotlin/file_classes/C.kt b/java/ql/integration-tests/all-platforms/kotlin/file_classes/C.kt new file mode 100644 index 00000000000..2b433c8f2f6 --- /dev/null +++ b/java/ql/integration-tests/all-platforms/kotlin/file_classes/C.kt @@ -0,0 +1,3 @@ +class C { + fun c() {} +} diff --git a/java/ql/integration-tests/all-platforms/kotlin/file_classes/classes.expected b/java/ql/integration-tests/all-platforms/kotlin/file_classes/classes.expected new file mode 100644 index 00000000000..ee1e95c031c --- /dev/null +++ b/java/ql/integration-tests/all-platforms/kotlin/file_classes/classes.expected @@ -0,0 +1,3 @@ +| AKt.class:0:0:0:0 | AKt | true | +| B.kt:0:0:0:0 | BKt | true | +| C.kt:1:1:3:1 | C | false | diff --git a/java/ql/integration-tests/all-platforms/kotlin/file_classes/classes.ql b/java/ql/integration-tests/all-platforms/kotlin/file_classes/classes.ql new file mode 100644 index 00000000000..74dad88a095 --- /dev/null +++ b/java/ql/integration-tests/all-platforms/kotlin/file_classes/classes.ql @@ -0,0 +1,5 @@ +import java + +from Class c +where exists(c.getLocation().getFile().getRelativePath()) +select c, any(boolean b | if c.isFileClass() then b = true else b = false) diff --git a/java/ql/integration-tests/all-platforms/kotlin/file_classes/test.py b/java/ql/integration-tests/all-platforms/kotlin/file_classes/test.py new file mode 100644 index 00000000000..fbc4101be04 --- /dev/null +++ b/java/ql/integration-tests/all-platforms/kotlin/file_classes/test.py @@ -0,0 +1,4 @@ +from create_database_utils import * + +runSuccessfully([get_cmd("kotlinc"), 'A.kt']) +run_codeql_database_create(['kotlinc -cp . B.kt C.kt'], lang="java") diff --git a/java/ql/lib/CHANGELOG.md b/java/ql/lib/CHANGELOG.md index 1056cefb86a..77961e193da 100644 --- a/java/ql/lib/CHANGELOG.md +++ b/java/ql/lib/CHANGELOG.md @@ -1,3 +1,29 @@ +## 0.7.0 + +### Deprecated APIs + +* The `ExecCallable` class in `ExternalProcess.qll` has been deprecated. + +### Major Analysis Improvements + +* The data flow library now performs type strengthening. This increases precision for all data flow queries by excluding paths that can be inferred to be impossible due to incompatible types. + +### Minor Analysis Improvements + +* Added automatically-generated dataflow models for `javax.portlet`. +* Added a missing summary model for the method `java.net.URL.toString`. +* Added automatically-generated dataflow models for the following frameworks and libraries: + * `hudson` + * `jenkins` + * `net.sf.json` + * `stapler` +* Added more models for the Hudson framework. +* Added more models for the Stapler framework. + +## 0.6.4 + +No user-facing changes. + ## 0.6.3 ### New Features 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 deleted file mode 100644 index fc21d1825bf..00000000000 --- a/java/ql/lib/change-notes/2023-04-19-deprecated-execcallable.md +++ /dev/null @@ -1,4 +0,0 @@ ---- -category: deprecated ---- -* The `ExecCallable` class in `ExternalProcess.qll` has been deprecated. diff --git a/java/ql/lib/change-notes/2023-05-22-hudson-models.md b/java/ql/lib/change-notes/2023-05-22-hudson-models.md deleted file mode 100644 index 55e2acae00e..00000000000 --- a/java/ql/lib/change-notes/2023-05-22-hudson-models.md +++ /dev/null @@ -1,4 +0,0 @@ ---- -category: minorAnalysis ---- -* Added more models for the Hudson framework. \ No newline at end of file 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 deleted file mode 100644 index 37c7250b953..00000000000 --- a/java/ql/lib/change-notes/2023-05-22-stapler-models.md +++ /dev/null @@ -1,4 +0,0 @@ ---- -category: minorAnalysis ---- -* Added more models for the Stapler framework. diff --git a/java/ql/lib/change-notes/2023-06-08-new-models.md b/java/ql/lib/change-notes/2023-06-08-new-models.md new file mode 100644 index 00000000000..b6e8a15be42 --- /dev/null +++ b/java/ql/lib/change-notes/2023-06-08-new-models.md @@ -0,0 +1,14 @@ +--- +category: minorAnalysis +--- +* Added models for the following packages: + + * java.io + * java.lang + * java.net + * java.nio.channels + * java.nio.file + * java.util.zip + * okhttp3 + * org.gradle.api.file + * retrofit2 diff --git a/java/ql/lib/change-notes/2023-06-08-type-strengthening.md b/java/ql/lib/change-notes/2023-06-08-type-strengthening.md deleted file mode 100644 index 60daaa53058..00000000000 --- a/java/ql/lib/change-notes/2023-06-08-type-strengthening.md +++ /dev/null @@ -1,4 +0,0 @@ ---- -category: majorAnalysis ---- -* The data flow library now performs type strengthening. This increases precision for all data flow queries by excluding paths that can be inferred to be impossible due to incompatible types. diff --git a/java/ql/lib/change-notes/2023-06-14-jenkins-autogenerated-models.md b/java/ql/lib/change-notes/2023-06-14-jenkins-autogenerated-models.md deleted file mode 100644 index da2f90c2326..00000000000 --- a/java/ql/lib/change-notes/2023-06-14-jenkins-autogenerated-models.md +++ /dev/null @@ -1,8 +0,0 @@ ---- -category: minorAnalysis ---- -* Added automatically-generated dataflow models for the following frameworks and libraries: - * `hudson` - * `jenkins` - * `net.sf.json` - * `stapler` 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 deleted file mode 100644 index fc5a58ce4e6..00000000000 --- a/java/ql/lib/change-notes/2023-06-22-url-tostring-model.md +++ /dev/null @@ -1,4 +0,0 @@ ---- -category: minorAnalysis ---- -* Added a missing summary model for the method `java.net.URL.toString`. diff --git a/java/ql/lib/change-notes/2023-06-28-javax-portlet-autogenerated-models.md b/java/ql/lib/change-notes/2023-06-28-javax-portlet-autogenerated-models.md deleted file mode 100644 index 7e6e88f7595..00000000000 --- a/java/ql/lib/change-notes/2023-06-28-javax-portlet-autogenerated-models.md +++ /dev/null @@ -1,4 +0,0 @@ ---- -category: minorAnalysis ---- -* Added automatically-generated dataflow models for `javax.portlet`. diff --git a/java/ql/lib/change-notes/2023-07-10-kotlin-apply.md b/java/ql/lib/change-notes/2023-07-10-kotlin-apply.md new file mode 100644 index 00000000000..0250e7095da --- /dev/null +++ b/java/ql/lib/change-notes/2023-07-10-kotlin-apply.md @@ -0,0 +1,4 @@ +--- +category: minorAnalysis +--- +* Added support for the Kotlin method `apply`. diff --git a/java/ql/lib/change-notes/2023-07-11-file-classes.md b/java/ql/lib/change-notes/2023-07-11-file-classes.md new file mode 100644 index 00000000000..b19fe60022b --- /dev/null +++ b/java/ql/lib/change-notes/2023-07-11-file-classes.md @@ -0,0 +1,4 @@ +--- +category: feature +--- +* A `Class.isFileClass()` predicate, to identify Kotlin file classes, has been added. diff --git a/java/ql/lib/change-notes/2023-07-12-apache-commons-lang3-tostringbuilder.md b/java/ql/lib/change-notes/2023-07-12-apache-commons-lang3-tostringbuilder.md new file mode 100644 index 00000000000..e9739ee4886 --- /dev/null +++ b/java/ql/lib/change-notes/2023-07-12-apache-commons-lang3-tostringbuilder.md @@ -0,0 +1,4 @@ +--- +category: minorAnalysis +--- +* Added models for Apache Commons Lang3 `ToStringBuilder.reflectionToString` method. diff --git a/java/ql/lib/change-notes/2023-07-12-default-stateconfigsig-predicates.md b/java/ql/lib/change-notes/2023-07-12-default-stateconfigsig-predicates.md new file mode 100644 index 00000000000..c9aba58603a --- /dev/null +++ b/java/ql/lib/change-notes/2023-07-12-default-stateconfigsig-predicates.md @@ -0,0 +1,6 @@ +--- +category: feature +--- +* The `DataFlow::StateConfigSig` signature module has gained default implementations for `isBarrier/2` and `isAdditionalFlowStep/4`. + Hence it is no longer needed to provide `none()` implementations of these predicates if they are not needed. + diff --git a/java/ql/lib/change-notes/released/0.6.4.md b/java/ql/lib/change-notes/released/0.6.4.md new file mode 100644 index 00000000000..7e98b0159fc --- /dev/null +++ b/java/ql/lib/change-notes/released/0.6.4.md @@ -0,0 +1,3 @@ +## 0.6.4 + +No user-facing changes. diff --git a/java/ql/lib/change-notes/released/0.7.0.md b/java/ql/lib/change-notes/released/0.7.0.md new file mode 100644 index 00000000000..d355c5e5a18 --- /dev/null +++ b/java/ql/lib/change-notes/released/0.7.0.md @@ -0,0 +1,21 @@ +## 0.7.0 + +### Deprecated APIs + +* The `ExecCallable` class in `ExternalProcess.qll` has been deprecated. + +### Major Analysis Improvements + +* The data flow library now performs type strengthening. This increases precision for all data flow queries by excluding paths that can be inferred to be impossible due to incompatible types. + +### Minor Analysis Improvements + +* Added automatically-generated dataflow models for `javax.portlet`. +* Added a missing summary model for the method `java.net.URL.toString`. +* Added automatically-generated dataflow models for the following frameworks and libraries: + * `hudson` + * `jenkins` + * `net.sf.json` + * `stapler` +* Added more models for the Hudson framework. +* Added more models for the Stapler framework. diff --git a/java/ql/lib/codeql-pack.release.yml b/java/ql/lib/codeql-pack.release.yml index b7dafe32c5d..c761f3e7ab4 100644 --- a/java/ql/lib/codeql-pack.release.yml +++ b/java/ql/lib/codeql-pack.release.yml @@ -1,2 +1,2 @@ --- -lastReleaseVersion: 0.6.3 +lastReleaseVersion: 0.7.0 diff --git a/java/ql/lib/ext/android.webkit.model.yml b/java/ql/lib/ext/android.webkit.model.yml index d88199c04cb..bb375507194 100644 --- a/java/ql/lib/ext/android.webkit.model.yml +++ b/java/ql/lib/ext/android.webkit.model.yml @@ -3,13 +3,13 @@ extensions: pack: codeql/java-all extensible: sourceModel data: - - ["android.webkit", "WebView", False, "getOriginalUrl", "()", "", "ReturnValue", "remote", "manual"] - - ["android.webkit", "WebView", False, "getUrl", "()", "", "ReturnValue", "remote", "manual"] + - ["android.webkit", "WebView", True, "getOriginalUrl", "()", "", "ReturnValue", "remote", "manual"] + - ["android.webkit", "WebView", True, "getUrl", "()", "", "ReturnValue", "remote", "manual"] - addsTo: pack: codeql/java-all extensible: sinkModel data: # Models representing methods susceptible to XSS attacks. - - ["android.webkit", "WebView", False, "evaluateJavascript", "", "", "Argument[0]", "js-injection", "manual"] - - ["android.webkit", "WebView", False, "loadData", "", "", "Argument[0]", "html-injection", "manual"] - - ["android.webkit", "WebView", False, "loadDataWithBaseURL", "", "", "Argument[1]", "html-injection", "manual"] + - ["android.webkit", "WebView", True, "evaluateJavascript", "", "", "Argument[0]", "js-injection", "manual"] + - ["android.webkit", "WebView", True, "loadData", "", "", "Argument[0]", "html-injection", "manual"] + - ["android.webkit", "WebView", True, "loadDataWithBaseURL", "", "", "Argument[1]", "html-injection", "manual"] diff --git a/java/ql/lib/ext/java.io.model.yml b/java/ql/lib/ext/java.io.model.yml index 83e57a68c74..98c51a7bad5 100644 --- a/java/ql/lib/ext/java.io.model.yml +++ b/java/ql/lib/ext/java.io.model.yml @@ -7,6 +7,7 @@ extensions: - ["java.io", "File", False, "File", "(String)", "", "Argument[0]", "path-injection", "manual"] # old PathCreation - ["java.io", "File", False, "File", "(String,String)", "", "Argument[0..1]", "path-injection", "manual"] # old PathCreation - ["java.io", "File", False, "File", "(URI)", "", "Argument[0]", "path-injection", "manual"] # old PathCreation + - ["java.io", "File", True, "createNewFile", "()", "", "Argument[this]", "path-injection", "ai-manual"] - ["java.io", "File", True, "createTempFile", "(String,String,File)", "", "Argument[2]", "path-injection", "ai-manual"] - ["java.io", "File", True, "renameTo", "(File)", "", "Argument[0]", "path-injection", "ai-manual"] - ["java.io", "FileInputStream", True, "FileInputStream", "(File)", "", "Argument[0]", "path-injection", "ai-manual"] @@ -123,7 +124,6 @@ extensions: - ["java.io", "DataInput", "readLong", "()", "summary", "manual"] # taint-numeric - ["java.io", "DataOutput", "writeInt", "(int)", "summary", "manual"] # taint-numeric - ["java.io", "DataOutput", "writeLong", "(long)", "summary", "manual"] # taint-numeric - # sink neutrals - ["java.io", "File", "compareTo", "", "sink", "hq-manual"] - ["java.io", "File", "exists", "()", "sink", "hq-manual"] diff --git a/java/ql/lib/ext/java.lang.model.yml b/java/ql/lib/ext/java.lang.model.yml index 012fb65baab..817683836fb 100644 --- a/java/ql/lib/ext/java.lang.model.yml +++ b/java/ql/lib/ext/java.lang.model.yml @@ -47,6 +47,7 @@ extensions: - ["java.lang", "AbstractStringBuilder", True, "AbstractStringBuilder", "(String)", "", "Argument[0]", "Argument[this]", "taint", "manual"] - ["java.lang", "AbstractStringBuilder", True, "append", "", "", "Argument[this]", "ReturnValue", "value", "manual"] - ["java.lang", "AbstractStringBuilder", True, "append", "", "", "Argument[0]", "Argument[this]", "taint", "manual"] + - ["java.lang", "ProcessBuilder", False, "environment", "()", "", "Argument[this]", "ReturnValue", "taint", "ai-manual"] # When `WithoutElement` is implemented for Java, `java.lang.AbstractStringBuilder#delete` might require a `taint` step of the form `Argument[this].WithoutElement -> Argument[this]` in addition to the below `value` step. - ["java.lang", "AbstractStringBuilder", True, "delete", "(int,int)", "", "Argument[this]", "ReturnValue", "value", "manual"] - ["java.lang", "AbstractStringBuilder", True, "getChars", "", "", "Argument[this]", "Argument[2]", "taint", "manual"] @@ -136,7 +137,6 @@ extensions: - ["java.lang", "Throwable", True, "getLocalizedMessage", "()", "", "Argument[this].SyntheticField[java.lang.Throwable.message]", "ReturnValue", "value", "manual"] - ["java.lang", "Throwable", True, "toString", "()", "", "Argument[this].SyntheticField[java.lang.Throwable.message]", "ReturnValue", "taint", "manual"] - ["java.lang", "UnsupportedOperationException", False, "UnsupportedOperationException", "(String)", "", "Argument[0]", "Argument[this].SyntheticField[java.lang.Throwable.message]", "value", "manual"] - - addsTo: pack: codeql/java-all extensible: neutralModel diff --git a/java/ql/lib/ext/java.net.model.yml b/java/ql/lib/ext/java.net.model.yml index 24591459432..a3bc92dc7b3 100644 --- a/java/ql/lib/ext/java.net.model.yml +++ b/java/ql/lib/ext/java.net.model.yml @@ -43,6 +43,8 @@ extensions: - ["java.net", "URI", False, "toASCIIString", "", "", "Argument[this]", "ReturnValue", "taint", "manual"] - ["java.net", "URI", False, "toString", "", "", "Argument[this]", "ReturnValue", "taint", "manual"] - ["java.net", "URI", False, "toURL", "", "", "Argument[this]", "ReturnValue", "taint", "manual"] + - ["java.net", "URL", False, "getFile", "()", "", "Argument[this]", "ReturnValue", "taint", "ai-manual"] + - ["java.net", "URL", False, "getPath", "()", "", "Argument[this]", "ReturnValue", "taint", "ai-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"] diff --git a/java/ql/lib/ext/java.nio.channels.model.yml b/java/ql/lib/ext/java.nio.channels.model.yml index c4ba9a77a05..f82d224ca24 100644 --- a/java/ql/lib/ext/java.nio.channels.model.yml +++ b/java/ql/lib/ext/java.nio.channels.model.yml @@ -5,3 +5,11 @@ extensions: data: - ["java.nio.channels", "Channels", False, "newChannel", "(InputStream)", "", "Argument[0]", "ReturnValue", "taint", "manual"] - ["java.nio.channels", "ReadableByteChannel", True, "read", "(ByteBuffer)", "", "Argument[this]", "Argument[0]", "taint", "manual"] + - addsTo: + pack: codeql/java-all + extensible: sinkModel + data: + - ["java.nio.channels", "FileChannel", False, "open", "(Path,OpenOption[])", "", "Argument[0]", "path-injection", "ai-manual"] + - ["java.nio.channels", "FileChannel", False, "open", "(Path,Set,FileAttribute[])", "", "Argument[0]", "path-injection", "ai-manual"] + - ["java.nio.channels", "FileChannel", True, "write", "(ByteBuffer,long)", "", "Argument[0]", "file-content-store", "ai-manual"] + - ["java.nio.channels", "FileChannel", True, "write", "(ByteBuffer)", "", "Argument[0]", "file-content-store", "ai-manual"] diff --git a/java/ql/lib/ext/java.nio.file.model.yml b/java/ql/lib/ext/java.nio.file.model.yml index 5e3f32b5e6f..7b151739a07 100644 --- a/java/ql/lib/ext/java.nio.file.model.yml +++ b/java/ql/lib/ext/java.nio.file.model.yml @@ -43,6 +43,8 @@ extensions: - ["java.nio.file", "Files", True, "newInputStream", "(Path,OpenOption[])", "", "Argument[0]", "path-injection", "ai-manual"] - ["java.nio.file", "Files", True, "newOutputStream", "(Path,OpenOption[])", "", "Argument[0]", "path-injection", "ai-manual"] - ["java.nio.file", "FileSystem", False, "getPath", "", "", "Argument[0..1]", "path-injection", "manual"] # old PathCreation + - ["java.nio.file", "FileSystems", False, "newFileSystem", "(URI,Map)", "", "Argument[0]", "path-injection", "ai-manual"] + - ["java.nio.file", "FileSystems", False, "newFileSystem", "(URI,Map)", "", "Argument[0]", "request-forgery", "ai-manual"] - ["java.nio.file", "Path", False, "of", "(String,String[])", "", "Argument[0..1]", "path-injection", "manual"] # old PathCreation - ["java.nio.file", "Path", False, "of", "(URI)", "", "Argument[0]", "path-injection", "manual"] # old PathCreation - ["java.nio.file", "Path", False, "resolve", "(String)", "", "Argument[0]", "path-injection", "manual"] # old PathCreation @@ -79,6 +81,7 @@ extensions: - ["java.nio.file", "Path", True, "relativize", "(Path)", "", "Argument[0]", "ReturnValue", "taint", "ai-manual"] - ["java.nio.file", "Path", True, "resolve", "", "", "Argument[0]", "ReturnValue", "taint", "manual"] - ["java.nio.file", "Path", True, "resolve", "", "", "Argument[this]", "ReturnValue", "taint", "manual"] + - ["java.nio.file", "Path", True, "resolveSibling", "(String)", "", "Argument[0]", "ReturnValue", "taint", "ai-manual"] - ["java.nio.file", "Path", True, "toAbsolutePath", "", "", "Argument[this]", "ReturnValue", "taint", "manual"] - ["java.nio.file", "Path", False, "toFile", "", "", "Argument[this]", "ReturnValue", "taint", "manual"] - ["java.nio.file", "Path", True, "toString", "", "", "Argument[this]", "ReturnValue", "taint", "manual"] @@ -96,7 +99,6 @@ extensions: data: # summary neutrals - ["java.nio.file", "Files", "exists", "(Path,LinkOption[])", "summary", "manual"] - # sink neutrals - ["java.nio.file", "Files", "exists", "", "sink", "hq-manual"] - ["java.nio.file", "Files", "getLastModifiedTime", "", "sink", "hq-manual"] diff --git a/java/ql/lib/ext/java.util.zip.model.yml b/java/ql/lib/ext/java.util.zip.model.yml index 8e741f98c24..577e6b35723 100644 --- a/java/ql/lib/ext/java.util.zip.model.yml +++ b/java/ql/lib/ext/java.util.zip.model.yml @@ -4,4 +4,10 @@ extensions: extensible: summaryModel data: - ["java.util.zip", "GZIPInputStream", False, "GZIPInputStream", "", "", "Argument[0]", "Argument[this]", "taint", "manual"] + - ["java.util.zip", "ZipEntry", True, "ZipEntry", "(String)", "", "Argument[0]", "ReturnValue", "taint", "ai-manual"] - ["java.util.zip", "ZipInputStream", False, "ZipInputStream", "", "", "Argument[0]", "Argument[this]", "taint", "manual"] + - addsTo: + pack: codeql/java-all + extensible: sinkModel + data: + - ["java.util.zip", "ZipOutputStream", True, "putNextEntry", "(ZipEntry)", "", "Argument[0]", "file-content-store", "ai-manual"] diff --git a/java/ql/lib/ext/kotlin.model.yml b/java/ql/lib/ext/kotlin.model.yml index ea275a78515..0ae6a66f9f8 100644 --- a/java/ql/lib/ext/kotlin.model.yml +++ b/java/ql/lib/ext/kotlin.model.yml @@ -3,5 +3,7 @@ extensions: pack: codeql/java-all extensible: summaryModel data: + - ["kotlin", "StandardKt", False, "apply", "", "", "Argument[0]", "Argument[1].Parameter[0]", "value", "manual"] + - ["kotlin", "StandardKt", False, "apply", "", "", "Argument[0]", "ReturnValue", "value", "manual"] - ["kotlin", "StandardKt", False, "with", "", "", "Argument[0]", "Argument[1].Parameter[0]", "value", "manual"] - ["kotlin", "StandardKt", False, "with", "", "", "Argument[1].ReturnValue", "ReturnValue", "value", "manual"] diff --git a/java/ql/lib/ext/okhttp3.model.yml b/java/ql/lib/ext/okhttp3.model.yml index a0662408708..7b24c67975b 100644 --- a/java/ql/lib/ext/okhttp3.model.yml +++ b/java/ql/lib/ext/okhttp3.model.yml @@ -58,4 +58,6 @@ extensions: - ["okhttp3", "HttpUrl$Builder", False, "setQueryParameter", "", "", "Argument[this]", "ReturnValue", "value", "manual"] - ["okhttp3", "HttpUrl$Builder", False, "setQueryParameter", "", "", "Argument[0]", "Argument[this]", "taint", "manual"] - ["okhttp3", "HttpUrl$Builder", False, "username", "", "", "Argument[this]", "ReturnValue", "value", "manual"] + - ["okhttp3", "Request$Builder", False, "get", "()", "", "Argument[this]", "ReturnValue", "value", "ai-manual"] + - ["okhttp3", "Request$Builder", False, "url", "(String)", "", "Argument[this]", "ReturnValue", "value", "ai-manual"] - ["okhttp3", "Request$Builder", True, "build", "()", "", "Argument[this]", "ReturnValue", "taint", "ai-manual"] diff --git a/java/ql/lib/ext/org.apache.commons.lang3.builder.model.yml b/java/ql/lib/ext/org.apache.commons.lang3.builder.model.yml index 654eb626d84..8bb0de9bb1a 100644 --- a/java/ql/lib/ext/org.apache.commons.lang3.builder.model.yml +++ b/java/ql/lib/ext/org.apache.commons.lang3.builder.model.yml @@ -19,4 +19,5 @@ extensions: - ["org.apache.commons.lang3.builder", "ToStringBuilder", False, "appendToString", "", "", "Argument[0]", "Argument[this]", "taint", "manual"] - ["org.apache.commons.lang3.builder", "ToStringBuilder", False, "build", "", "", "Argument[this]", "ReturnValue", "taint", "manual"] - ["org.apache.commons.lang3.builder", "ToStringBuilder", False, "getStringBuffer", "", "", "Argument[this]", "ReturnValue", "taint", "manual"] + - ["org.apache.commons.lang3.builder", "ToStringBuilder", False, "reflectionToString", "", "", "Argument[0]", "ReturnValue", "taint", "manual"] - ["org.apache.commons.lang3.builder", "ToStringBuilder", False, "toString", "", "", "Argument[this]", "ReturnValue", "taint", "manual"] diff --git a/java/ql/lib/ext/org.gradle.api.file.model.yml b/java/ql/lib/ext/org.gradle.api.file.model.yml new file mode 100644 index 00000000000..4f492cdbcbc --- /dev/null +++ b/java/ql/lib/ext/org.gradle.api.file.model.yml @@ -0,0 +1,7 @@ +extensions: + - addsTo: + pack: codeql/java-all + extensible: summaryModel + data: + - ["org.gradle.api.file", "Directory", True, "getAsFile", "()", "", "Argument[this]", "ReturnValue", "taint", "ai-manual"] + - ["org.gradle.api.file", "DirectoryProperty", True, "file", "(String)", "", "Argument[this]", "ReturnValue", "taint", "ai-manual"] diff --git a/java/ql/lib/ext/retrofit2.model.yml b/java/ql/lib/ext/retrofit2.model.yml index 4ea997169a9..7096588aed6 100644 --- a/java/ql/lib/ext/retrofit2.model.yml +++ b/java/ql/lib/ext/retrofit2.model.yml @@ -4,3 +4,8 @@ extensions: extensible: sinkModel data: - ["retrofit2", "Retrofit$Builder", True, "baseUrl", "", "", "Argument[0]", "request-forgery", "manual"] + - addsTo: + pack: codeql/java-all + extensible: summaryModel + data: + - ["retrofit2", "Retrofit$Builder", False, "baseUrl", "(String)", "", "Argument[this]", "ReturnValue", "taint", "ai-manual"] diff --git a/java/ql/lib/qlpack.yml b/java/ql/lib/qlpack.yml index 699c78730fd..607e6cda2b2 100644 --- a/java/ql/lib/qlpack.yml +++ b/java/ql/lib/qlpack.yml @@ -1,5 +1,5 @@ name: codeql/java-all -version: 0.6.4-dev +version: 0.7.1-dev groups: java dbscheme: config/semmlecode.dbscheme extractor: java diff --git a/java/ql/lib/semmle/code/java/Constants.qll b/java/ql/lib/semmle/code/java/Constants.qll index e9ace210d70..9e35a925be3 100644 --- a/java/ql/lib/semmle/code/java/Constants.qll +++ b/java/ql/lib/semmle/code/java/Constants.qll @@ -17,7 +17,6 @@ signature int getIntValSig(Expr e); */ module CalculateConstants { /** Gets the value of a constant boolean expression. */ - pragma[assume_small_delta] boolean calculateBooleanValue(Expr e) { // No casts relevant to booleans. // `!` is the only unary operator that evaluates to a boolean. @@ -99,7 +98,6 @@ module CalculateConstants } /** Gets the value of a constant integer expression. */ - pragma[assume_small_delta] int calculateIntValue(Expr e) { exists(IntegralType t | e.getType() = t | t.getName().toLowerCase() != "long") and ( diff --git a/java/ql/lib/semmle/code/java/ControlFlowGraph.qll b/java/ql/lib/semmle/code/java/ControlFlowGraph.qll index f94658e1372..229c526d270 100644 --- a/java/ql/lib/semmle/code/java/ControlFlowGraph.qll +++ b/java/ql/lib/semmle/code/java/ControlFlowGraph.qll @@ -365,7 +365,6 @@ private module ControlFlowGraphImpl { /** * Gets a non-overridable method that always throws an exception or calls `exit`. */ - pragma[assume_small_delta] private Method nonReturningMethod() { result instanceof MethodExit or @@ -382,7 +381,6 @@ private module ControlFlowGraphImpl { /** * Gets a virtual method that always throws an exception or calls `exit`. */ - pragma[assume_small_delta] private EffectivelyNonVirtualMethod likelyNonReturningMethod() { result.getReturnType() instanceof VoidType and not exists(ReturnStmt ret | ret.getEnclosingCallable() = result) and @@ -402,7 +400,6 @@ private module ControlFlowGraphImpl { /** * Gets a statement that always throws an exception or calls `exit`. */ - pragma[assume_small_delta] private Stmt nonReturningStmt() { result instanceof ThrowStmt or @@ -424,7 +421,6 @@ private module ControlFlowGraphImpl { /** * Gets an expression that always throws an exception or calls `exit`. */ - pragma[assume_small_delta] private Expr nonReturningExpr() { result = nonReturningMethodAccess() or diff --git a/java/ql/lib/semmle/code/java/Expr.qll b/java/ql/lib/semmle/code/java/Expr.qll index 92c81650bc3..312d7767ac2 100644 --- a/java/ql/lib/semmle/code/java/Expr.qll +++ b/java/ql/lib/semmle/code/java/Expr.qll @@ -131,7 +131,6 @@ private predicate primitiveOrString(Type t) { * See JLS v8, section 15.28 (Constant Expressions). */ class CompileTimeConstantExpr extends Expr { - pragma[assume_small_delta] CompileTimeConstantExpr() { primitiveOrString(this.getType()) and ( @@ -181,7 +180,6 @@ class CompileTimeConstantExpr extends Expr { /** * Gets the string value of this expression, where possible. */ - pragma[assume_small_delta] pragma[nomagic] string getStringValue() { result = this.(StringLiteral).getValue() @@ -207,7 +205,6 @@ class CompileTimeConstantExpr extends Expr { /** * Gets the boolean value of this expression, where possible. */ - pragma[assume_small_delta] pragma[nomagic] boolean getBooleanValue() { // Literal value. @@ -1910,7 +1907,6 @@ class TypeAccess extends Expr, Annotatable, @typeaccess { override CompilationUnit getCompilationUnit() { result = Expr.super.getCompilationUnit() } /** Gets a printable representation of this expression. */ - pragma[assume_small_delta] override string toString() { result = this.getQualifier().toString() + "." + this.getType().toString() or diff --git a/java/ql/lib/semmle/code/java/Member.qll b/java/ql/lib/semmle/code/java/Member.qll index d09fa9042d9..565da1b6d97 100644 --- a/java/ql/lib/semmle/code/java/Member.qll +++ b/java/ql/lib/semmle/code/java/Member.qll @@ -736,7 +736,6 @@ class FieldDeclaration extends ExprParent, @fielddecl, Annotatable { /** Gets the number of fields declared in this declaration. */ int getNumField() { result = max(int idx | fieldDeclaredIn(_, this, idx) | idx) + 1 } - pragma[assume_small_delta] override string toString() { if this.getNumField() = 1 then result = this.getTypeAccess() + " " + this.getField(0) + ";" diff --git a/java/ql/lib/semmle/code/java/Type.qll b/java/ql/lib/semmle/code/java/Type.qll index fcf31e3be0d..a85df42eb55 100644 --- a/java/ql/lib/semmle/code/java/Type.qll +++ b/java/ql/lib/semmle/code/java/Type.qll @@ -309,7 +309,6 @@ private predicate hasSubtypeStar1(RefType t, RefType sub) { /** * Holds if `hasSubtype*(t, sub)`, but manual-magic'ed with `getAWildcardLowerBound(sub)`. */ -pragma[assume_small_delta] pragma[nomagic] private predicate hasSubtypeStar2(RefType t, RefType sub) { sub = t and getAWildcardLowerBound(sub) @@ -710,6 +709,12 @@ class Class extends ClassOrInterface { ) } + /** + * Holds if this class is a Kotlin "file class", e.g. the class FooKt + * for top-level entities in Foo.kt. + */ + predicate isFileClass() { file_class(this) } + override string getAPrimaryQlClass() { result = "Class" } } diff --git a/java/ql/lib/semmle/code/java/dataflow/NullGuards.qll b/java/ql/lib/semmle/code/java/dataflow/NullGuards.qll index 25b39458656..011932bc48b 100644 --- a/java/ql/lib/semmle/code/java/dataflow/NullGuards.qll +++ b/java/ql/lib/semmle/code/java/dataflow/NullGuards.qll @@ -42,7 +42,6 @@ EqualityTest varEqualityTestExpr(SsaVariable v1, SsaVariable v2, boolean isEqual } /** Gets an expression that is provably not `null`. */ -pragma[assume_small_delta] Expr clearlyNotNullExpr(Expr reason) { result instanceof ClassInstanceExpr and reason = result or @@ -237,7 +236,6 @@ Expr directNullGuard(SsaVariable v, boolean branch, boolean isnull) { * If `result` evaluates to `branch`, then `v` is guaranteed to be null if `isnull` * is true, and non-null if `isnull` is false. */ -pragma[assume_small_delta] Guard nullGuard(SsaVariable v, boolean branch, boolean isnull) { result = directNullGuard(v, branch, isnull) or exists(boolean branch0 | implies_v3(result, branch, nullGuard(v, branch0, isnull), branch0)) diff --git a/java/ql/lib/semmle/code/java/dataflow/SSA.qll b/java/ql/lib/semmle/code/java/dataflow/SSA.qll index dd478b2a869..fb2e87e9bc7 100644 --- a/java/ql/lib/semmle/code/java/dataflow/SSA.qll +++ b/java/ql/lib/semmle/code/java/dataflow/SSA.qll @@ -61,7 +61,6 @@ class SsaSourceVariable extends TSsaSourceVariable { * accessed from nested callables are therefore associated with several * `SsaSourceVariable`s. */ - pragma[assume_small_delta] cached VarAccess getAnAccess() { exists(LocalScopeVariable v, Callable c | @@ -451,7 +450,6 @@ private module SsaImpl { * Holds if `f` is live in `b` at index `i`. The rank of `i` is `rankix` as * defined by `callDefUseRank`. */ - pragma[assume_small_delta] private predicate liveAtRank(TrackedField f, BasicBlock b, int rankix, int i) { callDefUseRank(f, b, rankix, i) and ( @@ -565,7 +563,6 @@ private module SsaImpl { } /** Holds if a phi node for `v` is needed at the beginning of basic block `b`. */ - pragma[assume_small_delta] cached predicate phiNode(TrackedVar v, BasicBlock b) { liveAtEntry(v, b) and diff --git a/java/ql/lib/semmle/code/java/dataflow/TypeFlow.qll b/java/ql/lib/semmle/code/java/dataflow/TypeFlow.qll index add7ebc66d4..6d1946a47f6 100644 --- a/java/ql/lib/semmle/code/java/dataflow/TypeFlow.qll +++ b/java/ql/lib/semmle/code/java/dataflow/TypeFlow.qll @@ -241,7 +241,6 @@ private module ForAll E, TypePropagation T> { * Holds if `t` is a candidate bound for `n` that is also valid for data coming * through the edges into `n` ranked from `1` to `r`. */ - pragma[assume_small_delta] private predicate flowJoin(int r, Node n, T::Typ t) { ( r = 1 and candJoinType(n, t) diff --git a/java/ql/lib/semmle/code/java/dataflow/internal/BaseSSA.qll b/java/ql/lib/semmle/code/java/dataflow/internal/BaseSSA.qll index 6e41c803553..f4af8f506d6 100644 --- a/java/ql/lib/semmle/code/java/dataflow/internal/BaseSSA.qll +++ b/java/ql/lib/semmle/code/java/dataflow/internal/BaseSSA.qll @@ -151,7 +151,6 @@ private module SsaImpl { } /** Holds if a phi node for `v` is needed at the beginning of basic block `b`. */ - pragma[assume_small_delta] cached predicate phiNode(BaseSsaSourceVariable v, BasicBlock b) { liveAtEntry(v, b) and diff --git a/java/ql/lib/semmle/code/java/dataflow/internal/DataFlow.qll b/java/ql/lib/semmle/code/java/dataflow/internal/DataFlow.qll index f34554d6eea..47329d133a4 100644 --- a/java/ql/lib/semmle/code/java/dataflow/internal/DataFlow.qll +++ b/java/ql/lib/semmle/code/java/dataflow/internal/DataFlow.qll @@ -114,7 +114,7 @@ signature module StateConfigSig { * Holds if data flow through `node` is prohibited when the flow state is * `state`. */ - predicate isBarrier(Node node, FlowState state); + default predicate isBarrier(Node node, FlowState state) { none() } /** Holds if data flow into `node` is prohibited. */ default predicate isBarrierIn(Node node) { none() } @@ -131,7 +131,9 @@ signature module StateConfigSig { * Holds if data may flow from `node1` to `node2` in addition to the normal data-flow steps. * This step is only applicable in `state1` and updates the flow state to `state2`. */ - predicate isAdditionalFlowStep(Node node1, FlowState state1, Node node2, FlowState state2); + default predicate isAdditionalFlowStep(Node node1, FlowState state1, Node node2, FlowState state2) { + none() + } /** * Holds if an arbitrary number of implicit read steps of content `c` may be 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 284fff191ae..fe8633e9218 100644 --- a/java/ql/lib/semmle/code/java/dataflow/internal/DataFlowImpl.qll +++ b/java/ql/lib/semmle/code/java/dataflow/internal/DataFlowImpl.qll @@ -254,6 +254,11 @@ module Impl { not fullBarrier(node2) } + pragma[nomagic] + private predicate isUnreachableInCall1(NodeEx n, LocalCallContextSpecificCall cc) { + isUnreachableInCallCached(n.asNode(), cc.getCall()) + } + /** * Holds if data can flow in one local step from `node1` to `node2`. */ @@ -460,7 +465,6 @@ module Impl { * The Boolean `cc` records whether the node is reached through an * argument in a call. */ - pragma[assume_small_delta] private predicate fwdFlow(NodeEx node, Cc cc) { sourceNode(node, _) and if hasSourceCallCtx() then cc = true else cc = false @@ -570,7 +574,6 @@ module Impl { /** * Holds if `c` is the target of a store in the flow covered by `fwdFlow`. */ - pragma[assume_small_delta] pragma[nomagic] private predicate fwdFlowConsCand(Content c) { exists(NodeEx mid, NodeEx node | @@ -1216,7 +1219,6 @@ module Impl { fwdFlow1(_, _, _, _, _, _, t0, t, ap, _) and t0 != t } - pragma[assume_small_delta] pragma[nomagic] private predicate fwdFlow0( NodeEx node, FlowState state, Cc cc, ParamNodeOption summaryCtx, TypOption argT, @@ -2111,7 +2113,7 @@ module Impl { NodeEx node1, FlowState state, NodeEx node2, boolean preservesValue, DataFlowType t, LocalCallContext cc ) { - not isUnreachableInCallCached(node2.asNode(), cc.(LocalCallContextSpecificCall).getCall()) and + not isUnreachableInCall1(node2, cc) and ( localFlowEntry(node1, pragma[only_bind_into](state)) and ( @@ -2126,7 +2128,7 @@ module Impl { ) and node1 != node2 and cc.relevantFor(node1.getEnclosingCallable()) and - not isUnreachableInCallCached(node1.asNode(), cc.(LocalCallContextSpecificCall).getCall()) + not isUnreachableInCall1(node1, cc) or exists(NodeEx mid | localFlowStepPlus(node1, pragma[only_bind_into](state), mid, preservesValue, t, cc) and @@ -2163,10 +2165,8 @@ module Impl { preservesValue = false and t = node2.getDataFlowType() and callContext.relevantFor(node1.getEnclosingCallable()) and - not exists(DataFlowCall call | call = callContext.(LocalCallContextSpecificCall).getCall() | - isUnreachableInCallCached(node1.asNode(), call) or - isUnreachableInCallCached(node2.asNode(), call) - ) + not isUnreachableInCall1(node1, callContext) and + not isUnreachableInCall1(node2, callContext) } } @@ -2706,7 +2706,7 @@ module Impl { ParamNodeEx getParamNode() { result = p } - override string toString() { result = p + ": " + ap } + override string toString() { result = p + concat(" : " + ppReprType(t)) + " " + ap } predicate hasLocationInfo( string filepath, int startline, int startcolumn, int endline, int endcolumn @@ -2758,12 +2758,21 @@ module Impl { ) } + private predicate forceUnfold(AccessPathApprox apa) { + forceHighPrecision(apa.getHead()) + or + exists(Content c2 | + apa = TConsCons(_, _, c2, _) and + forceHighPrecision(c2) + ) + } + /** * Holds with `unfold = false` if a precise head-tail representation of `apa` is * expected to be expensive. Holds with `unfold = true` otherwise. */ private predicate evalUnfold(AccessPathApprox apa, boolean unfold) { - if forceHighPrecision(apa.getHead()) + if forceUnfold(apa) then unfold = true else exists(int aps, int nodes, int apLimit, int tupleLimit | @@ -2777,7 +2786,6 @@ module Impl { /** * Gets the number of `AccessPath`s that correspond to `apa`. */ - pragma[assume_small_delta] private int countAps(AccessPathApprox apa) { evalUnfold(apa, false) and result = 1 and @@ -2796,7 +2804,6 @@ module Impl { * that it is expanded to a precise head-tail representation. */ language[monotonicAggregates] - pragma[assume_small_delta] private int countPotentialAps(AccessPathApprox apa) { apa instanceof AccessPathApproxNil and result = 1 or @@ -2833,7 +2840,6 @@ module Impl { } private newtype TPathNode = - pragma[assume_small_delta] TPathNodeMid( NodeEx node, FlowState state, CallContext cc, SummaryCtx sc, DataFlowType t, AccessPath ap ) { @@ -2918,7 +2924,6 @@ module Impl { override AccessPathFrontHead getFront() { result = TFrontHead(head_) } - pragma[assume_small_delta] override AccessPathApproxCons getApprox() { result = TConsNil(head_, t) and tail_ = TAccessPathNil() or @@ -2927,7 +2932,6 @@ module Impl { result = TCons1(head_, this.length()) } - pragma[assume_small_delta] override int length() { result = 1 + tail_.length() } private string toStringImpl(boolean needsSuffix) { @@ -3097,6 +3101,12 @@ module Impl { result = " <" + this.(PathNodeMid).getCallContext().toString() + ">" } + private string ppSummaryCtx() { + this instanceof PathNodeSink and result = "" + or + result = " <" + this.(PathNodeMid).getSummaryCtx().toString() + ">" + } + /** Gets a textual representation of this element. */ string toString() { result = this.getNodeEx().toString() + this.ppType() + this.ppAp() } @@ -3105,7 +3115,9 @@ module Impl { * representation of the call context. */ string toStringWithContext() { - result = this.getNodeEx().toString() + this.ppType() + this.ppAp() + this.ppCtx() + result = + this.getNodeEx().toString() + this.ppType() + this.ppAp() + this.ppCtx() + + this.ppSummaryCtx() } /** @@ -3379,7 +3391,6 @@ module Impl { * Holds if data may flow from `mid` to `node`. The last step in or out of * a callable is recorded by `cc`. */ - pragma[assume_small_delta] pragma[nomagic] private predicate pathStep0( PathNodeMid mid, NodeEx node, FlowState state, CallContext cc, SummaryCtx sc, DataFlowType t, @@ -3592,7 +3603,6 @@ module Impl { ) } - pragma[assume_small_delta] pragma[nomagic] private predicate pathThroughCallable0( DataFlowCall call, PathNodeMid mid, ReturnKindExt kind, FlowState state, CallContext cc, diff --git a/java/ql/lib/semmle/code/java/dataflow/internal/DataFlowImplCommon.qll b/java/ql/lib/semmle/code/java/dataflow/internal/DataFlowImplCommon.qll index 0d4c033c95d..aff14e7b44d 100644 --- a/java/ql/lib/semmle/code/java/dataflow/internal/DataFlowImplCommon.qll +++ b/java/ql/lib/semmle/code/java/dataflow/internal/DataFlowImplCommon.qll @@ -187,7 +187,6 @@ private module LambdaFlow { else any() } - pragma[assume_small_delta] pragma[nomagic] predicate revLambdaFlow0( DataFlowCall lambdaCall, LambdaCallKind kind, Node node, DataFlowType t, boolean toReturn, @@ -274,7 +273,6 @@ private module LambdaFlow { ) } - pragma[assume_small_delta] pragma[nomagic] predicate revLambdaFlowOut( DataFlowCall lambdaCall, LambdaCallKind kind, TReturnPositionSimple pos, DataFlowType t, diff --git a/java/ql/lib/semmle/code/java/dataflow/internal/FlowSummaryImpl.qll b/java/ql/lib/semmle/code/java/dataflow/internal/FlowSummaryImpl.qll index 9ea7c44c50c..7977e18120f 100644 --- a/java/ql/lib/semmle/code/java/dataflow/internal/FlowSummaryImpl.qll +++ b/java/ql/lib/semmle/code/java/dataflow/internal/FlowSummaryImpl.qll @@ -23,29 +23,30 @@ module Public { * content type, or a return kind. */ class SummaryComponent extends TSummaryComponent { - /** Gets a textual representation of this summary component. */ - string toString() { - exists(ContentSet c | this = TContentSummaryComponent(c) and result = c.toString()) - or - exists(ContentSet c | this = TWithoutContentSummaryComponent(c) and result = "without " + c) - or - exists(ContentSet c | this = TWithContentSummaryComponent(c) and result = "with " + c) + /** Gets a textual representation of this component used for MaD models. */ + string getMadRepresentation() { + result = getMadRepresentationSpecific(this) or exists(ArgumentPosition pos | - this = TParameterSummaryComponent(pos) and result = "parameter " + pos + this = TParameterSummaryComponent(pos) and + result = "Parameter[" + getArgumentPosition(pos) + "]" ) or exists(ParameterPosition pos | - this = TArgumentSummaryComponent(pos) and result = "argument " + pos + this = TArgumentSummaryComponent(pos) and + result = "Argument[" + getParameterPosition(pos) + "]" ) or - exists(ReturnKind rk | this = TReturnSummaryComponent(rk) and result = "return (" + rk + ")") - or - exists(SummaryComponent::SyntheticGlobal sg | - this = TSyntheticGlobalSummaryComponent(sg) and - result = "synthetic global (" + sg + ")" + exists(string synthetic | + this = TSyntheticGlobalSummaryComponent(synthetic) and + result = "SyntheticGlobal[" + synthetic + "]" ) + or + this = TReturnSummaryComponent(getReturnValueKind()) and result = "ReturnValue" } + + /** Gets a textual representation of this summary component. */ + string toString() { result = this.getMadRepresentation() } } /** Provides predicates for constructing summary components. */ @@ -110,7 +111,6 @@ module Public { } /** Gets the stack obtained by dropping the first `i` elements, if any. */ - pragma[assume_small_delta] SummaryComponentStack drop(int i) { i = 0 and result = this or @@ -125,19 +125,22 @@ module Public { this = TSingletonSummaryComponentStack(result) or result = this.tail().bottom() } - /** Gets a textual representation of this stack. */ - string toString() { + /** Gets a textual representation of this stack used for MaD models. */ + string getMadRepresentation() { exists(SummaryComponent head, SummaryComponentStack tail | head = this.head() and tail = this.tail() and - result = tail + "." + head + result = tail.getMadRepresentation() + "." + head.getMadRepresentation() ) or exists(SummaryComponent c | this = TSingletonSummaryComponentStack(c) and - result = c.toString() + result = c.getMadRepresentation() ) } + + /** Gets a textual representation of this stack. */ + string toString() { result = this.getMadRepresentation() } } /** Provides predicates for constructing stacks of summary components. */ @@ -166,42 +169,6 @@ module Public { SummaryComponentStack return(ReturnKind rk) { result = singleton(SummaryComponent::return(rk)) } } - /** Gets a textual representation of this component used for flow summaries. */ - private string getComponent(SummaryComponent sc) { - result = getComponentSpecific(sc) - or - exists(ArgumentPosition pos | - sc = TParameterSummaryComponent(pos) and - result = "Parameter[" + getArgumentPosition(pos) + "]" - ) - or - exists(ParameterPosition pos | - sc = TArgumentSummaryComponent(pos) and - result = "Argument[" + getParameterPosition(pos) + "]" - ) - or - exists(string synthetic | - sc = TSyntheticGlobalSummaryComponent(synthetic) and - result = "SyntheticGlobal[" + synthetic + "]" - ) - or - sc = TReturnSummaryComponent(getReturnValueKind()) and result = "ReturnValue" - } - - /** Gets a textual representation of this stack used for flow summaries. */ - string getComponentStack(SummaryComponentStack stack) { - exists(SummaryComponent head, SummaryComponentStack tail | - head = stack.head() and - tail = stack.tail() and - result = getComponentStack(tail) + "." + getComponent(head) - ) - or - exists(SummaryComponent c | - stack = TSingletonSummaryComponentStack(c) and - result = getComponent(c) - ) - } - /** * A class that exists for QL technical reasons only (the IPA type used * to represent component stacks needs to be bounded). @@ -1382,8 +1349,8 @@ module Private { c.relevantSummary(input, output, preservesValue) and csv = c.getCallableCsv() // Callable information - + getComponentStack(input) + ";" // input - + getComponentStack(output) + ";" // output + + input.getMadRepresentation() + ";" // input + + output.getMadRepresentation() + ";" // output + renderKind(preservesValue) + ";" // kind + renderProvenance(c) // provenance ) diff --git a/java/ql/lib/semmle/code/java/dataflow/internal/FlowSummaryImplSpecific.qll b/java/ql/lib/semmle/code/java/dataflow/internal/FlowSummaryImplSpecific.qll index 77780aa3a46..d9782c2eecf 100644 --- a/java/ql/lib/semmle/code/java/dataflow/internal/FlowSummaryImplSpecific.qll +++ b/java/ql/lib/semmle/code/java/dataflow/internal/FlowSummaryImplSpecific.qll @@ -193,8 +193,8 @@ private string getContentSpecific(Content c) { c instanceof MapValueContent and result = "MapValue" } -/** Gets the textual representation of the content in the format used for flow summaries. */ -string getComponentSpecific(SummaryComponent sc) { +/** Gets the textual representation of the content in the format used for MaD models. */ +string getMadRepresentationSpecific(SummaryComponent sc) { exists(Content c | sc = TContentSummaryComponent(c) and result = getContentSpecific(c)) } diff --git a/java/ql/lib/semmle/code/java/dataflow/internal/TaintTrackingUtil.qll b/java/ql/lib/semmle/code/java/dataflow/internal/TaintTrackingUtil.qll index a43aa5be4f1..c992f92ee8a 100644 --- a/java/ql/lib/semmle/code/java/dataflow/internal/TaintTrackingUtil.qll +++ b/java/ql/lib/semmle/code/java/dataflow/internal/TaintTrackingUtil.qll @@ -617,7 +617,6 @@ private MethodAccess callReturningSameType(Expr ref) { result.getMethod().getReturnType() = ref.getType() } -pragma[assume_small_delta] private SrcRefType entrypointType() { exists(RemoteFlowSource s, RefType t | s instanceof DataFlow::ExplicitParameterNode and diff --git a/java/ql/lib/semmle/code/java/dispatch/DispatchFlow.qll b/java/ql/lib/semmle/code/java/dispatch/DispatchFlow.qll index c6419f4c26b..e3eb298dd13 100644 --- a/java/ql/lib/semmle/code/java/dispatch/DispatchFlow.qll +++ b/java/ql/lib/semmle/code/java/dispatch/DispatchFlow.qll @@ -31,7 +31,6 @@ private Callable dispatchCand(Call c) { /** * Holds if `t` and all its enclosing types are public. */ -pragma[assume_small_delta] private predicate veryPublic(RefType t) { t.isPublic() and ( diff --git a/java/ql/lib/semmle/code/java/dispatch/ObjFlow.qll b/java/ql/lib/semmle/code/java/dispatch/ObjFlow.qll index d4c0d7e5ab5..14ea6e81718 100644 --- a/java/ql/lib/semmle/code/java/dispatch/ObjFlow.qll +++ b/java/ql/lib/semmle/code/java/dispatch/ObjFlow.qll @@ -206,7 +206,6 @@ private predicate relevantNodeBack(ObjNode n) { exists(ObjNode mid | objStep(n, mid) and relevantNodeBack(mid)) } -pragma[assume_small_delta] private predicate relevantNode(ObjNode n) { source(_, n) and relevantNodeBack(n) or diff --git a/java/ql/lib/semmle/code/java/frameworks/JaxWS.qll b/java/ql/lib/semmle/code/java/frameworks/JaxWS.qll index 5c20af2f457..54b41f28a08 100644 --- a/java/ql/lib/semmle/code/java/frameworks/JaxWS.qll +++ b/java/ql/lib/semmle/code/java/frameworks/JaxWS.qll @@ -53,7 +53,6 @@ private predicate hasPathAnnotation(Annotatable annotatable) { * A method which is annotated with one or more JaxRS resource type annotations e.g. `@GET`, `@POST` etc. */ class JaxRsResourceMethod extends Method { - pragma[assume_small_delta] JaxRsResourceMethod() { exists(AnnotationType a | a = this.getAnAnnotation().getType() and @@ -92,7 +91,6 @@ class JaxRsResourceMethod extends Method { * This class contains resource methods, which are executed in response to requests. */ class JaxRsResourceClass extends Class { - pragma[assume_small_delta] JaxRsResourceClass() { // A root resource class has a @Path annotation on the class. hasPathAnnotation(this) diff --git a/java/ql/lib/semmle/code/java/frameworks/Rmi.qll b/java/ql/lib/semmle/code/java/frameworks/Rmi.qll index 7cff44a69ff..922f90bccb6 100644 --- a/java/ql/lib/semmle/code/java/frameworks/Rmi.qll +++ b/java/ql/lib/semmle/code/java/frameworks/Rmi.qll @@ -12,7 +12,6 @@ class RemoteCallableMethod extends Method { RemoteCallableMethod() { remoteCallableMethod(this) } } -pragma[assume_small_delta] private predicate remoteCallableMethod(Method method) { method.getDeclaringType().getASupertype() instanceof TypeRemote or diff --git a/java/ql/lib/semmle/code/java/frameworks/google/GsonSerializability.qll b/java/ql/lib/semmle/code/java/frameworks/google/GsonSerializability.qll index f7de80daaf4..8a286c93a16 100644 --- a/java/ql/lib/semmle/code/java/frameworks/google/GsonSerializability.qll +++ b/java/ql/lib/semmle/code/java/frameworks/google/GsonSerializability.qll @@ -45,7 +45,6 @@ private class FieldReferencedGsonDeserializableType extends GsonDeserializableTy /** A field that may be deserialized using the Gson JSON framework. */ private class GsonDeserializableField extends DeserializableField { - pragma[assume_small_delta] GsonDeserializableField() { exists(GsonDeserializableType superType | superType = this.getDeclaringType().getAnAncestor() and diff --git a/java/ql/lib/semmle/code/java/frameworks/jackson/JacksonSerializability.qll b/java/ql/lib/semmle/code/java/frameworks/jackson/JacksonSerializability.qll index f1395431a3c..4f857afb660 100644 --- a/java/ql/lib/semmle/code/java/frameworks/jackson/JacksonSerializability.qll +++ b/java/ql/lib/semmle/code/java/frameworks/jackson/JacksonSerializability.qll @@ -146,7 +146,6 @@ class JacksonSerializableField extends SerializableField { /** A field that may be deserialized using the Jackson JSON framework. */ class JacksonDeserializableField extends DeserializableField { - pragma[assume_small_delta] JacksonDeserializableField() { exists(JacksonDeserializableType superType | superType = this.getDeclaringType().getAnAncestor() and diff --git a/java/ql/lib/semmle/code/java/frameworks/kotlin/Kotlin.qll b/java/ql/lib/semmle/code/java/frameworks/kotlin/Kotlin.qll new file mode 100644 index 00000000000..71e0af2018d --- /dev/null +++ b/java/ql/lib/semmle/code/java/frameworks/kotlin/Kotlin.qll @@ -0,0 +1,21 @@ +/** Provides classes and predicates related to `kotlin`. */ + +import java + +/** A call to Kotlin's `apply` method. */ +class KotlinApply extends MethodAccess { + ExtensionMethod m; + + KotlinApply() { + this.getMethod() = m and + m.hasQualifiedName("kotlin", "StandardKt", "apply") + } + + /** Gets the function block argument of this call. */ + LambdaExpr getLambdaArg() { + result = this.getArgument(m.getExtensionReceiverParameterIndex() + 1) + } + + /** Gets the receiver argument of this call. */ + Argument getReceiver() { result = this.getArgument(m.getExtensionReceiverParameterIndex()) } +} diff --git a/java/ql/lib/semmle/code/java/security/ArbitraryApkInstallationQuery.qll b/java/ql/lib/semmle/code/java/security/ArbitraryApkInstallationQuery.qll index 9df15067be9..fa3dbdb4474 100644 --- a/java/ql/lib/semmle/code/java/security/ArbitraryApkInstallationQuery.qll +++ b/java/ql/lib/semmle/code/java/security/ArbitraryApkInstallationQuery.qll @@ -68,8 +68,6 @@ private module InstallPackageActionConfig implements DataFlow::StateConfigSig { predicate isSink(DataFlow::Node node, FlowState state) { state instanceof HasInstallPackageAction and node.asExpr().getType() instanceof TypeIntent } - - predicate isBarrier(DataFlow::Node node, FlowState state) { none() } } private module InstallPackageActionFlow = @@ -113,8 +111,6 @@ private module PackageArchiveMimeTypeConfig implements DataFlow::StateConfigSig state instanceof HasPackageArchiveMimeType and node instanceof SetDataSink } - - predicate isBarrier(DataFlow::Node node, FlowState state) { none() } } private module PackageArchiveMimeTypeFlow = diff --git a/java/ql/lib/semmle/code/java/security/ImplicitPendingIntentsQuery.qll b/java/ql/lib/semmle/code/java/security/ImplicitPendingIntentsQuery.qll index c02abb4de81..9ef330459bf 100644 --- a/java/ql/lib/semmle/code/java/security/ImplicitPendingIntentsQuery.qll +++ b/java/ql/lib/semmle/code/java/security/ImplicitPendingIntentsQuery.qll @@ -72,8 +72,6 @@ module ImplicitPendingIntentStartConfig implements DataFlow::StateConfigSig { predicate isBarrier(DataFlow::Node sanitizer) { sanitizer instanceof ExplicitIntentSanitizer } - predicate isBarrier(DataFlow::Node node, FlowState state) { none() } - predicate isAdditionalFlowStep(DataFlow::Node node1, DataFlow::Node node2) { any(ImplicitPendingIntentAdditionalTaintStep c).step(node1, node2) } diff --git a/java/ql/lib/semmle/code/java/security/UnsafeAndroidAccess.qll b/java/ql/lib/semmle/code/java/security/UnsafeAndroidAccess.qll index ba162ede986..9a85a771406 100644 --- a/java/ql/lib/semmle/code/java/security/UnsafeAndroidAccess.qll +++ b/java/ql/lib/semmle/code/java/security/UnsafeAndroidAccess.qll @@ -5,6 +5,7 @@ import java private import semmle.code.java.dataflow.DataFlow private import semmle.code.java.frameworks.android.WebView +private import semmle.code.java.frameworks.kotlin.Kotlin /** * A sink that represents a method that fetches a web resource in Android. @@ -62,10 +63,26 @@ private class WebViewRef extends Element { t.isOwnInstanceAccess() or t.getInstanceAccess().isEnclosingInstanceAccess(this) ) or - result = DataFlow::exprNode(this.(Variable).getAnAccess()) + exists(Variable v | result.asExpr() = v.getAnAccess() | + v = this + or + applyReceiverVariable(this, v) + ) } } +/** + * Holds if `p` is the lambda parameter that holds the receiver of an `apply` expression in Kotlin, + * and `v` is the variable of the receiver in the outer scope. + */ +private predicate applyReceiverVariable(Parameter p, Variable v) { + exists(LambdaExpr lambda, KotlinApply apply | + p.getCallable() = lambda.asMethod() and + lambda = apply.getLambdaArg() and + v = apply.getReceiver().(VarAccess).getVariable() + ) +} + /** * Holds if a `WebViewLoadUrlMethod` is called on an access of `webview` * with `urlArg` as its first argument. diff --git a/java/ql/src/CHANGELOG.md b/java/ql/src/CHANGELOG.md index 4852323b9b8..63dffc3382f 100644 --- a/java/ql/src/CHANGELOG.md +++ b/java/ql/src/CHANGELOG.md @@ -1,3 +1,18 @@ +## 0.7.0 + +### Minor Analysis Improvements + +* New models have been added for `org.apache.commons.lang`. +* The query `java/unsafe-deserialization` has been updated to take into account `SerialKiller`, a library used to prevent deserialization of arbitrary classes. + +### Bug Fixes + +* The query "Arbitrary file write during archive extraction ("Zip Slip")" (`java/zipslip`) has been renamed to "Arbitrary file access during archive extraction ("Zip Slip")." + +## 0.6.4 + +No user-facing changes. + ## 0.6.3 ### Minor Analysis Improvements diff --git a/java/ql/src/Security/CWE/CWE-200/AndroidWebViewSettingsAllowsContentAccess.ql b/java/ql/src/Security/CWE/CWE-200/AndroidWebViewSettingsAllowsContentAccess.ql index c16b8577d73..6fd6da06a27 100644 --- a/java/ql/src/Security/CWE/CWE-200/AndroidWebViewSettingsAllowsContentAccess.ql +++ b/java/ql/src/Security/CWE/CWE-200/AndroidWebViewSettingsAllowsContentAccess.ql @@ -97,8 +97,6 @@ module WebViewDisallowContentAccessConfig implements DataFlow::StateConfigSig { state instanceof IsSettings and node instanceof WebSettingsDisallowContentAccessSink } - - predicate isBarrier(DataFlow::Node node, FlowState state) { none() } } module WebViewDisallowContentAccessFlow = diff --git a/java/ql/src/Telemetry/AutomodelApplicationModeExtractCandidates.ql b/java/ql/src/Telemetry/AutomodelApplicationModeExtractCandidates.ql index 1e4c9c7e248..4e40fadd750 100644 --- a/java/ql/src/Telemetry/AutomodelApplicationModeExtractCandidates.ql +++ b/java/ql/src/Telemetry/AutomodelApplicationModeExtractCandidates.ql @@ -12,9 +12,44 @@ * @tags internal extract automodel application-mode candidates */ +import java private import AutomodelApplicationModeCharacteristics private import AutomodelJavaUtil +/** + * Gets a sample of endpoints (of at most `limit` samples) with the given method signature. + * + * The main purpose of this helper predicate is to avoid selecting too many candidates, as this may + * cause the SARIF file to exceed the maximum size limit. + */ +bindingset[limit] +private Endpoint getSampleForSignature( + int limit, string package, string type, string subtypes, string name, string signature, + string input +) { + exists(int n, int num_endpoints, ApplicationModeMetadataExtractor meta | + num_endpoints = + count(Endpoint e | meta.hasMetadata(e, package, type, subtypes, name, signature, input)) + | + result = + rank[n](Endpoint e, Location loc | + loc = e.getLocation() and + meta.hasMetadata(e, package, type, subtypes, name, signature, input) + | + e + order by + loc.getFile().getAbsolutePath(), loc.getStartLine(), loc.getStartColumn(), + loc.getEndLine(), loc.getEndColumn() + ) and + // To avoid selecting samples that are too close together (as the ranking above goes by file + // path first), we select `limit` evenly spaced samples from the ranked list of endpoints. By + // default this would always include the first sample, so we add a random-chosen prime offset + // to the first sample index, and reduce modulo the number of endpoints. + // Finally, we add 1 to the result, as ranking results in a 1-indexed relation. + n = 1 + (([0 .. limit - 1] * (num_endpoints / limit).floor() + 46337) % num_endpoints) + ) +} + from Endpoint endpoint, string message, ApplicationModeMetadataExtractor meta, DollarAtString package, DollarAtString type, DollarAtString subtypes, DollarAtString name, DollarAtString signature, @@ -23,6 +58,7 @@ where not exists(CharacteristicsImpl::UninterestingToModelCharacteristic u | u.appliesToEndpoint(endpoint) ) and + endpoint = getSampleForSignature(9, package, type, subtypes, name, signature, input) and // If a node is already a known sink for any of our existing ATM queries and is already modeled as a MaD sink, we // don't include it as a candidate. Otherwise, we might include it as a candidate for query A, but the model will // label it as a sink for one of the sink types of query B, for which it's already a known sink. This would result in diff --git a/java/ql/src/Telemetry/ExternalApi.qll b/java/ql/src/Telemetry/ExternalApi.qll index ed8dc3fa3eb..5e9a2527c73 100644 --- a/java/ql/src/Telemetry/ExternalApi.qll +++ b/java/ql/src/Telemetry/ExternalApi.qll @@ -96,7 +96,7 @@ deprecated class ExternalAPI = ExternalApi; /** * Gets the limit for the number of results produced by a telemetry query. */ -int resultLimit() { result = 1000 } +int resultLimit() { result = 100 } /** * Holds if it is relevant to count usages of `api`. 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 deleted file mode 100644 index 588e83d4795..00000000000 --- a/java/ql/src/change-notes/2023-06-02-unsafe-deserialization-serialkiller.md +++ /dev/null @@ -1,4 +0,0 @@ ---- -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 deleted file mode 100644 index fa1343317ba..00000000000 --- a/java/ql/src/change-notes/2023-06-16-zipslip-rename.md +++ /dev/null @@ -1,4 +0,0 @@ ---- -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 deleted file mode 100644 index dc33878d2e5..00000000000 --- a/java/ql/src/change-notes/2023-06-23-apache-commons-lang.md +++ /dev/null @@ -1,4 +0,0 @@ ---- -category: minorAnalysis ---- -* New models have been added for `org.apache.commons.lang`. diff --git a/java/ql/src/change-notes/2023-07-10-unsafe-android-access-apply.md b/java/ql/src/change-notes/2023-07-10-unsafe-android-access-apply.md new file mode 100644 index 00000000000..ddb5f7c055c --- /dev/null +++ b/java/ql/src/change-notes/2023-07-10-unsafe-android-access-apply.md @@ -0,0 +1,4 @@ +--- +category: minorAnalysis +--- +* The query "Unsafe resource fetching in Android WebView" (`java/android/unsafe-android-webview-fetch`) now recognizes WebViews where `setJavascriptEnabled`, `setAllowFileAccess`, `setAllowUniversalAccessFromFileURLs`, and/or `setAllowFileAccessFromFileURLs` are set inside the function block of the Kotlin `apply` function. diff --git a/java/ql/src/change-notes/released/0.6.4.md b/java/ql/src/change-notes/released/0.6.4.md new file mode 100644 index 00000000000..7e98b0159fc --- /dev/null +++ b/java/ql/src/change-notes/released/0.6.4.md @@ -0,0 +1,3 @@ +## 0.6.4 + +No user-facing changes. diff --git a/java/ql/src/change-notes/released/0.7.0.md b/java/ql/src/change-notes/released/0.7.0.md new file mode 100644 index 00000000000..e12c2ef58fe --- /dev/null +++ b/java/ql/src/change-notes/released/0.7.0.md @@ -0,0 +1,10 @@ +## 0.7.0 + +### Minor Analysis Improvements + +* New models have been added for `org.apache.commons.lang`. +* The query `java/unsafe-deserialization` has been updated to take into account `SerialKiller`, a library used to prevent deserialization of arbitrary classes. + +### Bug Fixes + +* 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/codeql-pack.release.yml b/java/ql/src/codeql-pack.release.yml index b7dafe32c5d..c761f3e7ab4 100644 --- a/java/ql/src/codeql-pack.release.yml +++ b/java/ql/src/codeql-pack.release.yml @@ -1,2 +1,2 @@ --- -lastReleaseVersion: 0.6.3 +lastReleaseVersion: 0.7.0 diff --git a/java/ql/src/qlpack.yml b/java/ql/src/qlpack.yml index b75aea1c0a0..b769c24f224 100644 --- a/java/ql/src/qlpack.yml +++ b/java/ql/src/qlpack.yml @@ -1,5 +1,5 @@ name: codeql/java-queries -version: 0.6.4-dev +version: 0.7.1-dev groups: - java - queries diff --git a/java/ql/src/utils/modelgenerator/internal/CaptureModels.qll b/java/ql/src/utils/modelgenerator/internal/CaptureModels.qll index e2a0e130ca4..9c19e5b9cbb 100644 --- a/java/ql/src/utils/modelgenerator/internal/CaptureModels.qll +++ b/java/ql/src/utils/modelgenerator/internal/CaptureModels.qll @@ -160,8 +160,6 @@ module ThroughFlowConfig implements DataFlow::StateConfigSig { exists(Type t | t = n.getType() and not isRelevantType(t)) } - predicate isBarrier(DataFlow::Node node, FlowState state) { none() } - DataFlow::FlowFeature getAFeature() { result instanceof DataFlow::FeatureEqualSourceSinkCallContext } diff --git a/java/ql/test/kotlin/library-tests/annotation-accessor-result-type/test.ql b/java/ql/test/kotlin/library-tests/annotation-accessor-result-type/test.ql index 1f245499552..65576a7c19d 100644 --- a/java/ql/test/kotlin/library-tests/annotation-accessor-result-type/test.ql +++ b/java/ql/test/kotlin/library-tests/annotation-accessor-result-type/test.ql @@ -1,6 +1,7 @@ import java query predicate classExprs(Expr e, string tstr) { + exists(e.getFile().getRelativePath()) and tstr = e.getType().toString() and tstr.matches("%Class%") } diff --git a/java/ql/test/kotlin/library-tests/annotation_classes/PrintAst.expected b/java/ql/test/kotlin/library-tests/annotation_classes/PrintAst.expected index 0ff19b747fa..643c416b736 100644 --- a/java/ql/test/kotlin/library-tests/annotation_classes/PrintAst.expected +++ b/java/ql/test/kotlin/library-tests/annotation_classes/PrintAst.expected @@ -169,15 +169,18 @@ def.kt: # 33| 0: [SuperConstructorInvocationStmt] super(...) # 33| 1: [BlockStmt] { ... } # 34| 5: [Class] Y -# 0| 2: [Method] valueOf +# 0| 2: [Method] getEntries +# 0| 3: [TypeAccess] EnumEntries +# 0| 0: [TypeAccess] Y +# 0| 3: [Method] valueOf # 0| 3: [TypeAccess] Y #-----| 4: (Parameters) # 0| 0: [Parameter] value # 0| 0: [TypeAccess] String -# 0| 3: [Method] values +# 0| 4: [Method] values # 0| 3: [TypeAccess] Y[] # 0| 0: [TypeAccess] Y -# 34| 4: [Constructor] Y +# 34| 5: [Constructor] Y # 34| 5: [BlockStmt] { ... } # 34| 0: [ExprStmt] ; # 34| 0: [ClassInstanceExpr] new Enum(...) @@ -186,15 +189,15 @@ def.kt: # 34| 0: [NullLiteral] null # 34| 1: [IntegerLiteral] 0 # 34| 1: [BlockStmt] { ... } -# 35| 5: [FieldDeclaration] Y A; +# 35| 6: [FieldDeclaration] Y A; # 35| -1: [TypeAccess] Y # 35| 0: [ClassInstanceExpr] new Y(...) # 35| -3: [TypeAccess] Y -# 35| 6: [FieldDeclaration] Y B; +# 35| 7: [FieldDeclaration] Y B; # 35| -1: [TypeAccess] Y # 35| 0: [ClassInstanceExpr] new Y(...) # 35| -3: [TypeAccess] Y -# 35| 7: [FieldDeclaration] Y C; +# 35| 8: [FieldDeclaration] Y C; # 35| -1: [TypeAccess] Y # 35| 0: [ClassInstanceExpr] new Y(...) # 35| -3: [TypeAccess] Y diff --git a/java/ql/test/kotlin/library-tests/classes/PrintAst.expected b/java/ql/test/kotlin/library-tests/classes/PrintAst.expected index 6f19fb37fee..8da4a380a80 100644 --- a/java/ql/test/kotlin/library-tests/classes/PrintAst.expected +++ b/java/ql/test/kotlin/library-tests/classes/PrintAst.expected @@ -160,15 +160,18 @@ classes.kt: # 42| -1: [TypeAccess] int # 42| 0: [IntegerLiteral] 3 # 49| 11: [Class] Direction -# 0| 2: [Method] valueOf +# 0| 2: [Method] getEntries +# 0| 3: [TypeAccess] EnumEntries +# 0| 0: [TypeAccess] Direction +# 0| 3: [Method] valueOf # 0| 3: [TypeAccess] Direction #-----| 4: (Parameters) # 0| 0: [Parameter] value # 0| 0: [TypeAccess] String -# 0| 3: [Method] values +# 0| 4: [Method] values # 0| 3: [TypeAccess] Direction[] # 0| 0: [TypeAccess] Direction -# 49| 4: [Constructor] Direction +# 49| 5: [Constructor] Direction # 49| 5: [BlockStmt] { ... } # 49| 0: [ExprStmt] ; # 49| 0: [ClassInstanceExpr] new Enum(...) @@ -177,32 +180,35 @@ classes.kt: # 49| 0: [NullLiteral] null # 49| 1: [IntegerLiteral] 0 # 49| 1: [BlockStmt] { ... } -# 50| 5: [FieldDeclaration] Direction NORTH; +# 50| 6: [FieldDeclaration] Direction NORTH; # 50| -1: [TypeAccess] Direction # 50| 0: [ClassInstanceExpr] new Direction(...) # 50| -3: [TypeAccess] Direction -# 50| 6: [FieldDeclaration] Direction SOUTH; +# 50| 7: [FieldDeclaration] Direction SOUTH; # 50| -1: [TypeAccess] Direction # 50| 0: [ClassInstanceExpr] new Direction(...) # 50| -3: [TypeAccess] Direction -# 50| 7: [FieldDeclaration] Direction WEST; +# 50| 8: [FieldDeclaration] Direction WEST; # 50| -1: [TypeAccess] Direction # 50| 0: [ClassInstanceExpr] new Direction(...) # 50| -3: [TypeAccess] Direction -# 50| 8: [FieldDeclaration] Direction EAST; +# 50| 9: [FieldDeclaration] Direction EAST; # 50| -1: [TypeAccess] Direction # 50| 0: [ClassInstanceExpr] new Direction(...) # 50| -3: [TypeAccess] Direction # 53| 12: [Class] Color -# 0| 2: [Method] valueOf +# 0| 2: [Method] getEntries +# 0| 3: [TypeAccess] EnumEntries +# 0| 0: [TypeAccess] Color +# 0| 3: [Method] valueOf # 0| 3: [TypeAccess] Color #-----| 4: (Parameters) # 0| 0: [Parameter] value # 0| 0: [TypeAccess] String -# 0| 3: [Method] values +# 0| 4: [Method] values # 0| 3: [TypeAccess] Color[] # 0| 0: [TypeAccess] Color -# 53| 4: [Constructor] Color +# 53| 5: [Constructor] Color #-----| 4: (Parameters) # 53| 0: [Parameter] rgb # 53| 0: [TypeAccess] int @@ -217,26 +223,26 @@ classes.kt: # 53| 0: [ExprStmt] ; # 53| 0: [KtInitializerAssignExpr] ...=... # 53| 0: [VarAccess] rgb -# 53| 5: [Method] getRgb +# 53| 6: [Method] getRgb # 53| 3: [TypeAccess] int # 53| 5: [BlockStmt] { ... } # 53| 0: [ReturnStmt] return ... # 53| 0: [VarAccess] this.rgb # 53| -1: [ThisAccess] this -# 53| 6: [FieldDeclaration] int rgb; +# 53| 7: [FieldDeclaration] int rgb; # 53| -1: [TypeAccess] int # 53| 0: [VarAccess] rgb -# 54| 7: [FieldDeclaration] Color RED; +# 54| 8: [FieldDeclaration] Color RED; # 54| -1: [TypeAccess] Color # 54| 0: [ClassInstanceExpr] new Color(...) # 54| -3: [TypeAccess] Color # 54| 0: [IntegerLiteral] 16711680 -# 55| 8: [FieldDeclaration] Color GREEN; +# 55| 9: [FieldDeclaration] Color GREEN; # 55| -1: [TypeAccess] Color # 55| 0: [ClassInstanceExpr] new Color(...) # 55| -3: [TypeAccess] Color # 55| 0: [IntegerLiteral] 65280 -# 56| 9: [FieldDeclaration] Color BLUE; +# 56| 10: [FieldDeclaration] Color BLUE; # 56| -1: [TypeAccess] Color # 56| 0: [ClassInstanceExpr] new Color(...) # 56| -3: [TypeAccess] Color diff --git a/java/ql/test/kotlin/library-tests/dataflow/summaries/apply.expected b/java/ql/test/kotlin/library-tests/dataflow/summaries/apply.expected new file mode 100644 index 00000000000..62e8d2b0ff5 --- /dev/null +++ b/java/ql/test/kotlin/library-tests/dataflow/summaries/apply.expected @@ -0,0 +1,2 @@ +| apply.kt:6:9:6:41 | apply(...) | +| apply.kt:7:14:7:40 | apply(...) | diff --git a/java/ql/test/kotlin/library-tests/dataflow/summaries/apply.kt b/java/ql/test/kotlin/library-tests/dataflow/summaries/apply.kt new file mode 100644 index 00000000000..8d5373d081f --- /dev/null +++ b/java/ql/test/kotlin/library-tests/dataflow/summaries/apply.kt @@ -0,0 +1,9 @@ +class ApplyFlowTest { + fun taint(t: T) = t + fun sink(s: String) { } + + fun test(input: String) { + taint(input).apply { sink(this) } // $ hasValueFlow + sink(taint(input).apply { this }) // $ hasValueFlow + } +} diff --git a/java/ql/test/kotlin/library-tests/dataflow/summaries/apply.ql b/java/ql/test/kotlin/library-tests/dataflow/summaries/apply.ql new file mode 100644 index 00000000000..540cea703f0 --- /dev/null +++ b/java/ql/test/kotlin/library-tests/dataflow/summaries/apply.ql @@ -0,0 +1,5 @@ +import java +import semmle.code.java.frameworks.kotlin.Kotlin + +from KotlinApply a +select a diff --git a/java/ql/test/kotlin/library-tests/exprs/PrintAst.expected b/java/ql/test/kotlin/library-tests/exprs/PrintAst.expected index 3b4cc35d4d4..fbcea06528f 100644 --- a/java/ql/test/kotlin/library-tests/exprs/PrintAst.expected +++ b/java/ql/test/kotlin/library-tests/exprs/PrintAst.expected @@ -3344,15 +3344,18 @@ exprs.kt: # 154| 0: [SuperConstructorInvocationStmt] super(...) # 154| 1: [BlockStmt] { ... } # 174| 6: [Class] Direction -# 0| 2: [Method] valueOf +# 0| 2: [Method] getEntries +# 0| 3: [TypeAccess] EnumEntries +# 0| 0: [TypeAccess] Direction +# 0| 3: [Method] valueOf # 0| 3: [TypeAccess] Direction #-----| 4: (Parameters) # 0| 0: [Parameter] value # 0| 0: [TypeAccess] String -# 0| 3: [Method] values +# 0| 4: [Method] values # 0| 3: [TypeAccess] Direction[] # 0| 0: [TypeAccess] Direction -# 174| 4: [Constructor] Direction +# 174| 5: [Constructor] Direction # 174| 5: [BlockStmt] { ... } # 174| 0: [ExprStmt] ; # 174| 0: [ClassInstanceExpr] new Enum(...) @@ -3361,32 +3364,35 @@ exprs.kt: # 174| 0: [NullLiteral] null # 174| 1: [IntegerLiteral] 0 # 174| 1: [BlockStmt] { ... } -# 175| 5: [FieldDeclaration] Direction NORTH; +# 175| 6: [FieldDeclaration] Direction NORTH; # 175| -1: [TypeAccess] Direction # 175| 0: [ClassInstanceExpr] new Direction(...) # 175| -3: [TypeAccess] Direction -# 175| 6: [FieldDeclaration] Direction SOUTH; +# 175| 7: [FieldDeclaration] Direction SOUTH; # 175| -1: [TypeAccess] Direction # 175| 0: [ClassInstanceExpr] new Direction(...) # 175| -3: [TypeAccess] Direction -# 175| 7: [FieldDeclaration] Direction WEST; +# 175| 8: [FieldDeclaration] Direction WEST; # 175| -1: [TypeAccess] Direction # 175| 0: [ClassInstanceExpr] new Direction(...) # 175| -3: [TypeAccess] Direction -# 175| 8: [FieldDeclaration] Direction EAST; +# 175| 9: [FieldDeclaration] Direction EAST; # 175| -1: [TypeAccess] Direction # 175| 0: [ClassInstanceExpr] new Direction(...) # 175| -3: [TypeAccess] Direction # 178| 7: [Class] Color -# 0| 2: [Method] valueOf +# 0| 2: [Method] getEntries +# 0| 3: [TypeAccess] EnumEntries +# 0| 0: [TypeAccess] Color +# 0| 3: [Method] valueOf # 0| 3: [TypeAccess] Color #-----| 4: (Parameters) # 0| 0: [Parameter] value # 0| 0: [TypeAccess] String -# 0| 3: [Method] values +# 0| 4: [Method] values # 0| 3: [TypeAccess] Color[] # 0| 0: [TypeAccess] Color -# 178| 4: [Constructor] Color +# 178| 5: [Constructor] Color #-----| 4: (Parameters) # 178| 0: [Parameter] rgb # 178| 0: [TypeAccess] int @@ -3401,26 +3407,26 @@ exprs.kt: # 178| 0: [ExprStmt] ; # 178| 0: [KtInitializerAssignExpr] ...=... # 178| 0: [VarAccess] rgb -# 178| 5: [Method] getRgb +# 178| 6: [Method] getRgb # 178| 3: [TypeAccess] int # 178| 5: [BlockStmt] { ... } # 178| 0: [ReturnStmt] return ... # 178| 0: [VarAccess] this.rgb # 178| -1: [ThisAccess] this -# 178| 6: [FieldDeclaration] int rgb; +# 178| 7: [FieldDeclaration] int rgb; # 178| -1: [TypeAccess] int # 178| 0: [VarAccess] rgb -# 179| 7: [FieldDeclaration] Color RED; +# 179| 8: [FieldDeclaration] Color RED; # 179| -1: [TypeAccess] Color # 179| 0: [ClassInstanceExpr] new Color(...) # 179| -3: [TypeAccess] Color # 179| 0: [IntegerLiteral] 16711680 -# 180| 8: [FieldDeclaration] Color GREEN; +# 180| 9: [FieldDeclaration] Color GREEN; # 180| -1: [TypeAccess] Color # 180| 0: [ClassInstanceExpr] new Color(...) # 180| -3: [TypeAccess] Color # 180| 0: [IntegerLiteral] 65280 -# 181| 9: [FieldDeclaration] Color BLUE; +# 181| 10: [FieldDeclaration] Color BLUE; # 181| -1: [TypeAccess] Color # 181| 0: [ClassInstanceExpr] new Color(...) # 181| -3: [TypeAccess] Color diff --git a/java/ql/test/kotlin/library-tests/exprs/exprs.expected b/java/ql/test/kotlin/library-tests/exprs/exprs.expected index 07d57c444e4..69c4f096de9 100644 --- a/java/ql/test/kotlin/library-tests/exprs/exprs.expected +++ b/java/ql/test/kotlin/library-tests/exprs/exprs.expected @@ -885,10 +885,14 @@ | delegatedProperties.kt:87:34:87:46 | this | delegatedProperties.kt:87:34:87:46 | invoke | ThisAccess | | exprs.kt:0:0:0:0 | Color | file://:0:0:0:0 | | TypeAccess | | exprs.kt:0:0:0:0 | Color | file://:0:0:0:0 | | TypeAccess | +| exprs.kt:0:0:0:0 | Color | file://:0:0:0:0 | | TypeAccess | | exprs.kt:0:0:0:0 | Color[] | file://:0:0:0:0 | | TypeAccess | | exprs.kt:0:0:0:0 | Direction | file://:0:0:0:0 | | TypeAccess | | exprs.kt:0:0:0:0 | Direction | file://:0:0:0:0 | | TypeAccess | +| exprs.kt:0:0:0:0 | Direction | file://:0:0:0:0 | | TypeAccess | | exprs.kt:0:0:0:0 | Direction[] | file://:0:0:0:0 | | TypeAccess | +| exprs.kt:0:0:0:0 | EnumEntries | file://:0:0:0:0 | | TypeAccess | +| exprs.kt:0:0:0:0 | EnumEntries | file://:0:0:0:0 | | TypeAccess | | exprs.kt:0:0:0:0 | String | file://:0:0:0:0 | | TypeAccess | | exprs.kt:0:0:0:0 | String | file://:0:0:0:0 | | TypeAccess | | exprs.kt:4:1:142:1 | int | file://:0:0:0:0 | | TypeAccess | diff --git a/java/ql/test/kotlin/library-tests/exprs_typeaccess/PrintAst.expected b/java/ql/test/kotlin/library-tests/exprs_typeaccess/PrintAst.expected index 5af83a46a01..56a295cfaf7 100644 --- a/java/ql/test/kotlin/library-tests/exprs_typeaccess/PrintAst.expected +++ b/java/ql/test/kotlin/library-tests/exprs_typeaccess/PrintAst.expected @@ -74,15 +74,18 @@ A.kt: # 20| 0: [VarAccess] B.x # 20| -1: [TypeAccess] B # 23| 11: [Class] Enu -# 0| 2: [Method] valueOf +# 0| 2: [Method] getEntries +# 0| 3: [TypeAccess] EnumEntries +# 0| 0: [TypeAccess] Enu +# 0| 3: [Method] valueOf # 0| 3: [TypeAccess] Enu #-----| 4: (Parameters) # 0| 0: [Parameter] value # 0| 0: [TypeAccess] String -# 0| 3: [Method] values +# 0| 4: [Method] values # 0| 3: [TypeAccess] Enu[] # 0| 0: [TypeAccess] Enu -# 23| 4: [Constructor] Enu +# 23| 5: [Constructor] Enu # 23| 5: [BlockStmt] { ... } # 23| 0: [ExprStmt] ; # 23| 0: [ClassInstanceExpr] new Enum(...) @@ -91,15 +94,15 @@ A.kt: # 23| 0: [NullLiteral] null # 23| 1: [IntegerLiteral] 0 # 23| 1: [BlockStmt] { ... } -# 24| 5: [FieldDeclaration] Enu A; +# 24| 6: [FieldDeclaration] Enu A; # 24| -1: [TypeAccess] Enu # 24| 0: [ClassInstanceExpr] new Enu(...) # 24| -3: [TypeAccess] Enu -# 24| 6: [FieldDeclaration] Enu B; +# 24| 7: [FieldDeclaration] Enu B; # 24| -1: [TypeAccess] Enu # 24| 0: [ClassInstanceExpr] new Enu(...) # 24| -3: [TypeAccess] Enu -# 24| 7: [FieldDeclaration] Enu C; +# 24| 8: [FieldDeclaration] Enu C; # 24| -1: [TypeAccess] Enu # 24| 0: [ClassInstanceExpr] new Enu(...) # 24| -3: [TypeAccess] Enu diff --git a/java/ql/test/kotlin/library-tests/methods/exprs.expected b/java/ql/test/kotlin/library-tests/methods/exprs.expected index 5de17a12bf9..ed0a9fbd6d8 100644 --- a/java/ql/test/kotlin/library-tests/methods/exprs.expected +++ b/java/ql/test/kotlin/library-tests/methods/exprs.expected @@ -225,7 +225,11 @@ | delegates.kt:10:33:10:35 | new | VarAccess | | enumClass.kt:0:0:0:0 | EnumClass | TypeAccess | | enumClass.kt:0:0:0:0 | EnumClass | TypeAccess | +| enumClass.kt:0:0:0:0 | EnumClass | TypeAccess | | enumClass.kt:0:0:0:0 | EnumClass[] | TypeAccess | +| enumClass.kt:0:0:0:0 | EnumEntries | TypeAccess | +| enumClass.kt:0:0:0:0 | EnumEntries | TypeAccess | +| enumClass.kt:0:0:0:0 | EnumWithFunctions | TypeAccess | | enumClass.kt:0:0:0:0 | EnumWithFunctions | TypeAccess | | enumClass.kt:0:0:0:0 | EnumWithFunctions | TypeAccess | | enumClass.kt:0:0:0:0 | EnumWithFunctions[] | TypeAccess | diff --git a/java/ql/test/kotlin/library-tests/methods/methods.expected b/java/ql/test/kotlin/library-tests/methods/methods.expected index 89cdd03f303..e254e862090 100644 --- a/java/ql/test/kotlin/library-tests/methods/methods.expected +++ b/java/ql/test/kotlin/library-tests/methods/methods.expected @@ -26,10 +26,12 @@ methods | delegates.kt:8:32:11:5 | new KMutableProperty1(...) { ... } | delegates.kt:8:32:11:5 | set | set(MyClass,java.lang.String) | override, public | | | delegates.kt:8:66:11:5 | new Function3,String,String,Unit>(...) { ... } | delegates.kt:8:66:11:5 | invoke | invoke(kotlin.reflect.KProperty,java.lang.String,java.lang.String) | final, override, public | | | enumClass.kt:1:1:4:1 | EnumClass | enumClass.kt:0:0:0:0 | | () | static | Compiler generated | +| enumClass.kt:1:1:4:1 | EnumClass | enumClass.kt:0:0:0:0 | getEntries | getEntries() | final, public, static | Compiler generated | | enumClass.kt:1:1:4:1 | EnumClass | enumClass.kt:0:0:0:0 | valueOf | valueOf(java.lang.String) | final, public, static | Compiler generated | | enumClass.kt:1:1:4:1 | EnumClass | enumClass.kt:0:0:0:0 | values | values() | final, public, static | Compiler generated | | enumClass.kt:1:1:4:1 | EnumClass | enumClass.kt:1:22:1:31 | getV | getV() | final, public | Compiler generated | | enumClass.kt:6:1:16:1 | EnumWithFunctions | enumClass.kt:0:0:0:0 | | () | static | Compiler generated | +| enumClass.kt:6:1:16:1 | EnumWithFunctions | enumClass.kt:0:0:0:0 | getEntries | getEntries() | final, public, static | Compiler generated | | enumClass.kt:6:1:16:1 | EnumWithFunctions | enumClass.kt:0:0:0:0 | valueOf | valueOf(java.lang.String) | final, public, static | Compiler generated | | enumClass.kt:6:1:16:1 | EnumWithFunctions | enumClass.kt:0:0:0:0 | values | values() | final, public, static | Compiler generated | | enumClass.kt:6:1:16:1 | EnumWithFunctions | enumClass.kt:13:12:13:29 | f | f(int) | abstract, public | | diff --git a/java/ql/test/library-tests/file_classes/A.kt b/java/ql/test/library-tests/file_classes/A.kt new file mode 100644 index 00000000000..8ece2422c02 --- /dev/null +++ b/java/ql/test/library-tests/file_classes/A.kt @@ -0,0 +1,2 @@ +fun a() { +} diff --git a/java/ql/test/library-tests/file_classes/B.kt b/java/ql/test/library-tests/file_classes/B.kt new file mode 100644 index 00000000000..0f1fb9d6fd8 --- /dev/null +++ b/java/ql/test/library-tests/file_classes/B.kt @@ -0,0 +1,3 @@ +fun b() { + a() +} diff --git a/java/ql/test/library-tests/file_classes/C.kt b/java/ql/test/library-tests/file_classes/C.kt new file mode 100644 index 00000000000..2b433c8f2f6 --- /dev/null +++ b/java/ql/test/library-tests/file_classes/C.kt @@ -0,0 +1,3 @@ +class C { + fun c() {} +} diff --git a/java/ql/test/library-tests/file_classes/classes.expected b/java/ql/test/library-tests/file_classes/classes.expected new file mode 100644 index 00000000000..ea6fece1c4c --- /dev/null +++ b/java/ql/test/library-tests/file_classes/classes.expected @@ -0,0 +1,3 @@ +| A.kt:0:0:0:0 | AKt | true | +| B.kt:0:0:0:0 | BKt | true | +| C.kt:1:1:3:1 | C | false | diff --git a/java/ql/test/library-tests/file_classes/classes.ql b/java/ql/test/library-tests/file_classes/classes.ql new file mode 100644 index 00000000000..9f78c7f3b0f --- /dev/null +++ b/java/ql/test/library-tests/file_classes/classes.ql @@ -0,0 +1,5 @@ +import java + +from Class c +where c.fromSource() +select c, any(boolean b | if c.isFileClass() then b = true else b = false) diff --git a/java/ql/test/query-tests/security/CWE-749/UnsafeActivityKt.kt b/java/ql/test/query-tests/security/CWE-749/UnsafeActivityKt.kt index d20845f5c77..32004071c3c 100644 --- a/java/ql/test/query-tests/security/CWE-749/UnsafeActivityKt.kt +++ b/java/ql/test/query-tests/security/CWE-749/UnsafeActivityKt.kt @@ -9,12 +9,19 @@ import android.webkit.WebViewClient class UnsafeActivityKt : Activity() { override fun onCreate(savedInstanceState : Bundle) { + val src : String = intent.extras.getString("url") + val wv = findViewById(-1) // Implicit not-nulls happening here wv.settings.setJavaScriptEnabled(true) wv.settings.setAllowFileAccessFromFileURLs(true) - val thisUrl : String = intent.extras.getString("url") - wv.loadUrl(thisUrl) // $ hasUnsafeAndroidAccess + wv.loadUrl(src) // $ hasUnsafeAndroidAccess + + val wv2 = findViewById(-1) + wv2.apply { + settings.setJavaScriptEnabled(true) + } + wv2.loadUrl(src) // $ hasUnsafeAndroidAccess } } diff --git a/javascript/ql/experimental/adaptivethreatmodeling/lib/experimental/adaptivethreatmodeling/XssATM.qll b/javascript/ql/experimental/adaptivethreatmodeling/lib/experimental/adaptivethreatmodeling/XssATM.qll index 5daac270292..eff490b638d 100644 --- a/javascript/ql/experimental/adaptivethreatmodeling/lib/experimental/adaptivethreatmodeling/XssATM.qll +++ b/javascript/ql/experimental/adaptivethreatmodeling/lib/experimental/adaptivethreatmodeling/XssATM.qll @@ -23,7 +23,8 @@ class DomBasedXssAtmConfig extends AtmConfig { override predicate isSanitizer(DataFlow::Node node) { super.isSanitizer(node) or - node instanceof DomBasedXss::Sanitizer + node instanceof DomBasedXss::Sanitizer or + DomBasedXss::isOptionallySanitizedNode(node) } override predicate isSanitizerGuard(TaintTracking::SanitizerGuardNode guard) { @@ -31,10 +32,6 @@ class DomBasedXssAtmConfig extends AtmConfig { guard instanceof QuoteGuard or guard instanceof ContainsHtmlGuard } - - override predicate isSanitizerEdge(DataFlow::Node pred, DataFlow::Node succ) { - DomBasedXss::isOptionallySanitizedEdge(pred, succ) - } } private import semmle.javascript.security.dataflow.Xss::Shared as Shared diff --git a/javascript/ql/experimental/adaptivethreatmodeling/lib/experimental/adaptivethreatmodeling/XssThroughDomATM.qll b/javascript/ql/experimental/adaptivethreatmodeling/lib/experimental/adaptivethreatmodeling/XssThroughDomATM.qll index e188da15a7e..0eeba5d23ad 100644 --- a/javascript/ql/experimental/adaptivethreatmodeling/lib/experimental/adaptivethreatmodeling/XssThroughDomATM.qll +++ b/javascript/ql/experimental/adaptivethreatmodeling/lib/experimental/adaptivethreatmodeling/XssThroughDomATM.qll @@ -23,7 +23,8 @@ class XssThroughDomAtmConfig extends AtmConfig { override predicate isSanitizer(DataFlow::Node node) { super.isSanitizer(node) or - node instanceof DomBasedXss::Sanitizer + node instanceof DomBasedXss::Sanitizer or + DomBasedXss::isOptionallySanitizedNode(node) } override predicate isSanitizerGuard(TaintTracking::SanitizerGuardNode guard) { @@ -34,10 +35,6 @@ class XssThroughDomAtmConfig extends AtmConfig { guard instanceof QuoteGuard or guard instanceof ContainsHtmlGuard } - - override predicate isSanitizerEdge(DataFlow::Node pred, DataFlow::Node succ) { - DomBasedXss::isOptionallySanitizedEdge(pred, succ) - } } /** diff --git a/javascript/ql/lib/CHANGELOG.md b/javascript/ql/lib/CHANGELOG.md index 47c4130c3af..14cbfac4141 100644 --- a/javascript/ql/lib/CHANGELOG.md +++ b/javascript/ql/lib/CHANGELOG.md @@ -1,3 +1,13 @@ +## 0.7.0 + +### Minor Analysis Improvements + +* Added models for the Webix Framework. + +## 0.6.4 + +No user-facing changes. + ## 0.6.3 ### Major Analysis Improvements diff --git a/javascript/ql/lib/change-notes/released/0.6.4.md b/javascript/ql/lib/change-notes/released/0.6.4.md new file mode 100644 index 00000000000..7e98b0159fc --- /dev/null +++ b/javascript/ql/lib/change-notes/released/0.6.4.md @@ -0,0 +1,3 @@ +## 0.6.4 + +No user-facing changes. diff --git a/javascript/ql/lib/change-notes/released/0.7.0.md b/javascript/ql/lib/change-notes/released/0.7.0.md new file mode 100644 index 00000000000..3c2c9c44d8e --- /dev/null +++ b/javascript/ql/lib/change-notes/released/0.7.0.md @@ -0,0 +1,5 @@ +## 0.7.0 + +### Minor Analysis Improvements + +* Added models for the Webix Framework. diff --git a/javascript/ql/lib/codeql-pack.release.yml b/javascript/ql/lib/codeql-pack.release.yml index b7dafe32c5d..c761f3e7ab4 100644 --- a/javascript/ql/lib/codeql-pack.release.yml +++ b/javascript/ql/lib/codeql-pack.release.yml @@ -1,2 +1,2 @@ --- -lastReleaseVersion: 0.6.3 +lastReleaseVersion: 0.7.0 diff --git a/javascript/ql/lib/javascript.qll b/javascript/ql/lib/javascript.qll index ed38db6550e..07fb759bd65 100644 --- a/javascript/ql/lib/javascript.qll +++ b/javascript/ql/lib/javascript.qll @@ -134,6 +134,7 @@ import semmle.javascript.frameworks.TrustedTypes import semmle.javascript.frameworks.UriLibraries import semmle.javascript.frameworks.Vue import semmle.javascript.frameworks.Vuex +import semmle.javascript.frameworks.Webix import semmle.javascript.frameworks.WebSocket import semmle.javascript.frameworks.XmlParsers import semmle.javascript.frameworks.xUnit diff --git a/javascript/ql/lib/qlpack.yml b/javascript/ql/lib/qlpack.yml index bd3f17d627f..71ebe601c88 100644 --- a/javascript/ql/lib/qlpack.yml +++ b/javascript/ql/lib/qlpack.yml @@ -1,5 +1,5 @@ name: codeql/javascript-all -version: 0.6.4-dev +version: 0.7.1-dev groups: javascript dbscheme: semmlecode.javascript.dbscheme extractor: javascript diff --git a/javascript/ql/lib/semmle/javascript/Extend.qll b/javascript/ql/lib/semmle/javascript/Extend.qll index 3b389691434..d0eeade5892 100644 --- a/javascript/ql/lib/semmle/javascript/Extend.qll +++ b/javascript/ql/lib/semmle/javascript/Extend.qll @@ -96,7 +96,10 @@ private class ExtendCallDeep extends ExtendCall { callee = LodashUnderscore::member("merge") or callee = LodashUnderscore::member("mergeWith") or callee = LodashUnderscore::member("defaultsDeep") or - callee = AngularJS::angular().getAPropertyRead("merge") + callee = AngularJS::angular().getAPropertyRead("merge") or + callee = + [DataFlow::moduleImport("webix"), DataFlow::globalVarRef("webix")] + .getAPropertyRead(["extend", "copy"]) ) } diff --git a/javascript/ql/lib/semmle/javascript/InclusionTests.qll b/javascript/ql/lib/semmle/javascript/InclusionTests.qll index 849376374ad..3d9921bf6ff 100644 --- a/javascript/ql/lib/semmle/javascript/InclusionTests.qll +++ b/javascript/ql/lib/semmle/javascript/InclusionTests.qll @@ -69,7 +69,6 @@ module InclusionTest { inner.getContainerNode().getALocalSource() = DataFlow::parameterNode(callee.getAParameter()) } - pragma[assume_small_delta] override DataFlow::Node getContainerNode() { exists(int arg | inner.getContainerNode().getALocalSource() = @@ -78,7 +77,6 @@ module InclusionTest { ) } - pragma[assume_small_delta] override DataFlow::Node getContainedNode() { exists(int arg | inner.getContainedNode().getALocalSource() = diff --git a/javascript/ql/lib/semmle/javascript/StringOps.qll b/javascript/ql/lib/semmle/javascript/StringOps.qll index da751b550d7..6b7820e964d 100644 --- a/javascript/ql/lib/semmle/javascript/StringOps.qll +++ b/javascript/ql/lib/semmle/javascript/StringOps.qll @@ -67,7 +67,6 @@ module StringOps { inner.getSubstring().getALocalSource().getEnclosingExpr() = callee.getAParameter() } - pragma[assume_small_delta] override DataFlow::Node getBaseString() { exists(int arg | inner.getBaseString().getALocalSource().getEnclosingExpr() = callee.getParameter(arg) and @@ -75,7 +74,6 @@ module StringOps { ) } - pragma[assume_small_delta] override DataFlow::Node getSubstring() { exists(int arg | inner.getSubstring().getALocalSource().getEnclosingExpr() = callee.getParameter(arg) and @@ -294,7 +292,6 @@ module StringOps { inner.getSubstring().getALocalSource().getEnclosingExpr() = callee.getAParameter() } - pragma[assume_small_delta] override DataFlow::Node getBaseString() { exists(int arg | inner.getBaseString().getALocalSource().getEnclosingExpr() = callee.getParameter(arg) and @@ -302,7 +299,6 @@ module StringOps { ) } - pragma[assume_small_delta] override DataFlow::Node getSubstring() { exists(int arg | inner.getSubstring().getALocalSource().getEnclosingExpr() = callee.getParameter(arg) and diff --git a/javascript/ql/lib/semmle/javascript/dataflow/Configuration.qll b/javascript/ql/lib/semmle/javascript/dataflow/Configuration.qll index 29c50fca302..179e1b4dfe5 100644 --- a/javascript/ql/lib/semmle/javascript/dataflow/Configuration.qll +++ b/javascript/ql/lib/semmle/javascript/dataflow/Configuration.qll @@ -166,6 +166,26 @@ abstract class Configuration extends string { ) } + /** + * Holds if flow into `node` is prohibited. + */ + predicate isBarrierIn(DataFlow::Node node) { none() } + + /** + * Holds if flow out `node` is prohibited. + */ + predicate isBarrierOut(DataFlow::Node node) { none() } + + /** + * Holds if flow into `node` is prohibited for the flow label `lbl`. + */ + predicate isBarrierIn(DataFlow::Node node, FlowLabel lbl) { none() } + + /** + * Holds if flow out `node` is prohibited for the flow label `lbl`. + */ + predicate isBarrierOut(DataFlow::Node node, FlowLabel lbl) { none() } + /** * Holds if flow from `pred` to `succ` is prohibited. */ @@ -494,7 +514,7 @@ private BasicBlock getADominatedBasicBlock(BarrierGuardNode guard, ConditionGuar * * Only holds for barriers that should apply to all flow labels. */ -private predicate isBarrierEdge(Configuration cfg, DataFlow::Node pred, DataFlow::Node succ) { +private predicate isBarrierEdgeRaw(Configuration cfg, DataFlow::Node pred, DataFlow::Node succ) { cfg.isBarrierEdge(pred, succ) or exists(DataFlow::BarrierGuardNode guard | @@ -503,11 +523,26 @@ private predicate isBarrierEdge(Configuration cfg, DataFlow::Node pred, DataFlow ) } +/** + * Holds if there is a barrier edge `pred -> succ` in `cfg` either through an explicit barrier edge + * or one implied by a barrier guard, or by an out/in barrier for `pred` or `succ`, respectively. + * + * Only holds for barriers that should apply to all flow labels. + */ +pragma[inline] +private predicate isBarrierEdge(Configuration cfg, DataFlow::Node pred, DataFlow::Node succ) { + isBarrierEdgeRaw(cfg, pred, succ) + or + cfg.isBarrierOut(pred) + or + cfg.isBarrierIn(succ) +} + /** * Holds if there is a labeled barrier edge `pred -> succ` in `cfg` either through an explicit barrier edge * or one implied by a barrier guard. */ -private predicate isLabeledBarrierEdge( +private predicate isLabeledBarrierEdgeRaw( Configuration cfg, DataFlow::Node pred, DataFlow::Node succ, DataFlow::FlowLabel label ) { cfg.isBarrierEdge(pred, succ, label) @@ -518,6 +553,21 @@ private predicate isLabeledBarrierEdge( ) } +/** + * Holds if there is a labeled barrier edge `pred -> succ` in `cfg` either through an explicit barrier edge + * or one implied by a barrier guard, or by an out/in barrier for `pred` or `succ`, respectively. + */ +pragma[inline] +private predicate isLabeledBarrierEdge( + Configuration cfg, DataFlow::Node pred, DataFlow::Node succ, DataFlow::FlowLabel label +) { + isLabeledBarrierEdgeRaw(cfg, pred, succ, label) + or + cfg.isBarrierOut(pred, label) + or + cfg.isBarrierIn(succ, label) +} + /** * A guard node that only blocks specific labels. */ diff --git a/javascript/ql/lib/semmle/javascript/dataflow/TaintTracking.qll b/javascript/ql/lib/semmle/javascript/dataflow/TaintTracking.qll index 96d97a270c6..11ce802ac72 100644 --- a/javascript/ql/lib/semmle/javascript/dataflow/TaintTracking.qll +++ b/javascript/ql/lib/semmle/javascript/dataflow/TaintTracking.qll @@ -62,6 +62,26 @@ module TaintTracking { */ predicate isSanitizer(DataFlow::Node node) { none() } + /** + * Holds if flow into `node` is prohibited. + */ + predicate isSanitizerIn(DataFlow::Node node) { none() } + + /** + * Holds if flow out `node` is prohibited. + */ + predicate isSanitizerOut(DataFlow::Node node) { none() } + + /** + * Holds if flow into `node` is prohibited for the flow label `lbl`. + */ + predicate isSanitizerIn(DataFlow::Node node, DataFlow::FlowLabel lbl) { none() } + + /** + * Holds if flow out `node` is prohibited for the flow label `lbl`. + */ + predicate isSanitizerOut(DataFlow::Node node, DataFlow::FlowLabel lbl) { none() } + /** Holds if the edge from `pred` to `succ` is a taint sanitizer. */ predicate isSanitizerEdge(DataFlow::Node pred, DataFlow::Node succ) { none() } @@ -108,6 +128,22 @@ module TaintTracking { this.isSanitizerEdge(source, sink) and lbl.isTaint() } + final override predicate isBarrierIn(DataFlow::Node node) { none() } + + final override predicate isBarrierOut(DataFlow::Node node) { none() } + + final override predicate isBarrierIn(DataFlow::Node node, DataFlow::FlowLabel lbl) { + this.isSanitizerIn(node, lbl) + or + this.isSanitizerIn(node) and lbl.isTaint() + } + + final override predicate isBarrierOut(DataFlow::Node node, DataFlow::FlowLabel lbl) { + this.isSanitizerOut(node, lbl) + or + this.isSanitizerOut(node) and lbl.isTaint() + } + final override predicate isBarrierGuard(DataFlow::BarrierGuardNode guard) { super.isBarrierGuard(guard) or guard.(AdditionalSanitizerGuardNode).appliesTo(this) or diff --git a/javascript/ql/lib/semmle/javascript/frameworks/NodeJSLib.qll b/javascript/ql/lib/semmle/javascript/frameworks/NodeJSLib.qll index 9ca15b1872f..258a583e1ca 100644 --- a/javascript/ql/lib/semmle/javascript/frameworks/NodeJSLib.qll +++ b/javascript/ql/lib/semmle/javascript/frameworks/NodeJSLib.qll @@ -554,7 +554,11 @@ module NodeJSLib { t.start() or t.start() and - result = DataFlow::moduleMember("fs", "promises") + ( + result = DataFlow::moduleMember("fs", "promises") + or + result = DataFlow::moduleImport("fs/promises") + ) or exists(DataFlow::TypeTracker t2, DataFlow::SourceNode pred | pred = fsModule(t2) | result = pred.track(t2, t) diff --git a/javascript/ql/lib/semmle/javascript/frameworks/Webix.qll b/javascript/ql/lib/semmle/javascript/frameworks/Webix.qll new file mode 100644 index 00000000000..effd49c632b --- /dev/null +++ b/javascript/ql/lib/semmle/javascript/frameworks/Webix.qll @@ -0,0 +1,23 @@ +/** + * Provides classes and predicates for working with the `webix` library. + */ + +private import javascript + +/** + * Provides classes and predicates for working with the `webix` library. + */ +module Webix { + /** The global variable `webix` as an entry point for API graphs. */ + private class WebixGlobalEntry extends API::EntryPoint { + WebixGlobalEntry() { this = "WebixGlobalEntry" } + + override DataFlow::SourceNode getASource() { result = DataFlow::globalVarRef("webix") } + } + + /** Gets a reference to the Webix package. */ + API::Node webix() { + result = API::moduleImport("webix") or + result = any(WebixGlobalEntry w).getANode() + } +} diff --git a/javascript/ql/lib/semmle/javascript/security/IncompleteMultiCharacterSanitizationQuery.qll b/javascript/ql/lib/semmle/javascript/security/IncompleteMultiCharacterSanitizationQuery.qll index 10f07b39725..2a2d4d4d601 100644 --- a/javascript/ql/lib/semmle/javascript/security/IncompleteMultiCharacterSanitizationQuery.qll +++ b/javascript/ql/lib/semmle/javascript/security/IncompleteMultiCharacterSanitizationQuery.qll @@ -15,6 +15,14 @@ private class DangerousPrefix extends string { this = " - + diff --git a/javascript/ql/src/Security/CWE-078/IndirectCommandInjection.qhelp b/javascript/ql/src/Security/CWE-078/IndirectCommandInjection.qhelp index 41898a1d097..83ddaef0814 100644 --- a/javascript/ql/src/Security/CWE-078/IndirectCommandInjection.qhelp +++ b/javascript/ql/src/Security/CWE-078/IndirectCommandInjection.qhelp @@ -27,27 +27,25 @@

    - If possible, use hard-coded string literals to specify the - command to run or library to load. Instead of forwarding the - command-line arguments to the process, examine the command-line - arguments and then choose among hard-coded string literals. + If possible, use APIs that don't run shell commands and accept + command arguments as an array of strings rather than a single + concatenated string. This is both safer and more portable.

    - If the applicable libraries or commands cannot be determined - at compile time, then add code to verify that each forwarded - command-line argument is properly escaped before using it. +If given arguments as a single string, avoid simply splitting the string on +whitespace. Arguments may contain quoted whitespace, causing them to split into +multiple arguments. Use a library like shell-quote to parse the string +into an array of arguments instead.

    - If the forwarded command-line arguments are part of the - arguments of the system command, prefer a library routine that handles - the arguments as an array of strings rather than a single concatenated - string. This prevents the unexpected evaluation of special characters. + If this approach is not viable, then add code to verify that each + forwarded command-line argument is properly escaped before using it.

    @@ -91,6 +89,17 @@ +

    + + If you want to allow the user to specify other options to + node, you can use a library like + shell-quote to parse the user input into an array of + arguments without risking command injection: + +

    + + + @@ -100,6 +109,11 @@ Command Injection. +
  • + npm: + shell-quote. +
  • +
    diff --git a/javascript/ql/src/Security/CWE-078/UnsafeShellCommandConstruction.qhelp b/javascript/ql/src/Security/CWE-078/UnsafeShellCommandConstruction.qhelp index 5017db6119d..e2829cf85e0 100644 --- a/javascript/ql/src/Security/CWE-078/UnsafeShellCommandConstruction.qhelp +++ b/javascript/ql/src/Security/CWE-078/UnsafeShellCommandConstruction.qhelp @@ -27,9 +27,22 @@

    - Alternatively, if the shell command must be constructed - dynamically, then add code to ensure that special characters - do not alter the shell command unexpectedly. + + If given arguments as a single string, avoid simply splitting the string + on whitespace. Arguments may contain quoted whitespace, causing them to + split into multiple arguments. Use a library like + shell-quote to parse the string into an array of arguments + instead. + +

    + +

    + + Alternatively, if the command must be interpreted by a shell (for + example because it includes I/O redirections), you can use + shell-quote to escape any special characters in the input + before embedding it in the command. +

    @@ -63,6 +76,27 @@ +

    + + As another example, consider the following code which is similar to the + preceding example, but pipes the output of wget into wc -l + to count the number of lines in the downloaded file. + +

    + + + +

    + + In this case, using child_process.execFile is not an option + because the shell is needed to interpret the pipe operator. Instead, you + can use shell-quote to escape the input before embedding it + in the command: + +

    + + + @@ -71,5 +105,10 @@ Command Injection. +
  • + npm: + shell-quote. +
  • +
    diff --git a/javascript/ql/src/Security/CWE-078/examples/command-injection.js b/javascript/ql/src/Security/CWE-078/examples/command-injection.js index e88e7571043..60031ab18e3 100644 --- a/javascript/ql/src/Security/CWE-078/examples/command-injection.js +++ b/javascript/ql/src/Security/CWE-078/examples/command-injection.js @@ -3,7 +3,7 @@ var cp = require("child_process"), url = require('url'); var server = http.createServer(function(req, res) { - let cmd = url.parse(req.url, true).query.path; + let file = url.parse(req.url, true).query.path; - cp.exec(cmd); // BAD + cp.execSync(`wc -l ${file}`); // BAD }); diff --git a/javascript/ql/src/Security/CWE-078/examples/command-injection_allowlist.js b/javascript/ql/src/Security/CWE-078/examples/command-injection_allowlist.js new file mode 100644 index 00000000000..96573c4840f --- /dev/null +++ b/javascript/ql/src/Security/CWE-078/examples/command-injection_allowlist.js @@ -0,0 +1,12 @@ +var cp = require("child_process"), + http = require('http'), + url = require('url'); + +var server = http.createServer(function(req, res) { + let file = url.parse(req.url, true).query.path; + + // only allow safe characters in file name + if (file.match(/^[\w\.\-\/]+$/)) { + cp.execSync(`wc -l ${file}`); // GOOD + } +}); diff --git a/javascript/ql/src/Security/CWE-078/examples/command-injection_fixed.js b/javascript/ql/src/Security/CWE-078/examples/command-injection_fixed.js new file mode 100644 index 00000000000..17011e6e662 --- /dev/null +++ b/javascript/ql/src/Security/CWE-078/examples/command-injection_fixed.js @@ -0,0 +1,9 @@ +var cp = require("child_process"), + http = require('http'), + url = require('url'); + +var server = http.createServer(function(req, res) { + let file = url.parse(req.url, true).query.path; + + cp.execFileSync('wc', ['-l', file]); // GOOD +}); diff --git a/javascript/ql/src/Security/CWE-078/examples/command-injection_shellquote.js b/javascript/ql/src/Security/CWE-078/examples/command-injection_shellquote.js new file mode 100644 index 00000000000..ba566e89d41 --- /dev/null +++ b/javascript/ql/src/Security/CWE-078/examples/command-injection_shellquote.js @@ -0,0 +1,10 @@ +var cp = require("child_process"), + http = require('http'), + url = require('url'), + shellQuote = require('shell-quote'); + +var server = http.createServer(function(req, res) { + let options = url.parse(req.url, true).query.options; + + cp.execFileSync('wc', shellQuote.parse(options)); // GOOD +}); diff --git a/javascript/ql/src/Security/CWE-078/examples/indirect-command-injection.js b/javascript/ql/src/Security/CWE-078/examples/indirect-command-injection.js index bb14b35f363..3926ea284ce 100644 --- a/javascript/ql/src/Security/CWE-078/examples/indirect-command-injection.js +++ b/javascript/ql/src/Security/CWE-078/examples/indirect-command-injection.js @@ -2,4 +2,4 @@ var cp = require("child_process"); const args = process.argv.slice(2); const script = path.join(__dirname, 'bin', 'main.js'); -cp.execSync(`node ${script} ${args.join(' ')}"`); // BAD +cp.execSync(`node ${script} ${args.join(' ')}`); // BAD diff --git a/javascript/ql/src/Security/CWE-078/examples/indirect-command-injection_shellquote.js b/javascript/ql/src/Security/CWE-078/examples/indirect-command-injection_shellquote.js new file mode 100644 index 00000000000..58dd322e412 --- /dev/null +++ b/javascript/ql/src/Security/CWE-078/examples/indirect-command-injection_shellquote.js @@ -0,0 +1,11 @@ +var cp = require("child_process"), + shellQuote = require("shell-quote"); + +const args = process.argv.slice(2); +let nodeOpts = ''; +if (args[0] === '--node-opts') { + nodeOpts = args[1]; + args.splice(0, 2); +} +const script = path.join(__dirname, 'bin', 'main.js'); +cp.execFileSync('node', shellQuote.parse(nodeOpts).concat(script).concat(args)); // GOOD diff --git a/javascript/ql/src/Security/CWE-078/examples/unsafe-shell-command-construction.js b/javascript/ql/src/Security/CWE-078/examples/unsafe-shell-command-construction.js index f8f3d8b7514..d2d1869746f 100644 --- a/javascript/ql/src/Security/CWE-078/examples/unsafe-shell-command-construction.js +++ b/javascript/ql/src/Security/CWE-078/examples/unsafe-shell-command-construction.js @@ -1,5 +1,5 @@ var cp = require("child_process"); module.exports = function download(path, callback) { - cp.execSync("wget " + path, callback); + cp.exec("wget " + path, callback); } diff --git a/javascript/ql/src/Security/CWE-078/examples/unsafe-shell-command-construction_fixed.js b/javascript/ql/src/Security/CWE-078/examples/unsafe-shell-command-construction_fixed.js index 4a8c880ad8f..9f6bb249adc 100644 --- a/javascript/ql/src/Security/CWE-078/examples/unsafe-shell-command-construction_fixed.js +++ b/javascript/ql/src/Security/CWE-078/examples/unsafe-shell-command-construction_fixed.js @@ -1,5 +1,5 @@ var cp = require("child_process"); module.exports = function download(path, callback) { - cp.execFileSync("wget", [path], callback); + cp.execFile("wget", [path], callback); } diff --git a/javascript/ql/src/Security/CWE-078/examples/unsafe-shell-command-construction_pipe.js b/javascript/ql/src/Security/CWE-078/examples/unsafe-shell-command-construction_pipe.js new file mode 100644 index 00000000000..3ebf2e615a9 --- /dev/null +++ b/javascript/ql/src/Security/CWE-078/examples/unsafe-shell-command-construction_pipe.js @@ -0,0 +1,5 @@ +var cp = require("child_process"); + +module.exports = function download(path, callback) { + cp.exec("wget " + path + " | wc -l", callback); +}; diff --git a/javascript/ql/src/Security/CWE-078/examples/unsafe-shell-command-construction_pipe_fixed.js b/javascript/ql/src/Security/CWE-078/examples/unsafe-shell-command-construction_pipe_fixed.js new file mode 100644 index 00000000000..147f10ae78b --- /dev/null +++ b/javascript/ql/src/Security/CWE-078/examples/unsafe-shell-command-construction_pipe_fixed.js @@ -0,0 +1,5 @@ +var cp = require("child_process"); + +module.exports = function download(path, callback) { + cp.exec("wget " + shellQuote.quote([path]) + " | wc -l", callback); +}; diff --git a/javascript/ql/src/change-notes/2023-07-10-path-join-spread.md b/javascript/ql/src/change-notes/2023-07-10-path-join-spread.md new file mode 100644 index 00000000000..cf3b82fbbf7 --- /dev/null +++ b/javascript/ql/src/change-notes/2023-07-10-path-join-spread.md @@ -0,0 +1,5 @@ +--- +category: minorAnalysis +--- +* The `fs/promises` package is now recognised as an alias for `require('fs').promises`. +* The `js/path-injection` query can now track taint through calls to `path.join()` with a spread argument, such as `path.join(baseDir, ...args)`. diff --git a/javascript/ql/src/change-notes/released/0.6.4.md b/javascript/ql/src/change-notes/released/0.6.4.md new file mode 100644 index 00000000000..7e98b0159fc --- /dev/null +++ b/javascript/ql/src/change-notes/released/0.6.4.md @@ -0,0 +1,3 @@ +## 0.6.4 + +No user-facing changes. diff --git a/javascript/ql/src/change-notes/2023-06-16-zipslip-rename.md b/javascript/ql/src/change-notes/released/0.7.0.md similarity index 87% rename from javascript/ql/src/change-notes/2023-06-16-zipslip-rename.md rename to javascript/ql/src/change-notes/released/0.7.0.md index 3a0654e642e..96e0a1f9894 100644 --- a/javascript/ql/src/change-notes/2023-06-16-zipslip-rename.md +++ b/javascript/ql/src/change-notes/released/0.7.0.md @@ -1,4 +1,5 @@ ---- -category: fix ---- +## 0.7.0 + +### Bug Fixes + * 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/codeql-pack.release.yml b/javascript/ql/src/codeql-pack.release.yml index b7dafe32c5d..c761f3e7ab4 100644 --- a/javascript/ql/src/codeql-pack.release.yml +++ b/javascript/ql/src/codeql-pack.release.yml @@ -1,2 +1,2 @@ --- -lastReleaseVersion: 0.6.3 +lastReleaseVersion: 0.7.0 diff --git a/javascript/ql/src/experimental/Security/CWE-918/SSRF.qll b/javascript/ql/src/experimental/Security/CWE-918/SSRF.qll index c43418d453a..95d46aad868 100644 --- a/javascript/ql/src/experimental/Security/CWE-918/SSRF.qll +++ b/javascript/ql/src/experimental/Security/CWE-918/SSRF.qll @@ -29,8 +29,8 @@ class Configuration extends TaintTracking::Configuration { ) } - override predicate isSanitizerEdge(DataFlow::Node source, DataFlow::Node sink) { - this.strictSanitizingPrefixEdge(source, sink) + override predicate isSanitizerOut(DataFlow::Node node) { + this.strictSanitizingPrefixEdge(node, _) } override predicate isSanitizerGuard(TaintTracking::SanitizerGuardNode nd) { diff --git a/javascript/ql/src/qlpack.yml b/javascript/ql/src/qlpack.yml index a1a1ed2b4f2..7aae317b438 100644 --- a/javascript/ql/src/qlpack.yml +++ b/javascript/ql/src/qlpack.yml @@ -1,5 +1,5 @@ name: codeql/javascript-queries -version: 0.6.4-dev +version: 0.7.1-dev groups: - javascript - queries diff --git a/javascript/ql/test/library-tests/InterProceduralFlow/DataFlowConfig.qll b/javascript/ql/test/library-tests/InterProceduralFlow/DataFlowConfig.qll index eccf834a0e1..12edfc8b713 100644 --- a/javascript/ql/test/library-tests/InterProceduralFlow/DataFlowConfig.qll +++ b/javascript/ql/test/library-tests/InterProceduralFlow/DataFlowConfig.qll @@ -22,10 +22,7 @@ class TestDataFlowConfiguration extends DataFlow::Configuration { f.getName().matches("%noReturnTracking%") and node = f.getAReturnedExpr().flow() ) - } - - override predicate isBarrierEdge(DataFlow::Node src, DataFlow::Node snk) { - src = src and - snk.asExpr().(PropAccess).getPropertyName() = "notTracked" + or + node.asExpr().(PropAccess).getPropertyName() = "notTracked" } } diff --git a/javascript/ql/test/library-tests/InterProceduralFlow/tests.ql b/javascript/ql/test/library-tests/InterProceduralFlow/tests.ql index a6733749676..a490c4c9146 100644 --- a/javascript/ql/test/library-tests/InterProceduralFlow/tests.ql +++ b/javascript/ql/test/library-tests/InterProceduralFlow/tests.ql @@ -61,11 +61,8 @@ class TestTaintTrackingConfiguration extends TaintTracking::Configuration { f.getName().matches("%noReturnTracking%") and node = f.getAReturnedExpr().flow() ) - } - - override predicate isSanitizerEdge(DataFlow::Node src, DataFlow::Node snk) { - src = src and - snk.asExpr().(PropAccess).getPropertyName() = "notTracked" + or + node.asExpr().(PropAccess).getPropertyName() = "notTracked" } } @@ -99,11 +96,8 @@ class GermanFlowConfig extends DataFlow::Configuration { f.getName().matches("%noReturnTracking%") and node = f.getAReturnedExpr().flow() ) - } - - override predicate isBarrierEdge(DataFlow::Node src, DataFlow::Node snk) { - src = src and - snk.asExpr().(PropAccess).getPropertyName() = "notTracked" + or + node.asExpr().(PropAccess).getPropertyName() = "notTracked" } } diff --git a/javascript/ql/test/library-tests/TaintBarriers/ExampleConfiguration.qll b/javascript/ql/test/library-tests/TaintBarriers/ExampleConfiguration.qll index f730dd81891..50ac0fbfd24 100644 --- a/javascript/ql/test/library-tests/TaintBarriers/ExampleConfiguration.qll +++ b/javascript/ql/test/library-tests/TaintBarriers/ExampleConfiguration.qll @@ -1,10 +1,18 @@ import javascript +DataFlow::Node sourceVariable() { result.asExpr().(VarRef).getName() = "sourceVariable" } + +StringOps::ConcatenationRoot sinkConcatenation() { + result.getConstantStringParts().matches("%") +} + class ExampleConfiguration extends TaintTracking::Configuration { ExampleConfiguration() { this = "ExampleConfiguration" } override predicate isSource(DataFlow::Node source) { source.asExpr().(CallExpr).getCalleeName() = "SOURCE" + or + source = sourceVariable() } override predicate isSink(DataFlow::Node sink) { @@ -12,8 +20,14 @@ class ExampleConfiguration extends TaintTracking::Configuration { callExpr.getCalleeName() = "SINK" and DataFlow::valueNode(callExpr.getArgument(0)) = sink ) + or + sink = sinkConcatenation() } + override predicate isSanitizerIn(DataFlow::Node node) { node = sourceVariable() } + + override predicate isSanitizerOut(DataFlow::Node node) { node = sinkConcatenation() } + override predicate isSanitizer(DataFlow::Node node) { exists(CallExpr callExpr | callExpr.getCalleeName() = "SANITIZE" and diff --git a/javascript/ql/test/library-tests/TaintBarriers/sanitizer-in-out.js b/javascript/ql/test/library-tests/TaintBarriers/sanitizer-in-out.js new file mode 100644 index 00000000000..244d3899856 --- /dev/null +++ b/javascript/ql/test/library-tests/TaintBarriers/sanitizer-in-out.js @@ -0,0 +1,18 @@ +import 'dummy'; + +function barrierIn() { + var sourceVariable = 123; + SINK(sourceVariable); // NOT OK + + flowWithSourceParam(sourceVariable); +} + +function barrierInParameter(sourceVariable) { + SINK(sourceVariable); // NOT OK, but only report the parameter as the source +} + +function barrierOut() { + let taint = SOURCE(); + taint = "" + taint + ""; // NOT OK + taint = "" + taint + ""; // OK - only report first instance +} diff --git a/javascript/ql/test/library-tests/TaintBarriers/tests.expected b/javascript/ql/test/library-tests/TaintBarriers/tests.expected index 3ee1223b87d..4417a918423 100644 --- a/javascript/ql/test/library-tests/TaintBarriers/tests.expected +++ b/javascript/ql/test/library-tests/TaintBarriers/tests.expected @@ -133,6 +133,9 @@ sanitizingGuard | tst.js:399:16:399:41 | o.hasOw ... "p.q"]) | tst.js:399:33:399:40 | v["p.q"] | true | | tst.js:401:16:401:34 | Object.hasOwn(o, v) | tst.js:401:33:401:33 | v | true | taintedSink +| sanitizer-in-out.js:5:10:5:23 | sourceVariable | sanitizer-in-out.js:5:10:5:23 | sourceVariable | +| sanitizer-in-out.js:11:10:11:23 | sourceVariable | sanitizer-in-out.js:11:10:11:23 | sourceVariable | +| sanitizer-in-out.js:15:17:15:24 | SOURCE() | sanitizer-in-out.js:16:13:16:40 | " ... /sink>" | | tst.js:2:13:2:20 | SOURCE() | tst.js:3:10:3:10 | v | | tst.js:2:13:2:20 | SOURCE() | tst.js:8:14:8:14 | v | | tst.js:2:13:2:20 | SOURCE() | tst.js:12:14:12:14 | v | diff --git a/javascript/ql/test/query-tests/Security/CWE-022/TaintedPath/TaintedPath.expected b/javascript/ql/test/query-tests/Security/CWE-022/TaintedPath/TaintedPath.expected index b41071a3989..2d1692dce00 100644 --- a/javascript/ql/test/query-tests/Security/CWE-022/TaintedPath/TaintedPath.expected +++ b/javascript/ql/test/query-tests/Security/CWE-022/TaintedPath/TaintedPath.expected @@ -2163,6 +2163,28 @@ nodes | normalizedPaths.js:399:21:399:24 | path | | normalizedPaths.js:399:21:399:24 | path | | normalizedPaths.js:399:21:399:24 | path | +| normalizedPaths.js:407:19:407:67 | pathMod ... t('/')) | +| normalizedPaths.js:407:19:407:67 | pathMod ... t('/')) | +| normalizedPaths.js:407:19:407:67 | pathMod ... t('/')) | +| normalizedPaths.js:407:19:407:67 | pathMod ... t('/')) | +| normalizedPaths.js:407:45:407:55 | req.query.x | +| normalizedPaths.js:407:45:407:55 | req.query.x | +| normalizedPaths.js:407:45:407:55 | req.query.x | +| normalizedPaths.js:407:45:407:55 | req.query.x | +| normalizedPaths.js:407:45:407:66 | req.que ... it('/') | +| normalizedPaths.js:407:45:407:66 | req.que ... it('/') | +| normalizedPaths.js:407:45:407:66 | req.que ... it('/') | +| normalizedPaths.js:408:19:408:60 | pathMod ... t('/')) | +| normalizedPaths.js:408:19:408:60 | pathMod ... t('/')) | +| normalizedPaths.js:408:19:408:60 | pathMod ... t('/')) | +| normalizedPaths.js:408:19:408:60 | pathMod ... t('/')) | +| normalizedPaths.js:408:38:408:48 | req.query.x | +| normalizedPaths.js:408:38:408:48 | req.query.x | +| normalizedPaths.js:408:38:408:48 | req.query.x | +| normalizedPaths.js:408:38:408:48 | req.query.x | +| normalizedPaths.js:408:38:408:59 | req.que ... it('/') | +| normalizedPaths.js:408:38:408:59 | req.que ... it('/') | +| normalizedPaths.js:408:38:408:59 | req.que ... it('/') | | other-fs-libraries.js:9:7:9:48 | path | | other-fs-libraries.js:9:7:9:48 | path | | other-fs-libraries.js:9:7:9:48 | path | @@ -2813,6 +2835,92 @@ nodes | other-fs-libraries.js:72:15:72:18 | path | | other-fs-libraries.js:72:15:72:18 | path | | other-fs-libraries.js:72:15:72:18 | path | +| other-fs-libraries.js:77:7:77:48 | path | +| other-fs-libraries.js:77:7:77:48 | path | +| other-fs-libraries.js:77:7:77:48 | path | +| other-fs-libraries.js:77:7:77:48 | path | +| other-fs-libraries.js:77:7:77:48 | path | +| other-fs-libraries.js:77:7:77:48 | path | +| other-fs-libraries.js:77:7:77:48 | path | +| other-fs-libraries.js:77:7:77:48 | path | +| other-fs-libraries.js:77:7:77:48 | path | +| other-fs-libraries.js:77:7:77:48 | path | +| other-fs-libraries.js:77:7:77:48 | path | +| other-fs-libraries.js:77:7:77:48 | path | +| other-fs-libraries.js:77:7:77:48 | path | +| other-fs-libraries.js:77:7:77:48 | path | +| other-fs-libraries.js:77:7:77:48 | path | +| other-fs-libraries.js:77:7:77:48 | path | +| other-fs-libraries.js:77:14:77:37 | url.par ... , true) | +| other-fs-libraries.js:77:14:77:37 | url.par ... , true) | +| other-fs-libraries.js:77:14:77:37 | url.par ... , true) | +| other-fs-libraries.js:77:14:77:37 | url.par ... , true) | +| other-fs-libraries.js:77:14:77:37 | url.par ... , true) | +| other-fs-libraries.js:77:14:77:37 | url.par ... , true) | +| other-fs-libraries.js:77:14:77:37 | url.par ... , true) | +| other-fs-libraries.js:77:14:77:37 | url.par ... , true) | +| other-fs-libraries.js:77:14:77:37 | url.par ... , true) | +| other-fs-libraries.js:77:14:77:37 | url.par ... , true) | +| other-fs-libraries.js:77:14:77:37 | url.par ... , true) | +| other-fs-libraries.js:77:14:77:37 | url.par ... , true) | +| other-fs-libraries.js:77:14:77:37 | url.par ... , true) | +| other-fs-libraries.js:77:14:77:37 | url.par ... , true) | +| other-fs-libraries.js:77:14:77:37 | url.par ... , true) | +| other-fs-libraries.js:77:14:77:37 | url.par ... , true) | +| other-fs-libraries.js:77:14:77:43 | url.par ... ).query | +| other-fs-libraries.js:77:14:77:43 | url.par ... ).query | +| other-fs-libraries.js:77:14:77:43 | url.par ... ).query | +| other-fs-libraries.js:77:14:77:43 | url.par ... ).query | +| other-fs-libraries.js:77:14:77:43 | url.par ... ).query | +| other-fs-libraries.js:77:14:77:43 | url.par ... ).query | +| other-fs-libraries.js:77:14:77:43 | url.par ... ).query | +| other-fs-libraries.js:77:14:77:43 | url.par ... ).query | +| other-fs-libraries.js:77:14:77:43 | url.par ... ).query | +| other-fs-libraries.js:77:14:77:43 | url.par ... ).query | +| other-fs-libraries.js:77:14:77:43 | url.par ... ).query | +| other-fs-libraries.js:77:14:77:43 | url.par ... ).query | +| other-fs-libraries.js:77:14:77:43 | url.par ... ).query | +| other-fs-libraries.js:77:14:77:43 | url.par ... ).query | +| other-fs-libraries.js:77:14:77:43 | url.par ... ).query | +| other-fs-libraries.js:77:14:77:43 | url.par ... ).query | +| other-fs-libraries.js:77:14:77:48 | url.par ... ry.path | +| other-fs-libraries.js:77:14:77:48 | url.par ... ry.path | +| other-fs-libraries.js:77:14:77:48 | url.par ... ry.path | +| other-fs-libraries.js:77:14:77:48 | url.par ... ry.path | +| other-fs-libraries.js:77:14:77:48 | url.par ... ry.path | +| other-fs-libraries.js:77:14:77:48 | url.par ... ry.path | +| other-fs-libraries.js:77:14:77:48 | url.par ... ry.path | +| other-fs-libraries.js:77:14:77:48 | url.par ... ry.path | +| other-fs-libraries.js:77:14:77:48 | url.par ... ry.path | +| other-fs-libraries.js:77:14:77:48 | url.par ... ry.path | +| other-fs-libraries.js:77:14:77:48 | url.par ... ry.path | +| other-fs-libraries.js:77:14:77:48 | url.par ... ry.path | +| other-fs-libraries.js:77:14:77:48 | url.par ... ry.path | +| other-fs-libraries.js:77:14:77:48 | url.par ... ry.path | +| other-fs-libraries.js:77:14:77:48 | url.par ... ry.path | +| other-fs-libraries.js:77:14:77:48 | url.par ... ry.path | +| other-fs-libraries.js:77:24:77:30 | req.url | +| other-fs-libraries.js:77:24:77:30 | req.url | +| other-fs-libraries.js:77:24:77:30 | req.url | +| other-fs-libraries.js:77:24:77:30 | req.url | +| other-fs-libraries.js:77:24:77:30 | req.url | +| other-fs-libraries.js:79:16:79:19 | path | +| other-fs-libraries.js:79:16:79:19 | path | +| other-fs-libraries.js:79:16:79:19 | path | +| other-fs-libraries.js:79:16:79:19 | path | +| other-fs-libraries.js:79:16:79:19 | path | +| other-fs-libraries.js:79:16:79:19 | path | +| other-fs-libraries.js:79:16:79:19 | path | +| other-fs-libraries.js:79:16:79:19 | path | +| other-fs-libraries.js:79:16:79:19 | path | +| other-fs-libraries.js:79:16:79:19 | path | +| other-fs-libraries.js:79:16:79:19 | path | +| other-fs-libraries.js:79:16:79:19 | path | +| other-fs-libraries.js:79:16:79:19 | path | +| other-fs-libraries.js:79:16:79:19 | path | +| other-fs-libraries.js:79:16:79:19 | path | +| other-fs-libraries.js:79:16:79:19 | path | +| other-fs-libraries.js:79:16:79:19 | path | | prettier.js:6:11:6:28 | p | | prettier.js:6:11:6:28 | p | | prettier.js:6:11:6:28 | p | @@ -7264,6 +7372,30 @@ edges | normalizedPaths.js:385:35:385:45 | req.query.x | normalizedPaths.js:385:14:385:46 | pathMod ... uery.x) | | normalizedPaths.js:385:35:385:45 | req.query.x | normalizedPaths.js:385:14:385:46 | pathMod ... uery.x) | | normalizedPaths.js:385:35:385:45 | req.query.x | normalizedPaths.js:385:14:385:46 | pathMod ... uery.x) | +| normalizedPaths.js:407:45:407:55 | req.query.x | normalizedPaths.js:407:45:407:66 | req.que ... it('/') | +| normalizedPaths.js:407:45:407:55 | req.query.x | normalizedPaths.js:407:45:407:66 | req.que ... it('/') | +| normalizedPaths.js:407:45:407:55 | req.query.x | normalizedPaths.js:407:45:407:66 | req.que ... it('/') | +| normalizedPaths.js:407:45:407:55 | req.query.x | normalizedPaths.js:407:45:407:66 | req.que ... it('/') | +| normalizedPaths.js:407:45:407:55 | req.query.x | normalizedPaths.js:407:45:407:66 | req.que ... it('/') | +| normalizedPaths.js:407:45:407:55 | req.query.x | normalizedPaths.js:407:45:407:66 | req.que ... it('/') | +| normalizedPaths.js:407:45:407:66 | req.que ... it('/') | normalizedPaths.js:407:19:407:67 | pathMod ... t('/')) | +| normalizedPaths.js:407:45:407:66 | req.que ... it('/') | normalizedPaths.js:407:19:407:67 | pathMod ... t('/')) | +| normalizedPaths.js:407:45:407:66 | req.que ... it('/') | normalizedPaths.js:407:19:407:67 | pathMod ... t('/')) | +| normalizedPaths.js:407:45:407:66 | req.que ... it('/') | normalizedPaths.js:407:19:407:67 | pathMod ... t('/')) | +| normalizedPaths.js:407:45:407:66 | req.que ... it('/') | normalizedPaths.js:407:19:407:67 | pathMod ... t('/')) | +| normalizedPaths.js:407:45:407:66 | req.que ... it('/') | normalizedPaths.js:407:19:407:67 | pathMod ... t('/')) | +| normalizedPaths.js:408:38:408:48 | req.query.x | normalizedPaths.js:408:38:408:59 | req.que ... it('/') | +| normalizedPaths.js:408:38:408:48 | req.query.x | normalizedPaths.js:408:38:408:59 | req.que ... it('/') | +| normalizedPaths.js:408:38:408:48 | req.query.x | normalizedPaths.js:408:38:408:59 | req.que ... it('/') | +| normalizedPaths.js:408:38:408:48 | req.query.x | normalizedPaths.js:408:38:408:59 | req.que ... it('/') | +| normalizedPaths.js:408:38:408:48 | req.query.x | normalizedPaths.js:408:38:408:59 | req.que ... it('/') | +| normalizedPaths.js:408:38:408:48 | req.query.x | normalizedPaths.js:408:38:408:59 | req.que ... it('/') | +| normalizedPaths.js:408:38:408:59 | req.que ... it('/') | normalizedPaths.js:408:19:408:60 | pathMod ... t('/')) | +| normalizedPaths.js:408:38:408:59 | req.que ... it('/') | normalizedPaths.js:408:19:408:60 | pathMod ... t('/')) | +| normalizedPaths.js:408:38:408:59 | req.que ... it('/') | normalizedPaths.js:408:19:408:60 | pathMod ... t('/')) | +| normalizedPaths.js:408:38:408:59 | req.que ... it('/') | normalizedPaths.js:408:19:408:60 | pathMod ... t('/')) | +| normalizedPaths.js:408:38:408:59 | req.que ... it('/') | normalizedPaths.js:408:19:408:60 | pathMod ... t('/')) | +| normalizedPaths.js:408:38:408:59 | req.que ... it('/') | normalizedPaths.js:408:19:408:60 | pathMod ... t('/')) | | other-fs-libraries.js:9:7:9:48 | path | other-fs-libraries.js:11:19:11:22 | path | | other-fs-libraries.js:9:7:9:48 | path | other-fs-libraries.js:11:19:11:22 | path | | other-fs-libraries.js:9:7:9:48 | path | other-fs-libraries.js:11:19:11:22 | path | @@ -8288,6 +8420,118 @@ edges | other-fs-libraries.js:68:24:68:30 | req.url | other-fs-libraries.js:68:14:68:37 | url.par ... , true) | | other-fs-libraries.js:68:24:68:30 | req.url | other-fs-libraries.js:68:14:68:37 | url.par ... , true) | | other-fs-libraries.js:68:24:68:30 | req.url | other-fs-libraries.js:68:14:68:37 | url.par ... , true) | +| other-fs-libraries.js:77:7:77:48 | path | other-fs-libraries.js:79:16:79:19 | path | +| other-fs-libraries.js:77:7:77:48 | path | other-fs-libraries.js:79:16:79:19 | path | +| other-fs-libraries.js:77:7:77:48 | path | other-fs-libraries.js:79:16:79:19 | path | +| other-fs-libraries.js:77:7:77:48 | path | other-fs-libraries.js:79:16:79:19 | path | +| other-fs-libraries.js:77:7:77:48 | path | other-fs-libraries.js:79:16:79:19 | path | +| other-fs-libraries.js:77:7:77:48 | path | other-fs-libraries.js:79:16:79:19 | path | +| other-fs-libraries.js:77:7:77:48 | path | other-fs-libraries.js:79:16:79:19 | path | +| other-fs-libraries.js:77:7:77:48 | path | other-fs-libraries.js:79:16:79:19 | path | +| other-fs-libraries.js:77:7:77:48 | path | other-fs-libraries.js:79:16:79:19 | path | +| other-fs-libraries.js:77:7:77:48 | path | other-fs-libraries.js:79:16:79:19 | path | +| other-fs-libraries.js:77:7:77:48 | path | other-fs-libraries.js:79:16:79:19 | path | +| other-fs-libraries.js:77:7:77:48 | path | other-fs-libraries.js:79:16:79:19 | path | +| other-fs-libraries.js:77:7:77:48 | path | other-fs-libraries.js:79:16:79:19 | path | +| other-fs-libraries.js:77:7:77:48 | path | other-fs-libraries.js:79:16:79:19 | path | +| other-fs-libraries.js:77:7:77:48 | path | other-fs-libraries.js:79:16:79:19 | path | +| other-fs-libraries.js:77:7:77:48 | path | other-fs-libraries.js:79:16:79:19 | path | +| other-fs-libraries.js:77:7:77:48 | path | other-fs-libraries.js:79:16:79:19 | path | +| other-fs-libraries.js:77:7:77:48 | path | other-fs-libraries.js:79:16:79:19 | path | +| other-fs-libraries.js:77:7:77:48 | path | other-fs-libraries.js:79:16:79:19 | path | +| other-fs-libraries.js:77:7:77:48 | path | other-fs-libraries.js:79:16:79:19 | path | +| other-fs-libraries.js:77:7:77:48 | path | other-fs-libraries.js:79:16:79:19 | path | +| other-fs-libraries.js:77:7:77:48 | path | other-fs-libraries.js:79:16:79:19 | path | +| other-fs-libraries.js:77:7:77:48 | path | other-fs-libraries.js:79:16:79:19 | path | +| other-fs-libraries.js:77:7:77:48 | path | other-fs-libraries.js:79:16:79:19 | path | +| other-fs-libraries.js:77:7:77:48 | path | other-fs-libraries.js:79:16:79:19 | path | +| other-fs-libraries.js:77:7:77:48 | path | other-fs-libraries.js:79:16:79:19 | path | +| other-fs-libraries.js:77:7:77:48 | path | other-fs-libraries.js:79:16:79:19 | path | +| other-fs-libraries.js:77:7:77:48 | path | other-fs-libraries.js:79:16:79:19 | path | +| other-fs-libraries.js:77:7:77:48 | path | other-fs-libraries.js:79:16:79:19 | path | +| other-fs-libraries.js:77:7:77:48 | path | other-fs-libraries.js:79:16:79:19 | path | +| other-fs-libraries.js:77:7:77:48 | path | other-fs-libraries.js:79:16:79:19 | path | +| other-fs-libraries.js:77:7:77:48 | path | other-fs-libraries.js:79:16:79:19 | path | +| other-fs-libraries.js:77:14:77:37 | url.par ... , true) | other-fs-libraries.js:77:14:77:43 | url.par ... ).query | +| other-fs-libraries.js:77:14:77:37 | url.par ... , true) | other-fs-libraries.js:77:14:77:43 | url.par ... ).query | +| other-fs-libraries.js:77:14:77:37 | url.par ... , true) | other-fs-libraries.js:77:14:77:43 | url.par ... ).query | +| other-fs-libraries.js:77:14:77:37 | url.par ... , true) | other-fs-libraries.js:77:14:77:43 | url.par ... ).query | +| other-fs-libraries.js:77:14:77:37 | url.par ... , true) | other-fs-libraries.js:77:14:77:43 | url.par ... ).query | +| other-fs-libraries.js:77:14:77:37 | url.par ... , true) | other-fs-libraries.js:77:14:77:43 | url.par ... ).query | +| other-fs-libraries.js:77:14:77:37 | url.par ... , true) | other-fs-libraries.js:77:14:77:43 | url.par ... ).query | +| other-fs-libraries.js:77:14:77:37 | url.par ... , true) | other-fs-libraries.js:77:14:77:43 | url.par ... ).query | +| other-fs-libraries.js:77:14:77:37 | url.par ... , true) | other-fs-libraries.js:77:14:77:43 | url.par ... ).query | +| other-fs-libraries.js:77:14:77:37 | url.par ... , true) | other-fs-libraries.js:77:14:77:43 | url.par ... ).query | +| other-fs-libraries.js:77:14:77:37 | url.par ... , true) | other-fs-libraries.js:77:14:77:43 | url.par ... ).query | +| other-fs-libraries.js:77:14:77:37 | url.par ... , true) | other-fs-libraries.js:77:14:77:43 | url.par ... ).query | +| other-fs-libraries.js:77:14:77:37 | url.par ... , true) | other-fs-libraries.js:77:14:77:43 | url.par ... ).query | +| other-fs-libraries.js:77:14:77:37 | url.par ... , true) | other-fs-libraries.js:77:14:77:43 | url.par ... ).query | +| other-fs-libraries.js:77:14:77:37 | url.par ... , true) | other-fs-libraries.js:77:14:77:43 | url.par ... ).query | +| other-fs-libraries.js:77:14:77:37 | url.par ... , true) | other-fs-libraries.js:77:14:77:43 | url.par ... ).query | +| other-fs-libraries.js:77:14:77:43 | url.par ... ).query | other-fs-libraries.js:77:14:77:48 | url.par ... ry.path | +| other-fs-libraries.js:77:14:77:43 | url.par ... ).query | other-fs-libraries.js:77:14:77:48 | url.par ... ry.path | +| other-fs-libraries.js:77:14:77:43 | url.par ... ).query | other-fs-libraries.js:77:14:77:48 | url.par ... ry.path | +| other-fs-libraries.js:77:14:77:43 | url.par ... ).query | other-fs-libraries.js:77:14:77:48 | url.par ... ry.path | +| other-fs-libraries.js:77:14:77:43 | url.par ... ).query | other-fs-libraries.js:77:14:77:48 | url.par ... ry.path | +| other-fs-libraries.js:77:14:77:43 | url.par ... ).query | other-fs-libraries.js:77:14:77:48 | url.par ... ry.path | +| other-fs-libraries.js:77:14:77:43 | url.par ... ).query | other-fs-libraries.js:77:14:77:48 | url.par ... ry.path | +| other-fs-libraries.js:77:14:77:43 | url.par ... ).query | other-fs-libraries.js:77:14:77:48 | url.par ... ry.path | +| other-fs-libraries.js:77:14:77:43 | url.par ... ).query | other-fs-libraries.js:77:14:77:48 | url.par ... ry.path | +| other-fs-libraries.js:77:14:77:43 | url.par ... ).query | other-fs-libraries.js:77:14:77:48 | url.par ... ry.path | +| other-fs-libraries.js:77:14:77:43 | url.par ... ).query | other-fs-libraries.js:77:14:77:48 | url.par ... ry.path | +| other-fs-libraries.js:77:14:77:43 | url.par ... ).query | other-fs-libraries.js:77:14:77:48 | url.par ... ry.path | +| other-fs-libraries.js:77:14:77:43 | url.par ... ).query | other-fs-libraries.js:77:14:77:48 | url.par ... ry.path | +| other-fs-libraries.js:77:14:77:43 | url.par ... ).query | other-fs-libraries.js:77:14:77:48 | url.par ... ry.path | +| other-fs-libraries.js:77:14:77:43 | url.par ... ).query | other-fs-libraries.js:77:14:77:48 | url.par ... ry.path | +| other-fs-libraries.js:77:14:77:43 | url.par ... ).query | other-fs-libraries.js:77:14:77:48 | url.par ... ry.path | +| other-fs-libraries.js:77:14:77:48 | url.par ... ry.path | other-fs-libraries.js:77:7:77:48 | path | +| other-fs-libraries.js:77:14:77:48 | url.par ... ry.path | other-fs-libraries.js:77:7:77:48 | path | +| other-fs-libraries.js:77:14:77:48 | url.par ... ry.path | other-fs-libraries.js:77:7:77:48 | path | +| other-fs-libraries.js:77:14:77:48 | url.par ... ry.path | other-fs-libraries.js:77:7:77:48 | path | +| other-fs-libraries.js:77:14:77:48 | url.par ... ry.path | other-fs-libraries.js:77:7:77:48 | path | +| other-fs-libraries.js:77:14:77:48 | url.par ... ry.path | other-fs-libraries.js:77:7:77:48 | path | +| other-fs-libraries.js:77:14:77:48 | url.par ... ry.path | other-fs-libraries.js:77:7:77:48 | path | +| other-fs-libraries.js:77:14:77:48 | url.par ... ry.path | other-fs-libraries.js:77:7:77:48 | path | +| other-fs-libraries.js:77:14:77:48 | url.par ... ry.path | other-fs-libraries.js:77:7:77:48 | path | +| other-fs-libraries.js:77:14:77:48 | url.par ... ry.path | other-fs-libraries.js:77:7:77:48 | path | +| other-fs-libraries.js:77:14:77:48 | url.par ... ry.path | other-fs-libraries.js:77:7:77:48 | path | +| other-fs-libraries.js:77:14:77:48 | url.par ... ry.path | other-fs-libraries.js:77:7:77:48 | path | +| other-fs-libraries.js:77:14:77:48 | url.par ... ry.path | other-fs-libraries.js:77:7:77:48 | path | +| other-fs-libraries.js:77:14:77:48 | url.par ... ry.path | other-fs-libraries.js:77:7:77:48 | path | +| other-fs-libraries.js:77:14:77:48 | url.par ... ry.path | other-fs-libraries.js:77:7:77:48 | path | +| other-fs-libraries.js:77:14:77:48 | url.par ... ry.path | other-fs-libraries.js:77:7:77:48 | path | +| other-fs-libraries.js:77:24:77:30 | req.url | other-fs-libraries.js:77:14:77:37 | url.par ... , true) | +| other-fs-libraries.js:77:24:77:30 | req.url | other-fs-libraries.js:77:14:77:37 | url.par ... , true) | +| other-fs-libraries.js:77:24:77:30 | req.url | other-fs-libraries.js:77:14:77:37 | url.par ... , true) | +| other-fs-libraries.js:77:24:77:30 | req.url | other-fs-libraries.js:77:14:77:37 | url.par ... , true) | +| other-fs-libraries.js:77:24:77:30 | req.url | other-fs-libraries.js:77:14:77:37 | url.par ... , true) | +| other-fs-libraries.js:77:24:77:30 | req.url | other-fs-libraries.js:77:14:77:37 | url.par ... , true) | +| other-fs-libraries.js:77:24:77:30 | req.url | other-fs-libraries.js:77:14:77:37 | url.par ... , true) | +| other-fs-libraries.js:77:24:77:30 | req.url | other-fs-libraries.js:77:14:77:37 | url.par ... , true) | +| other-fs-libraries.js:77:24:77:30 | req.url | other-fs-libraries.js:77:14:77:37 | url.par ... , true) | +| other-fs-libraries.js:77:24:77:30 | req.url | other-fs-libraries.js:77:14:77:37 | url.par ... , true) | +| other-fs-libraries.js:77:24:77:30 | req.url | other-fs-libraries.js:77:14:77:37 | url.par ... , true) | +| other-fs-libraries.js:77:24:77:30 | req.url | other-fs-libraries.js:77:14:77:37 | url.par ... , true) | +| other-fs-libraries.js:77:24:77:30 | req.url | other-fs-libraries.js:77:14:77:37 | url.par ... , true) | +| other-fs-libraries.js:77:24:77:30 | req.url | other-fs-libraries.js:77:14:77:37 | url.par ... , true) | +| other-fs-libraries.js:77:24:77:30 | req.url | other-fs-libraries.js:77:14:77:37 | url.par ... , true) | +| other-fs-libraries.js:77:24:77:30 | req.url | other-fs-libraries.js:77:14:77:37 | url.par ... , true) | +| other-fs-libraries.js:77:24:77:30 | req.url | other-fs-libraries.js:77:14:77:37 | url.par ... , true) | +| other-fs-libraries.js:77:24:77:30 | req.url | other-fs-libraries.js:77:14:77:37 | url.par ... , true) | +| other-fs-libraries.js:77:24:77:30 | req.url | other-fs-libraries.js:77:14:77:37 | url.par ... , true) | +| other-fs-libraries.js:77:24:77:30 | req.url | other-fs-libraries.js:77:14:77:37 | url.par ... , true) | +| other-fs-libraries.js:77:24:77:30 | req.url | other-fs-libraries.js:77:14:77:37 | url.par ... , true) | +| other-fs-libraries.js:77:24:77:30 | req.url | other-fs-libraries.js:77:14:77:37 | url.par ... , true) | +| other-fs-libraries.js:77:24:77:30 | req.url | other-fs-libraries.js:77:14:77:37 | url.par ... , true) | +| other-fs-libraries.js:77:24:77:30 | req.url | other-fs-libraries.js:77:14:77:37 | url.par ... , true) | +| other-fs-libraries.js:77:24:77:30 | req.url | other-fs-libraries.js:77:14:77:37 | url.par ... , true) | +| other-fs-libraries.js:77:24:77:30 | req.url | other-fs-libraries.js:77:14:77:37 | url.par ... , true) | +| other-fs-libraries.js:77:24:77:30 | req.url | other-fs-libraries.js:77:14:77:37 | url.par ... , true) | +| other-fs-libraries.js:77:24:77:30 | req.url | other-fs-libraries.js:77:14:77:37 | url.par ... , true) | +| other-fs-libraries.js:77:24:77:30 | req.url | other-fs-libraries.js:77:14:77:37 | url.par ... , true) | +| other-fs-libraries.js:77:24:77:30 | req.url | other-fs-libraries.js:77:14:77:37 | url.par ... , true) | +| other-fs-libraries.js:77:24:77:30 | req.url | other-fs-libraries.js:77:14:77:37 | url.par ... , true) | +| other-fs-libraries.js:77:24:77:30 | req.url | other-fs-libraries.js:77:14:77:37 | url.par ... , true) | | prettier.js:6:11:6:28 | p | prettier.js:7:28:7:28 | p | | prettier.js:6:11:6:28 | p | prettier.js:7:28:7:28 | p | | prettier.js:6:11:6:28 | p | prettier.js:7:28:7:28 | p | @@ -10165,6 +10409,8 @@ edges | normalizedPaths.js:381:19:381:29 | slash(path) | normalizedPaths.js:377:14:377:27 | req.query.path | normalizedPaths.js:381:19:381:29 | slash(path) | This path depends on a $@. | normalizedPaths.js:377:14:377:27 | req.query.path | user-provided value | | normalizedPaths.js:388:19:388:22 | path | normalizedPaths.js:385:35:385:45 | req.query.x | normalizedPaths.js:388:19:388:22 | path | This path depends on a $@. | normalizedPaths.js:385:35:385:45 | req.query.x | user-provided value | | normalizedPaths.js:399:21:399:24 | path | normalizedPaths.js:385:35:385:45 | req.query.x | normalizedPaths.js:399:21:399:24 | path | This path depends on a $@. | normalizedPaths.js:385:35:385:45 | req.query.x | user-provided value | +| normalizedPaths.js:407:19:407:67 | pathMod ... t('/')) | normalizedPaths.js:407:45:407:55 | req.query.x | normalizedPaths.js:407:19:407:67 | pathMod ... t('/')) | This path depends on a $@. | normalizedPaths.js:407:45:407:55 | req.query.x | user-provided value | +| normalizedPaths.js:408:19:408:60 | pathMod ... t('/')) | normalizedPaths.js:408:38:408:48 | req.query.x | normalizedPaths.js:408:19:408:60 | pathMod ... t('/')) | This path depends on a $@. | normalizedPaths.js:408:38:408:48 | req.query.x | user-provided value | | other-fs-libraries.js:11:19:11:22 | path | other-fs-libraries.js:9:24:9:30 | req.url | other-fs-libraries.js:11:19:11:22 | path | This path depends on a $@. | other-fs-libraries.js:9:24:9:30 | req.url | user-provided value | | other-fs-libraries.js:12:27:12:30 | path | other-fs-libraries.js:9:24:9:30 | req.url | other-fs-libraries.js:12:27:12:30 | path | This path depends on a $@. | other-fs-libraries.js:9:24:9:30 | req.url | user-provided value | | other-fs-libraries.js:13:24:13:27 | path | other-fs-libraries.js:9:24:9:30 | req.url | other-fs-libraries.js:13:24:13:27 | path | This path depends on a $@. | other-fs-libraries.js:9:24:9:30 | req.url | user-provided value | @@ -10187,6 +10433,7 @@ edges | other-fs-libraries.js:70:19:70:22 | path | other-fs-libraries.js:68:24:68:30 | req.url | other-fs-libraries.js:70:19:70:22 | path | This path depends on a $@. | other-fs-libraries.js:68:24:68:30 | req.url | user-provided value | | other-fs-libraries.js:71:10:71:13 | path | other-fs-libraries.js:68:24:68:30 | req.url | other-fs-libraries.js:71:10:71:13 | path | This path depends on a $@. | other-fs-libraries.js:68:24:68:30 | req.url | user-provided value | | other-fs-libraries.js:72:15:72:18 | path | other-fs-libraries.js:68:24:68:30 | req.url | other-fs-libraries.js:72:15:72:18 | path | This path depends on a $@. | other-fs-libraries.js:68:24:68:30 | req.url | user-provided value | +| other-fs-libraries.js:79:16:79:19 | path | other-fs-libraries.js:77:24:77:30 | req.url | other-fs-libraries.js:79:16:79:19 | path | This path depends on a $@. | other-fs-libraries.js:77:24:77:30 | req.url | user-provided value | | prettier.js:7:28:7:28 | p | prettier.js:6:13:6:13 | p | prettier.js:7:28:7:28 | p | This path depends on a $@. | prettier.js:6:13:6:13 | p | user-provided value | | prettier.js:11:44:11:44 | p | prettier.js:6:13:6:13 | p | prettier.js:11:44:11:44 | p | This path depends on a $@. | prettier.js:6:13:6:13 | p | user-provided value | | pupeteer.js:9:28:9:34 | tainted | pupeteer.js:5:28:5:53 | parseTo ... t).name | pupeteer.js:9:28:9:34 | tainted | This path depends on a $@. | pupeteer.js:5:28:5:53 | parseTo ... t).name | user-provided value | diff --git a/javascript/ql/test/query-tests/Security/CWE-022/TaintedPath/fs.js b/javascript/ql/test/query-tests/Security/CWE-022/TaintedPath/fs.js index fc6b4ab3581..721f301093c 100644 --- a/javascript/ql/test/query-tests/Security/CWE-022/TaintedPath/fs.js +++ b/javascript/ql/test/query-tests/Security/CWE-022/TaintedPath/fs.js @@ -45,4 +45,12 @@ var fs = {}; */ fs.readFileSync = function(filename, encoding) {}; +/** + * @param {string} filename + * @param {string} encoding + * @param {(function(NodeJS.ErrnoException, string): void)} callback + * @return {void} + */ +fs.readFile = function(filename, encoding, callback) {}; + module.exports = fs; diff --git a/javascript/ql/test/query-tests/Security/CWE-022/TaintedPath/normalizedPaths.js b/javascript/ql/test/query-tests/Security/CWE-022/TaintedPath/normalizedPaths.js index f5caae46c45..a5453a728d3 100644 --- a/javascript/ql/test/query-tests/Security/CWE-022/TaintedPath/normalizedPaths.js +++ b/javascript/ql/test/query-tests/Security/CWE-022/TaintedPath/normalizedPaths.js @@ -32,17 +32,17 @@ app.get('/normalize-notAbsolute', (req, res) => { if (pathModule.isAbsolute(path)) return; - + fs.readFileSync(path); // NOT OK if (!path.startsWith(".")) fs.readFileSync(path); // OK else fs.readFileSync(path); // NOT OK - wrong polarity - + if (!path.startsWith("..")) fs.readFileSync(path); // OK - + if (!path.startsWith("../")) fs.readFileSync(path); // OK @@ -52,7 +52,7 @@ app.get('/normalize-notAbsolute', (req, res) => { app.get('/normalize-noInitialDotDot', (req, res) => { let path = pathModule.normalize(req.query.path); - + if (path.startsWith("..")) return; @@ -80,7 +80,7 @@ app.get('/prepend-normalize', (req, res) => { app.get('/absolute', (req, res) => { let path = req.query.path; - + if (!pathModule.isAbsolute(path)) return; @@ -92,10 +92,10 @@ app.get('/absolute', (req, res) => { app.get('/normalized-absolute', (req, res) => { let path = pathModule.normalize(req.query.path); - + if (!pathModule.isAbsolute(path)) return; - + res.write(fs.readFileSync(path)); // NOT OK if (path.startsWith('/home/user/www')) @@ -104,7 +104,7 @@ app.get('/normalized-absolute', (req, res) => { app.get('/combined-check', (req, res) => { let path = pathModule.normalize(req.query.path); - + // Combined absoluteness and folder check in one startsWith call if (path.startsWith("/home/user/www")) fs.readFileSync(path); // OK @@ -121,7 +121,7 @@ app.get('/realpath', (req, res) => { if (path.startsWith("/home/user/www")) fs.readFileSync(path); // OK - both absolute and normalized before check - + fs.readFileSync(pathModule.join('.', path)); // OK - normalized and coerced to relative fs.readFileSync(pathModule.join('/home/user/www', path)); // OK }); @@ -212,7 +212,7 @@ app.get('/join-regression', (req, res) => { app.get('/decode-after-normalization', (req, res) => { let path = pathModule.normalize(req.query.path); - + if (!pathModule.isAbsolute(path) && !path.startsWith('..')) fs.readFileSync(path); // OK @@ -238,7 +238,7 @@ app.get('/resolve-path', (req, res) => { fs.readFileSync(path); // NOT OK var self = something(); - + if (path.substring(0, self.dir.length) === self.dir) fs.readFileSync(path); // OK else @@ -256,12 +256,12 @@ app.get('/relative-startswith', (req, res) => { fs.readFileSync(path); // NOT OK var self = something(); - + var relative = pathModule.relative(self.webroot, path); if(relative.startsWith(".." + pathModule.sep) || relative == "..") { - fs.readFileSync(path); // NOT OK! + fs.readFileSync(path); // NOT OK! } else { - fs.readFileSync(path); // OK! + fs.readFileSync(path); // OK! } let newpath = pathModule.normalize(path); @@ -277,7 +277,7 @@ app.get('/relative-startswith', (req, res) => { if (relativePath.indexOf('../') === 0) { fs.readFileSync(newpath); // NOT OK! } else { - fs.readFileSync(newpath); // OK! + fs.readFileSync(newpath); // OK! } let newpath = pathModule.normalize(path); @@ -285,7 +285,7 @@ app.get('/relative-startswith', (req, res) => { if (pathModule.normalize(relativePath).indexOf('../') === 0) { fs.readFileSync(newpath); // NOT OK! } else { - fs.readFileSync(newpath); // OK! + fs.readFileSync(newpath); // OK! } let newpath = pathModule.normalize(path); @@ -293,7 +293,7 @@ app.get('/relative-startswith', (req, res) => { if (pathModule.normalize(relativePath).indexOf('../')) { fs.readFileSync(newpath); // OK! } else { - fs.readFileSync(newpath); // NOT OK! + fs.readFileSync(newpath); // NOT OK! } }); @@ -340,7 +340,7 @@ app.get('/yet-another-prefix', (req, res) => { fs.readFileSync(path); // NOT OK - var abs = pathModule.resolve(path); + var abs = pathModule.resolve(path); if (abs.indexOf(root) !== 0) { fs.readFileSync(path); // NOT OK @@ -402,3 +402,8 @@ app.get('/dotdot-regexp', (req, res) => { fs.readFileSync(path); // OK } }); + +app.get('/join-spread', (req, res) => { + fs.readFileSync(pathModule.join('foo', ...req.query.x.split('/'))); // NOT OK + fs.readFileSync(pathModule.join(...req.query.x.split('/'))); // NOT OK +}); diff --git a/javascript/ql/test/query-tests/Security/CWE-022/TaintedPath/other-fs-libraries.js b/javascript/ql/test/query-tests/Security/CWE-022/TaintedPath/other-fs-libraries.js index bda7051ba80..1a618105226 100644 --- a/javascript/ql/test/query-tests/Security/CWE-022/TaintedPath/other-fs-libraries.js +++ b/javascript/ql/test/query-tests/Security/CWE-022/TaintedPath/other-fs-libraries.js @@ -71,3 +71,10 @@ http.createServer(function(req, res) { mkdirp(path); // NOT OK mkdirp.sync(path); // NOT OK }); + +const fsp = require("fs/promises"); +http.createServer(function(req, res) { + var path = url.parse(req.url, true).query.path; + + fsp.readFile(path); // NOT OK +}); diff --git a/javascript/ql/test/query-tests/Security/CWE-094/CodeInjection/CodeInjection.expected b/javascript/ql/test/query-tests/Security/CWE-094/CodeInjection/CodeInjection.expected index d866329402a..1193c5e33bc 100644 --- a/javascript/ql/test/query-tests/Security/CWE-094/CodeInjection/CodeInjection.expected +++ b/javascript/ql/test/query-tests/Security/CWE-094/CodeInjection/CodeInjection.expected @@ -112,37 +112,37 @@ nodes | react.js:10:56:10:77 | documen ... on.hash | | react.js:10:56:10:77 | documen ... on.hash | | react.js:10:56:10:77 | documen ... on.hash | -| template-sinks.js:17:9:17:31 | tainted | -| template-sinks.js:17:19:17:31 | req.query.foo | -| template-sinks.js:17:19:17:31 | req.query.foo | -| template-sinks.js:19:17:19:23 | tainted | -| template-sinks.js:19:17:19:23 | tainted | -| template-sinks.js:20:16:20:22 | tainted | -| template-sinks.js:20:16:20:22 | tainted | -| template-sinks.js:21:18:21:24 | tainted | -| template-sinks.js:21:18:21:24 | tainted | -| template-sinks.js:22:17:22:23 | tainted | -| template-sinks.js:22:17:22:23 | tainted | -| template-sinks.js:23:18:23:24 | tainted | -| template-sinks.js:23:18:23:24 | tainted | -| template-sinks.js:24:16:24:22 | tainted | -| template-sinks.js:24:16:24:22 | tainted | -| template-sinks.js:25:27:25:33 | tainted | -| template-sinks.js:25:27:25:33 | tainted | -| template-sinks.js:26:21:26:27 | tainted | -| template-sinks.js:26:21:26:27 | tainted | -| template-sinks.js:27:17:27:23 | tainted | -| template-sinks.js:27:17:27:23 | tainted | -| template-sinks.js:28:24:28:30 | tainted | -| template-sinks.js:28:24:28:30 | tainted | -| template-sinks.js:29:21:29:27 | tainted | -| template-sinks.js:29:21:29:27 | tainted | -| template-sinks.js:30:19:30:25 | tainted | -| template-sinks.js:30:19:30:25 | tainted | -| template-sinks.js:31:16:31:22 | tainted | -| template-sinks.js:31:16:31:22 | tainted | -| template-sinks.js:32:17:32:23 | tainted | -| template-sinks.js:32:17:32:23 | tainted | +| template-sinks.js:18:9:18:31 | tainted | +| template-sinks.js:18:19:18:31 | req.query.foo | +| template-sinks.js:18:19:18:31 | req.query.foo | +| template-sinks.js:20:17:20:23 | tainted | +| template-sinks.js:20:17:20:23 | tainted | +| template-sinks.js:21:16:21:22 | tainted | +| template-sinks.js:21:16:21:22 | tainted | +| template-sinks.js:22:18:22:24 | tainted | +| template-sinks.js:22:18:22:24 | tainted | +| template-sinks.js:23:17:23:23 | tainted | +| template-sinks.js:23:17:23:23 | tainted | +| template-sinks.js:24:18:24:24 | tainted | +| template-sinks.js:24:18:24:24 | tainted | +| template-sinks.js:25:16:25:22 | tainted | +| template-sinks.js:25:16:25:22 | tainted | +| template-sinks.js:26:27:26:33 | tainted | +| template-sinks.js:26:27:26:33 | tainted | +| template-sinks.js:27:21:27:27 | tainted | +| template-sinks.js:27:21:27:27 | tainted | +| template-sinks.js:28:17:28:23 | tainted | +| template-sinks.js:28:17:28:23 | tainted | +| template-sinks.js:29:24:29:30 | tainted | +| template-sinks.js:29:24:29:30 | tainted | +| template-sinks.js:30:21:30:27 | tainted | +| template-sinks.js:30:21:30:27 | tainted | +| template-sinks.js:31:19:31:25 | tainted | +| template-sinks.js:31:19:31:25 | tainted | +| template-sinks.js:32:16:32:22 | tainted | +| template-sinks.js:32:16:32:22 | tainted | +| template-sinks.js:33:17:33:23 | tainted | +| template-sinks.js:33:17:33:23 | tainted | | tst.js:2:6:2:27 | documen ... on.href | | tst.js:2:6:2:27 | documen ... on.href | | tst.js:2:6:2:83 | documen ... t=")+8) | @@ -181,6 +181,24 @@ nodes | tst.js:35:28:35:33 | source | | tst.js:37:33:37:38 | source | | tst.js:37:33:37:38 | source | +| webix/webix.html:3:16:3:37 | documen ... on.hash | +| webix/webix.html:3:16:3:37 | documen ... on.hash | +| webix/webix.html:3:16:3:37 | documen ... on.hash | +| webix/webix.html:4:26:4:47 | documen ... on.hash | +| webix/webix.html:4:26:4:47 | documen ... on.hash | +| webix/webix.html:4:26:4:47 | documen ... on.hash | +| webix/webix.html:5:47:5:68 | documen ... on.hash | +| webix/webix.html:5:47:5:68 | documen ... on.hash | +| webix/webix.html:5:47:5:68 | documen ... on.hash | +| webix/webix.js:3:12:3:33 | documen ... on.hash | +| webix/webix.js:3:12:3:33 | documen ... on.hash | +| webix/webix.js:3:12:3:33 | documen ... on.hash | +| webix/webix.js:4:22:4:43 | documen ... on.hash | +| webix/webix.js:4:22:4:43 | documen ... on.hash | +| webix/webix.js:4:22:4:43 | documen ... on.hash | +| webix/webix.js:5:43:5:64 | documen ... on.hash | +| webix/webix.js:5:43:5:64 | documen ... on.hash | +| webix/webix.js:5:43:5:64 | documen ... on.hash | edges | NoSQLCodeInjection.js:18:24:18:31 | req.body | NoSQLCodeInjection.js:18:24:18:37 | req.body.query | | NoSQLCodeInjection.js:18:24:18:31 | req.body | NoSQLCodeInjection.js:18:24:18:37 | req.body.query | @@ -246,36 +264,36 @@ edges | react-native.js:7:17:7:33 | req.param("code") | react-native.js:7:7:7:33 | tainted | | react-native.js:7:17:7:33 | req.param("code") | react-native.js:7:7:7:33 | tainted | | react.js:10:56:10:77 | documen ... on.hash | react.js:10:56:10:77 | documen ... on.hash | -| template-sinks.js:17:9:17:31 | tainted | template-sinks.js:19:17:19:23 | tainted | -| template-sinks.js:17:9:17:31 | tainted | template-sinks.js:19:17:19:23 | tainted | -| template-sinks.js:17:9:17:31 | tainted | template-sinks.js:20:16:20:22 | tainted | -| template-sinks.js:17:9:17:31 | tainted | template-sinks.js:20:16:20:22 | tainted | -| template-sinks.js:17:9:17:31 | tainted | template-sinks.js:21:18:21:24 | tainted | -| template-sinks.js:17:9:17:31 | tainted | template-sinks.js:21:18:21:24 | tainted | -| template-sinks.js:17:9:17:31 | tainted | template-sinks.js:22:17:22:23 | tainted | -| template-sinks.js:17:9:17:31 | tainted | template-sinks.js:22:17:22:23 | tainted | -| template-sinks.js:17:9:17:31 | tainted | template-sinks.js:23:18:23:24 | tainted | -| template-sinks.js:17:9:17:31 | tainted | template-sinks.js:23:18:23:24 | tainted | -| template-sinks.js:17:9:17:31 | tainted | template-sinks.js:24:16:24:22 | tainted | -| template-sinks.js:17:9:17:31 | tainted | template-sinks.js:24:16:24:22 | tainted | -| template-sinks.js:17:9:17:31 | tainted | template-sinks.js:25:27:25:33 | tainted | -| template-sinks.js:17:9:17:31 | tainted | template-sinks.js:25:27:25:33 | tainted | -| template-sinks.js:17:9:17:31 | tainted | template-sinks.js:26:21:26:27 | tainted | -| template-sinks.js:17:9:17:31 | tainted | template-sinks.js:26:21:26:27 | tainted | -| template-sinks.js:17:9:17:31 | tainted | template-sinks.js:27:17:27:23 | tainted | -| template-sinks.js:17:9:17:31 | tainted | template-sinks.js:27:17:27:23 | tainted | -| template-sinks.js:17:9:17:31 | tainted | template-sinks.js:28:24:28:30 | tainted | -| template-sinks.js:17:9:17:31 | tainted | template-sinks.js:28:24:28:30 | tainted | -| template-sinks.js:17:9:17:31 | tainted | template-sinks.js:29:21:29:27 | tainted | -| template-sinks.js:17:9:17:31 | tainted | template-sinks.js:29:21:29:27 | tainted | -| template-sinks.js:17:9:17:31 | tainted | template-sinks.js:30:19:30:25 | tainted | -| template-sinks.js:17:9:17:31 | tainted | template-sinks.js:30:19:30:25 | tainted | -| template-sinks.js:17:9:17:31 | tainted | template-sinks.js:31:16:31:22 | tainted | -| template-sinks.js:17:9:17:31 | tainted | template-sinks.js:31:16:31:22 | tainted | -| template-sinks.js:17:9:17:31 | tainted | template-sinks.js:32:17:32:23 | tainted | -| template-sinks.js:17:9:17:31 | tainted | template-sinks.js:32:17:32:23 | tainted | -| template-sinks.js:17:19:17:31 | req.query.foo | template-sinks.js:17:9:17:31 | tainted | -| template-sinks.js:17:19:17:31 | req.query.foo | template-sinks.js:17:9:17:31 | tainted | +| template-sinks.js:18:9:18:31 | tainted | template-sinks.js:20:17:20:23 | tainted | +| template-sinks.js:18:9:18:31 | tainted | template-sinks.js:20:17:20:23 | tainted | +| template-sinks.js:18:9:18:31 | tainted | template-sinks.js:21:16:21:22 | tainted | +| template-sinks.js:18:9:18:31 | tainted | template-sinks.js:21:16:21:22 | tainted | +| template-sinks.js:18:9:18:31 | tainted | template-sinks.js:22:18:22:24 | tainted | +| template-sinks.js:18:9:18:31 | tainted | template-sinks.js:22:18:22:24 | tainted | +| template-sinks.js:18:9:18:31 | tainted | template-sinks.js:23:17:23:23 | tainted | +| template-sinks.js:18:9:18:31 | tainted | template-sinks.js:23:17:23:23 | tainted | +| template-sinks.js:18:9:18:31 | tainted | template-sinks.js:24:18:24:24 | tainted | +| template-sinks.js:18:9:18:31 | tainted | template-sinks.js:24:18:24:24 | tainted | +| template-sinks.js:18:9:18:31 | tainted | template-sinks.js:25:16:25:22 | tainted | +| template-sinks.js:18:9:18:31 | tainted | template-sinks.js:25:16:25:22 | tainted | +| template-sinks.js:18:9:18:31 | tainted | template-sinks.js:26:27:26:33 | tainted | +| template-sinks.js:18:9:18:31 | tainted | template-sinks.js:26:27:26:33 | tainted | +| template-sinks.js:18:9:18:31 | tainted | template-sinks.js:27:21:27:27 | tainted | +| template-sinks.js:18:9:18:31 | tainted | template-sinks.js:27:21:27:27 | tainted | +| template-sinks.js:18:9:18:31 | tainted | template-sinks.js:28:17:28:23 | tainted | +| template-sinks.js:18:9:18:31 | tainted | template-sinks.js:28:17:28:23 | tainted | +| template-sinks.js:18:9:18:31 | tainted | template-sinks.js:29:24:29:30 | tainted | +| template-sinks.js:18:9:18:31 | tainted | template-sinks.js:29:24:29:30 | tainted | +| template-sinks.js:18:9:18:31 | tainted | template-sinks.js:30:21:30:27 | tainted | +| template-sinks.js:18:9:18:31 | tainted | template-sinks.js:30:21:30:27 | tainted | +| template-sinks.js:18:9:18:31 | tainted | template-sinks.js:31:19:31:25 | tainted | +| template-sinks.js:18:9:18:31 | tainted | template-sinks.js:31:19:31:25 | tainted | +| template-sinks.js:18:9:18:31 | tainted | template-sinks.js:32:16:32:22 | tainted | +| template-sinks.js:18:9:18:31 | tainted | template-sinks.js:32:16:32:22 | tainted | +| template-sinks.js:18:9:18:31 | tainted | template-sinks.js:33:17:33:23 | tainted | +| template-sinks.js:18:9:18:31 | tainted | template-sinks.js:33:17:33:23 | tainted | +| template-sinks.js:18:19:18:31 | req.query.foo | template-sinks.js:18:9:18:31 | tainted | +| template-sinks.js:18:19:18:31 | req.query.foo | template-sinks.js:18:9:18:31 | tainted | | tst.js:2:6:2:27 | documen ... on.href | tst.js:2:6:2:83 | documen ... t=")+8) | | tst.js:2:6:2:27 | documen ... on.href | tst.js:2:6:2:83 | documen ... t=")+8) | | tst.js:2:6:2:27 | documen ... on.href | tst.js:2:6:2:83 | documen ... t=")+8) | @@ -306,6 +324,12 @@ edges | tst.js:29:18:29:41 | documen ... .search | tst.js:29:18:29:82 | documen ... , "$1") | | tst.js:29:18:29:41 | documen ... .search | tst.js:29:18:29:82 | documen ... , "$1") | | tst.js:29:18:29:82 | documen ... , "$1") | tst.js:29:9:29:82 | source | +| webix/webix.html:3:16:3:37 | documen ... on.hash | webix/webix.html:3:16:3:37 | documen ... on.hash | +| webix/webix.html:4:26:4:47 | documen ... on.hash | webix/webix.html:4:26:4:47 | documen ... on.hash | +| webix/webix.html:5:47:5:68 | documen ... on.hash | webix/webix.html:5:47:5:68 | documen ... on.hash | +| webix/webix.js:3:12:3:33 | documen ... on.hash | webix/webix.js:3:12:3:33 | documen ... on.hash | +| webix/webix.js:4:22:4:43 | documen ... on.hash | webix/webix.js:4:22:4:43 | documen ... on.hash | +| webix/webix.js:5:43:5:64 | documen ... on.hash | webix/webix.js:5:43:5:64 | documen ... on.hash | #select | NoSQLCodeInjection.js:18:24:18:37 | req.body.query | NoSQLCodeInjection.js:18:24:18:31 | req.body | NoSQLCodeInjection.js:18:24:18:37 | req.body.query | This code execution depends on a $@. | NoSQLCodeInjection.js:18:24:18:31 | req.body | user-provided value | | NoSQLCodeInjection.js:19:24:19:48 | "name = ... dy.name | NoSQLCodeInjection.js:19:36:19:43 | req.body | NoSQLCodeInjection.js:19:24:19:48 | "name = ... dy.name | This code execution depends on a $@. | NoSQLCodeInjection.js:19:36:19:43 | req.body | user-provided value | @@ -340,20 +364,20 @@ edges | react-native.js:8:32:8:38 | tainted | react-native.js:7:17:7:33 | req.param("code") | react-native.js:8:32:8:38 | tainted | This code execution depends on a $@. | react-native.js:7:17:7:33 | req.param("code") | user-provided value | | react-native.js:10:23:10:29 | tainted | react-native.js:7:17:7:33 | req.param("code") | react-native.js:10:23:10:29 | tainted | This code execution depends on a $@. | react-native.js:7:17:7:33 | req.param("code") | user-provided value | | react.js:10:56:10:77 | documen ... on.hash | react.js:10:56:10:77 | documen ... on.hash | react.js:10:56:10:77 | documen ... on.hash | This code execution depends on a $@. | react.js:10:56:10:77 | documen ... on.hash | user-provided value | -| template-sinks.js:19:17:19:23 | tainted | template-sinks.js:17:19:17:31 | req.query.foo | template-sinks.js:19:17:19:23 | tainted | Template, which may contain code, depends on a $@. | template-sinks.js:17:19:17:31 | req.query.foo | user-provided value | -| template-sinks.js:20:16:20:22 | tainted | template-sinks.js:17:19:17:31 | req.query.foo | template-sinks.js:20:16:20:22 | tainted | Template, which may contain code, depends on a $@. | template-sinks.js:17:19:17:31 | req.query.foo | user-provided value | -| template-sinks.js:21:18:21:24 | tainted | template-sinks.js:17:19:17:31 | req.query.foo | template-sinks.js:21:18:21:24 | tainted | Template, which may contain code, depends on a $@. | template-sinks.js:17:19:17:31 | req.query.foo | user-provided value | -| template-sinks.js:22:17:22:23 | tainted | template-sinks.js:17:19:17:31 | req.query.foo | template-sinks.js:22:17:22:23 | tainted | Template, which may contain code, depends on a $@. | template-sinks.js:17:19:17:31 | req.query.foo | user-provided value | -| template-sinks.js:23:18:23:24 | tainted | template-sinks.js:17:19:17:31 | req.query.foo | template-sinks.js:23:18:23:24 | tainted | Template, which may contain code, depends on a $@. | template-sinks.js:17:19:17:31 | req.query.foo | user-provided value | -| template-sinks.js:24:16:24:22 | tainted | template-sinks.js:17:19:17:31 | req.query.foo | template-sinks.js:24:16:24:22 | tainted | Template, which may contain code, depends on a $@. | template-sinks.js:17:19:17:31 | req.query.foo | user-provided value | -| template-sinks.js:25:27:25:33 | tainted | template-sinks.js:17:19:17:31 | req.query.foo | template-sinks.js:25:27:25:33 | tainted | Template, which may contain code, depends on a $@. | template-sinks.js:17:19:17:31 | req.query.foo | user-provided value | -| template-sinks.js:26:21:26:27 | tainted | template-sinks.js:17:19:17:31 | req.query.foo | template-sinks.js:26:21:26:27 | tainted | Template, which may contain code, depends on a $@. | template-sinks.js:17:19:17:31 | req.query.foo | user-provided value | -| template-sinks.js:27:17:27:23 | tainted | template-sinks.js:17:19:17:31 | req.query.foo | template-sinks.js:27:17:27:23 | tainted | Template, which may contain code, depends on a $@. | template-sinks.js:17:19:17:31 | req.query.foo | user-provided value | -| template-sinks.js:28:24:28:30 | tainted | template-sinks.js:17:19:17:31 | req.query.foo | template-sinks.js:28:24:28:30 | tainted | Template, which may contain code, depends on a $@. | template-sinks.js:17:19:17:31 | req.query.foo | user-provided value | -| template-sinks.js:29:21:29:27 | tainted | template-sinks.js:17:19:17:31 | req.query.foo | template-sinks.js:29:21:29:27 | tainted | Template, which may contain code, depends on a $@. | template-sinks.js:17:19:17:31 | req.query.foo | user-provided value | -| template-sinks.js:30:19:30:25 | tainted | template-sinks.js:17:19:17:31 | req.query.foo | template-sinks.js:30:19:30:25 | tainted | Template, which may contain code, depends on a $@. | template-sinks.js:17:19:17:31 | req.query.foo | user-provided value | -| template-sinks.js:31:16:31:22 | tainted | template-sinks.js:17:19:17:31 | req.query.foo | template-sinks.js:31:16:31:22 | tainted | Template, which may contain code, depends on a $@. | template-sinks.js:17:19:17:31 | req.query.foo | user-provided value | -| template-sinks.js:32:17:32:23 | tainted | template-sinks.js:17:19:17:31 | req.query.foo | template-sinks.js:32:17:32:23 | tainted | Template, which may contain code, depends on a $@. | template-sinks.js:17:19:17:31 | req.query.foo | user-provided value | +| template-sinks.js:20:17:20:23 | tainted | template-sinks.js:18:19:18:31 | req.query.foo | template-sinks.js:20:17:20:23 | tainted | Template, which may contain code, depends on a $@. | template-sinks.js:18:19:18:31 | req.query.foo | user-provided value | +| template-sinks.js:21:16:21:22 | tainted | template-sinks.js:18:19:18:31 | req.query.foo | template-sinks.js:21:16:21:22 | tainted | Template, which may contain code, depends on a $@. | template-sinks.js:18:19:18:31 | req.query.foo | user-provided value | +| template-sinks.js:22:18:22:24 | tainted | template-sinks.js:18:19:18:31 | req.query.foo | template-sinks.js:22:18:22:24 | tainted | Template, which may contain code, depends on a $@. | template-sinks.js:18:19:18:31 | req.query.foo | user-provided value | +| template-sinks.js:23:17:23:23 | tainted | template-sinks.js:18:19:18:31 | req.query.foo | template-sinks.js:23:17:23:23 | tainted | Template, which may contain code, depends on a $@. | template-sinks.js:18:19:18:31 | req.query.foo | user-provided value | +| template-sinks.js:24:18:24:24 | tainted | template-sinks.js:18:19:18:31 | req.query.foo | template-sinks.js:24:18:24:24 | tainted | Template, which may contain code, depends on a $@. | template-sinks.js:18:19:18:31 | req.query.foo | user-provided value | +| template-sinks.js:25:16:25:22 | tainted | template-sinks.js:18:19:18:31 | req.query.foo | template-sinks.js:25:16:25:22 | tainted | Template, which may contain code, depends on a $@. | template-sinks.js:18:19:18:31 | req.query.foo | user-provided value | +| template-sinks.js:26:27:26:33 | tainted | template-sinks.js:18:19:18:31 | req.query.foo | template-sinks.js:26:27:26:33 | tainted | Template, which may contain code, depends on a $@. | template-sinks.js:18:19:18:31 | req.query.foo | user-provided value | +| template-sinks.js:27:21:27:27 | tainted | template-sinks.js:18:19:18:31 | req.query.foo | template-sinks.js:27:21:27:27 | tainted | Template, which may contain code, depends on a $@. | template-sinks.js:18:19:18:31 | req.query.foo | user-provided value | +| template-sinks.js:28:17:28:23 | tainted | template-sinks.js:18:19:18:31 | req.query.foo | template-sinks.js:28:17:28:23 | tainted | Template, which may contain code, depends on a $@. | template-sinks.js:18:19:18:31 | req.query.foo | user-provided value | +| template-sinks.js:29:24:29:30 | tainted | template-sinks.js:18:19:18:31 | req.query.foo | template-sinks.js:29:24:29:30 | tainted | Template, which may contain code, depends on a $@. | template-sinks.js:18:19:18:31 | req.query.foo | user-provided value | +| template-sinks.js:30:21:30:27 | tainted | template-sinks.js:18:19:18:31 | req.query.foo | template-sinks.js:30:21:30:27 | tainted | Template, which may contain code, depends on a $@. | template-sinks.js:18:19:18:31 | req.query.foo | user-provided value | +| template-sinks.js:31:19:31:25 | tainted | template-sinks.js:18:19:18:31 | req.query.foo | template-sinks.js:31:19:31:25 | tainted | Template, which may contain code, depends on a $@. | template-sinks.js:18:19:18:31 | req.query.foo | user-provided value | +| template-sinks.js:32:16:32:22 | tainted | template-sinks.js:18:19:18:31 | req.query.foo | template-sinks.js:32:16:32:22 | tainted | Template, which may contain code, depends on a $@. | template-sinks.js:18:19:18:31 | req.query.foo | user-provided value | +| template-sinks.js:33:17:33:23 | tainted | template-sinks.js:18:19:18:31 | req.query.foo | template-sinks.js:33:17:33:23 | tainted | Template, which may contain code, depends on a $@. | template-sinks.js:18:19:18:31 | req.query.foo | user-provided value | | tst.js:2:6:2:83 | documen ... t=")+8) | tst.js:2:6:2:27 | documen ... on.href | tst.js:2:6:2:83 | documen ... t=")+8) | This code execution depends on a $@. | tst.js:2:6:2:27 | documen ... on.href | user-provided value | | tst.js:5:12:5:33 | documen ... on.hash | tst.js:5:12:5:33 | documen ... on.hash | tst.js:5:12:5:33 | documen ... on.hash | This code execution depends on a $@. | tst.js:5:12:5:33 | documen ... on.hash | user-provided value | | tst.js:14:10:14:74 | documen ... , "$1") | tst.js:14:10:14:33 | documen ... .search | tst.js:14:10:14:74 | documen ... , "$1") | This code execution depends on a $@. | tst.js:14:10:14:33 | documen ... .search | user-provided value | @@ -365,3 +389,9 @@ edges | tst.js:33:14:33:19 | source | tst.js:29:18:29:41 | documen ... .search | tst.js:33:14:33:19 | source | This code execution depends on a $@. | tst.js:29:18:29:41 | documen ... .search | user-provided value | | tst.js:35:28:35:33 | source | tst.js:29:18:29:41 | documen ... .search | tst.js:35:28:35:33 | source | This code execution depends on a $@. | tst.js:29:18:29:41 | documen ... .search | user-provided value | | tst.js:37:33:37:38 | source | tst.js:29:18:29:41 | documen ... .search | tst.js:37:33:37:38 | source | This code execution depends on a $@. | tst.js:29:18:29:41 | documen ... .search | user-provided value | +| webix/webix.html:3:16:3:37 | documen ... on.hash | webix/webix.html:3:16:3:37 | documen ... on.hash | webix/webix.html:3:16:3:37 | documen ... on.hash | This code execution depends on a $@. | webix/webix.html:3:16:3:37 | documen ... on.hash | user-provided value | +| webix/webix.html:4:26:4:47 | documen ... on.hash | webix/webix.html:4:26:4:47 | documen ... on.hash | webix/webix.html:4:26:4:47 | documen ... on.hash | Template, which may contain code, depends on a $@. | webix/webix.html:4:26:4:47 | documen ... on.hash | user-provided value | +| webix/webix.html:5:47:5:68 | documen ... on.hash | webix/webix.html:5:47:5:68 | documen ... on.hash | webix/webix.html:5:47:5:68 | documen ... on.hash | Template, which may contain code, depends on a $@. | webix/webix.html:5:47:5:68 | documen ... on.hash | user-provided value | +| webix/webix.js:3:12:3:33 | documen ... on.hash | webix/webix.js:3:12:3:33 | documen ... on.hash | webix/webix.js:3:12:3:33 | documen ... on.hash | This code execution depends on a $@. | webix/webix.js:3:12:3:33 | documen ... on.hash | user-provided value | +| webix/webix.js:4:22:4:43 | documen ... on.hash | webix/webix.js:4:22:4:43 | documen ... on.hash | webix/webix.js:4:22:4:43 | documen ... on.hash | Template, which may contain code, depends on a $@. | webix/webix.js:4:22:4:43 | documen ... on.hash | user-provided value | +| webix/webix.js:5:43:5:64 | documen ... on.hash | webix/webix.js:5:43:5:64 | documen ... on.hash | webix/webix.js:5:43:5:64 | documen ... on.hash | Template, which may contain code, depends on a $@. | webix/webix.js:5:43:5:64 | documen ... on.hash | user-provided value | diff --git a/javascript/ql/test/query-tests/Security/CWE-094/CodeInjection/HeuristicSourceCodeInjection.expected b/javascript/ql/test/query-tests/Security/CWE-094/CodeInjection/HeuristicSourceCodeInjection.expected index be221820c07..7e4bd305955 100644 --- a/javascript/ql/test/query-tests/Security/CWE-094/CodeInjection/HeuristicSourceCodeInjection.expected +++ b/javascript/ql/test/query-tests/Security/CWE-094/CodeInjection/HeuristicSourceCodeInjection.expected @@ -116,37 +116,37 @@ nodes | react.js:10:56:10:77 | documen ... on.hash | | react.js:10:56:10:77 | documen ... on.hash | | react.js:10:56:10:77 | documen ... on.hash | -| template-sinks.js:17:9:17:31 | tainted | -| template-sinks.js:17:19:17:31 | req.query.foo | -| template-sinks.js:17:19:17:31 | req.query.foo | -| template-sinks.js:19:17:19:23 | tainted | -| template-sinks.js:19:17:19:23 | tainted | -| template-sinks.js:20:16:20:22 | tainted | -| template-sinks.js:20:16:20:22 | tainted | -| template-sinks.js:21:18:21:24 | tainted | -| template-sinks.js:21:18:21:24 | tainted | -| template-sinks.js:22:17:22:23 | tainted | -| template-sinks.js:22:17:22:23 | tainted | -| template-sinks.js:23:18:23:24 | tainted | -| template-sinks.js:23:18:23:24 | tainted | -| template-sinks.js:24:16:24:22 | tainted | -| template-sinks.js:24:16:24:22 | tainted | -| template-sinks.js:25:27:25:33 | tainted | -| template-sinks.js:25:27:25:33 | tainted | -| template-sinks.js:26:21:26:27 | tainted | -| template-sinks.js:26:21:26:27 | tainted | -| template-sinks.js:27:17:27:23 | tainted | -| template-sinks.js:27:17:27:23 | tainted | -| template-sinks.js:28:24:28:30 | tainted | -| template-sinks.js:28:24:28:30 | tainted | -| template-sinks.js:29:21:29:27 | tainted | -| template-sinks.js:29:21:29:27 | tainted | -| template-sinks.js:30:19:30:25 | tainted | -| template-sinks.js:30:19:30:25 | tainted | -| template-sinks.js:31:16:31:22 | tainted | -| template-sinks.js:31:16:31:22 | tainted | -| template-sinks.js:32:17:32:23 | tainted | -| template-sinks.js:32:17:32:23 | tainted | +| template-sinks.js:18:9:18:31 | tainted | +| template-sinks.js:18:19:18:31 | req.query.foo | +| template-sinks.js:18:19:18:31 | req.query.foo | +| template-sinks.js:20:17:20:23 | tainted | +| template-sinks.js:20:17:20:23 | tainted | +| template-sinks.js:21:16:21:22 | tainted | +| template-sinks.js:21:16:21:22 | tainted | +| template-sinks.js:22:18:22:24 | tainted | +| template-sinks.js:22:18:22:24 | tainted | +| template-sinks.js:23:17:23:23 | tainted | +| template-sinks.js:23:17:23:23 | tainted | +| template-sinks.js:24:18:24:24 | tainted | +| template-sinks.js:24:18:24:24 | tainted | +| template-sinks.js:25:16:25:22 | tainted | +| template-sinks.js:25:16:25:22 | tainted | +| template-sinks.js:26:27:26:33 | tainted | +| template-sinks.js:26:27:26:33 | tainted | +| template-sinks.js:27:21:27:27 | tainted | +| template-sinks.js:27:21:27:27 | tainted | +| template-sinks.js:28:17:28:23 | tainted | +| template-sinks.js:28:17:28:23 | tainted | +| template-sinks.js:29:24:29:30 | tainted | +| template-sinks.js:29:24:29:30 | tainted | +| template-sinks.js:30:21:30:27 | tainted | +| template-sinks.js:30:21:30:27 | tainted | +| template-sinks.js:31:19:31:25 | tainted | +| template-sinks.js:31:19:31:25 | tainted | +| template-sinks.js:32:16:32:22 | tainted | +| template-sinks.js:32:16:32:22 | tainted | +| template-sinks.js:33:17:33:23 | tainted | +| template-sinks.js:33:17:33:23 | tainted | | tst.js:2:6:2:27 | documen ... on.href | | tst.js:2:6:2:27 | documen ... on.href | | tst.js:2:6:2:83 | documen ... t=")+8) | @@ -185,6 +185,24 @@ nodes | tst.js:35:28:35:33 | source | | tst.js:37:33:37:38 | source | | tst.js:37:33:37:38 | source | +| webix/webix.html:3:16:3:37 | documen ... on.hash | +| webix/webix.html:3:16:3:37 | documen ... on.hash | +| webix/webix.html:3:16:3:37 | documen ... on.hash | +| webix/webix.html:4:26:4:47 | documen ... on.hash | +| webix/webix.html:4:26:4:47 | documen ... on.hash | +| webix/webix.html:4:26:4:47 | documen ... on.hash | +| webix/webix.html:5:47:5:68 | documen ... on.hash | +| webix/webix.html:5:47:5:68 | documen ... on.hash | +| webix/webix.html:5:47:5:68 | documen ... on.hash | +| webix/webix.js:3:12:3:33 | documen ... on.hash | +| webix/webix.js:3:12:3:33 | documen ... on.hash | +| webix/webix.js:3:12:3:33 | documen ... on.hash | +| webix/webix.js:4:22:4:43 | documen ... on.hash | +| webix/webix.js:4:22:4:43 | documen ... on.hash | +| webix/webix.js:4:22:4:43 | documen ... on.hash | +| webix/webix.js:5:43:5:64 | documen ... on.hash | +| webix/webix.js:5:43:5:64 | documen ... on.hash | +| webix/webix.js:5:43:5:64 | documen ... on.hash | edges | NoSQLCodeInjection.js:18:24:18:31 | req.body | NoSQLCodeInjection.js:18:24:18:37 | req.body.query | | NoSQLCodeInjection.js:18:24:18:31 | req.body | NoSQLCodeInjection.js:18:24:18:37 | req.body.query | @@ -254,36 +272,36 @@ edges | react-native.js:7:17:7:33 | req.param("code") | react-native.js:7:7:7:33 | tainted | | react-native.js:7:17:7:33 | req.param("code") | react-native.js:7:7:7:33 | tainted | | react.js:10:56:10:77 | documen ... on.hash | react.js:10:56:10:77 | documen ... on.hash | -| template-sinks.js:17:9:17:31 | tainted | template-sinks.js:19:17:19:23 | tainted | -| template-sinks.js:17:9:17:31 | tainted | template-sinks.js:19:17:19:23 | tainted | -| template-sinks.js:17:9:17:31 | tainted | template-sinks.js:20:16:20:22 | tainted | -| template-sinks.js:17:9:17:31 | tainted | template-sinks.js:20:16:20:22 | tainted | -| template-sinks.js:17:9:17:31 | tainted | template-sinks.js:21:18:21:24 | tainted | -| template-sinks.js:17:9:17:31 | tainted | template-sinks.js:21:18:21:24 | tainted | -| template-sinks.js:17:9:17:31 | tainted | template-sinks.js:22:17:22:23 | tainted | -| template-sinks.js:17:9:17:31 | tainted | template-sinks.js:22:17:22:23 | tainted | -| template-sinks.js:17:9:17:31 | tainted | template-sinks.js:23:18:23:24 | tainted | -| template-sinks.js:17:9:17:31 | tainted | template-sinks.js:23:18:23:24 | tainted | -| template-sinks.js:17:9:17:31 | tainted | template-sinks.js:24:16:24:22 | tainted | -| template-sinks.js:17:9:17:31 | tainted | template-sinks.js:24:16:24:22 | tainted | -| template-sinks.js:17:9:17:31 | tainted | template-sinks.js:25:27:25:33 | tainted | -| template-sinks.js:17:9:17:31 | tainted | template-sinks.js:25:27:25:33 | tainted | -| template-sinks.js:17:9:17:31 | tainted | template-sinks.js:26:21:26:27 | tainted | -| template-sinks.js:17:9:17:31 | tainted | template-sinks.js:26:21:26:27 | tainted | -| template-sinks.js:17:9:17:31 | tainted | template-sinks.js:27:17:27:23 | tainted | -| template-sinks.js:17:9:17:31 | tainted | template-sinks.js:27:17:27:23 | tainted | -| template-sinks.js:17:9:17:31 | tainted | template-sinks.js:28:24:28:30 | tainted | -| template-sinks.js:17:9:17:31 | tainted | template-sinks.js:28:24:28:30 | tainted | -| template-sinks.js:17:9:17:31 | tainted | template-sinks.js:29:21:29:27 | tainted | -| template-sinks.js:17:9:17:31 | tainted | template-sinks.js:29:21:29:27 | tainted | -| template-sinks.js:17:9:17:31 | tainted | template-sinks.js:30:19:30:25 | tainted | -| template-sinks.js:17:9:17:31 | tainted | template-sinks.js:30:19:30:25 | tainted | -| template-sinks.js:17:9:17:31 | tainted | template-sinks.js:31:16:31:22 | tainted | -| template-sinks.js:17:9:17:31 | tainted | template-sinks.js:31:16:31:22 | tainted | -| template-sinks.js:17:9:17:31 | tainted | template-sinks.js:32:17:32:23 | tainted | -| template-sinks.js:17:9:17:31 | tainted | template-sinks.js:32:17:32:23 | tainted | -| template-sinks.js:17:19:17:31 | req.query.foo | template-sinks.js:17:9:17:31 | tainted | -| template-sinks.js:17:19:17:31 | req.query.foo | template-sinks.js:17:9:17:31 | tainted | +| template-sinks.js:18:9:18:31 | tainted | template-sinks.js:20:17:20:23 | tainted | +| template-sinks.js:18:9:18:31 | tainted | template-sinks.js:20:17:20:23 | tainted | +| template-sinks.js:18:9:18:31 | tainted | template-sinks.js:21:16:21:22 | tainted | +| template-sinks.js:18:9:18:31 | tainted | template-sinks.js:21:16:21:22 | tainted | +| template-sinks.js:18:9:18:31 | tainted | template-sinks.js:22:18:22:24 | tainted | +| template-sinks.js:18:9:18:31 | tainted | template-sinks.js:22:18:22:24 | tainted | +| template-sinks.js:18:9:18:31 | tainted | template-sinks.js:23:17:23:23 | tainted | +| template-sinks.js:18:9:18:31 | tainted | template-sinks.js:23:17:23:23 | tainted | +| template-sinks.js:18:9:18:31 | tainted | template-sinks.js:24:18:24:24 | tainted | +| template-sinks.js:18:9:18:31 | tainted | template-sinks.js:24:18:24:24 | tainted | +| template-sinks.js:18:9:18:31 | tainted | template-sinks.js:25:16:25:22 | tainted | +| template-sinks.js:18:9:18:31 | tainted | template-sinks.js:25:16:25:22 | tainted | +| template-sinks.js:18:9:18:31 | tainted | template-sinks.js:26:27:26:33 | tainted | +| template-sinks.js:18:9:18:31 | tainted | template-sinks.js:26:27:26:33 | tainted | +| template-sinks.js:18:9:18:31 | tainted | template-sinks.js:27:21:27:27 | tainted | +| template-sinks.js:18:9:18:31 | tainted | template-sinks.js:27:21:27:27 | tainted | +| template-sinks.js:18:9:18:31 | tainted | template-sinks.js:28:17:28:23 | tainted | +| template-sinks.js:18:9:18:31 | tainted | template-sinks.js:28:17:28:23 | tainted | +| template-sinks.js:18:9:18:31 | tainted | template-sinks.js:29:24:29:30 | tainted | +| template-sinks.js:18:9:18:31 | tainted | template-sinks.js:29:24:29:30 | tainted | +| template-sinks.js:18:9:18:31 | tainted | template-sinks.js:30:21:30:27 | tainted | +| template-sinks.js:18:9:18:31 | tainted | template-sinks.js:30:21:30:27 | tainted | +| template-sinks.js:18:9:18:31 | tainted | template-sinks.js:31:19:31:25 | tainted | +| template-sinks.js:18:9:18:31 | tainted | template-sinks.js:31:19:31:25 | tainted | +| template-sinks.js:18:9:18:31 | tainted | template-sinks.js:32:16:32:22 | tainted | +| template-sinks.js:18:9:18:31 | tainted | template-sinks.js:32:16:32:22 | tainted | +| template-sinks.js:18:9:18:31 | tainted | template-sinks.js:33:17:33:23 | tainted | +| template-sinks.js:18:9:18:31 | tainted | template-sinks.js:33:17:33:23 | tainted | +| template-sinks.js:18:19:18:31 | req.query.foo | template-sinks.js:18:9:18:31 | tainted | +| template-sinks.js:18:19:18:31 | req.query.foo | template-sinks.js:18:9:18:31 | tainted | | tst.js:2:6:2:27 | documen ... on.href | tst.js:2:6:2:83 | documen ... t=")+8) | | tst.js:2:6:2:27 | documen ... on.href | tst.js:2:6:2:83 | documen ... t=")+8) | | tst.js:2:6:2:27 | documen ... on.href | tst.js:2:6:2:83 | documen ... t=")+8) | @@ -314,5 +332,11 @@ edges | tst.js:29:18:29:41 | documen ... .search | tst.js:29:18:29:82 | documen ... , "$1") | | tst.js:29:18:29:41 | documen ... .search | tst.js:29:18:29:82 | documen ... , "$1") | | tst.js:29:18:29:82 | documen ... , "$1") | tst.js:29:9:29:82 | source | +| webix/webix.html:3:16:3:37 | documen ... on.hash | webix/webix.html:3:16:3:37 | documen ... on.hash | +| webix/webix.html:4:26:4:47 | documen ... on.hash | webix/webix.html:4:26:4:47 | documen ... on.hash | +| webix/webix.html:5:47:5:68 | documen ... on.hash | webix/webix.html:5:47:5:68 | documen ... on.hash | +| webix/webix.js:3:12:3:33 | documen ... on.hash | webix/webix.js:3:12:3:33 | documen ... on.hash | +| webix/webix.js:4:22:4:43 | documen ... on.hash | webix/webix.js:4:22:4:43 | documen ... on.hash | +| webix/webix.js:5:43:5:64 | documen ... on.hash | webix/webix.js:5:43:5:64 | documen ... on.hash | #select | eslint-escope-build.js:21:16:21:16 | c | eslint-escope-build.js:20:22:20:22 | c | eslint-escope-build.js:21:16:21:16 | c | $@ flows to here and is interpreted as code. | eslint-escope-build.js:20:22:20:22 | c | User-provided value | diff --git a/javascript/ql/test/query-tests/Security/CWE-094/CodeInjection/template-sinks.js b/javascript/ql/test/query-tests/Security/CWE-094/CodeInjection/template-sinks.js index f2cc7a400f6..51554663e4e 100644 --- a/javascript/ql/test/query-tests/Security/CWE-094/CodeInjection/template-sinks.js +++ b/javascript/ql/test/query-tests/Security/CWE-094/CodeInjection/template-sinks.js @@ -10,10 +10,11 @@ import * as mustache from 'mustache'; const Hogan = require("hogan.js"); import * as Eta from 'eta'; import * as Sqrl from 'squirrelly' +import * as webix from "webix"; var app = express(); -app.get('/some/path', function(req, res) { +app.get('/some/path', function (req, res) { let tainted = req.query.foo; pug.compile(tainted); // NOT OK diff --git a/javascript/ql/test/query-tests/Security/CWE-094/CodeInjection/webix/webix.html b/javascript/ql/test/query-tests/Security/CWE-094/CodeInjection/webix/webix.html new file mode 100644 index 00000000000..3f62fd32621 --- /dev/null +++ b/javascript/ql/test/query-tests/Security/CWE-094/CodeInjection/webix/webix.html @@ -0,0 +1,6 @@ + + \ No newline at end of file diff --git a/javascript/ql/test/query-tests/Security/CWE-094/CodeInjection/webix/webix.js b/javascript/ql/test/query-tests/Security/CWE-094/CodeInjection/webix/webix.js new file mode 100644 index 00000000000..00fb98af967 --- /dev/null +++ b/javascript/ql/test/query-tests/Security/CWE-094/CodeInjection/webix/webix.js @@ -0,0 +1,5 @@ +import * as webix from 'webix'; + +webix.exec(document.location.hash); // NOT OK +webix.ui({ template: document.location.hash }); // NOT OK +webix.ui({ template: function () { return document.location.hash } }); // NOT OK \ No newline at end of file diff --git a/javascript/ql/test/query-tests/Security/CWE-116/IncompleteSanitization/tst-multi-character-sanitization.js b/javascript/ql/test/query-tests/Security/CWE-116/IncompleteSanitization/tst-multi-character-sanitization.js index 43f650acce9..2c31e247bf2 100644 --- a/javascript/ql/test/query-tests/Security/CWE-116/IncompleteSanitization/tst-multi-character-sanitization.js +++ b/javascript/ql/test/query-tests/Security/CWE-116/IncompleteSanitization/tst-multi-character-sanitization.js @@ -152,4 +152,6 @@ n.cloneNode(false).outerHTML.replace(/<\/?[\w:\-]+ ?|=[\"][^\"]+\"|=\'[^\']+\'|=[\w\-]+|>/gi, '').replace(/[\w:\-]+/gi, function(a) { // NOT OK o.push({specified : 1, nodeName : a}); }); + + content = content.replace(/.+?(?=\s)/, ''); // OK }); \ No newline at end of file diff --git a/javascript/ql/test/query-tests/Security/CWE-312/BuildArtifactLeak.expected b/javascript/ql/test/query-tests/Security/CWE-312/BuildArtifactLeak.expected index e7f3be74b04..8514ae58104 100644 --- a/javascript/ql/test/query-tests/Security/CWE-312/BuildArtifactLeak.expected +++ b/javascript/ql/test/query-tests/Security/CWE-312/BuildArtifactLeak.expected @@ -9,12 +9,15 @@ nodes | build-leaks.js:14:18:14:20 | env | | build-leaks.js:15:24:15:34 | process.env | | build-leaks.js:15:24:15:34 | process.env | +| build-leaks.js:15:24:15:39 | process.env[key] | | build-leaks.js:16:20:16:22 | env | | build-leaks.js:21:11:26:5 | stringifed | | build-leaks.js:21:24:26:5 | {\\n ... )\\n } | | build-leaks.js:22:24:25:14 | Object. ... }, {}) | | build-leaks.js:22:49:22:51 | env | +| build-leaks.js:23:24:23:47 | JSON.st ... w[key]) | | build-leaks.js:23:39:23:41 | raw | +| build-leaks.js:23:39:23:46 | raw[key] | | build-leaks.js:24:20:24:22 | env | | build-leaks.js:30:22:30:31 | stringifed | | build-leaks.js:34:26:34:57 | getEnv( ... ngified | @@ -36,13 +39,19 @@ edges | build-leaks.js:14:18:14:20 | env | build-leaks.js:16:20:16:22 | env | | build-leaks.js:15:24:15:34 | process.env | build-leaks.js:14:18:14:20 | env | | build-leaks.js:15:24:15:34 | process.env | build-leaks.js:14:18:14:20 | env | +| build-leaks.js:15:24:15:34 | process.env | build-leaks.js:15:24:15:39 | process.env[key] | +| build-leaks.js:15:24:15:34 | process.env | build-leaks.js:15:24:15:39 | process.env[key] | +| build-leaks.js:15:24:15:39 | process.env[key] | build-leaks.js:14:18:14:20 | env | | build-leaks.js:16:20:16:22 | env | build-leaks.js:13:17:19:10 | Object. ... }) | | build-leaks.js:16:20:16:22 | env | build-leaks.js:14:18:14:20 | env | | build-leaks.js:21:11:26:5 | stringifed | build-leaks.js:30:22:30:31 | stringifed | | build-leaks.js:21:24:26:5 | {\\n ... )\\n } | build-leaks.js:21:11:26:5 | stringifed | | build-leaks.js:22:24:25:14 | Object. ... }, {}) | build-leaks.js:21:24:26:5 | {\\n ... )\\n } | | build-leaks.js:22:49:22:51 | env | build-leaks.js:24:20:24:22 | env | +| build-leaks.js:23:24:23:47 | JSON.st ... w[key]) | build-leaks.js:22:49:22:51 | env | | build-leaks.js:23:39:23:41 | raw | build-leaks.js:22:49:22:51 | env | +| build-leaks.js:23:39:23:41 | raw | build-leaks.js:23:39:23:46 | raw[key] | +| build-leaks.js:23:39:23:46 | raw[key] | build-leaks.js:23:24:23:47 | JSON.st ... w[key]) | | build-leaks.js:24:20:24:22 | env | build-leaks.js:22:24:25:14 | Object. ... }, {}) | | build-leaks.js:24:20:24:22 | env | build-leaks.js:22:49:22:51 | env | | build-leaks.js:30:22:30:31 | stringifed | build-leaks.js:34:26:34:57 | getEnv( ... ngified | diff --git a/javascript/ql/test/query-tests/Security/CWE-915/PrototypePollutingMergeCall/PrototypePollutingMergeCall.expected b/javascript/ql/test/query-tests/Security/CWE-915/PrototypePollutingMergeCall/PrototypePollutingMergeCall.expected index f7b8f8df1d3..a697bd24760 100644 --- a/javascript/ql/test/query-tests/Security/CWE-915/PrototypePollutingMergeCall/PrototypePollutingMergeCall.expected +++ b/javascript/ql/test/query-tests/Security/CWE-915/PrototypePollutingMergeCall/PrototypePollutingMergeCall.expected @@ -17,6 +17,26 @@ nodes | src-vulnerable-lodash/tst.js:17:17:19:5 | {\\n ... K\\n } | | src-vulnerable-lodash/tst.js:17:17:19:5 | {\\n ... K\\n } | | src-vulnerable-lodash/tst.js:18:16:18:25 | opts.thing | +| webix/webix.html:3:34:3:38 | event | +| webix/webix.html:3:34:3:38 | event | +| webix/webix.html:4:26:4:47 | JSON.pa ... t.data) | +| webix/webix.html:4:26:4:47 | JSON.pa ... t.data) | +| webix/webix.html:4:37:4:41 | event | +| webix/webix.html:4:37:4:46 | event.data | +| webix/webix.html:5:24:5:45 | JSON.pa ... t.data) | +| webix/webix.html:5:24:5:45 | JSON.pa ... t.data) | +| webix/webix.html:5:35:5:39 | event | +| webix/webix.html:5:35:5:44 | event.data | +| webix/webix.js:3:30:3:34 | event | +| webix/webix.js:3:30:3:34 | event | +| webix/webix.js:4:22:4:43 | JSON.pa ... t.data) | +| webix/webix.js:4:22:4:43 | JSON.pa ... t.data) | +| webix/webix.js:4:33:4:37 | event | +| webix/webix.js:4:33:4:42 | event.data | +| webix/webix.js:5:20:5:41 | JSON.pa ... t.data) | +| webix/webix.js:5:20:5:41 | JSON.pa ... t.data) | +| webix/webix.js:5:31:5:35 | event | +| webix/webix.js:5:31:5:40 | event.data | edges | angularmerge.js:1:30:1:34 | event | angularmerge.js:2:32:2:36 | event | | angularmerge.js:1:30:1:34 | event | angularmerge.js:2:32:2:36 | event | @@ -32,8 +52,32 @@ edges | src-vulnerable-lodash/tst.js:15:14:15:28 | req.query.value | src-vulnerable-lodash/tst.js:18:16:18:25 | opts.thing | | src-vulnerable-lodash/tst.js:18:16:18:25 | opts.thing | src-vulnerable-lodash/tst.js:17:17:19:5 | {\\n ... K\\n } | | src-vulnerable-lodash/tst.js:18:16:18:25 | opts.thing | src-vulnerable-lodash/tst.js:17:17:19:5 | {\\n ... K\\n } | +| webix/webix.html:3:34:3:38 | event | webix/webix.html:4:37:4:41 | event | +| webix/webix.html:3:34:3:38 | event | webix/webix.html:4:37:4:41 | event | +| webix/webix.html:3:34:3:38 | event | webix/webix.html:5:35:5:39 | event | +| webix/webix.html:3:34:3:38 | event | webix/webix.html:5:35:5:39 | event | +| webix/webix.html:4:37:4:41 | event | webix/webix.html:4:37:4:46 | event.data | +| webix/webix.html:4:37:4:46 | event.data | webix/webix.html:4:26:4:47 | JSON.pa ... t.data) | +| webix/webix.html:4:37:4:46 | event.data | webix/webix.html:4:26:4:47 | JSON.pa ... t.data) | +| webix/webix.html:5:35:5:39 | event | webix/webix.html:5:35:5:44 | event.data | +| webix/webix.html:5:35:5:44 | event.data | webix/webix.html:5:24:5:45 | JSON.pa ... t.data) | +| webix/webix.html:5:35:5:44 | event.data | webix/webix.html:5:24:5:45 | JSON.pa ... t.data) | +| webix/webix.js:3:30:3:34 | event | webix/webix.js:4:33:4:37 | event | +| webix/webix.js:3:30:3:34 | event | webix/webix.js:4:33:4:37 | event | +| webix/webix.js:3:30:3:34 | event | webix/webix.js:5:31:5:35 | event | +| webix/webix.js:3:30:3:34 | event | webix/webix.js:5:31:5:35 | event | +| webix/webix.js:4:33:4:37 | event | webix/webix.js:4:33:4:42 | event.data | +| webix/webix.js:4:33:4:42 | event.data | webix/webix.js:4:22:4:43 | JSON.pa ... t.data) | +| webix/webix.js:4:33:4:42 | event.data | webix/webix.js:4:22:4:43 | JSON.pa ... t.data) | +| webix/webix.js:5:31:5:35 | event | webix/webix.js:5:31:5:40 | event.data | +| webix/webix.js:5:31:5:40 | event.data | webix/webix.js:5:20:5:41 | JSON.pa ... t.data) | +| webix/webix.js:5:31:5:40 | event.data | webix/webix.js:5:20:5:41 | JSON.pa ... t.data) | #select | angularmerge.js:2:21:2:42 | JSON.pa ... t.data) | angularmerge.js:1:30:1:34 | event | angularmerge.js:2:21:2:42 | JSON.pa ... t.data) | Prototype pollution caused by merging a $@ using a vulnerable version of $@. | angularmerge.js:1:30:1:34 | event | user-controlled value | angularmerge.js:2:3:2:43 | angular ... .data)) | angular | | src-vulnerable-lodash/tst.js:7:17:7:29 | req.query.foo | src-vulnerable-lodash/tst.js:7:17:7:29 | req.query.foo | src-vulnerable-lodash/tst.js:7:17:7:29 | req.query.foo | Prototype pollution caused by merging a $@ using a vulnerable version of $@. | src-vulnerable-lodash/tst.js:7:17:7:29 | req.query.foo | user-controlled value | src-vulnerable-lodash/package.json:3:19:3:26 | "4.17.4" | lodash | | src-vulnerable-lodash/tst.js:10:17:12:5 | {\\n ... K\\n } | src-vulnerable-lodash/tst.js:11:16:11:30 | req.query.value | src-vulnerable-lodash/tst.js:10:17:12:5 | {\\n ... K\\n } | Prototype pollution caused by merging a $@ using a vulnerable version of $@. | src-vulnerable-lodash/tst.js:11:16:11:30 | req.query.value | user-controlled value | src-vulnerable-lodash/package.json:3:19:3:26 | "4.17.4" | lodash | | src-vulnerable-lodash/tst.js:17:17:19:5 | {\\n ... K\\n } | src-vulnerable-lodash/tst.js:15:14:15:28 | req.query.value | src-vulnerable-lodash/tst.js:17:17:19:5 | {\\n ... K\\n } | Prototype pollution caused by merging a $@ using a vulnerable version of $@. | src-vulnerable-lodash/tst.js:15:14:15:28 | req.query.value | user-controlled value | src-vulnerable-lodash/package.json:3:19:3:26 | "4.17.4" | lodash | +| webix/webix.html:4:26:4:47 | JSON.pa ... t.data) | webix/webix.html:3:34:3:38 | event | webix/webix.html:4:26:4:47 | JSON.pa ... t.data) | Prototype pollution caused by merging a $@ using a vulnerable version of $@. | webix/webix.html:3:34:3:38 | event | user-controlled value | webix/webix.html:4:9:4:48 | webix.e ... .data)) | webix | +| webix/webix.html:5:24:5:45 | JSON.pa ... t.data) | webix/webix.html:3:34:3:38 | event | webix/webix.html:5:24:5:45 | JSON.pa ... t.data) | Prototype pollution caused by merging a $@ using a vulnerable version of $@. | webix/webix.html:3:34:3:38 | event | user-controlled value | webix/webix.html:5:9:5:46 | webix.c ... .data)) | webix | +| webix/webix.js:4:22:4:43 | JSON.pa ... t.data) | webix/webix.js:3:30:3:34 | event | webix/webix.js:4:22:4:43 | JSON.pa ... t.data) | Prototype pollution caused by merging a $@ using a vulnerable version of $@. | webix/webix.js:3:30:3:34 | event | user-controlled value | webix/webix.js:4:5:4:44 | webix.e ... .data)) | webix | +| webix/webix.js:5:20:5:41 | JSON.pa ... t.data) | webix/webix.js:3:30:3:34 | event | webix/webix.js:5:20:5:41 | JSON.pa ... t.data) | Prototype pollution caused by merging a $@ using a vulnerable version of $@. | webix/webix.js:3:30:3:34 | event | user-controlled value | webix/webix.js:5:5:5:42 | webix.c ... .data)) | webix | diff --git a/javascript/ql/test/query-tests/Security/CWE-915/PrototypePollutingMergeCall/webix/webix.html b/javascript/ql/test/query-tests/Security/CWE-915/PrototypePollutingMergeCall/webix/webix.html new file mode 100644 index 00000000000..02d6d086d54 --- /dev/null +++ b/javascript/ql/test/query-tests/Security/CWE-915/PrototypePollutingMergeCall/webix/webix.html @@ -0,0 +1,7 @@ + + \ No newline at end of file diff --git a/javascript/ql/test/query-tests/Security/CWE-915/PrototypePollutingMergeCall/webix/webix.js b/javascript/ql/test/query-tests/Security/CWE-915/PrototypePollutingMergeCall/webix/webix.js new file mode 100644 index 00000000000..acbfa2acbb4 --- /dev/null +++ b/javascript/ql/test/query-tests/Security/CWE-915/PrototypePollutingMergeCall/webix/webix.js @@ -0,0 +1,6 @@ +import * as webix from "webix"; + +addEventListener("message", (event) => { + webix.extend({}, JSON.parse(event.data)); // NOT OK + webix.copy({}, JSON.parse(event.data)); // NOT OK +}); diff --git a/misc/suite-helpers/CHANGELOG.md b/misc/suite-helpers/CHANGELOG.md index 9571c393549..f3a5d5d8a89 100644 --- a/misc/suite-helpers/CHANGELOG.md +++ b/misc/suite-helpers/CHANGELOG.md @@ -1,3 +1,11 @@ +## 0.6.0 + +No user-facing changes. + +## 0.5.4 + +No user-facing changes. + ## 0.5.3 No user-facing changes. diff --git a/misc/suite-helpers/change-notes/released/0.5.4.md b/misc/suite-helpers/change-notes/released/0.5.4.md new file mode 100644 index 00000000000..1686ab4354d --- /dev/null +++ b/misc/suite-helpers/change-notes/released/0.5.4.md @@ -0,0 +1,3 @@ +## 0.5.4 + +No user-facing changes. diff --git a/misc/suite-helpers/change-notes/released/0.6.0.md b/misc/suite-helpers/change-notes/released/0.6.0.md new file mode 100644 index 00000000000..4383f5ec1ea --- /dev/null +++ b/misc/suite-helpers/change-notes/released/0.6.0.md @@ -0,0 +1,3 @@ +## 0.6.0 + +No user-facing changes. diff --git a/misc/suite-helpers/codeql-pack.release.yml b/misc/suite-helpers/codeql-pack.release.yml index 2164e038a5d..a3f820f884d 100644 --- a/misc/suite-helpers/codeql-pack.release.yml +++ b/misc/suite-helpers/codeql-pack.release.yml @@ -1,2 +1,2 @@ --- -lastReleaseVersion: 0.5.3 +lastReleaseVersion: 0.6.0 diff --git a/misc/suite-helpers/qlpack.yml b/misc/suite-helpers/qlpack.yml index bd19b10ebaf..576f6195701 100644 --- a/misc/suite-helpers/qlpack.yml +++ b/misc/suite-helpers/qlpack.yml @@ -1,4 +1,4 @@ name: codeql/suite-helpers -version: 0.5.4-dev +version: 0.6.1-dev groups: shared warnOnImplicitThis: true diff --git a/python/ql/lib/CHANGELOG.md b/python/ql/lib/CHANGELOG.md index 3bfc2ddf115..b94cc6f9162 100644 --- a/python/ql/lib/CHANGELOG.md +++ b/python/ql/lib/CHANGELOG.md @@ -1,3 +1,26 @@ +## 0.10.0 + +### New Features + +* It is now possible to specify flow summaries in the format "MyPkg;Member[list_map];Argument[1].ListElement;Argument[0].Parameter[0];value" + +### Minor Analysis Improvements + +* Deleted many models that used the old dataflow library, the new models can be found in the `python/ql/lib/semmle/python/frameworks` folder. +* More precise modeling of several container functions (such as `sorted`, `reversed`) and methods (such as `set.add`, `list.append`). +* Added modeling of taint flow through the template argument of `flask.render_template_string` and `flask.stream_template_string`. +* Deleted many deprecated predicates and classes with uppercase `API`, `HTTP`, `XSS`, `SQL`, etc. in their names. Use the PascalCased versions instead. +* Deleted the deprecated `getName()` predicate from the `Container` class, use `getAbsolutePath()` instead. +* Deleted many deprecated module names that started with a lowercase letter, use the versions that start with an uppercase letter instead. +* Deleted many deprecated predicates in `PointsTo.qll`. +* Deleted many deprecated files from the `semmle.python.security` package. +* Deleted the deprecated `BottleRoutePointToExtension` class from `Extensions.qll`. +* Type tracking is now aware of flow summaries. This leads to a richer API graph, and may lead to more results in some queries. + +## 0.9.4 + +No user-facing changes. + ## 0.9.3 No user-facing changes. 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 deleted file mode 100644 index 11c01629987..00000000000 --- a/python/ql/lib/change-notes/2023-05-30-typetracking-via-flow-summaries.md +++ /dev/null @@ -1,4 +0,0 @@ ---- -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-09-delete-deps.md b/python/ql/lib/change-notes/2023-06-09-delete-deps.md deleted file mode 100644 index 75753ea93b7..00000000000 --- a/python/ql/lib/change-notes/2023-06-09-delete-deps.md +++ /dev/null @@ -1,9 +0,0 @@ ---- -category: minorAnalysis ---- -* Deleted many deprecated predicates and classes with uppercase `API`, `HTTP`, `XSS`, `SQL`, etc. in their names. Use the PascalCased versions instead. -* Deleted the deprecated `getName()` predicate from the `Container` class, use `getAbsolutePath()` instead. -* Deleted many deprecated module names that started with a lowercase letter, use the versions that start with an uppercase letter instead. -* Deleted many deprecated predicates in `PointsTo.qll`. -* Deleted many deprecated files from the `semmle.python.security` package. -* Deleted the deprecated `BottleRoutePointToExtension` class from `Extensions.qll`. \ No newline at end of file diff --git a/python/ql/lib/change-notes/2023-06-12-flask-render-template-string.md b/python/ql/lib/change-notes/2023-06-12-flask-render-template-string.md deleted file mode 100644 index d9f1a2e5d5c..00000000000 --- a/python/ql/lib/change-notes/2023-06-12-flask-render-template-string.md +++ /dev/null @@ -1,4 +0,0 @@ ---- -category: minorAnalysis ---- -* Added modeling of taint flow through the template argument of `flask.render_template_string` and `flask.stream_template_string`. diff --git a/python/ql/lib/change-notes/2023-06-13-container-store-steps.md b/python/ql/lib/change-notes/2023-06-13-container-store-steps.md deleted file mode 100644 index 3e12554a92b..00000000000 --- a/python/ql/lib/change-notes/2023-06-13-container-store-steps.md +++ /dev/null @@ -1,4 +0,0 @@ ---- -category: minorAnalysis ---- -* More precise modelling of several container functions (such as `sorted`, `reversed`) and methods (such as `set.add`, `list.append`). 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 deleted file mode 100644 index 16946163f5e..00000000000 --- a/python/ql/lib/change-notes/2023-06-14-delete-deps.md +++ /dev/null @@ -1,4 +0,0 @@ ---- -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 deleted file mode 100644 index feded1bb6c5..00000000000 --- a/python/ql/lib/change-notes/2023-06-20-summaries-from-models.md +++ /dev/null @@ -1,4 +0,0 @@ ---- -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/2023-07-12-default-stateconfigsig-predicates.md b/python/ql/lib/change-notes/2023-07-12-default-stateconfigsig-predicates.md new file mode 100644 index 00000000000..c9aba58603a --- /dev/null +++ b/python/ql/lib/change-notes/2023-07-12-default-stateconfigsig-predicates.md @@ -0,0 +1,6 @@ +--- +category: feature +--- +* The `DataFlow::StateConfigSig` signature module has gained default implementations for `isBarrier/2` and `isAdditionalFlowStep/4`. + Hence it is no longer needed to provide `none()` implementations of these predicates if they are not needed. + diff --git a/python/ql/lib/change-notes/released/0.10.0.md b/python/ql/lib/change-notes/released/0.10.0.md new file mode 100644 index 00000000000..bd0224251fd --- /dev/null +++ b/python/ql/lib/change-notes/released/0.10.0.md @@ -0,0 +1,18 @@ +## 0.10.0 + +### New Features + +* It is now possible to specify flow summaries in the format "MyPkg;Member[list_map];Argument[1].ListElement;Argument[0].Parameter[0];value" + +### Minor Analysis Improvements + +* Deleted many models that used the old dataflow library, the new models can be found in the `python/ql/lib/semmle/python/frameworks` folder. +* More precise modeling of several container functions (such as `sorted`, `reversed`) and methods (such as `set.add`, `list.append`). +* Added modeling of taint flow through the template argument of `flask.render_template_string` and `flask.stream_template_string`. +* Deleted many deprecated predicates and classes with uppercase `API`, `HTTP`, `XSS`, `SQL`, etc. in their names. Use the PascalCased versions instead. +* Deleted the deprecated `getName()` predicate from the `Container` class, use `getAbsolutePath()` instead. +* Deleted many deprecated module names that started with a lowercase letter, use the versions that start with an uppercase letter instead. +* Deleted many deprecated predicates in `PointsTo.qll`. +* Deleted many deprecated files from the `semmle.python.security` package. +* Deleted the deprecated `BottleRoutePointToExtension` class from `Extensions.qll`. +* 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/released/0.9.4.md b/python/ql/lib/change-notes/released/0.9.4.md new file mode 100644 index 00000000000..092c14cb5d4 --- /dev/null +++ b/python/ql/lib/change-notes/released/0.9.4.md @@ -0,0 +1,3 @@ +## 0.9.4 + +No user-facing changes. diff --git a/python/ql/lib/codeql-pack.release.yml b/python/ql/lib/codeql-pack.release.yml index 7af7247cbb0..b21db623245 100644 --- a/python/ql/lib/codeql-pack.release.yml +++ b/python/ql/lib/codeql-pack.release.yml @@ -1,2 +1,2 @@ --- -lastReleaseVersion: 0.9.3 +lastReleaseVersion: 0.10.0 diff --git a/python/ql/lib/qlpack.yml b/python/ql/lib/qlpack.yml index 5701aac8cbf..47cdde09a15 100644 --- a/python/ql/lib/qlpack.yml +++ b/python/ql/lib/qlpack.yml @@ -1,5 +1,5 @@ name: codeql/python-all -version: 0.9.4-dev +version: 0.10.1-dev groups: python dbscheme: semmlecode.python.dbscheme extractor: python diff --git a/python/ql/lib/semmle/python/Concepts.qll b/python/ql/lib/semmle/python/Concepts.qll index 21ef902f2e5..f3026d8faad 100644 --- a/python/ql/lib/semmle/python/Concepts.qll +++ b/python/ql/lib/semmle/python/Concepts.qll @@ -680,6 +680,9 @@ module Escaping { /** Gets the escape-kind for escaping a string so it can safely be included in HTML. */ string getHtmlKind() { result = "html" } + /** Gets the escape-kind for escaping a string so it can safely be included in XML. */ + string getXmlKind() { result = "xml" } + /** Gets the escape-kind for escaping a string so it can safely be included in a regular expression. */ string getRegexKind() { result = "regex" } @@ -710,6 +713,15 @@ class HtmlEscaping extends Escaping { HtmlEscaping() { super.getKind() = Escaping::getHtmlKind() } } +/** + * An escape of a string so it can be safely included in + * the body of an XML element, for example, replacing `&` and `<>` in + * `&xxe;`. + */ +class XmlEscaping extends Escaping { + XmlEscaping() { super.getKind() = Escaping::getXmlKind() } +} + /** * An escape of a string so it can be safely included in * the body of a regex. diff --git a/python/ql/lib/semmle/python/dataflow/new/internal/DataFlow.qll b/python/ql/lib/semmle/python/dataflow/new/internal/DataFlow.qll index f34554d6eea..47329d133a4 100644 --- a/python/ql/lib/semmle/python/dataflow/new/internal/DataFlow.qll +++ b/python/ql/lib/semmle/python/dataflow/new/internal/DataFlow.qll @@ -114,7 +114,7 @@ signature module StateConfigSig { * Holds if data flow through `node` is prohibited when the flow state is * `state`. */ - predicate isBarrier(Node node, FlowState state); + default predicate isBarrier(Node node, FlowState state) { none() } /** Holds if data flow into `node` is prohibited. */ default predicate isBarrierIn(Node node) { none() } @@ -131,7 +131,9 @@ signature module StateConfigSig { * Holds if data may flow from `node1` to `node2` in addition to the normal data-flow steps. * This step is only applicable in `state1` and updates the flow state to `state2`. */ - predicate isAdditionalFlowStep(Node node1, FlowState state1, Node node2, FlowState state2); + default predicate isAdditionalFlowStep(Node node1, FlowState state1, Node node2, FlowState state2) { + none() + } /** * Holds if an arbitrary number of implicit read steps of content `c` may be 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 ba451a21fdf..97802218f3d 100644 --- a/python/ql/lib/semmle/python/dataflow/new/internal/DataFlowDispatch.qll +++ b/python/ql/lib/semmle/python/dataflow/new/internal/DataFlowDispatch.qll @@ -1227,7 +1227,6 @@ predicate normalCallArg(CallNode call, Node arg, ArgumentPosition apos) { * time the bound method is used, such that the `clear()` call would essentially be * translated into `l.clear()`, and we can still have use-use flow. */ -pragma[assume_small_delta] cached predicate getCallArg(CallNode call, Function target, CallType type, Node arg, ArgumentPosition apos) { Stages::DataFlow::ref() and 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 284fff191ae..fe8633e9218 100644 --- a/python/ql/lib/semmle/python/dataflow/new/internal/DataFlowImpl.qll +++ b/python/ql/lib/semmle/python/dataflow/new/internal/DataFlowImpl.qll @@ -254,6 +254,11 @@ module Impl { not fullBarrier(node2) } + pragma[nomagic] + private predicate isUnreachableInCall1(NodeEx n, LocalCallContextSpecificCall cc) { + isUnreachableInCallCached(n.asNode(), cc.getCall()) + } + /** * Holds if data can flow in one local step from `node1` to `node2`. */ @@ -460,7 +465,6 @@ module Impl { * The Boolean `cc` records whether the node is reached through an * argument in a call. */ - pragma[assume_small_delta] private predicate fwdFlow(NodeEx node, Cc cc) { sourceNode(node, _) and if hasSourceCallCtx() then cc = true else cc = false @@ -570,7 +574,6 @@ module Impl { /** * Holds if `c` is the target of a store in the flow covered by `fwdFlow`. */ - pragma[assume_small_delta] pragma[nomagic] private predicate fwdFlowConsCand(Content c) { exists(NodeEx mid, NodeEx node | @@ -1216,7 +1219,6 @@ module Impl { fwdFlow1(_, _, _, _, _, _, t0, t, ap, _) and t0 != t } - pragma[assume_small_delta] pragma[nomagic] private predicate fwdFlow0( NodeEx node, FlowState state, Cc cc, ParamNodeOption summaryCtx, TypOption argT, @@ -2111,7 +2113,7 @@ module Impl { NodeEx node1, FlowState state, NodeEx node2, boolean preservesValue, DataFlowType t, LocalCallContext cc ) { - not isUnreachableInCallCached(node2.asNode(), cc.(LocalCallContextSpecificCall).getCall()) and + not isUnreachableInCall1(node2, cc) and ( localFlowEntry(node1, pragma[only_bind_into](state)) and ( @@ -2126,7 +2128,7 @@ module Impl { ) and node1 != node2 and cc.relevantFor(node1.getEnclosingCallable()) and - not isUnreachableInCallCached(node1.asNode(), cc.(LocalCallContextSpecificCall).getCall()) + not isUnreachableInCall1(node1, cc) or exists(NodeEx mid | localFlowStepPlus(node1, pragma[only_bind_into](state), mid, preservesValue, t, cc) and @@ -2163,10 +2165,8 @@ module Impl { preservesValue = false and t = node2.getDataFlowType() and callContext.relevantFor(node1.getEnclosingCallable()) and - not exists(DataFlowCall call | call = callContext.(LocalCallContextSpecificCall).getCall() | - isUnreachableInCallCached(node1.asNode(), call) or - isUnreachableInCallCached(node2.asNode(), call) - ) + not isUnreachableInCall1(node1, callContext) and + not isUnreachableInCall1(node2, callContext) } } @@ -2706,7 +2706,7 @@ module Impl { ParamNodeEx getParamNode() { result = p } - override string toString() { result = p + ": " + ap } + override string toString() { result = p + concat(" : " + ppReprType(t)) + " " + ap } predicate hasLocationInfo( string filepath, int startline, int startcolumn, int endline, int endcolumn @@ -2758,12 +2758,21 @@ module Impl { ) } + private predicate forceUnfold(AccessPathApprox apa) { + forceHighPrecision(apa.getHead()) + or + exists(Content c2 | + apa = TConsCons(_, _, c2, _) and + forceHighPrecision(c2) + ) + } + /** * Holds with `unfold = false` if a precise head-tail representation of `apa` is * expected to be expensive. Holds with `unfold = true` otherwise. */ private predicate evalUnfold(AccessPathApprox apa, boolean unfold) { - if forceHighPrecision(apa.getHead()) + if forceUnfold(apa) then unfold = true else exists(int aps, int nodes, int apLimit, int tupleLimit | @@ -2777,7 +2786,6 @@ module Impl { /** * Gets the number of `AccessPath`s that correspond to `apa`. */ - pragma[assume_small_delta] private int countAps(AccessPathApprox apa) { evalUnfold(apa, false) and result = 1 and @@ -2796,7 +2804,6 @@ module Impl { * that it is expanded to a precise head-tail representation. */ language[monotonicAggregates] - pragma[assume_small_delta] private int countPotentialAps(AccessPathApprox apa) { apa instanceof AccessPathApproxNil and result = 1 or @@ -2833,7 +2840,6 @@ module Impl { } private newtype TPathNode = - pragma[assume_small_delta] TPathNodeMid( NodeEx node, FlowState state, CallContext cc, SummaryCtx sc, DataFlowType t, AccessPath ap ) { @@ -2918,7 +2924,6 @@ module Impl { override AccessPathFrontHead getFront() { result = TFrontHead(head_) } - pragma[assume_small_delta] override AccessPathApproxCons getApprox() { result = TConsNil(head_, t) and tail_ = TAccessPathNil() or @@ -2927,7 +2932,6 @@ module Impl { result = TCons1(head_, this.length()) } - pragma[assume_small_delta] override int length() { result = 1 + tail_.length() } private string toStringImpl(boolean needsSuffix) { @@ -3097,6 +3101,12 @@ module Impl { result = " <" + this.(PathNodeMid).getCallContext().toString() + ">" } + private string ppSummaryCtx() { + this instanceof PathNodeSink and result = "" + or + result = " <" + this.(PathNodeMid).getSummaryCtx().toString() + ">" + } + /** Gets a textual representation of this element. */ string toString() { result = this.getNodeEx().toString() + this.ppType() + this.ppAp() } @@ -3105,7 +3115,9 @@ module Impl { * representation of the call context. */ string toStringWithContext() { - result = this.getNodeEx().toString() + this.ppType() + this.ppAp() + this.ppCtx() + result = + this.getNodeEx().toString() + this.ppType() + this.ppAp() + this.ppCtx() + + this.ppSummaryCtx() } /** @@ -3379,7 +3391,6 @@ module Impl { * Holds if data may flow from `mid` to `node`. The last step in or out of * a callable is recorded by `cc`. */ - pragma[assume_small_delta] pragma[nomagic] private predicate pathStep0( PathNodeMid mid, NodeEx node, FlowState state, CallContext cc, SummaryCtx sc, DataFlowType t, @@ -3592,7 +3603,6 @@ module Impl { ) } - pragma[assume_small_delta] pragma[nomagic] private predicate pathThroughCallable0( DataFlowCall call, PathNodeMid mid, ReturnKindExt kind, FlowState state, CallContext cc, diff --git a/python/ql/lib/semmle/python/dataflow/new/internal/DataFlowImplCommon.qll b/python/ql/lib/semmle/python/dataflow/new/internal/DataFlowImplCommon.qll index 0d4c033c95d..aff14e7b44d 100644 --- a/python/ql/lib/semmle/python/dataflow/new/internal/DataFlowImplCommon.qll +++ b/python/ql/lib/semmle/python/dataflow/new/internal/DataFlowImplCommon.qll @@ -187,7 +187,6 @@ private module LambdaFlow { else any() } - pragma[assume_small_delta] pragma[nomagic] predicate revLambdaFlow0( DataFlowCall lambdaCall, LambdaCallKind kind, Node node, DataFlowType t, boolean toReturn, @@ -274,7 +273,6 @@ private module LambdaFlow { ) } - pragma[assume_small_delta] pragma[nomagic] predicate revLambdaFlowOut( DataFlowCall lambdaCall, LambdaCallKind kind, TReturnPositionSimple pos, DataFlowType t, 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 29504b6aa38..85b083e701a 100644 --- a/python/ql/lib/semmle/python/dataflow/new/internal/DataFlowPrivate.qll +++ b/python/ql/lib/semmle/python/dataflow/new/internal/DataFlowPrivate.qll @@ -304,8 +304,12 @@ module EssaFlow { // see `with_flow` in `python/ql/src/semmle/python/dataflow/Implementation.qll` with.getContextExpr() = contextManager.getNode() and with.getOptionalVars() = var.getNode() and - not with.isAsync() and contextManager.strictlyDominates(var) + // note: we allow this for both `with` and `async with`, since some + // implementations do `async def __aenter__(self): return self`, so you can do + // both: + // * `foo = x.foo(); await foo.async_method(); foo.close()` and + // * `async with x.foo() as foo: await foo.async_method()`. ) or // Async with var definition @@ -314,6 +318,12 @@ module EssaFlow { // nodeTo is `x`, essa var // // This makes the cfg node the local source of the awaited value. + // + // We have this step in addition to the step above, to handle cases where the QL + // modeling of `f(42)` requires a `.getAwaited()` step (in API graphs) when not + // using `async with`, so you can do both: + // * `foo = await x.foo(); await foo.async_method(); foo.close()` and + // * `async with x.foo() as foo: await foo.async_method()`. exists(With with, ControlFlowNode var | nodeFrom.(CfgNode).getNode() = var and nodeTo.(EssaNode).getVar().getDefinition().(WithDefinition).getDefiningNode() = var and diff --git a/python/ql/lib/semmle/python/dataflow/new/internal/FlowSummaryImpl.qll b/python/ql/lib/semmle/python/dataflow/new/internal/FlowSummaryImpl.qll index 9ea7c44c50c..7977e18120f 100644 --- a/python/ql/lib/semmle/python/dataflow/new/internal/FlowSummaryImpl.qll +++ b/python/ql/lib/semmle/python/dataflow/new/internal/FlowSummaryImpl.qll @@ -23,29 +23,30 @@ module Public { * content type, or a return kind. */ class SummaryComponent extends TSummaryComponent { - /** Gets a textual representation of this summary component. */ - string toString() { - exists(ContentSet c | this = TContentSummaryComponent(c) and result = c.toString()) - or - exists(ContentSet c | this = TWithoutContentSummaryComponent(c) and result = "without " + c) - or - exists(ContentSet c | this = TWithContentSummaryComponent(c) and result = "with " + c) + /** Gets a textual representation of this component used for MaD models. */ + string getMadRepresentation() { + result = getMadRepresentationSpecific(this) or exists(ArgumentPosition pos | - this = TParameterSummaryComponent(pos) and result = "parameter " + pos + this = TParameterSummaryComponent(pos) and + result = "Parameter[" + getArgumentPosition(pos) + "]" ) or exists(ParameterPosition pos | - this = TArgumentSummaryComponent(pos) and result = "argument " + pos + this = TArgumentSummaryComponent(pos) and + result = "Argument[" + getParameterPosition(pos) + "]" ) or - exists(ReturnKind rk | this = TReturnSummaryComponent(rk) and result = "return (" + rk + ")") - or - exists(SummaryComponent::SyntheticGlobal sg | - this = TSyntheticGlobalSummaryComponent(sg) and - result = "synthetic global (" + sg + ")" + exists(string synthetic | + this = TSyntheticGlobalSummaryComponent(synthetic) and + result = "SyntheticGlobal[" + synthetic + "]" ) + or + this = TReturnSummaryComponent(getReturnValueKind()) and result = "ReturnValue" } + + /** Gets a textual representation of this summary component. */ + string toString() { result = this.getMadRepresentation() } } /** Provides predicates for constructing summary components. */ @@ -110,7 +111,6 @@ module Public { } /** Gets the stack obtained by dropping the first `i` elements, if any. */ - pragma[assume_small_delta] SummaryComponentStack drop(int i) { i = 0 and result = this or @@ -125,19 +125,22 @@ module Public { this = TSingletonSummaryComponentStack(result) or result = this.tail().bottom() } - /** Gets a textual representation of this stack. */ - string toString() { + /** Gets a textual representation of this stack used for MaD models. */ + string getMadRepresentation() { exists(SummaryComponent head, SummaryComponentStack tail | head = this.head() and tail = this.tail() and - result = tail + "." + head + result = tail.getMadRepresentation() + "." + head.getMadRepresentation() ) or exists(SummaryComponent c | this = TSingletonSummaryComponentStack(c) and - result = c.toString() + result = c.getMadRepresentation() ) } + + /** Gets a textual representation of this stack. */ + string toString() { result = this.getMadRepresentation() } } /** Provides predicates for constructing stacks of summary components. */ @@ -166,42 +169,6 @@ module Public { SummaryComponentStack return(ReturnKind rk) { result = singleton(SummaryComponent::return(rk)) } } - /** Gets a textual representation of this component used for flow summaries. */ - private string getComponent(SummaryComponent sc) { - result = getComponentSpecific(sc) - or - exists(ArgumentPosition pos | - sc = TParameterSummaryComponent(pos) and - result = "Parameter[" + getArgumentPosition(pos) + "]" - ) - or - exists(ParameterPosition pos | - sc = TArgumentSummaryComponent(pos) and - result = "Argument[" + getParameterPosition(pos) + "]" - ) - or - exists(string synthetic | - sc = TSyntheticGlobalSummaryComponent(synthetic) and - result = "SyntheticGlobal[" + synthetic + "]" - ) - or - sc = TReturnSummaryComponent(getReturnValueKind()) and result = "ReturnValue" - } - - /** Gets a textual representation of this stack used for flow summaries. */ - string getComponentStack(SummaryComponentStack stack) { - exists(SummaryComponent head, SummaryComponentStack tail | - head = stack.head() and - tail = stack.tail() and - result = getComponentStack(tail) + "." + getComponent(head) - ) - or - exists(SummaryComponent c | - stack = TSingletonSummaryComponentStack(c) and - result = getComponent(c) - ) - } - /** * A class that exists for QL technical reasons only (the IPA type used * to represent component stacks needs to be bounded). @@ -1382,8 +1349,8 @@ module Private { c.relevantSummary(input, output, preservesValue) and csv = c.getCallableCsv() // Callable information - + getComponentStack(input) + ";" // input - + getComponentStack(output) + ";" // output + + input.getMadRepresentation() + ";" // input + + output.getMadRepresentation() + ";" // output + renderKind(preservesValue) + ";" // kind + renderProvenance(c) // provenance ) diff --git a/python/ql/lib/semmle/python/dataflow/new/internal/FlowSummaryImplSpecific.qll b/python/ql/lib/semmle/python/dataflow/new/internal/FlowSummaryImplSpecific.qll index 0f58d6988f0..51002143919 100644 --- a/python/ql/lib/semmle/python/dataflow/new/internal/FlowSummaryImplSpecific.qll +++ b/python/ql/lib/semmle/python/dataflow/new/internal/FlowSummaryImplSpecific.qll @@ -128,10 +128,30 @@ SummaryComponent interpretComponentSpecific(AccessPathToken c) { ) } -/** Gets the textual representation of a summary component in the format used for flow summaries. */ -string getComponentSpecific(SummaryComponent sc) { - sc = TContentSummaryComponent(any(ListElementContent c)) and - result = "ListElement" +private string getContentSpecific(Content cs) { + cs = TListElementContent() and result = "ListElement" + or + cs = TSetElementContent() and result = "SetElement" + or + exists(int index | + cs = TTupleElementContent(index) and result = "TupleElement[" + index.toString() + "]" + ) + or + exists(string key | + cs = TDictionaryElementContent(key) and result = "DictionaryElement[" + key + "]" + ) + or + cs = TDictionaryElementAnyContent() and result = "DictionaryElementAny" + or + exists(string attr | cs = TAttributeContent(attr) and result = "Attribute[" + attr + "]") +} + +/** Gets the textual representation of a summary component in the format used for MaD models. */ +string getMadRepresentationSpecific(SummaryComponent sc) { + exists(Content c | + sc = TContentSummaryComponent(c) and + result = getContentSpecific(c) + ) } /** Gets the textual representation of a parameter position in the format used for flow summaries. */ 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 fb3d7bf828f..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 @@ -318,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`. @@ -378,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() } } /** @@ -540,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] diff --git a/python/ql/lib/semmle/python/frameworks/MarkupSafe.qll b/python/ql/lib/semmle/python/frameworks/MarkupSafe.qll index e77b92a40dc..99cfd75bec6 100644 --- a/python/ql/lib/semmle/python/frameworks/MarkupSafe.qll +++ b/python/ql/lib/semmle/python/frameworks/MarkupSafe.qll @@ -83,7 +83,7 @@ private module MarkupSafeModel { } /** Taint propagation for `markupsafe.Markup`. */ - private class AddtionalTaintStep extends TaintTracking::AdditionalTaintStep { + private class AdditionalTaintStep extends TaintTracking::AdditionalTaintStep { override predicate step(DataFlow::Node nodeFrom, DataFlow::Node nodeTo) { nodeTo.(ClassInstantiation).getArg(0) = nodeFrom } @@ -92,11 +92,7 @@ private module MarkupSafeModel { /** Any escaping performed via the `markupsafe` package. */ abstract private class MarkupSafeEscape extends Escaping::Range { - override string getKind() { - // TODO: this package claims to escape for both HTML and XML, but for now we don't - // model XML. - result = Escaping::getHtmlKind() - } + override string getKind() { result in [Escaping::getHtmlKind(), Escaping::getXmlKind()] } } /** A call to any of the escaping functions in `markupsafe` */ diff --git a/python/ql/lib/semmle/python/security/dataflow/XxeCustomizations.qll b/python/ql/lib/semmle/python/security/dataflow/XxeCustomizations.qll index 525bceeb04a..c98e8153be9 100644 --- a/python/ql/lib/semmle/python/security/dataflow/XxeCustomizations.qll +++ b/python/ql/lib/semmle/python/security/dataflow/XxeCustomizations.qll @@ -44,4 +44,11 @@ module Xxe { ) } } + + /** + * An XML escaping, considered as a sanitizer. + */ + class XmlEscapingAsSanitizer extends Sanitizer { + XmlEscapingAsSanitizer() { this = any(XmlEscaping esc).getOutput() } + } } diff --git a/python/ql/src/CHANGELOG.md b/python/ql/src/CHANGELOG.md index 655914b4a32..d97cca6084c 100644 --- a/python/ql/src/CHANGELOG.md +++ b/python/ql/src/CHANGELOG.md @@ -1,3 +1,13 @@ +## 0.8.0 + +### Bug Fixes + +* The query "Arbitrary file write during archive extraction ("Zip Slip")" (`py/zipslip`) has been renamed to "Arbitrary file access during archive extraction ("Zip Slip")." + +## 0.7.4 + +No user-facing changes. + ## 0.7.3 ### Bug Fixes diff --git a/python/ql/src/change-notes/2023-07-06-aiohttp-clientsession-modeling.md b/python/ql/src/change-notes/2023-07-06-aiohttp-clientsession-modeling.md new file mode 100644 index 00000000000..c8da353f5cb --- /dev/null +++ b/python/ql/src/change-notes/2023-07-06-aiohttp-clientsession-modeling.md @@ -0,0 +1,4 @@ +--- +category: minorAnalysis +--- +* Fixed modeling of `aiohttp.ClientSession` so we properly handle `async with` uses. This can impact results of server-side request forgery queries (`py/full-ssrf`, `py/partial-ssrf`). diff --git a/python/ql/src/change-notes/released/0.7.4.md b/python/ql/src/change-notes/released/0.7.4.md new file mode 100644 index 00000000000..1b33df9cb1e --- /dev/null +++ b/python/ql/src/change-notes/released/0.7.4.md @@ -0,0 +1,3 @@ +## 0.7.4 + +No user-facing changes. diff --git a/python/ql/src/change-notes/2023-06-16-zipslip-rename.md b/python/ql/src/change-notes/released/0.8.0.md similarity index 87% rename from python/ql/src/change-notes/2023-06-16-zipslip-rename.md rename to python/ql/src/change-notes/released/0.8.0.md index 4d4d4db15c3..6e2a75767e0 100644 --- a/python/ql/src/change-notes/2023-06-16-zipslip-rename.md +++ b/python/ql/src/change-notes/released/0.8.0.md @@ -1,4 +1,5 @@ ---- -category: fix ---- +## 0.8.0 + +### Bug Fixes + * 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/codeql-pack.release.yml b/python/ql/src/codeql-pack.release.yml index a4ea9c8de17..37eab3197dc 100644 --- a/python/ql/src/codeql-pack.release.yml +++ b/python/ql/src/codeql-pack.release.yml @@ -1,2 +1,2 @@ --- -lastReleaseVersion: 0.7.3 +lastReleaseVersion: 0.8.0 diff --git a/python/ql/src/qlpack.yml b/python/ql/src/qlpack.yml index 7dd13516d8b..ab9e14dd966 100644 --- a/python/ql/src/qlpack.yml +++ b/python/ql/src/qlpack.yml @@ -1,5 +1,5 @@ name: codeql/python-queries -version: 0.7.4-dev +version: 0.8.1-dev groups: - python - queries diff --git a/python/ql/test/experimental/dataflow/TestUtil/FlowTest.qll b/python/ql/test/experimental/dataflow/TestUtil/FlowTest.qll index e6abf741b36..f2068ebe723 100644 --- a/python/ql/test/experimental/dataflow/TestUtil/FlowTest.qll +++ b/python/ql/test/experimental/dataflow/TestUtil/FlowTest.qll @@ -9,7 +9,7 @@ signature module FlowTestSig { predicate relevantFlow(DataFlow::Node fromNode, DataFlow::Node toNode); } -private module FlowTest implements TestSig { +module MakeTestSig implements TestSig { string getARelevantTag() { result = Impl::flowTag() } predicate hasActualResult(Location location, string element, string tag, string value) { @@ -37,11 +37,3 @@ private module FlowTest implements TestSig { ) } } - -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 6cbfe917fd4..8fe65e53153 100644 --- a/python/ql/test/experimental/dataflow/TestUtil/LocalFlowStepTest.qll +++ b/python/ql/test/experimental/dataflow/TestUtil/LocalFlowStepTest.qll @@ -10,4 +10,4 @@ module LocalFlowStepTest implements FlowTestSig { } } -import MakeFlowTest +import MakeTest> diff --git a/python/ql/test/experimental/dataflow/TestUtil/MaximalFlowTest.qll b/python/ql/test/experimental/dataflow/TestUtil/MaximalFlowTest.qll index 681e51ca604..7587584a269 100644 --- a/python/ql/test/experimental/dataflow/TestUtil/MaximalFlowTest.qll +++ b/python/ql/test/experimental/dataflow/TestUtil/MaximalFlowTest.qll @@ -12,7 +12,7 @@ module MaximalFlowTest implements FlowTestSig { } } -import MakeFlowTest +import MakeTest> /** * A configuration to find all "maximal" flows. diff --git a/python/ql/test/experimental/dataflow/TestUtil/NormalDataflowTest.qll b/python/ql/test/experimental/dataflow/TestUtil/NormalDataflowTest.qll index a327886fedd..d62262cec98 100644 --- a/python/ql/test/experimental/dataflow/TestUtil/NormalDataflowTest.qll +++ b/python/ql/test/experimental/dataflow/TestUtil/NormalDataflowTest.qll @@ -11,7 +11,7 @@ module DataFlowTest implements FlowTestSig { } } -import MakeFlowTest +import MakeTest> query predicate missingAnnotationOnSink(Location location, string error, string element) { error = "ERROR, you should add `# $ MISSING: flow` annotation" and diff --git a/python/ql/test/experimental/dataflow/TestUtil/NormalTaintTrackingTest.qll b/python/ql/test/experimental/dataflow/TestUtil/NormalTaintTrackingTest.qll index 4a07dc4d2d6..23262dfb3e5 100644 --- a/python/ql/test/experimental/dataflow/TestUtil/NormalTaintTrackingTest.qll +++ b/python/ql/test/experimental/dataflow/TestUtil/NormalTaintTrackingTest.qll @@ -11,7 +11,7 @@ module DataFlowTest implements FlowTestSig { } } -import MakeFlowTest +import MakeTest> query predicate missingAnnotationOnSink(Location location, string error, string element) { error = "ERROR, you should add `# $ MISSING: flow` annotation" and diff --git a/python/ql/test/experimental/dataflow/TestUtil/RoutingTest.qll b/python/ql/test/experimental/dataflow/TestUtil/RoutingTest.qll index 36b603baa78..6c2df0e4348 100644 --- a/python/ql/test/experimental/dataflow/TestUtil/RoutingTest.qll +++ b/python/ql/test/experimental/dataflow/TestUtil/RoutingTest.qll @@ -10,22 +10,25 @@ private import semmle.python.dataflow.new.internal.DataFlowPrivate as DataFlowPr * the functions tested sink their arguments sequentially, that is * `SINK1(arg1)`, etc. */ -abstract class RoutingTest extends InlineExpectationsTest { - bindingset[this] - RoutingTest() { any() } +signature module RoutingTestSig { + class Argument; - abstract string flowTag(); + string flowTag(Argument arg); - abstract predicate relevantFlow(DataFlow::Node fromNode, DataFlow::Node toNode); + predicate relevantFlow(DataFlow::Node fromNode, DataFlow::Node toNode, Argument arg); +} - override string getARelevantTag() { result in ["func", this.flowTag()] } +module MakeTestSig implements TestSig { + string getARelevantTag() { result in ["func", Impl::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::Argument arg | + Impl::relevantFlow(fromNode, toNode, arg) + | location = fromNode.getLocation() and element = fromNode.toString() and ( - tag = this.flowTag() and + tag = Impl::flowTag(arg) and if "\"" + tag + "\"" = fromValue(fromNode) then value = "" else value = fromValue(fromNode) or // only have result for `func` tag if the function where `arg` is used, is diff --git a/python/ql/test/experimental/dataflow/coverage-py2/argumentRoutingTest.expected b/python/ql/test/experimental/dataflow/coverage-py2/argumentRoutingTest.expected index e69de29bb2d..48de9172b36 100644 --- a/python/ql/test/experimental/dataflow/coverage-py2/argumentRoutingTest.expected +++ b/python/ql/test/experimental/dataflow/coverage-py2/argumentRoutingTest.expected @@ -0,0 +1,2 @@ +failures +testFailures diff --git a/python/ql/test/experimental/dataflow/coverage-py3/argumentRoutingTest.expected b/python/ql/test/experimental/dataflow/coverage-py3/argumentRoutingTest.expected index e69de29bb2d..48de9172b36 100644 --- a/python/ql/test/experimental/dataflow/coverage-py3/argumentRoutingTest.expected +++ b/python/ql/test/experimental/dataflow/coverage-py3/argumentRoutingTest.expected @@ -0,0 +1,2 @@ +failures +testFailures diff --git a/python/ql/test/experimental/dataflow/coverage/argumentRoutingTest.expected b/python/ql/test/experimental/dataflow/coverage/argumentRoutingTest.expected index e69de29bb2d..48de9172b36 100644 --- a/python/ql/test/experimental/dataflow/coverage/argumentRoutingTest.expected +++ b/python/ql/test/experimental/dataflow/coverage/argumentRoutingTest.expected @@ -0,0 +1,2 @@ +failures +testFailures diff --git a/python/ql/test/experimental/dataflow/coverage/argumentRoutingTest.ql b/python/ql/test/experimental/dataflow/coverage/argumentRoutingTest.ql index 2adbd635090..eccbbea2b4d 100644 --- a/python/ql/test/experimental/dataflow/coverage/argumentRoutingTest.ql +++ b/python/ql/test/experimental/dataflow/coverage/argumentRoutingTest.ql @@ -3,19 +3,22 @@ import semmle.python.dataflow.new.DataFlow private import semmle.python.dataflow.new.internal.DataFlowPrivate as DataFlowPrivate import experimental.dataflow.TestUtil.RoutingTest -class Argument1RoutingTest extends RoutingTest { - Argument1RoutingTest() { this = "Argument1RoutingTest" } +module Argument1RoutingTest implements RoutingTestSig { + class Argument = Unit; - override string flowTag() { result = "arg1" } + string flowTag(Argument arg) { result = "arg1" and exists(arg) } - override predicate relevantFlow(DataFlow::Node source, DataFlow::Node sink) { - exists(Argument1ExtraRoutingConfig cfg | cfg.hasFlow(source, sink)) - or - exists(ArgumentRoutingConfig cfg | - cfg.hasFlow(source, sink) and - cfg.isArgSource(source, 1) and - cfg.isGoodSink(sink, 1) - ) + predicate relevantFlow(DataFlow::Node source, DataFlow::Node sink, Argument arg) { + ( + exists(Argument1ExtraRoutingConfig cfg | cfg.hasFlow(source, sink)) + or + exists(ArgumentRoutingConfig cfg | + cfg.hasFlow(source, sink) and + cfg.isArgSource(source, 1) and + cfg.isGoodSink(sink, 1) + ) + ) and + exists(arg) } } @@ -87,59 +90,54 @@ class Argument1ExtraRoutingConfig extends DataFlow::Configuration { override predicate isBarrierIn(DataFlow::Node node) { this.isSource(node) } } -class RestArgumentRoutingTest extends RoutingTest { - ArgNumber argNumber; +module RestArgumentRoutingTest implements RoutingTestSig { + class Argument = ArgNumber; - RestArgumentRoutingTest() { - argNumber > 1 and - this = "Argument" + argNumber + "RoutingTest" - } + string flowTag(Argument arg) { result = "arg" + arg } - override string flowTag() { result = "arg" + argNumber } - - override predicate relevantFlow(DataFlow::Node source, DataFlow::Node sink) { + predicate relevantFlow(DataFlow::Node source, DataFlow::Node sink, Argument arg) { exists(ArgumentRoutingConfig cfg | cfg.hasFlow(source, sink) and - cfg.isArgSource(source, argNumber) and - cfg.isGoodSink(sink, argNumber) - ) + cfg.isArgSource(source, arg) and + cfg.isGoodSink(sink, arg) + ) and + arg > 1 } } /** Bad flow from `arg` to `SINK_F` */ -class BadArgumentRoutingTestSinkF extends RoutingTest { - ArgNumber argNumber; +module BadArgumentRoutingTestSinkF implements RoutingTestSig { + class Argument = ArgNumber; - BadArgumentRoutingTestSinkF() { this = "BadArgumentRoutingTestSinkF" + argNumber } + string flowTag(Argument arg) { result = "bad" + arg } - override string flowTag() { result = "bad" + argNumber } - - override predicate relevantFlow(DataFlow::Node source, DataFlow::Node sink) { + predicate relevantFlow(DataFlow::Node source, DataFlow::Node sink, Argument arg) { exists(ArgumentRoutingConfig cfg | cfg.hasFlow(source, sink) and - cfg.isArgSource(source, argNumber) and - cfg.isBadSink(sink, argNumber) + cfg.isArgSource(source, arg) and + cfg.isBadSink(sink, arg) ) } } /** Bad flow from `arg` to `SINK` or `SINK_F`, where `n != m`. */ -class BadArgumentRoutingTestWrongSink extends RoutingTest { - ArgNumber argNumber; +module BadArgumentRoutingTestWrongSink implements RoutingTestSig { + class Argument = ArgNumber; - BadArgumentRoutingTestWrongSink() { this = "BadArgumentRoutingTestWrongSink" + argNumber } + string flowTag(Argument arg) { result = "bad" + arg } - override string flowTag() { result = "bad" + argNumber } - - override predicate relevantFlow(DataFlow::Node source, DataFlow::Node sink) { + predicate relevantFlow(DataFlow::Node source, DataFlow::Node sink, Argument arg) { exists(ArgumentRoutingConfig cfg | cfg.hasFlow(source, sink) and - cfg.isArgSource(source, any(ArgNumber i | not i = argNumber)) and + cfg.isArgSource(source, any(ArgNumber i | not i = arg)) and ( - cfg.isGoodSink(sink, argNumber) + cfg.isGoodSink(sink, arg) or - cfg.isBadSink(sink, argNumber) + cfg.isBadSink(sink, arg) ) ) } } + +import MakeTest, MakeTestSig, + MakeTestSig, MakeTestSig>> diff --git a/python/ql/test/experimental/dataflow/module-initialization/localFlow.ql b/python/ql/test/experimental/dataflow/module-initialization/localFlow.ql index 8ef3860955d..179ab2e2d88 100644 --- a/python/ql/test/experimental/dataflow/module-initialization/localFlow.ql +++ b/python/ql/test/experimental/dataflow/module-initialization/localFlow.ql @@ -31,4 +31,4 @@ module RuntimeLocalFlowTest implements FlowTestSig { } } -import MakeFlowTest2 +import MakeTest, MakeTestSig>> diff --git a/python/ql/test/experimental/dataflow/typetracking-summaries/tracked.expected b/python/ql/test/experimental/dataflow/typetracking-summaries/tracked.expected index e69de29bb2d..48de9172b36 100644 --- a/python/ql/test/experimental/dataflow/typetracking-summaries/tracked.expected +++ b/python/ql/test/experimental/dataflow/typetracking-summaries/tracked.expected @@ -0,0 +1,2 @@ +failures +testFailures diff --git a/python/ql/test/experimental/dataflow/typetracking-summaries/tracked.ql b/python/ql/test/experimental/dataflow/typetracking-summaries/tracked.ql index e5bf62053a0..6ce1afa8d60 100644 --- a/python/ql/test/experimental/dataflow/typetracking-summaries/tracked.ql +++ b/python/ql/test/experimental/dataflow/typetracking-summaries/tracked.ql @@ -15,12 +15,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 | exists(e.getLocation().getFile().getRelativePath()) and e.getLocation().getStartLine() > 0 and @@ -34,3 +32,5 @@ class TrackedTest extends InlineExpectationsTest { ) } } + +import MakeTest diff --git a/python/ql/test/experimental/library-tests/CallGraph/InlineCallGraphTest.ql b/python/ql/test/experimental/library-tests/CallGraph/InlineCallGraphTest.ql index 42a7d90af5a..1f2b37168b7 100644 --- a/python/ql/test/experimental/library-tests/CallGraph/InlineCallGraphTest.ql +++ b/python/ql/test/experimental/library-tests/CallGraph/InlineCallGraphTest.ql @@ -108,7 +108,7 @@ query predicate pointsTo_found_typeTracker_notFound(CallNode call, string qualna not typeTrackerCallEdge(call, target) and qualname = getCallEdgeValue(call, target) and // ignore SPURIOUS call edges - not exists(FalsePositiveExpectation spuriousResult | + not exists(FalsePositiveTestExpectation spuriousResult | spuriousResult.getTag() = "pt" and spuriousResult.getValue() = getCallEdgeValue(call, target) and spuriousResult.getLocation().getFile() = call.getLocation().getFile() and @@ -127,7 +127,7 @@ query predicate typeTracker_found_pointsTo_notFound(CallNode call, string qualna // between the two). not typeTrackerClassCall(call, target) and // ignore SPURIOUS call edges - not exists(FalsePositiveExpectation spuriousResult | + not exists(FalsePositiveTestExpectation spuriousResult | spuriousResult.getTag() = "tt" and spuriousResult.getValue() = getCallEdgeValue(call, target) and spuriousResult.getLocation().getFile() = call.getLocation().getFile() and diff --git a/python/ql/test/experimental/meta/ConceptsTest.qll b/python/ql/test/experimental/meta/ConceptsTest.qll index 48803e11fb4..13dac722c3b 100644 --- a/python/ql/test/experimental/meta/ConceptsTest.qll +++ b/python/ql/test/experimental/meta/ConceptsTest.qll @@ -255,52 +255,64 @@ module HttpServerRequestHandlerTest implements TestSig { } } -class HttpServerHttpResponseTest extends InlineExpectationsTest { - File file; +abstract class DedicatedResponseTest extends string { + bindingset[this] + DedicatedResponseTest() { any() } - HttpServerHttpResponseTest() { - file.getExtension() = "py" and - this = "HttpServerHttpResponseTest: " + file - } + string toString() { result = this } - override string getARelevantTag() { result in ["HttpResponse", "responseBody", "mimetype"] } + abstract predicate isDedicatedFile(File file); +} - override predicate hasActualResult(Location location, string element, string tag, string value) { +module HttpServerHttpResponseTest implements TestSig { + string getARelevantTag() { result in ["HttpResponse", "responseBody", "mimetype"] } + + predicate hasActualResult(Location location, string element, string tag, string value) { // By adding `file` as a class field, and these two restrictions, it's possible to // say that we only want to check _some_ tags for certain files. This helped make // flask tests more readable since adding full annotations for HttpResponses in the // the tests for routing setup is both annoying and not very useful. - location.getFile() = file and - exists(file.getRelativePath()) and - // we need to do this step since we expect subclasses could override getARelevantTag - tag = this.getARelevantTag() and - ( - exists(Http::Server::HttpResponse response | - location = response.getLocation() and - element = response.toString() and - value = "" and - tag = "HttpResponse" - ) - or - exists(Http::Server::HttpResponse response | - location = response.getLocation() and - element = response.toString() and - value = prettyNodeForInlineTest(response.getBody()) and - tag = "responseBody" - ) - or - exists(Http::Server::HttpResponse response | - location = response.getLocation() and - element = response.toString() and - // Ensure that an expectation value such as "mimetype=text/html; charset=utf-8" is parsed as a - // single expectation with tag mimetype, and not as two expectations with tags mimetype and - // charset. + exists(File file | + location.getFile() = file and + file.getExtension() = "py" and + exists(file.getRelativePath()) and + // we need to do this step since we expect subclasses could override getARelevantTag + tag = getARelevantTag() and + ( + exists(Http::Server::HttpResponse response | + location = response.getLocation() and + element = response.toString() and + value = "" and + tag = "HttpResponse" + ) + or ( - if exists(response.getMimetype().indexOf(" ")) - then value = "\"" + response.getMimetype() + "\"" - else value = response.getMimetype() + not exists(DedicatedResponseTest d) + or + exists(DedicatedResponseTest d | d.isDedicatedFile(file)) ) and - tag = "mimetype" + ( + exists(Http::Server::HttpResponse response | + location = response.getLocation() and + element = response.toString() and + value = prettyNodeForInlineTest(response.getBody()) and + tag = "responseBody" + ) + or + exists(Http::Server::HttpResponse response | + location = response.getLocation() and + element = response.toString() and + // Ensure that an expectation value such as "mimetype=text/html; charset=utf-8" is parsed as a + // single expectation with tag mimetype, and not as two expectations with tags mimetype and + // charset. + ( + if exists(response.getMimetype().indexOf(" ")) + then value = "\"" + response.getMimetype() + "\"" + else value = response.getMimetype() + ) and + tag = "mimetype" + ) + ) ) ) } @@ -545,7 +557,7 @@ import MakeTest, MergeTests5, - MergeTests4, MergeTests5, diff --git a/python/ql/test/library-tests/frameworks/aiohttp/ConceptsTest.ql b/python/ql/test/library-tests/frameworks/aiohttp/ConceptsTest.ql index 1e2c1fab3ee..c379783c55e 100644 --- a/python/ql/test/library-tests/frameworks/aiohttp/ConceptsTest.ql +++ b/python/ql/test/library-tests/frameworks/aiohttp/ConceptsTest.ql @@ -1,12 +1,8 @@ import python import experimental.meta.ConceptsTest -class DedicatedResponseTest extends HttpServerHttpResponseTest { - DedicatedResponseTest() { file.getShortName() = "response_test.py" } -} +class DedicatedTest extends DedicatedResponseTest { + DedicatedTest() { this = "response_test.py" } -class OtherResponseTest extends HttpServerHttpResponseTest { - OtherResponseTest() { not this instanceof DedicatedResponseTest } - - override string getARelevantTag() { result = "HttpResponse" } + override predicate isDedicatedFile(File file) { file.getShortName() = this } } diff --git a/python/ql/test/library-tests/frameworks/aiohttp/client_request.py b/python/ql/test/library-tests/frameworks/aiohttp/client_request.py index 28efedf703f..1bafb4ef583 100644 --- a/python/ql/test/library-tests/frameworks/aiohttp/client_request.py +++ b/python/ql/test/library-tests/frameworks/aiohttp/client_request.py @@ -1,35 +1,35 @@ import aiohttp -import asyncio import ssl -s = aiohttp.ClientSession() -resp = s.request("method", "url") # $ clientRequestUrlPart="url" -resp = s.request("method", url="url") # $ clientRequestUrlPart="url" +async def test(): + s = aiohttp.ClientSession() + resp = await s.request("method", "url") # $ clientRequestUrlPart="url" + resp = await s.request("method", url="url") # $ clientRequestUrlPart="url" -with aiohttp.ClientSession() as session: - resp = session.get("url") # $ clientRequestUrlPart="url" - resp = session.request(method="GET", url="url") # $ clientRequestUrlPart="url" + async with aiohttp.ClientSession() as session: + resp = await session.get("url") # $ clientRequestUrlPart="url" + resp = await session.request(method="GET", url="url") # $ clientRequestUrlPart="url" -# other methods than GET -s = aiohttp.ClientSession() -resp = s.post("url") # $ clientRequestUrlPart="url" -resp = s.patch("url") # $ clientRequestUrlPart="url" -resp = s.options("url") # $ clientRequestUrlPart="url" + # other methods than GET + s = aiohttp.ClientSession() + resp = await s.post("url") # $ clientRequestUrlPart="url" + resp = await s.patch("url") # $ clientRequestUrlPart="url" + resp = await s.options("url") # $ clientRequestUrlPart="url" -# disabling of SSL validation -# see https://docs.aiohttp.org/en/stable/client_reference.html#aiohttp.ClientSession.request -s.get("url", ssl=False) # $ clientRequestUrlPart="url" clientRequestCertValidationDisabled -s.get("url", ssl=0) # $ clientRequestUrlPart="url" clientRequestCertValidationDisabled -# None is treated as default and so does _not_ disable the check -s.get("url", ssl=None) # $ clientRequestUrlPart="url" + # disabling of SSL validation + # see https://docs.aiohttp.org/en/stable/client_reference.html#aiohttp.ClientSession.request + s.get("url", ssl=False) # $ clientRequestUrlPart="url" clientRequestCertValidationDisabled + s.get("url", ssl=0) # $ clientRequestUrlPart="url" clientRequestCertValidationDisabled + # None is treated as default and so does _not_ disable the check + s.get("url", ssl=None) # $ clientRequestUrlPart="url" -# deprecated since 3.0, but still supported -s.get("url", verify_ssl=False) # $ clientRequestUrlPart="url" clientRequestCertValidationDisabled + # deprecated since 3.0, but still supported + s.get("url", verify_ssl=False) # $ clientRequestUrlPart="url" clientRequestCertValidationDisabled -# A manually constructed SSLContext does not have safe defaults, so is effectively the -# same as turning off SSL validation -context = ssl.SSLContext() -assert context.check_hostname == False -assert context.verify_mode == ssl.VerifyMode.CERT_NONE + # A manually constructed SSLContext does not have safe defaults, so is effectively the + # same as turning off SSL validation + context = ssl.SSLContext() + assert context.check_hostname == False + assert context.verify_mode == ssl.VerifyMode.CERT_NONE -s.get("url", ssl=context) # $ clientRequestUrlPart="url" MISSING: clientRequestCertValidationDisabled + s.get("url", ssl=context) # $ clientRequestUrlPart="url" MISSING: clientRequestCertValidationDisabled diff --git a/python/ql/test/library-tests/frameworks/django-v1/ConceptsTest.ql b/python/ql/test/library-tests/frameworks/django-v1/ConceptsTest.ql index 1e2c1fab3ee..c379783c55e 100644 --- a/python/ql/test/library-tests/frameworks/django-v1/ConceptsTest.ql +++ b/python/ql/test/library-tests/frameworks/django-v1/ConceptsTest.ql @@ -1,12 +1,8 @@ import python import experimental.meta.ConceptsTest -class DedicatedResponseTest extends HttpServerHttpResponseTest { - DedicatedResponseTest() { file.getShortName() = "response_test.py" } -} +class DedicatedTest extends DedicatedResponseTest { + DedicatedTest() { this = "response_test.py" } -class OtherResponseTest extends HttpServerHttpResponseTest { - OtherResponseTest() { not this instanceof DedicatedResponseTest } - - override string getARelevantTag() { result = "HttpResponse" } + override predicate isDedicatedFile(File file) { file.getShortName() = this } } diff --git a/python/ql/test/library-tests/frameworks/django-v2-v3/ConceptsTest.ql b/python/ql/test/library-tests/frameworks/django-v2-v3/ConceptsTest.ql index 1e2c1fab3ee..c379783c55e 100644 --- a/python/ql/test/library-tests/frameworks/django-v2-v3/ConceptsTest.ql +++ b/python/ql/test/library-tests/frameworks/django-v2-v3/ConceptsTest.ql @@ -1,12 +1,8 @@ import python import experimental.meta.ConceptsTest -class DedicatedResponseTest extends HttpServerHttpResponseTest { - DedicatedResponseTest() { file.getShortName() = "response_test.py" } -} +class DedicatedTest extends DedicatedResponseTest { + DedicatedTest() { this = "response_test.py" } -class OtherResponseTest extends HttpServerHttpResponseTest { - OtherResponseTest() { not this instanceof DedicatedResponseTest } - - override string getARelevantTag() { result = "HttpResponse" } + override predicate isDedicatedFile(File file) { file.getShortName() = this } } diff --git a/python/ql/test/library-tests/frameworks/fastapi/ConceptsTest.ql b/python/ql/test/library-tests/frameworks/fastapi/ConceptsTest.ql index 1e2c1fab3ee..c379783c55e 100644 --- a/python/ql/test/library-tests/frameworks/fastapi/ConceptsTest.ql +++ b/python/ql/test/library-tests/frameworks/fastapi/ConceptsTest.ql @@ -1,12 +1,8 @@ import python import experimental.meta.ConceptsTest -class DedicatedResponseTest extends HttpServerHttpResponseTest { - DedicatedResponseTest() { file.getShortName() = "response_test.py" } -} +class DedicatedTest extends DedicatedResponseTest { + DedicatedTest() { this = "response_test.py" } -class OtherResponseTest extends HttpServerHttpResponseTest { - OtherResponseTest() { not this instanceof DedicatedResponseTest } - - override string getARelevantTag() { result = "HttpResponse" } + override predicate isDedicatedFile(File file) { file.getShortName() = this } } diff --git a/python/ql/test/library-tests/frameworks/flask/ConceptsTest.ql b/python/ql/test/library-tests/frameworks/flask/ConceptsTest.ql index 1e2c1fab3ee..c379783c55e 100644 --- a/python/ql/test/library-tests/frameworks/flask/ConceptsTest.ql +++ b/python/ql/test/library-tests/frameworks/flask/ConceptsTest.ql @@ -1,12 +1,8 @@ import python import experimental.meta.ConceptsTest -class DedicatedResponseTest extends HttpServerHttpResponseTest { - DedicatedResponseTest() { file.getShortName() = "response_test.py" } -} +class DedicatedTest extends DedicatedResponseTest { + DedicatedTest() { this = "response_test.py" } -class OtherResponseTest extends HttpServerHttpResponseTest { - OtherResponseTest() { not this instanceof DedicatedResponseTest } - - override string getARelevantTag() { result = "HttpResponse" } + override predicate isDedicatedFile(File file) { file.getShortName() = this } } diff --git a/python/ql/test/library-tests/frameworks/flask_admin/ConceptsTest.ql b/python/ql/test/library-tests/frameworks/flask_admin/ConceptsTest.ql index 1e2c1fab3ee..c379783c55e 100644 --- a/python/ql/test/library-tests/frameworks/flask_admin/ConceptsTest.ql +++ b/python/ql/test/library-tests/frameworks/flask_admin/ConceptsTest.ql @@ -1,12 +1,8 @@ import python import experimental.meta.ConceptsTest -class DedicatedResponseTest extends HttpServerHttpResponseTest { - DedicatedResponseTest() { file.getShortName() = "response_test.py" } -} +class DedicatedTest extends DedicatedResponseTest { + DedicatedTest() { this = "response_test.py" } -class OtherResponseTest extends HttpServerHttpResponseTest { - OtherResponseTest() { not this instanceof DedicatedResponseTest } - - override string getARelevantTag() { result = "HttpResponse" } + override predicate isDedicatedFile(File file) { file.getShortName() = this } } diff --git a/python/ql/test/library-tests/frameworks/markupsafe/taint_test.py b/python/ql/test/library-tests/frameworks/markupsafe/taint_test.py index 7f3c2b37cf7..8d25b6d05ba 100644 --- a/python/ql/test/library-tests/frameworks/markupsafe/taint_test.py +++ b/python/ql/test/library-tests/frameworks/markupsafe/taint_test.py @@ -27,40 +27,40 @@ def test(): # as tainted even after it has been escaped in some place. This _might_ not be the # case since data-flow library has taint-steps from adjacent uses... ensure_tainted(ts) # $ tainted - ensure_not_tainted(escape(ts)) # $ escapeInput=ts escapeKind=html escapeOutput=escape(..) + ensure_not_tainted(escape(ts)) # $ escapeInput=ts escapeKind=html escapeKind=xml escapeOutput=escape(..) ensure_tainted(ts) # $ tainted ensure_tainted( ts, # $ tainted m_unsafe, # $ tainted - m_unsafe + SAFE, # $ escapeInput=SAFE escapeKind=html escapeOutput=BinaryExpr MISSING: tainted - SAFE + m_unsafe, # $ escapeInput=SAFE escapeKind=html escapeOutput=BinaryExpr MISSING: tainted - m_unsafe.format(SAFE), # $ escapeInput=SAFE escapeKind=html escapeOutput=m_unsafe.format(..) MISSING: tainted - m_unsafe % SAFE, # $ escapeInput=SAFE escapeKind=html escapeOutput=BinaryExpr MISSING: tainted - m_unsafe + ts, # $ escapeInput=ts escapeKind=html escapeOutput=BinaryExpr MISSING: tainted + m_unsafe + SAFE, # $ escapeInput=SAFE escapeKind=html escapeKind=xml escapeOutput=BinaryExpr MISSING: tainted + SAFE + m_unsafe, # $ escapeInput=SAFE escapeKind=html escapeKind=xml escapeOutput=BinaryExpr MISSING: tainted + m_unsafe.format(SAFE), # $ escapeInput=SAFE escapeKind=html escapeKind=xml escapeOutput=m_unsafe.format(..) MISSING: tainted + m_unsafe % SAFE, # $ escapeInput=SAFE escapeKind=html escapeKind=xml escapeOutput=BinaryExpr MISSING: tainted + m_unsafe + ts, # $ escapeInput=ts escapeKind=html escapeKind=xml escapeOutput=BinaryExpr MISSING: tainted m_safe.format(m_unsafe), # $ tainted m_safe % m_unsafe, # $ tainted - escape(ts).unescape(), # $ escapeInput=ts escapeKind=html escapeOutput=escape(..) MISSING: tainted - escape_silent(ts).unescape(), # $ escapeInput=ts escapeKind=html escapeOutput=escape_silent(..) MISSING: tainted + escape(ts).unescape(), # $ escapeInput=ts escapeKind=html escapeKind=xml escapeOutput=escape(..) MISSING: tainted + escape_silent(ts).unescape(), # $ escapeInput=ts escapeKind=html escapeKind=xml escapeOutput=escape_silent(..) MISSING: tainted ) ensure_not_tainted( - escape(ts), # $ escapeInput=ts escapeKind=html escapeOutput=escape(..) - escape_silent(ts), # $ escapeInput=ts escapeKind=html escapeOutput=escape_silent(..) + escape(ts), # $ escapeInput=ts escapeKind=html escapeKind=xml escapeOutput=escape(..) + escape_silent(ts), # $ escapeInput=ts escapeKind=html escapeKind=xml escapeOutput=escape_silent(..) - Markup.escape(ts), # $ escapeInput=ts escapeKind=html escapeOutput=Markup.escape(..) + Markup.escape(ts), # $ escapeInput=ts escapeKind=html escapeKind=xml escapeOutput=Markup.escape(..) m_safe, - m_safe + ts, # $ escapeInput=ts escapeKind=html escapeOutput=BinaryExpr - ts + m_safe, # $ escapeInput=ts escapeKind=html escapeOutput=BinaryExpr - m_safe.format(ts), # $ escapeInput=ts escapeKind=html escapeOutput=m_safe.format(..) - m_safe % ts, # $ escapeInput=ts escapeKind=html escapeOutput=BinaryExpr + m_safe + ts, # $ escapeInput=ts escapeKind=html escapeKind=xml escapeOutput=BinaryExpr + ts + m_safe, # $ escapeInput=ts escapeKind=html escapeKind=xml escapeOutput=BinaryExpr + m_safe.format(ts), # $ escapeInput=ts escapeKind=html escapeKind=xml escapeOutput=m_safe.format(..) + m_safe % ts, # $ escapeInput=ts escapeKind=html escapeKind=xml escapeOutput=BinaryExpr - escape(ts) + ts, # $ escapeInput=ts escapeKind=html escapeOutput=BinaryExpr escapeOutput=escape(..) - escape_silent(ts) + ts, # $ escapeInput=ts escapeKind=html escapeOutput=BinaryExpr escapeOutput=escape_silent(..) - Markup.escape(ts) + ts, # $ escapeInput=ts escapeKind=html escapeOutput=BinaryExpr escapeOutput=Markup.escape(..) + escape(ts) + ts, # $ escapeInput=ts escapeKind=html escapeKind=xml escapeOutput=BinaryExpr escapeOutput=escape(..) + escape_silent(ts) + ts, # $ escapeInput=ts escapeKind=html escapeKind=xml escapeOutput=BinaryExpr escapeOutput=escape_silent(..) + Markup.escape(ts) + ts, # $ escapeInput=ts escapeKind=html escapeKind=xml escapeOutput=BinaryExpr escapeOutput=Markup.escape(..) ) # flask re-exports these, as: @@ -73,8 +73,8 @@ def test(): ) ensure_not_tainted( - flask.escape(ts), # $ escapeInput=ts escapeKind=html escapeOutput=flask.escape(..) - flask.Markup.escape(ts), # $ escapeInput=ts escapeKind=html escapeOutput=flask.Markup.escape(..) + flask.escape(ts), # $ escapeInput=ts escapeKind=html escapeKind=xml escapeOutput=flask.escape(..) + flask.Markup.escape(ts), # $ escapeInput=ts escapeKind=html escapeKind=xml escapeOutput=flask.Markup.escape(..) ) diff --git a/python/ql/test/library-tests/frameworks/tornado/ConceptsTest.ql b/python/ql/test/library-tests/frameworks/tornado/ConceptsTest.ql index 1e2c1fab3ee..c379783c55e 100644 --- a/python/ql/test/library-tests/frameworks/tornado/ConceptsTest.ql +++ b/python/ql/test/library-tests/frameworks/tornado/ConceptsTest.ql @@ -1,12 +1,8 @@ import python import experimental.meta.ConceptsTest -class DedicatedResponseTest extends HttpServerHttpResponseTest { - DedicatedResponseTest() { file.getShortName() = "response_test.py" } -} +class DedicatedTest extends DedicatedResponseTest { + DedicatedTest() { this = "response_test.py" } -class OtherResponseTest extends HttpServerHttpResponseTest { - OtherResponseTest() { not this instanceof DedicatedResponseTest } - - override string getARelevantTag() { result = "HttpResponse" } + override predicate isDedicatedFile(File file) { file.getShortName() = this } } diff --git a/python/ql/test/library-tests/frameworks/twisted/ConceptsTest.ql b/python/ql/test/library-tests/frameworks/twisted/ConceptsTest.ql index 1e2c1fab3ee..c379783c55e 100644 --- a/python/ql/test/library-tests/frameworks/twisted/ConceptsTest.ql +++ b/python/ql/test/library-tests/frameworks/twisted/ConceptsTest.ql @@ -1,12 +1,8 @@ import python import experimental.meta.ConceptsTest -class DedicatedResponseTest extends HttpServerHttpResponseTest { - DedicatedResponseTest() { file.getShortName() = "response_test.py" } -} +class DedicatedTest extends DedicatedResponseTest { + DedicatedTest() { this = "response_test.py" } -class OtherResponseTest extends HttpServerHttpResponseTest { - OtherResponseTest() { not this instanceof DedicatedResponseTest } - - override string getARelevantTag() { result = "HttpResponse" } + override predicate isDedicatedFile(File file) { file.getShortName() = this } } diff --git a/python/ql/test/query-tests/Security/CWE-611-Xxe/Xxe.expected b/python/ql/test/query-tests/Security/CWE-611-Xxe/Xxe.expected index c18b417db90..53dc9f018cb 100644 --- a/python/ql/test/query-tests/Security/CWE-611-Xxe/Xxe.expected +++ b/python/ql/test/query-tests/Security/CWE-611-Xxe/Xxe.expected @@ -1,25 +1,25 @@ edges | test.py:1:26:1:32 | ControlFlowNode for ImportMember | test.py:1:26:1:32 | GSSA Variable request | -| test.py:1:26:1:32 | GSSA Variable request | test.py:8:19:8:25 | ControlFlowNode for request | -| test.py:1:26:1:32 | GSSA Variable request | test.py:19:19:19:25 | ControlFlowNode for request | -| test.py:8:19:8:25 | ControlFlowNode for request | test.py:8:19:8:30 | ControlFlowNode for Attribute | -| test.py:8:19:8:30 | ControlFlowNode for Attribute | test.py:8:19:8:45 | ControlFlowNode for Subscript | -| test.py:8:19:8:45 | ControlFlowNode for Subscript | test.py:9:34:9:44 | ControlFlowNode for xml_content | -| test.py:19:19:19:25 | ControlFlowNode for request | test.py:19:19:19:30 | ControlFlowNode for Attribute | -| test.py:19:19:19:30 | ControlFlowNode for Attribute | test.py:19:19:19:45 | ControlFlowNode for Subscript | -| test.py:19:19:19:45 | ControlFlowNode for Subscript | test.py:30:34:30:44 | ControlFlowNode for xml_content | +| test.py:1:26:1:32 | GSSA Variable request | test.py:9:19:9:25 | ControlFlowNode for request | +| test.py:1:26:1:32 | GSSA Variable request | test.py:20:19:20:25 | ControlFlowNode for request | +| test.py:9:19:9:25 | ControlFlowNode for request | test.py:9:19:9:30 | ControlFlowNode for Attribute | +| test.py:9:19:9:30 | ControlFlowNode for Attribute | test.py:9:19:9:45 | ControlFlowNode for Subscript | +| test.py:9:19:9:45 | ControlFlowNode for Subscript | test.py:10:34:10:44 | ControlFlowNode for xml_content | +| test.py:20:19:20:25 | ControlFlowNode for request | test.py:20:19:20:30 | ControlFlowNode for Attribute | +| test.py:20:19:20:30 | ControlFlowNode for Attribute | test.py:20:19:20:45 | ControlFlowNode for Subscript | +| test.py:20:19:20:45 | ControlFlowNode for Subscript | test.py:31:34:31:44 | ControlFlowNode for xml_content | nodes | test.py:1:26:1:32 | ControlFlowNode for ImportMember | semmle.label | ControlFlowNode for ImportMember | | test.py:1:26:1:32 | GSSA Variable request | semmle.label | GSSA Variable request | -| test.py:8:19:8:25 | ControlFlowNode for request | semmle.label | ControlFlowNode for request | -| test.py:8:19:8:30 | ControlFlowNode for Attribute | semmle.label | ControlFlowNode for Attribute | -| test.py:8:19:8:45 | ControlFlowNode for Subscript | semmle.label | ControlFlowNode for Subscript | -| test.py:9:34:9:44 | ControlFlowNode for xml_content | semmle.label | ControlFlowNode for xml_content | -| test.py:19:19:19:25 | ControlFlowNode for request | semmle.label | ControlFlowNode for request | -| test.py:19:19:19:30 | ControlFlowNode for Attribute | semmle.label | ControlFlowNode for Attribute | -| test.py:19:19:19:45 | ControlFlowNode for Subscript | semmle.label | ControlFlowNode for Subscript | -| test.py:30:34:30:44 | ControlFlowNode for xml_content | semmle.label | ControlFlowNode for xml_content | +| test.py:9:19:9:25 | ControlFlowNode for request | semmle.label | ControlFlowNode for request | +| test.py:9:19:9:30 | ControlFlowNode for Attribute | semmle.label | ControlFlowNode for Attribute | +| test.py:9:19:9:45 | ControlFlowNode for Subscript | semmle.label | ControlFlowNode for Subscript | +| test.py:10:34:10:44 | ControlFlowNode for xml_content | semmle.label | ControlFlowNode for xml_content | +| test.py:20:19:20:25 | ControlFlowNode for request | semmle.label | ControlFlowNode for request | +| test.py:20:19:20:30 | ControlFlowNode for Attribute | semmle.label | ControlFlowNode for Attribute | +| test.py:20:19:20:45 | ControlFlowNode for Subscript | semmle.label | ControlFlowNode for Subscript | +| test.py:31:34:31:44 | ControlFlowNode for xml_content | semmle.label | ControlFlowNode for xml_content | subpaths #select -| test.py:9:34:9:44 | ControlFlowNode for xml_content | test.py:1:26:1:32 | ControlFlowNode for ImportMember | test.py:9:34:9:44 | ControlFlowNode for xml_content | XML parsing depends on a $@ without guarding against external entity expansion. | test.py:1:26:1:32 | ControlFlowNode for ImportMember | user-provided value | -| test.py:30:34:30:44 | ControlFlowNode for xml_content | test.py:1:26:1:32 | ControlFlowNode for ImportMember | test.py:30:34:30:44 | ControlFlowNode for xml_content | XML parsing depends on a $@ without guarding against external entity expansion. | test.py:1:26:1:32 | ControlFlowNode for ImportMember | user-provided value | +| test.py:10:34:10:44 | ControlFlowNode for xml_content | test.py:1:26:1:32 | ControlFlowNode for ImportMember | test.py:10:34:10:44 | ControlFlowNode for xml_content | XML parsing depends on a $@ without guarding against external entity expansion. | test.py:1:26:1:32 | ControlFlowNode for ImportMember | user-provided value | +| test.py:31:34:31:44 | ControlFlowNode for xml_content | test.py:1:26:1:32 | ControlFlowNode for ImportMember | test.py:31:34:31:44 | ControlFlowNode for xml_content | XML parsing depends on a $@ without guarding against external entity expansion. | test.py:1:26:1:32 | ControlFlowNode for ImportMember | user-provided value | diff --git a/python/ql/test/query-tests/Security/CWE-611-Xxe/test.py b/python/ql/test/query-tests/Security/CWE-611-Xxe/test.py index d9181c4cf34..104f2663d59 100644 --- a/python/ql/test/query-tests/Security/CWE-611-Xxe/test.py +++ b/python/ql/test/query-tests/Security/CWE-611-Xxe/test.py @@ -1,5 +1,6 @@ from flask import Flask, request import lxml.etree +import markupsafe app = Flask(__name__) @@ -28,3 +29,9 @@ def super_vuln_handler(): huge_tree=True, ) return lxml.etree.fromstring(xml_content, parser=parser).text + +@app.route("/sanitized-handler") +def sanitized_handler(): + xml_content = request.args['xml_content'] + xml_content = markupsafe.escape(xml_content) + return lxml.etree.fromstring(xml_content).text \ No newline at end of file diff --git a/ql/Cargo.lock b/ql/Cargo.lock index 7f62aa9e677..5ce63783ea8 100644 Binary files a/ql/Cargo.lock and b/ql/Cargo.lock differ diff --git a/ql/buramu/Cargo.toml b/ql/buramu/Cargo.toml index 3c1c885037e..d6b3ea6d7a6 100644 --- a/ql/buramu/Cargo.toml +++ b/ql/buramu/Cargo.toml @@ -9,4 +9,4 @@ edition = "2018" lazy_static = "1.4.0" chrono = "0.4.26" rayon = "1.7.0" -regex = "1.8.4" +regex = "1.9.1" diff --git a/ql/extractor/Cargo.toml b/ql/extractor/Cargo.toml index 90289b0d688..438173b45cf 100644 --- a/ql/extractor/Cargo.toml +++ b/ql/extractor/Cargo.toml @@ -16,5 +16,5 @@ clap = { version = "4.2", features = ["derive"] } tracing = "0.1" tracing-subscriber = { version = "0.3.17", features = ["env-filter"] } rayon = "1.7.0" -regex = "1.8.4" +regex = "1.9.1" codeql-extractor = { path = "../../shared/tree-sitter-extractor" } diff --git a/ql/ql/test/printAst/Foo.qll b/ql/ql/test/printAst/Foo.qll index 0a28779092b..0c7f4689428 100644 --- a/ql/ql/test/printAst/Foo.qll +++ b/ql/ql/test/printAst/Foo.qll @@ -26,11 +26,8 @@ predicate calls(Foo f) { true = false } -newtype TPathNode = - pragma[assume_small_delta] - TPathNodeMid() +newtype TPathNode = TPathNodeMid() private newtype TPathNode2 = - pragma[assume_small_delta] TPathNodeMid2(boolean foo) { foo = true } or TPathNodeSink(string bar) { bar = "bar" } diff --git a/ql/ql/test/printAst/printAst.expected b/ql/ql/test/printAst/printAst.expected index 2194cf06864..88f3af7c09a 100644 --- a/ql/ql/test/printAst/printAst.expected +++ b/ql/ql/test/printAst/printAst.expected @@ -1,8 +1,8 @@ nodes | Foo.qll:1:1:1:17 | Import | semmle.label | [Import] Import | | Foo.qll:1:1:1:17 | Import | semmle.order | 1 | -| Foo.qll:1:1:36:44 | TopLevel | semmle.label | [TopLevel] TopLevel | -| Foo.qll:1:1:36:44 | TopLevel | semmle.order | 1 | +| Foo.qll:1:1:33:44 | TopLevel | semmle.label | [TopLevel] TopLevel | +| Foo.qll:1:1:33:44 | TopLevel | semmle.order | 1 | | Foo.qll:1:8:1:17 | javascript | semmle.label | [ModuleExpr] javascript | | Foo.qll:1:8:1:17 | javascript | semmle.order | 3 | | Foo.qll:3:7:3:9 | Class Foo | semmle.label | [Class] Class Foo | @@ -155,44 +155,36 @@ nodes | Foo.qll:26:10:26:14 | Boolean | semmle.order | 77 | | Foo.qll:29:9:29:17 | NewType TPathNode | semmle.label | [NewType] NewType TPathNode | | Foo.qll:29:9:29:17 | NewType TPathNode | semmle.order | 78 | -| Foo.qll:30:3:30:28 | annotation | semmle.label | [Annotation] annotation | -| Foo.qll:30:3:30:28 | annotation | semmle.order | 79 | -| Foo.qll:30:10:30:27 | assume_small_delta | semmle.label | [AnnotationArg] assume_small_delta | -| Foo.qll:30:10:30:27 | assume_small_delta | semmle.order | 80 | -| Foo.qll:31:3:31:14 | NewTypeBranch TPathNodeMid | semmle.label | [NewTypeBranch] NewTypeBranch TPathNodeMid | -| Foo.qll:31:3:31:14 | NewTypeBranch TPathNodeMid | semmle.order | 81 | -| Foo.qll:33:1:33:7 | annotation | semmle.label | [Annotation] annotation | -| Foo.qll:33:1:33:7 | annotation | semmle.order | 82 | -| Foo.qll:33:17:33:26 | NewType TPathNode2 | semmle.label | [NewType] NewType TPathNode2 | -| Foo.qll:33:17:33:26 | NewType TPathNode2 | semmle.order | 83 | -| Foo.qll:34:3:34:28 | annotation | semmle.label | [Annotation] annotation | -| Foo.qll:34:3:34:28 | annotation | semmle.order | 84 | -| Foo.qll:34:10:34:27 | assume_small_delta | semmle.label | [AnnotationArg] assume_small_delta | -| Foo.qll:34:10:34:27 | assume_small_delta | semmle.order | 85 | -| Foo.qll:35:3:35:15 | NewTypeBranch TPathNodeMid2 | semmle.label | [NewTypeBranch] NewTypeBranch TPathNodeMid2 | -| Foo.qll:35:3:35:15 | NewTypeBranch TPathNodeMid2 | semmle.order | 86 | -| Foo.qll:35:17:35:23 | TypeExpr | semmle.label | [TypeExpr] TypeExpr | -| Foo.qll:35:17:35:23 | TypeExpr | semmle.order | 87 | -| Foo.qll:35:17:35:27 | foo | semmle.label | [VarDecl] foo | -| Foo.qll:35:17:35:27 | foo | semmle.order | 87 | -| Foo.qll:35:32:35:34 | foo | semmle.label | [VarAccess] foo | -| Foo.qll:35:32:35:34 | foo | semmle.order | 89 | -| Foo.qll:35:32:35:41 | ComparisonFormula | semmle.label | [ComparisonFormula] ComparisonFormula | -| Foo.qll:35:32:35:41 | ComparisonFormula | semmle.order | 89 | -| Foo.qll:35:38:35:41 | Boolean | semmle.label | [Boolean] Boolean | -| Foo.qll:35:38:35:41 | Boolean | semmle.order | 91 | -| Foo.qll:36:3:36:15 | NewTypeBranch TPathNodeSink | semmle.label | [NewTypeBranch] NewTypeBranch TPathNodeSink | -| Foo.qll:36:3:36:15 | NewTypeBranch TPathNodeSink | semmle.order | 92 | -| Foo.qll:36:17:36:22 | TypeExpr | semmle.label | [TypeExpr] TypeExpr | -| Foo.qll:36:17:36:22 | TypeExpr | semmle.order | 93 | -| Foo.qll:36:17:36:26 | bar | semmle.label | [VarDecl] bar | -| Foo.qll:36:17:36:26 | bar | semmle.order | 93 | -| Foo.qll:36:31:36:33 | bar | semmle.label | [VarAccess] bar | -| Foo.qll:36:31:36:33 | bar | semmle.order | 95 | -| Foo.qll:36:31:36:41 | ComparisonFormula | semmle.label | [ComparisonFormula] ComparisonFormula | -| Foo.qll:36:31:36:41 | ComparisonFormula | semmle.order | 95 | -| Foo.qll:36:37:36:41 | String | semmle.label | [String] String | -| Foo.qll:36:37:36:41 | String | semmle.order | 97 | +| Foo.qll:29:21:29:32 | NewTypeBranch TPathNodeMid | semmle.label | [NewTypeBranch] NewTypeBranch TPathNodeMid | +| Foo.qll:29:21:29:32 | NewTypeBranch TPathNodeMid | semmle.order | 79 | +| Foo.qll:31:1:31:7 | annotation | semmle.label | [Annotation] annotation | +| Foo.qll:31:1:31:7 | annotation | semmle.order | 80 | +| Foo.qll:31:17:31:26 | NewType TPathNode2 | semmle.label | [NewType] NewType TPathNode2 | +| Foo.qll:31:17:31:26 | NewType TPathNode2 | semmle.order | 81 | +| Foo.qll:32:3:32:15 | NewTypeBranch TPathNodeMid2 | semmle.label | [NewTypeBranch] NewTypeBranch TPathNodeMid2 | +| Foo.qll:32:3:32:15 | NewTypeBranch TPathNodeMid2 | semmle.order | 82 | +| Foo.qll:32:17:32:23 | TypeExpr | semmle.label | [TypeExpr] TypeExpr | +| Foo.qll:32:17:32:23 | TypeExpr | semmle.order | 83 | +| Foo.qll:32:17:32:27 | foo | semmle.label | [VarDecl] foo | +| Foo.qll:32:17:32:27 | foo | semmle.order | 83 | +| Foo.qll:32:32:32:34 | foo | semmle.label | [VarAccess] foo | +| Foo.qll:32:32:32:34 | foo | semmle.order | 85 | +| Foo.qll:32:32:32:41 | ComparisonFormula | semmle.label | [ComparisonFormula] ComparisonFormula | +| Foo.qll:32:32:32:41 | ComparisonFormula | semmle.order | 85 | +| Foo.qll:32:38:32:41 | Boolean | semmle.label | [Boolean] Boolean | +| Foo.qll:32:38:32:41 | Boolean | semmle.order | 87 | +| Foo.qll:33:3:33:15 | NewTypeBranch TPathNodeSink | semmle.label | [NewTypeBranch] NewTypeBranch TPathNodeSink | +| Foo.qll:33:3:33:15 | NewTypeBranch TPathNodeSink | semmle.order | 88 | +| Foo.qll:33:17:33:22 | TypeExpr | semmle.label | [TypeExpr] TypeExpr | +| Foo.qll:33:17:33:22 | TypeExpr | semmle.order | 89 | +| Foo.qll:33:17:33:26 | bar | semmle.label | [VarDecl] bar | +| Foo.qll:33:17:33:26 | bar | semmle.order | 89 | +| Foo.qll:33:31:33:33 | bar | semmle.label | [VarAccess] bar | +| Foo.qll:33:31:33:33 | bar | semmle.order | 91 | +| Foo.qll:33:31:33:41 | ComparisonFormula | semmle.label | [ComparisonFormula] ComparisonFormula | +| Foo.qll:33:31:33:41 | ComparisonFormula | semmle.order | 91 | +| Foo.qll:33:37:33:41 | String | semmle.label | [String] String | +| Foo.qll:33:37:33:41 | String | semmle.order | 93 | | file://:0:0:0:0 | abs | semmle.label | [BuiltinPredicate] abs | | file://:0:0:0:0 | abs | semmle.label | [BuiltinPredicate] abs | | file://:0:0:0:0 | acos | semmle.label | [BuiltinPredicate] acos | @@ -275,26 +267,26 @@ nodes | file://:0:0:0:0 | trim | semmle.label | [BuiltinPredicate] trim | | file://:0:0:0:0 | ulp | semmle.label | [BuiltinPredicate] ulp | | printAst.ql:1:1:1:28 | Import | semmle.label | [Import] Import | -| printAst.ql:1:1:1:28 | Import | semmle.order | 98 | +| printAst.ql:1:1:1:28 | Import | semmle.order | 94 | | printAst.ql:1:1:1:29 | TopLevel | semmle.label | [TopLevel] TopLevel | -| printAst.ql:1:1:1:29 | TopLevel | semmle.order | 98 | +| printAst.ql:1:1:1:29 | TopLevel | semmle.order | 94 | | printAst.ql:1:18:1:28 | printAstAst | semmle.label | [ModuleExpr] printAstAst | -| printAst.ql:1:18:1:28 | printAstAst | semmle.order | 100 | +| printAst.ql:1:18:1:28 | printAstAst | semmle.order | 96 | edges | Foo.qll:1:1:1:17 | Import | Foo.qll:1:8:1:17 | javascript | semmle.label | getModuleExpr() | | Foo.qll:1:1:1:17 | Import | Foo.qll:1:8:1:17 | javascript | semmle.order | 3 | -| Foo.qll:1:1:36:44 | TopLevel | Foo.qll:1:1:1:17 | Import | semmle.label | getAnImport() | -| Foo.qll:1:1:36:44 | TopLevel | Foo.qll:1:1:1:17 | Import | semmle.order | 1 | -| Foo.qll:1:1:36:44 | TopLevel | Foo.qll:3:7:3:9 | Class Foo | semmle.label | getAClass() | -| Foo.qll:1:1:36:44 | TopLevel | Foo.qll:3:7:3:9 | Class Foo | semmle.order | 4 | -| Foo.qll:1:1:36:44 | TopLevel | Foo.qll:9:17:9:19 | ClasslessPredicate foo | semmle.label | getAPredicate() | -| Foo.qll:1:1:36:44 | TopLevel | Foo.qll:9:17:9:19 | ClasslessPredicate foo | semmle.order | 16 | -| Foo.qll:1:1:36:44 | TopLevel | Foo.qll:13:11:13:15 | ClasslessPredicate calls | semmle.label | getAPredicate() | -| Foo.qll:1:1:36:44 | TopLevel | Foo.qll:13:11:13:15 | ClasslessPredicate calls | semmle.order | 32 | -| Foo.qll:1:1:36:44 | TopLevel | Foo.qll:29:9:29:17 | NewType TPathNode | semmle.label | getANewType() | -| Foo.qll:1:1:36:44 | TopLevel | Foo.qll:29:9:29:17 | NewType TPathNode | semmle.order | 78 | -| Foo.qll:1:1:36:44 | TopLevel | Foo.qll:33:17:33:26 | NewType TPathNode2 | semmle.label | getANewType() | -| Foo.qll:1:1:36:44 | TopLevel | Foo.qll:33:17:33:26 | NewType TPathNode2 | semmle.order | 83 | +| Foo.qll:1:1:33:44 | TopLevel | Foo.qll:1:1:1:17 | Import | semmle.label | getAnImport() | +| Foo.qll:1:1:33:44 | TopLevel | Foo.qll:1:1:1:17 | Import | semmle.order | 1 | +| Foo.qll:1:1:33:44 | TopLevel | Foo.qll:3:7:3:9 | Class Foo | semmle.label | getAClass() | +| Foo.qll:1:1:33:44 | TopLevel | Foo.qll:3:7:3:9 | Class Foo | semmle.order | 4 | +| Foo.qll:1:1:33:44 | TopLevel | Foo.qll:9:17:9:19 | ClasslessPredicate foo | semmle.label | getAPredicate() | +| Foo.qll:1:1:33:44 | TopLevel | Foo.qll:9:17:9:19 | ClasslessPredicate foo | semmle.order | 16 | +| Foo.qll:1:1:33:44 | TopLevel | Foo.qll:13:11:13:15 | ClasslessPredicate calls | semmle.label | getAPredicate() | +| Foo.qll:1:1:33:44 | TopLevel | Foo.qll:13:11:13:15 | ClasslessPredicate calls | semmle.order | 32 | +| Foo.qll:1:1:33:44 | TopLevel | Foo.qll:29:9:29:17 | NewType TPathNode | semmle.label | getANewType() | +| Foo.qll:1:1:33:44 | TopLevel | Foo.qll:29:9:29:17 | NewType TPathNode | semmle.order | 78 | +| Foo.qll:1:1:33:44 | TopLevel | Foo.qll:31:17:31:26 | NewType TPathNode2 | semmle.label | getANewType() | +| Foo.qll:1:1:33:44 | TopLevel | Foo.qll:31:17:31:26 | NewType TPathNode2 | semmle.order | 81 | | Foo.qll:3:7:3:9 | Class Foo | Foo.qll:3:19:3:22 | TypeExpr | semmle.label | getASuperType() | | Foo.qll:3:7:3:9 | Class Foo | Foo.qll:3:19:3:22 | TypeExpr | semmle.order | 5 | | Foo.qll:3:7:3:9 | Class Foo | Foo.qll:4:3:4:17 | CharPred Foo | semmle.label | getCharPred() | @@ -437,45 +429,37 @@ edges | Foo.qll:26:3:26:14 | ComparisonFormula | Foo.qll:26:3:26:6 | Boolean | semmle.order | 75 | | Foo.qll:26:3:26:14 | ComparisonFormula | Foo.qll:26:10:26:14 | Boolean | semmle.label | getRightOperand() | | Foo.qll:26:3:26:14 | ComparisonFormula | Foo.qll:26:10:26:14 | Boolean | semmle.order | 77 | -| Foo.qll:29:9:29:17 | NewType TPathNode | Foo.qll:31:3:31:14 | NewTypeBranch TPathNodeMid | semmle.label | getABranch() | -| Foo.qll:29:9:29:17 | NewType TPathNode | Foo.qll:31:3:31:14 | NewTypeBranch TPathNodeMid | semmle.order | 81 | -| Foo.qll:30:3:30:28 | annotation | Foo.qll:30:10:30:27 | assume_small_delta | semmle.label | getArgs(_) | -| Foo.qll:30:3:30:28 | annotation | Foo.qll:30:10:30:27 | assume_small_delta | semmle.order | 80 | -| Foo.qll:31:3:31:14 | NewTypeBranch TPathNodeMid | Foo.qll:30:3:30:28 | annotation | semmle.label | getAnAnnotation() | -| Foo.qll:31:3:31:14 | NewTypeBranch TPathNodeMid | Foo.qll:30:3:30:28 | annotation | semmle.order | 79 | -| Foo.qll:33:17:33:26 | NewType TPathNode2 | Foo.qll:33:1:33:7 | annotation | semmle.label | getAnAnnotation() | -| Foo.qll:33:17:33:26 | NewType TPathNode2 | Foo.qll:33:1:33:7 | annotation | semmle.order | 82 | -| Foo.qll:33:17:33:26 | NewType TPathNode2 | Foo.qll:35:3:35:15 | NewTypeBranch TPathNodeMid2 | semmle.label | getABranch() | -| Foo.qll:33:17:33:26 | NewType TPathNode2 | Foo.qll:35:3:35:15 | NewTypeBranch TPathNodeMid2 | semmle.order | 86 | -| Foo.qll:33:17:33:26 | NewType TPathNode2 | Foo.qll:36:3:36:15 | NewTypeBranch TPathNodeSink | semmle.label | getABranch() | -| Foo.qll:33:17:33:26 | NewType TPathNode2 | Foo.qll:36:3:36:15 | NewTypeBranch TPathNodeSink | semmle.order | 92 | -| Foo.qll:34:3:34:28 | annotation | Foo.qll:34:10:34:27 | assume_small_delta | semmle.label | getArgs(_) | -| Foo.qll:34:3:34:28 | annotation | Foo.qll:34:10:34:27 | assume_small_delta | semmle.order | 85 | -| Foo.qll:35:3:35:15 | NewTypeBranch TPathNodeMid2 | Foo.qll:34:3:34:28 | annotation | semmle.label | getAnAnnotation() | -| Foo.qll:35:3:35:15 | NewTypeBranch TPathNodeMid2 | Foo.qll:34:3:34:28 | annotation | semmle.order | 84 | -| Foo.qll:35:3:35:15 | NewTypeBranch TPathNodeMid2 | Foo.qll:35:17:35:27 | foo | semmle.label | getField(_) | -| Foo.qll:35:3:35:15 | NewTypeBranch TPathNodeMid2 | Foo.qll:35:17:35:27 | foo | semmle.order | 87 | -| Foo.qll:35:3:35:15 | NewTypeBranch TPathNodeMid2 | Foo.qll:35:32:35:41 | ComparisonFormula | semmle.label | getBody() | -| Foo.qll:35:3:35:15 | NewTypeBranch TPathNodeMid2 | Foo.qll:35:32:35:41 | ComparisonFormula | semmle.order | 89 | -| Foo.qll:35:17:35:27 | foo | Foo.qll:35:17:35:23 | TypeExpr | semmle.label | getTypeExpr() | -| Foo.qll:35:17:35:27 | foo | Foo.qll:35:17:35:23 | TypeExpr | semmle.order | 87 | -| Foo.qll:35:32:35:41 | ComparisonFormula | Foo.qll:35:32:35:34 | foo | semmle.label | getLeftOperand() | -| Foo.qll:35:32:35:41 | ComparisonFormula | Foo.qll:35:32:35:34 | foo | semmle.order | 89 | -| Foo.qll:35:32:35:41 | ComparisonFormula | Foo.qll:35:38:35:41 | Boolean | semmle.label | getRightOperand() | -| Foo.qll:35:32:35:41 | ComparisonFormula | Foo.qll:35:38:35:41 | Boolean | semmle.order | 91 | -| Foo.qll:36:3:36:15 | NewTypeBranch TPathNodeSink | Foo.qll:36:17:36:26 | bar | semmle.label | getField(_) | -| Foo.qll:36:3:36:15 | NewTypeBranch TPathNodeSink | Foo.qll:36:17:36:26 | bar | semmle.order | 93 | -| Foo.qll:36:3:36:15 | NewTypeBranch TPathNodeSink | Foo.qll:36:31:36:41 | ComparisonFormula | semmle.label | getBody() | -| Foo.qll:36:3:36:15 | NewTypeBranch TPathNodeSink | Foo.qll:36:31:36:41 | ComparisonFormula | semmle.order | 95 | -| Foo.qll:36:17:36:26 | bar | Foo.qll:36:17:36:22 | TypeExpr | semmle.label | getTypeExpr() | -| Foo.qll:36:17:36:26 | bar | Foo.qll:36:17:36:22 | TypeExpr | semmle.order | 93 | -| Foo.qll:36:31:36:41 | ComparisonFormula | Foo.qll:36:31:36:33 | bar | semmle.label | getLeftOperand() | -| Foo.qll:36:31:36:41 | ComparisonFormula | Foo.qll:36:31:36:33 | bar | semmle.order | 95 | -| Foo.qll:36:31:36:41 | ComparisonFormula | Foo.qll:36:37:36:41 | String | semmle.label | getRightOperand() | -| Foo.qll:36:31:36:41 | ComparisonFormula | Foo.qll:36:37:36:41 | String | semmle.order | 97 | +| Foo.qll:29:9:29:17 | NewType TPathNode | Foo.qll:29:21:29:32 | NewTypeBranch TPathNodeMid | semmle.label | getABranch() | +| Foo.qll:29:9:29:17 | NewType TPathNode | Foo.qll:29:21:29:32 | NewTypeBranch TPathNodeMid | semmle.order | 79 | +| Foo.qll:31:17:31:26 | NewType TPathNode2 | Foo.qll:31:1:31:7 | annotation | semmle.label | getAnAnnotation() | +| Foo.qll:31:17:31:26 | NewType TPathNode2 | Foo.qll:31:1:31:7 | annotation | semmle.order | 80 | +| Foo.qll:31:17:31:26 | NewType TPathNode2 | Foo.qll:32:3:32:15 | NewTypeBranch TPathNodeMid2 | semmle.label | getABranch() | +| Foo.qll:31:17:31:26 | NewType TPathNode2 | Foo.qll:32:3:32:15 | NewTypeBranch TPathNodeMid2 | semmle.order | 82 | +| Foo.qll:31:17:31:26 | NewType TPathNode2 | Foo.qll:33:3:33:15 | NewTypeBranch TPathNodeSink | semmle.label | getABranch() | +| Foo.qll:31:17:31:26 | NewType TPathNode2 | Foo.qll:33:3:33:15 | NewTypeBranch TPathNodeSink | semmle.order | 88 | +| Foo.qll:32:3:32:15 | NewTypeBranch TPathNodeMid2 | Foo.qll:32:17:32:27 | foo | semmle.label | getField(_) | +| Foo.qll:32:3:32:15 | NewTypeBranch TPathNodeMid2 | Foo.qll:32:17:32:27 | foo | semmle.order | 83 | +| Foo.qll:32:3:32:15 | NewTypeBranch TPathNodeMid2 | Foo.qll:32:32:32:41 | ComparisonFormula | semmle.label | getBody() | +| Foo.qll:32:3:32:15 | NewTypeBranch TPathNodeMid2 | Foo.qll:32:32:32:41 | ComparisonFormula | semmle.order | 85 | +| Foo.qll:32:17:32:27 | foo | Foo.qll:32:17:32:23 | TypeExpr | semmle.label | getTypeExpr() | +| Foo.qll:32:17:32:27 | foo | Foo.qll:32:17:32:23 | TypeExpr | semmle.order | 83 | +| Foo.qll:32:32:32:41 | ComparisonFormula | Foo.qll:32:32:32:34 | foo | semmle.label | getLeftOperand() | +| Foo.qll:32:32:32:41 | ComparisonFormula | Foo.qll:32:32:32:34 | foo | semmle.order | 85 | +| Foo.qll:32:32:32:41 | ComparisonFormula | Foo.qll:32:38:32:41 | Boolean | semmle.label | getRightOperand() | +| Foo.qll:32:32:32:41 | ComparisonFormula | Foo.qll:32:38:32:41 | Boolean | semmle.order | 87 | +| Foo.qll:33:3:33:15 | NewTypeBranch TPathNodeSink | Foo.qll:33:17:33:26 | bar | semmle.label | getField(_) | +| Foo.qll:33:3:33:15 | NewTypeBranch TPathNodeSink | Foo.qll:33:17:33:26 | bar | semmle.order | 89 | +| Foo.qll:33:3:33:15 | NewTypeBranch TPathNodeSink | Foo.qll:33:31:33:41 | ComparisonFormula | semmle.label | getBody() | +| Foo.qll:33:3:33:15 | NewTypeBranch TPathNodeSink | Foo.qll:33:31:33:41 | ComparisonFormula | semmle.order | 91 | +| Foo.qll:33:17:33:26 | bar | Foo.qll:33:17:33:22 | TypeExpr | semmle.label | getTypeExpr() | +| Foo.qll:33:17:33:26 | bar | Foo.qll:33:17:33:22 | TypeExpr | semmle.order | 89 | +| Foo.qll:33:31:33:41 | ComparisonFormula | Foo.qll:33:31:33:33 | bar | semmle.label | getLeftOperand() | +| Foo.qll:33:31:33:41 | ComparisonFormula | Foo.qll:33:31:33:33 | bar | semmle.order | 91 | +| Foo.qll:33:31:33:41 | ComparisonFormula | Foo.qll:33:37:33:41 | String | semmle.label | getRightOperand() | +| Foo.qll:33:31:33:41 | ComparisonFormula | Foo.qll:33:37:33:41 | String | semmle.order | 93 | | printAst.ql:1:1:1:28 | Import | printAst.ql:1:18:1:28 | printAstAst | semmle.label | getModuleExpr() | -| printAst.ql:1:1:1:28 | Import | printAst.ql:1:18:1:28 | printAstAst | semmle.order | 100 | +| printAst.ql:1:1:1:28 | Import | printAst.ql:1:18:1:28 | printAstAst | semmle.order | 96 | | printAst.ql:1:1:1:29 | TopLevel | printAst.ql:1:1:1:28 | Import | semmle.label | getAnImport() | -| printAst.ql:1:1:1:29 | TopLevel | printAst.ql:1:1:1:28 | Import | semmle.order | 98 | +| printAst.ql:1:1:1:29 | TopLevel | printAst.ql:1:1:1:28 | Import | semmle.order | 94 | graphProperties | semmle.graphKind | tree | diff --git a/ruby/ql/lib/CHANGELOG.md b/ruby/ql/lib/CHANGELOG.md index 5803375fd51..a06ccb6f8ad 100644 --- a/ruby/ql/lib/CHANGELOG.md +++ b/ruby/ql/lib/CHANGELOG.md @@ -1,3 +1,20 @@ +## 0.7.0 + +### Deprecated APIs + +* The `Configuration` taint flow configuration class from `codeql.ruby.security.InsecureDownloadQuery` has been deprecated. Use the `Flow` module instead. + +### Minor Analysis Improvements + +* More kinds of rack applications are now recognized. +* Rack::Response instances are now recognized as potential responses from rack applications. +* HTTP redirect responses from Rack applications are now recognized as a potential sink for open redirect alerts. +* Additional sinks for `rb/unsafe-deserialization` have been added. This includes various methods from the `yaml` and `plist` gems, which deserialize YAML and Property List data, respectively. + +## 0.6.4 + +No user-facing changes. + ## 0.6.3 ### Minor Analysis Improvements diff --git a/ruby/ql/lib/change-notes/2023-05-27-unsafe-deserialization.md b/ruby/ql/lib/change-notes/2023-05-27-unsafe-deserialization.md deleted file mode 100644 index 4039e7c90dc..00000000000 --- a/ruby/ql/lib/change-notes/2023-05-27-unsafe-deserialization.md +++ /dev/null @@ -1,4 +0,0 @@ ---- -category: minorAnalysis ---- -* Additional sinks for `rb/unsafe-deserialization` have been added. This includes various methods from the `yaml` and `plist` gems, which deserialize YAML and Property List data, respectively. 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 deleted file mode 100644 index 09687fa95be..00000000000 --- a/ruby/ql/lib/change-notes/2023-06-08-rack-redirect.md +++ /dev/null @@ -1,4 +0,0 @@ ---- -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 deleted file mode 100644 index 6bf019cd051..00000000000 --- a/ruby/ql/lib/change-notes/2023-06-14-insecure-download-config.md +++ /dev/null @@ -1,4 +0,0 @@ ---- -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/2023-07-12-default-stateconfigsig-predicates.md b/ruby/ql/lib/change-notes/2023-07-12-default-stateconfigsig-predicates.md new file mode 100644 index 00000000000..c9aba58603a --- /dev/null +++ b/ruby/ql/lib/change-notes/2023-07-12-default-stateconfigsig-predicates.md @@ -0,0 +1,6 @@ +--- +category: feature +--- +* The `DataFlow::StateConfigSig` signature module has gained default implementations for `isBarrier/2` and `isAdditionalFlowStep/4`. + Hence it is no longer needed to provide `none()` implementations of these predicates if they are not needed. + diff --git a/ruby/ql/lib/change-notes/released/0.6.4.md b/ruby/ql/lib/change-notes/released/0.6.4.md new file mode 100644 index 00000000000..7e98b0159fc --- /dev/null +++ b/ruby/ql/lib/change-notes/released/0.6.4.md @@ -0,0 +1,3 @@ +## 0.6.4 + +No user-facing changes. diff --git a/ruby/ql/lib/change-notes/released/0.7.0.md b/ruby/ql/lib/change-notes/released/0.7.0.md new file mode 100644 index 00000000000..4051d8a0c27 --- /dev/null +++ b/ruby/ql/lib/change-notes/released/0.7.0.md @@ -0,0 +1,12 @@ +## 0.7.0 + +### Deprecated APIs + +* The `Configuration` taint flow configuration class from `codeql.ruby.security.InsecureDownloadQuery` has been deprecated. Use the `Flow` module instead. + +### Minor Analysis Improvements + +* More kinds of rack applications are now recognized. +* Rack::Response instances are now recognized as potential responses from rack applications. +* HTTP redirect responses from Rack applications are now recognized as a potential sink for open redirect alerts. +* Additional sinks for `rb/unsafe-deserialization` have been added. This includes various methods from the `yaml` and `plist` gems, which deserialize YAML and Property List data, respectively. diff --git a/ruby/ql/lib/codeql-pack.release.yml b/ruby/ql/lib/codeql-pack.release.yml index b7dafe32c5d..c761f3e7ab4 100644 --- a/ruby/ql/lib/codeql-pack.release.yml +++ b/ruby/ql/lib/codeql-pack.release.yml @@ -1,2 +1,2 @@ --- -lastReleaseVersion: 0.6.3 +lastReleaseVersion: 0.7.0 diff --git a/ruby/ql/lib/codeql/ruby/ApiGraphs.qll b/ruby/ql/lib/codeql/ruby/ApiGraphs.qll index 1cf4c445781..0a650ea4ca2 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,307 @@ 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")) + ) + or + // Step through callable wrappers like `proc` and `lambda` calls. + exists(DataFlow::Node node | + pred = getBackwardEndNode(node) and + succ = getBackwardStartNode(node.asCallable()) ) } + 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) { not mod.getQualifiedName() = "Object" } or + /** The module object `mod` with epsilon edges to its descendents. */ + MkModuleObjectDown(DataFlow::ModuleNode mod) { not mod.getQualifiedName() = "Object" } or + /** Instances of `mod` with epsilon edges to its ancestors. */ + MkModuleInstanceUp(DataFlow::ModuleNode mod) { not mod.getQualifiedName() = "Object" } or + /** Instances of `mod` with epsilon edges to its descendents, and to its upward node. */ + MkModuleInstanceDown(DataFlow::ModuleNode mod) { not mod.getQualifiedName() = "Object" } 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 +1135,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 +1430,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 +1471,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 +1541,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/DataFlow.qll b/ruby/ql/lib/codeql/ruby/dataflow/internal/DataFlow.qll index f34554d6eea..47329d133a4 100644 --- a/ruby/ql/lib/codeql/ruby/dataflow/internal/DataFlow.qll +++ b/ruby/ql/lib/codeql/ruby/dataflow/internal/DataFlow.qll @@ -114,7 +114,7 @@ signature module StateConfigSig { * Holds if data flow through `node` is prohibited when the flow state is * `state`. */ - predicate isBarrier(Node node, FlowState state); + default predicate isBarrier(Node node, FlowState state) { none() } /** Holds if data flow into `node` is prohibited. */ default predicate isBarrierIn(Node node) { none() } @@ -131,7 +131,9 @@ signature module StateConfigSig { * Holds if data may flow from `node1` to `node2` in addition to the normal data-flow steps. * This step is only applicable in `state1` and updates the flow state to `state2`. */ - predicate isAdditionalFlowStep(Node node1, FlowState state1, Node node2, FlowState state2); + default predicate isAdditionalFlowStep(Node node1, FlowState state1, Node node2, FlowState state2) { + none() + } /** * Holds if an arbitrary number of implicit read steps of content `c` may be diff --git a/ruby/ql/lib/codeql/ruby/dataflow/internal/DataFlowImpl.qll b/ruby/ql/lib/codeql/ruby/dataflow/internal/DataFlowImpl.qll index 284fff191ae..fe8633e9218 100644 --- a/ruby/ql/lib/codeql/ruby/dataflow/internal/DataFlowImpl.qll +++ b/ruby/ql/lib/codeql/ruby/dataflow/internal/DataFlowImpl.qll @@ -254,6 +254,11 @@ module Impl { not fullBarrier(node2) } + pragma[nomagic] + private predicate isUnreachableInCall1(NodeEx n, LocalCallContextSpecificCall cc) { + isUnreachableInCallCached(n.asNode(), cc.getCall()) + } + /** * Holds if data can flow in one local step from `node1` to `node2`. */ @@ -460,7 +465,6 @@ module Impl { * The Boolean `cc` records whether the node is reached through an * argument in a call. */ - pragma[assume_small_delta] private predicate fwdFlow(NodeEx node, Cc cc) { sourceNode(node, _) and if hasSourceCallCtx() then cc = true else cc = false @@ -570,7 +574,6 @@ module Impl { /** * Holds if `c` is the target of a store in the flow covered by `fwdFlow`. */ - pragma[assume_small_delta] pragma[nomagic] private predicate fwdFlowConsCand(Content c) { exists(NodeEx mid, NodeEx node | @@ -1216,7 +1219,6 @@ module Impl { fwdFlow1(_, _, _, _, _, _, t0, t, ap, _) and t0 != t } - pragma[assume_small_delta] pragma[nomagic] private predicate fwdFlow0( NodeEx node, FlowState state, Cc cc, ParamNodeOption summaryCtx, TypOption argT, @@ -2111,7 +2113,7 @@ module Impl { NodeEx node1, FlowState state, NodeEx node2, boolean preservesValue, DataFlowType t, LocalCallContext cc ) { - not isUnreachableInCallCached(node2.asNode(), cc.(LocalCallContextSpecificCall).getCall()) and + not isUnreachableInCall1(node2, cc) and ( localFlowEntry(node1, pragma[only_bind_into](state)) and ( @@ -2126,7 +2128,7 @@ module Impl { ) and node1 != node2 and cc.relevantFor(node1.getEnclosingCallable()) and - not isUnreachableInCallCached(node1.asNode(), cc.(LocalCallContextSpecificCall).getCall()) + not isUnreachableInCall1(node1, cc) or exists(NodeEx mid | localFlowStepPlus(node1, pragma[only_bind_into](state), mid, preservesValue, t, cc) and @@ -2163,10 +2165,8 @@ module Impl { preservesValue = false and t = node2.getDataFlowType() and callContext.relevantFor(node1.getEnclosingCallable()) and - not exists(DataFlowCall call | call = callContext.(LocalCallContextSpecificCall).getCall() | - isUnreachableInCallCached(node1.asNode(), call) or - isUnreachableInCallCached(node2.asNode(), call) - ) + not isUnreachableInCall1(node1, callContext) and + not isUnreachableInCall1(node2, callContext) } } @@ -2706,7 +2706,7 @@ module Impl { ParamNodeEx getParamNode() { result = p } - override string toString() { result = p + ": " + ap } + override string toString() { result = p + concat(" : " + ppReprType(t)) + " " + ap } predicate hasLocationInfo( string filepath, int startline, int startcolumn, int endline, int endcolumn @@ -2758,12 +2758,21 @@ module Impl { ) } + private predicate forceUnfold(AccessPathApprox apa) { + forceHighPrecision(apa.getHead()) + or + exists(Content c2 | + apa = TConsCons(_, _, c2, _) and + forceHighPrecision(c2) + ) + } + /** * Holds with `unfold = false` if a precise head-tail representation of `apa` is * expected to be expensive. Holds with `unfold = true` otherwise. */ private predicate evalUnfold(AccessPathApprox apa, boolean unfold) { - if forceHighPrecision(apa.getHead()) + if forceUnfold(apa) then unfold = true else exists(int aps, int nodes, int apLimit, int tupleLimit | @@ -2777,7 +2786,6 @@ module Impl { /** * Gets the number of `AccessPath`s that correspond to `apa`. */ - pragma[assume_small_delta] private int countAps(AccessPathApprox apa) { evalUnfold(apa, false) and result = 1 and @@ -2796,7 +2804,6 @@ module Impl { * that it is expanded to a precise head-tail representation. */ language[monotonicAggregates] - pragma[assume_small_delta] private int countPotentialAps(AccessPathApprox apa) { apa instanceof AccessPathApproxNil and result = 1 or @@ -2833,7 +2840,6 @@ module Impl { } private newtype TPathNode = - pragma[assume_small_delta] TPathNodeMid( NodeEx node, FlowState state, CallContext cc, SummaryCtx sc, DataFlowType t, AccessPath ap ) { @@ -2918,7 +2924,6 @@ module Impl { override AccessPathFrontHead getFront() { result = TFrontHead(head_) } - pragma[assume_small_delta] override AccessPathApproxCons getApprox() { result = TConsNil(head_, t) and tail_ = TAccessPathNil() or @@ -2927,7 +2932,6 @@ module Impl { result = TCons1(head_, this.length()) } - pragma[assume_small_delta] override int length() { result = 1 + tail_.length() } private string toStringImpl(boolean needsSuffix) { @@ -3097,6 +3101,12 @@ module Impl { result = " <" + this.(PathNodeMid).getCallContext().toString() + ">" } + private string ppSummaryCtx() { + this instanceof PathNodeSink and result = "" + or + result = " <" + this.(PathNodeMid).getSummaryCtx().toString() + ">" + } + /** Gets a textual representation of this element. */ string toString() { result = this.getNodeEx().toString() + this.ppType() + this.ppAp() } @@ -3105,7 +3115,9 @@ module Impl { * representation of the call context. */ string toStringWithContext() { - result = this.getNodeEx().toString() + this.ppType() + this.ppAp() + this.ppCtx() + result = + this.getNodeEx().toString() + this.ppType() + this.ppAp() + this.ppCtx() + + this.ppSummaryCtx() } /** @@ -3379,7 +3391,6 @@ module Impl { * Holds if data may flow from `mid` to `node`. The last step in or out of * a callable is recorded by `cc`. */ - pragma[assume_small_delta] pragma[nomagic] private predicate pathStep0( PathNodeMid mid, NodeEx node, FlowState state, CallContext cc, SummaryCtx sc, DataFlowType t, @@ -3592,7 +3603,6 @@ module Impl { ) } - pragma[assume_small_delta] pragma[nomagic] private predicate pathThroughCallable0( DataFlowCall call, PathNodeMid mid, ReturnKindExt kind, FlowState state, CallContext cc, diff --git a/ruby/ql/lib/codeql/ruby/dataflow/internal/DataFlowImplCommon.qll b/ruby/ql/lib/codeql/ruby/dataflow/internal/DataFlowImplCommon.qll index 0d4c033c95d..aff14e7b44d 100644 --- a/ruby/ql/lib/codeql/ruby/dataflow/internal/DataFlowImplCommon.qll +++ b/ruby/ql/lib/codeql/ruby/dataflow/internal/DataFlowImplCommon.qll @@ -187,7 +187,6 @@ private module LambdaFlow { else any() } - pragma[assume_small_delta] pragma[nomagic] predicate revLambdaFlow0( DataFlowCall lambdaCall, LambdaCallKind kind, Node node, DataFlowType t, boolean toReturn, @@ -274,7 +273,6 @@ private module LambdaFlow { ) } - pragma[assume_small_delta] pragma[nomagic] predicate revLambdaFlowOut( DataFlowCall lambdaCall, LambdaCallKind kind, TReturnPositionSimple pos, DataFlowType t, diff --git a/ruby/ql/lib/codeql/ruby/dataflow/internal/DataFlowPrivate.qll b/ruby/ql/lib/codeql/ruby/dataflow/internal/DataFlowPrivate.qll index f8469e99a23..f3249d377d8 100644 --- a/ruby/ql/lib/codeql/ruby/dataflow/internal/DataFlowPrivate.qll +++ b/ruby/ql/lib/codeql/ruby/dataflow/internal/DataFlowPrivate.qll @@ -1333,11 +1333,20 @@ predicate lambdaCreation(Node creation, LambdaCallKind kind, DataFlowCallable c) creation.asExpr() = any(CfgNodes::ExprNodes::MethodCallCfgNode mc | c.asCallable() = mc.getBlock().getExpr() and - mc.getExpr().getMethodName() = ["lambda", "proc"] + isProcCreationCall(mc.getExpr()) ) ) } +/** Holds if `call` is a call to `lambda`, `proc`, or `Proc.new` */ +pragma[nomagic] +private predicate isProcCreationCall(MethodCall call) { + call.getMethodName() = ["proc", "lambda"] + or + call.getMethodName() = "new" and + call.getReceiver().(ConstantReadAccess).getAQualifiedName() = "Proc" +} + /** * Holds if `call` is a from-source lambda call of kind `kind` where `receiver` * is the lambda expression. diff --git a/ruby/ql/lib/codeql/ruby/dataflow/internal/DataFlowPublic.qll b/ruby/ql/lib/codeql/ruby/dataflow/internal/DataFlowPublic.qll index a98238e85d9..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) } } /** @@ -1334,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. * @@ -1359,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). * @@ -1436,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) ) } @@ -1458,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/dataflow/internal/FlowSummaryImpl.qll b/ruby/ql/lib/codeql/ruby/dataflow/internal/FlowSummaryImpl.qll index 9ea7c44c50c..7977e18120f 100644 --- a/ruby/ql/lib/codeql/ruby/dataflow/internal/FlowSummaryImpl.qll +++ b/ruby/ql/lib/codeql/ruby/dataflow/internal/FlowSummaryImpl.qll @@ -23,29 +23,30 @@ module Public { * content type, or a return kind. */ class SummaryComponent extends TSummaryComponent { - /** Gets a textual representation of this summary component. */ - string toString() { - exists(ContentSet c | this = TContentSummaryComponent(c) and result = c.toString()) - or - exists(ContentSet c | this = TWithoutContentSummaryComponent(c) and result = "without " + c) - or - exists(ContentSet c | this = TWithContentSummaryComponent(c) and result = "with " + c) + /** Gets a textual representation of this component used for MaD models. */ + string getMadRepresentation() { + result = getMadRepresentationSpecific(this) or exists(ArgumentPosition pos | - this = TParameterSummaryComponent(pos) and result = "parameter " + pos + this = TParameterSummaryComponent(pos) and + result = "Parameter[" + getArgumentPosition(pos) + "]" ) or exists(ParameterPosition pos | - this = TArgumentSummaryComponent(pos) and result = "argument " + pos + this = TArgumentSummaryComponent(pos) and + result = "Argument[" + getParameterPosition(pos) + "]" ) or - exists(ReturnKind rk | this = TReturnSummaryComponent(rk) and result = "return (" + rk + ")") - or - exists(SummaryComponent::SyntheticGlobal sg | - this = TSyntheticGlobalSummaryComponent(sg) and - result = "synthetic global (" + sg + ")" + exists(string synthetic | + this = TSyntheticGlobalSummaryComponent(synthetic) and + result = "SyntheticGlobal[" + synthetic + "]" ) + or + this = TReturnSummaryComponent(getReturnValueKind()) and result = "ReturnValue" } + + /** Gets a textual representation of this summary component. */ + string toString() { result = this.getMadRepresentation() } } /** Provides predicates for constructing summary components. */ @@ -110,7 +111,6 @@ module Public { } /** Gets the stack obtained by dropping the first `i` elements, if any. */ - pragma[assume_small_delta] SummaryComponentStack drop(int i) { i = 0 and result = this or @@ -125,19 +125,22 @@ module Public { this = TSingletonSummaryComponentStack(result) or result = this.tail().bottom() } - /** Gets a textual representation of this stack. */ - string toString() { + /** Gets a textual representation of this stack used for MaD models. */ + string getMadRepresentation() { exists(SummaryComponent head, SummaryComponentStack tail | head = this.head() and tail = this.tail() and - result = tail + "." + head + result = tail.getMadRepresentation() + "." + head.getMadRepresentation() ) or exists(SummaryComponent c | this = TSingletonSummaryComponentStack(c) and - result = c.toString() + result = c.getMadRepresentation() ) } + + /** Gets a textual representation of this stack. */ + string toString() { result = this.getMadRepresentation() } } /** Provides predicates for constructing stacks of summary components. */ @@ -166,42 +169,6 @@ module Public { SummaryComponentStack return(ReturnKind rk) { result = singleton(SummaryComponent::return(rk)) } } - /** Gets a textual representation of this component used for flow summaries. */ - private string getComponent(SummaryComponent sc) { - result = getComponentSpecific(sc) - or - exists(ArgumentPosition pos | - sc = TParameterSummaryComponent(pos) and - result = "Parameter[" + getArgumentPosition(pos) + "]" - ) - or - exists(ParameterPosition pos | - sc = TArgumentSummaryComponent(pos) and - result = "Argument[" + getParameterPosition(pos) + "]" - ) - or - exists(string synthetic | - sc = TSyntheticGlobalSummaryComponent(synthetic) and - result = "SyntheticGlobal[" + synthetic + "]" - ) - or - sc = TReturnSummaryComponent(getReturnValueKind()) and result = "ReturnValue" - } - - /** Gets a textual representation of this stack used for flow summaries. */ - string getComponentStack(SummaryComponentStack stack) { - exists(SummaryComponent head, SummaryComponentStack tail | - head = stack.head() and - tail = stack.tail() and - result = getComponentStack(tail) + "." + getComponent(head) - ) - or - exists(SummaryComponent c | - stack = TSingletonSummaryComponentStack(c) and - result = getComponent(c) - ) - } - /** * A class that exists for QL technical reasons only (the IPA type used * to represent component stacks needs to be bounded). @@ -1382,8 +1349,8 @@ module Private { c.relevantSummary(input, output, preservesValue) and csv = c.getCallableCsv() // Callable information - + getComponentStack(input) + ";" // input - + getComponentStack(output) + ";" // output + + input.getMadRepresentation() + ";" // input + + output.getMadRepresentation() + ";" // output + renderKind(preservesValue) + ";" // kind + renderProvenance(c) // provenance ) diff --git a/ruby/ql/lib/codeql/ruby/dataflow/internal/FlowSummaryImplSpecific.qll b/ruby/ql/lib/codeql/ruby/dataflow/internal/FlowSummaryImplSpecific.qll index d9c0594159a..e97014a5451 100644 --- a/ruby/ql/lib/codeql/ruby/dataflow/internal/FlowSummaryImplSpecific.qll +++ b/ruby/ql/lib/codeql/ruby/dataflow/internal/FlowSummaryImplSpecific.qll @@ -139,8 +139,53 @@ SummaryComponent interpretComponentSpecific(AccessPathToken c) { ) } -/** Gets the textual representation of a summary component in the format used for flow summaries. */ -string getComponentSpecific(SummaryComponent sc) { none() } +private string getContentSpecific(Content c) { + exists(string name | c = TFieldContent(name) and result = "Field[" + name + "]") + or + exists(ConstantValue cv | + c = TKnownElementContent(cv) and result = "Element[" + cv.serialize() + "!]" + ) + or + c = TUnknownElementContent() and result = "Element[?]" +} + +private string getContentSetSpecific(ContentSet cs) { + exists(Content c | cs = TSingletonContent(c) and result = getContentSpecific(c)) + or + cs = TAnyElementContent() and result = "Element[any]" + or + exists(Content::KnownElementContent kec | + cs = TKnownOrUnknownElementContent(kec) and + result = "Element[" + kec.getIndex().serialize() + "]" + ) + or + exists(int lower, boolean includeUnknown, string unknown | + cs = TElementLowerBoundContent(lower, includeUnknown) and + (if includeUnknown = true then unknown = "" else unknown = "!") and + result = "Element[" + lower + ".." + unknown + "]" + ) +} + +/** Gets the textual representation of a summary component in the format used for MaD models. */ +string getMadRepresentationSpecific(SummaryComponent sc) { + exists(ContentSet cs | sc = TContentSummaryComponent(cs) and result = getContentSetSpecific(cs)) + or + exists(ContentSet cs | + sc = TWithoutContentSummaryComponent(cs) and + result = "WithoutElement[" + getContentSetSpecific(cs) + "]" + ) + or + exists(ContentSet cs | + sc = TWithContentSummaryComponent(cs) and + result = "WithElement[" + getContentSetSpecific(cs) + "]" + ) + or + exists(ReturnKind rk | + sc = TReturnSummaryComponent(rk) and + not rk = getReturnValueKind() and + result = "ReturnValue[" + rk + "]" + ) +} /** Gets the textual representation of a parameter position in the format used for flow summaries. */ string getParameterPosition(ParameterPosition pos) { @@ -170,6 +215,9 @@ string getParameterPosition(ParameterPosition pos) { or pos.isAnyNamed() and result = "any-named" + or + pos.isHashSplat() and + result = "hash-splat" } /** Gets the textual representation of an argument position in the format used for flow summaries. */ 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/Sqlite3.qll b/ruby/ql/lib/codeql/ruby/frameworks/Sqlite3.qll index 77e26ca13d7..981ace2e7da 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 8c6a11b455b..b24301b676d 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::App::AppCandidate app).getEnv().getALocalUse() } + RackEnv() { this = any(Rack::App::RequestHandler handler).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/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 index bfdd988ac19..748291f55bd 100644 --- a/ruby/ql/lib/codeql/ruby/frameworks/rack/internal/App.qll +++ b/ruby/ql/lib/codeql/ruby/frameworks/rack/internal/App.qll @@ -2,24 +2,44 @@ * Provides modeling for Rack applications. */ +private import codeql.ruby.AST 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" } +/** + * A callable node that takes a single argument and, if it has a method name, + * is called "call". + */ +private class PotentialRequestHandler extends DataFlow::CallableNode { + PotentialRequestHandler() { + this.getNumberOfParameters() = 1 and + ( + this.(DataFlow::MethodNode).getMethodName() = "call" + or + not this instanceof DataFlow::MethodNode and + exists(DataFlow::CallNode cn | cn.getMethodName() = "run" | + this.(DataFlow::LocalSourceNode).flowsTo(cn.getArgument(0)) + or + // TODO: `Proc.new` should automatically propagate flow from its block argument + any(DataFlow::CallNode proc | + proc = API::getTopLevelMember("Proc").getAnInstantiation() and + proc.getBlock() = this + ).(DataFlow::LocalSourceNode).flowsTo(cn.getArgument(0)) + ) + ) + } } -private DataFlow::LocalSourceNode trackRackResponse(TypeBackTracker t, CallMethodNode call) { +private DataFlow::LocalSourceNode trackRackResponse(TypeBackTracker t, PotentialRequestHandler 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) { +private RP::PotentialResponseNode trackRackResponse(PotentialRequestHandler call) { result = trackRackResponse(TypeBackTracker::end(), call) } @@ -28,12 +48,13 @@ private RP::PotentialResponseNode trackRackResponse(CallMethodNode call) { */ module App { /** + * DEPRECATED: Use `RequestHandler` instead. * 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; + deprecated class AppCandidate extends DataFlow::ClassNode { + private RequestHandler call; private RP::PotentialResponseNode resp; AppCandidate() { @@ -50,4 +71,19 @@ module App { /** Gets the response returned from a request to this application. */ RP::PotentialResponseNode getResponse() { result = resp } } + + /** + * A callable node that looks like it implements the rack specification. + */ + class RequestHandler extends PotentialRequestHandler { + private RP::PotentialResponseNode resp; + + RequestHandler() { resp = trackRackResponse(this) } + + /** Gets the `env` parameter passed to this request handler. */ + DataFlow::ParameterNode getEnv() { result = this.getParameter(0) } + + /** Gets a response returned from this request handler. */ + RP::PotentialResponseNode getAResponse() { 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 index 9d998c780ae..5f5b1601453 100644 --- a/ruby/ql/lib/codeql/ruby/frameworks/rack/internal/Response.qll +++ b/ruby/ql/lib/codeql/ruby/frameworks/rack/internal/Response.qll @@ -13,15 +13,39 @@ 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 } - + abstract class PotentialResponseNode extends DataFlow::Node { /** Gets the headers returned with this response. */ - DataFlow::Node getHeaders() { result = this.getElement(1) } + abstract DataFlow::Node getHeaders(); /** Gets the body of this response. */ - DataFlow::Node getBody() { result = this.getElement(2) } + abstract DataFlow::Node getBody(); + } + + /** A rack response constructed directly using an array literal. */ + private class PotentialArrayResponse extends PotentialResponseNode, DataFlow::ArrayLiteralNode { + // [status, headers, body] + PotentialArrayResponse() { this.getNumberOfArguments() = 3 } + + override DataFlow::Node getHeaders() { result = this.getElement(1) } + + override DataFlow::Node getBody() { result = this.getElement(2) } + } + + /** A rack response constructed by calling `finish` on an instance of `Rack::Response`. */ + private class RackResponseConstruction extends PotentialResponseNode, DataFlow::CallNode { + private DataFlow::CallNode responseConstruction; + + // (body, status, headers) + RackResponseConstruction() { + responseConstruction = + API::getTopLevelMember("Rack").getMember("Response").getAnInstantiation() and + this = responseConstruction.getAMethodCall() and + this.getMethodName() = "finish" + } + + override DataFlow::Node getHeaders() { result = responseConstruction.getArgument(2) } + + override DataFlow::Node getBody() { result = responseConstruction.getArgument(0) } } } @@ -54,19 +78,30 @@ module Public { v.getStringlikeValue().toLowerCase() = headerName.toLowerCase() )) ) + or + // pair in a `Rack::Response.new` constructor + exists(DataFlow::PairNode headerPair | headerPair = headers | + headerPair.getKey().getConstantValue().getStringlikeValue().toLowerCase() = + headerName.toLowerCase() and + result = headerPair.getValue() + ) ) } /** 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() } + class ResponseNode extends Http::Server::HttpResponse::Range instanceof Private::PotentialResponseNode + { + ResponseNode() { this = any(A::App::RequestHandler handler).getAResponse() } - override DataFlow::Node getBody() { result = this.getElement(2) } + override DataFlow::Node getBody() { result = this.(Private::PotentialResponseNode).getBody() } override DataFlow::Node getMimetypeOrContentTypeArg() { result = getHeaderValue(this, "content-type") } + /** Gets the headers returned with this response. */ + DataFlow::Node getHeaders() { result = this.(Private::PotentialResponseNode).getHeaders() } + // TODO: is there a sensible value for this? override string getMimetypeDefault() { none() } } diff --git a/ruby/ql/lib/codeql/ruby/security/IncompleteMultiCharacterSanitizationQuery.qll b/ruby/ql/lib/codeql/ruby/security/IncompleteMultiCharacterSanitizationQuery.qll index 10f07b39725..2a2d4d4d601 100644 --- a/ruby/ql/lib/codeql/ruby/security/IncompleteMultiCharacterSanitizationQuery.qll +++ b/ruby/ql/lib/codeql/ruby/security/IncompleteMultiCharacterSanitizationQuery.qll @@ -15,6 +15,14 @@ private class DangerousPrefix extends string { this = "