diff --git a/powershell/ql/lib/semmle/code/powershell/ApiGraphs.qll b/powershell/ql/lib/semmle/code/powershell/ApiGraphs.qll index 5e04b6f75795..d194fbc80cbf 100644 --- a/powershell/ql/lib/semmle/code/powershell/ApiGraphs.qll +++ b/powershell/ql/lib/semmle/code/powershell/ApiGraphs.qll @@ -367,9 +367,11 @@ module API { Node methodEdge(string name) { none() } + Node instanceEdge() { none() } + final predicate isImplicit() { not this.isExplicit(_) } - predicate isExplicit(DataFlow::TypeNameNode typeName) { none() } + predicate isExplicit(DataFlow::Node node) { none() } } final class TypeNameNode = AbstractTypeNameNode; @@ -392,8 +394,8 @@ module API { ) } - final override predicate isExplicit(DataFlow::TypeNameNode typeName) { - Specific::needsExplicitTypeNameNode(typeName, prefix) + final override predicate isExplicit(DataFlow::Node node) { + Specific::needsExplicitTypeNameNode(node, prefix) } } @@ -424,6 +426,26 @@ module API { } } + class NewObjectTypeNameNode extends AbstractTypeNameNode, Impl::MkNewObjectTypeNameNode { + NewObjectTypeNameNode() { this = Impl::MkNewObjectTypeNameNode(prefix) } + + final override Node getSuccessor(string name) { + result = Impl::MkNewObjectTypeNameNode(prefix + "." + name) + } + + final override Node instanceEdge() { + exists(DataFlow::ObjectCreationNode creation | + prefix = creation.getLowerCaseConstructedTypeName() and + Specific::needsNewObjectTypeNameNode(creation, prefix) and + result = getForwardStartNode(creation) + ) + } + + final override predicate isExplicit(DataFlow::Node node) { + Specific::needsNewObjectTypeNameNode(node, prefix) + } + } + /** * An API entry point. * @@ -517,6 +539,7 @@ module API { MkMethodAccessNode(DataFlow::CallNode call) or MkExplicitTypeNameNode(string prefix) { Specific::needsExplicitTypeNameNode(_, prefix) } or MkImplicitTypeNameNode(string prefix) { Specific::needsImplicitTypeNameNode(prefix) } or + MkNewObjectTypeNameNode(string prefix) { Specific::needsNewObjectTypeNameNode(_, prefix) } or 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 @@ -565,7 +588,7 @@ module API { ) or exists(DataFlow::Node qualifier | pred = getForwardEndNode(getALocalSourceStrict(qualifier)) | - exists(CfgNodes::ExprNodes::MemberExprReadAccessCfgNode read | + exists(CfgNodes::ExprNodes::MemberExprCfgNode read | read.getQualifier() = qualifier.asExpr() and read.getLowerCaseMemberName() = name and succ = getForwardStartNode(DataFlow::exprNode(read)) @@ -678,6 +701,13 @@ module API { positionalParameterEdge(pred, n, succ) } + pragma[nomagic] + private DataFlow::CallNode getStaticConstructorLikeCall() { + exists(string type | + typeModel(type, type + "!", "Method[" + result.getLowerCaseName() + "].ReturnValue") + ) + } + cached predicate instanceEdge(Node pred, Node succ) { // TODO: Also model parameters with a given type here @@ -685,6 +715,14 @@ module API { pred = getForwardEndNode(objCreation.getConstructedTypeNode()) and succ = getForwardStartNode(objCreation) ) + or + exists(DataFlow::CallNode call | + call = getStaticConstructorLikeCall() and + pred = getForwardEndNode(call.getQualifier()) and + succ = getForwardStartNode(call) + ) + or + pred.(TypeNameNode).instanceEdge() = succ } cached diff --git a/powershell/ql/lib/semmle/code/powershell/ast/internal/ObjectCreation.qll b/powershell/ql/lib/semmle/code/powershell/ast/internal/ObjectCreation.qll index d5a51be00a37..4deb68146d57 100644 --- a/powershell/ql/lib/semmle/code/powershell/ast/internal/ObjectCreation.qll +++ b/powershell/ql/lib/semmle/code/powershell/ast/internal/ObjectCreation.qll @@ -46,12 +46,11 @@ class DotNetObjectCreation extends AbstractObjectCreation, CmdCall { final override Expr getConstructedTypeExpr() { // Either it's the named argument `TypeName` - result = CmdCall.super.getNamedArgument("TypeName") + result = CmdCall.super.getNamedArgument("typename") or // Or it's the first positional argument if that's the named argument - not CmdCall.super.hasNamedArgument("TypeName") and - result = CmdCall.super.getPositionalArgument(0) and - result = CmdCall.super.getNamedArgument(["ArgumentList", "Property"]) + not CmdCall.super.hasNamedArgument("typename") and + result = CmdCall.super.getPositionalArgument(0) } } diff --git a/powershell/ql/lib/semmle/code/powershell/dataflow/internal/DataFlowPublic.qll b/powershell/ql/lib/semmle/code/powershell/dataflow/internal/DataFlowPublic.qll index 3a194aada561..ed8b46891302 100644 --- a/powershell/ql/lib/semmle/code/powershell/dataflow/internal/DataFlowPublic.qll +++ b/powershell/ql/lib/semmle/code/powershell/dataflow/internal/DataFlowPublic.qll @@ -500,6 +500,10 @@ class ObjectCreationNode extends CallNode { string getAConstructedTypeName() { result = this.getObjectCreationNode().getAConstructedTypeName() } + + string getLowerCaseConstructedTypeName() { + result = this.getObjectCreationNode().getLowerCaseConstructedTypeName() + } } /** A call, viewed as a node in a data flow graph. */ diff --git a/powershell/ql/lib/semmle/code/powershell/frameworks/data/internal/ApiGraphModelsSpecific.qll b/powershell/ql/lib/semmle/code/powershell/frameworks/data/internal/ApiGraphModelsSpecific.qll index 58d4c8323787..cc1bec656997 100644 --- a/powershell/ql/lib/semmle/code/powershell/frameworks/data/internal/ApiGraphModelsSpecific.qll +++ b/powershell/ql/lib/semmle/code/powershell/frameworks/data/internal/ApiGraphModelsSpecific.qll @@ -101,6 +101,16 @@ predicate needsImplicitTypeNameNode(string component) { ) } +predicate needsNewObjectTypeNameNode(DataFlow::ObjectCreationNode creation, string component) { + creation.asExpr().getExpr() instanceof DotNetObjectCreation and + exists(string type, int index | + type = creation.getLowerCaseConstructedTypeName() and + index = [0 .. strictcount(type.indexOf("."))] and + component = + strictconcat(int i, string s | s = type.splitAt(".", i) and i <= index | s, "." order by i) + ) +} + /** Gets a Powershell-specific interpretation of the given `type`. */ API::Node getExtraNodeFromType(string rawType) { exists(string type, string suffix, DataFlow::TypeNameNode typeName | diff --git a/powershell/ql/test/library-tests/dataflow/typetracking/test.expected b/powershell/ql/test/library-tests/dataflow/typetracking/test.expected index 8748ef879ad2..e69de29bb2d1 100644 --- a/powershell/ql/test/library-tests/dataflow/typetracking/test.expected +++ b/powershell/ql/test/library-tests/dataflow/typetracking/test.expected @@ -1,2 +0,0 @@ -| test.ps1:15:20:15:36 | # $ type=PSObject | Missing result: type=PSObject | -| test.ps1:19:25:19:41 | # $ type=PSObject | Missing result: type=PSObject | diff --git a/powershell/ql/test/library-tests/dataflow/typetracking/test.ps1 b/powershell/ql/test/library-tests/dataflow/typetracking/test.ps1 index e9a36131c400..7680cbbcd083 100644 --- a/powershell/ql/test/library-tests/dataflow/typetracking/test.ps1 +++ b/powershell/ql/test/library-tests/dataflow/typetracking/test.ps1 @@ -12,8 +12,8 @@ Sink $myClass # $ type=myclass $withNamedArg = New-Object -TypeName PSObject -Sink $withNamedArg # $ type=PSObject +Sink $withNamedArg # $ type=psobject $withPositionalArg = New-Object PSObject -Sink $withPositionalArg # $ type=PSObject +Sink $withPositionalArg # $ type=psobject