diff --git a/powershell/ql/lib/semmle/code/powershell/ast/internal/If.qll b/powershell/ql/lib/semmle/code/powershell/ast/internal/If.qll index 94192397060c..4aa44a2359d0 100644 --- a/powershell/ql/lib/semmle/code/powershell/ast/internal/If.qll +++ b/powershell/ql/lib/semmle/code/powershell/ast/internal/If.qll @@ -53,5 +53,13 @@ class If extends Expr, TIf { ) } + StmtBlock getABranch(boolean b) { + b = true and result = this.getAThen() + or + b = false and result = this.getElse() + } + + StmtBlock getABranch() { result = this.getAThen() or result = this.getElse() } + predicate hasElse() { exists(this.getElse()) } } diff --git a/powershell/ql/lib/semmle/code/powershell/ast/internal/ScriptBlock.qll b/powershell/ql/lib/semmle/code/powershell/ast/internal/ScriptBlock.qll index ad71dd424263..343cd8927f1d 100644 --- a/powershell/ql/lib/semmle/code/powershell/ast/internal/ScriptBlock.qll +++ b/powershell/ql/lib/semmle/code/powershell/ast/internal/ScriptBlock.qll @@ -79,6 +79,9 @@ class ScriptBlock extends Ast, TScriptBlock { result = this.getParameter(index) ) or + i = ThisVar() and + result = this.getThisParameter() + or exists(int index | i = scriptBlockUsing(index) and result = this.getUsingStmt(index) @@ -90,13 +93,14 @@ class ScriptBlock extends Ast, TScriptBlock { or any(Synthesis s).pipelineParameterHasIndex(this, i) and synthChild(getRawAst(this), PipelineParamVar(), result) - or - i = -1 and - synthChild(getRawAst(this), ThisVar(), result) } + Parameter getThisParameter() { synthChild(getRawAst(this), ThisVar(), result) } + /** * Gets a parameter of this block. + * + * Note: This does not include the `this` parameter, but it does include pipeline parameters. */ Parameter getAParameter() { result = this.getParameter(_) } diff --git a/powershell/ql/lib/semmle/code/powershell/ast/internal/Synthesis.qll b/powershell/ql/lib/semmle/code/powershell/ast/internal/Synthesis.qll index 8aa76fd11a49..d58b2eb11b04 100644 --- a/powershell/ql/lib/semmle/code/powershell/ast/internal/Synthesis.qll +++ b/powershell/ql/lib/semmle/code/powershell/ast/internal/Synthesis.qll @@ -84,6 +84,8 @@ class Synthesis extends TSynthesis { predicate functionName(FunctionBase f, string name) { none() } + predicate getAnAccess(VarAccessSynth va, Variable v) { none() } + predicate memberName(Member m, string name) { none() } predicate typeName(Type t, string name) { none() } @@ -116,13 +118,26 @@ Raw::Ast getRawAst(Ast r) { r = getResultAst(result) } private module ThisSynthesis { private class ThisSynthesis extends Synthesis { + private predicate thisAccess(Raw::Ast parent, ChildIndex i, Child child, Raw::Scope scope) { + scope = parent.getScope() and + parent.getChild(toRawChildIndex(i)).(Raw::VarAccess).getUserPath().toLowerCase() = "this" and + child = SynthChild(VarAccessSynthKind(TVariableSynth(scope, ThisVar()))) + } + override predicate child(Raw::Ast parent, ChildIndex i, Child child) { parent instanceof Raw::MethodScriptBlock and i = ThisVar() and child = SynthChild(VarSynthKind(ThisVarKind())) or - parent.getChild(toRawChildIndex(i)).(Raw::VarAccess).getUserPath().toLowerCase() = "this" and - child = SynthChild(VarAccessSynthKind(TVariableSynth(parent.getScope(), ThisVar()))) + this.thisAccess(parent, i, child, _) + } + + final override predicate getAnAccess(VarAccessSynth va, Variable v) { + exists(Raw::Ast parent, Raw::Scope scope, ChildIndex i | + this.thisAccess(parent, i, _, scope) and + v = TVariableSynth(scope, ThisVar()) and + va = TVarAccessSynth(parent, i) + ) } override predicate variableSynthName(VariableSynth v, string name) { @@ -731,18 +746,26 @@ private module IteratorAccessSynth { ) } + final override predicate getAnAccess(VarAccessSynth va, Variable v) { + exists(Raw::Ast parent, ChildIndex i, Raw::VarAccess r | + this.expr(parent, i, r, _) and + va = TVarAccessSynth(parent, i) and + v = this.varAccess(r) + ) + } + override predicate exprStmtExpr(ExprStmt e, Expr expr) { exists(Raw::Ast p, Raw::VarAccess va, Raw::CmdExpr cmdExpr, ChildIndex i1, ChildIndex i2 | this.stmt(p, i1, _, _) and this.expr(cmdExpr, i2, va, _) and e = TExprStmtSynth(p, i1) and - expr = TVarAccessSynth(cmdExpr, i2, this.varAccess(va)) + expr = TVarAccessSynth(cmdExpr, i2) ) } final override Expr getResultAstImpl(Raw::Ast r) { exists(Raw::Ast parent, ChildIndex i | this.expr(parent, i, r, _) | - result = TVarAccessSynth(parent, i, this.varAccess(r)) + result = TVarAccessSynth(parent, i) ) } diff --git a/powershell/ql/lib/semmle/code/powershell/ast/internal/TAst.qll b/powershell/ql/lib/semmle/code/powershell/ast/internal/TAst.qll index 6bc4bf7a8bdb..ee7c58d4f656 100644 --- a/powershell/ql/lib/semmle/code/powershell/ast/internal/TAst.qll +++ b/powershell/ql/lib/semmle/code/powershell/ast/internal/TAst.qll @@ -61,7 +61,7 @@ private predicate hasScopeAndName(VariableImpl variable, Scope::Range scope, str scope = variable.getDeclaringScopeImpl() } -private predicate access(Raw::VarAccess va, VariableImpl v) { +predicate access(Raw::VarAccess va, VariableImpl v) { exists(string name, Scope::Range scope | pragma[only_bind_into](name) = variableNameInScope(va, scope) | @@ -150,11 +150,11 @@ private module Cached { ) } or TVariableSynth(Raw::Ast scope, ChildIndex i) { mkSynthChild(VarSynthKind(_), scope, i) } or - TVarAccessReal(Raw::VarAccess va, Variable v) { access(va, v) } or - TVarAccessSynth(Raw::Ast parent, ChildIndex i, Variable v) { - mkSynthChild(VarAccessRealKind(v), parent, i) + TVarAccessReal(Raw::VarAccess va) { access(va, _) } or + TVarAccessSynth(Raw::Ast parent, ChildIndex i) { + mkSynthChild(VarAccessRealKind(_), parent, i) or - mkSynthChild(VarAccessSynthKind(v), parent, i) + mkSynthChild(VarAccessSynthKind(_), parent, i) } or TWhileStmt(Raw::WhileStmt w) or TTypeNameExpr(Raw::TypeNameExpr t) or @@ -277,7 +277,7 @@ private module Cached { n = TTypeConstraint(result) or n = TUnaryExpr(result) or n = TUsingStmt(result) or - n = TVarAccessReal(result, _) or + n = TVarAccessReal(result) or n = TWhileStmt(result) or n = TFunctionDefinitionStmt(result) or n = TExpandableSubExpr(result) or @@ -308,7 +308,7 @@ private module Cached { result = TFunctionSynth(parent, i) or result = TBoolLiteral(parent, i) or result = TNullLiteral(parent, i) or - result = TVarAccessSynth(parent, i, _) or + result = TVarAccessSynth(parent, i) or result = TEnvVariable(parent, i) or result = TTypeSynth(parent, i) or result = TAutomaticVariable(parent, i) or diff --git a/powershell/ql/lib/semmle/code/powershell/ast/internal/Variable.qll b/powershell/ql/lib/semmle/code/powershell/ast/internal/Variable.qll index 27c2ac5063d7..c843a4ac52a7 100644 --- a/powershell/ql/lib/semmle/code/powershell/ast/internal/Variable.qll +++ b/powershell/ql/lib/semmle/code/powershell/ast/internal/Variable.qll @@ -95,25 +95,23 @@ module Private { class VarAccessReal extends VarAccessImpl, TVarAccessReal { Raw::VarAccess va; - Variable v; - VarAccessReal() { this = TVarAccessReal(va, v) } + VarAccessReal() { this = TVarAccessReal(va) } - final override Variable getVariableImpl() { result = v } + final override Variable getVariableImpl() { access(va, result) } - final override string toString() { result = v.getName() } + final override string toString() { result = va.getUserPath() } } class VarAccessSynth extends VarAccessImpl, TVarAccessSynth { Raw::Ast parent; ChildIndex i; - Variable v; - VarAccessSynth() { this = TVarAccessSynth(parent, i, v) } + VarAccessSynth() { this = TVarAccessSynth(parent, i) } - final override Variable getVariableImpl() { result = v } + final override Variable getVariableImpl() { any(Synthesis s).getAnAccess(this, result) } - final override string toString() { result = v.getName() } + final override string toString() { result = this.getVariableImpl().getName() } final override Location getLocation() { result = parent.getLocation() } } diff --git a/powershell/ql/lib/semmle/code/powershell/controlflow/CfgNodes.qll b/powershell/ql/lib/semmle/code/powershell/controlflow/CfgNodes.qll index 809ef64f8724..ef07239ab51e 100644 --- a/powershell/ql/lib/semmle/code/powershell/controlflow/CfgNodes.qll +++ b/powershell/ql/lib/semmle/code/powershell/controlflow/CfgNodes.qll @@ -284,6 +284,26 @@ class ProcessBlockCfgNode extends NamedBlockCfgNode { ScriptBlockCfgNode getScriptBlock() { result.getProcessBlock() = this } } +private class CatchClauseChildMapping extends NonExprChildMapping, CatchClause { + override predicate relevantChild(Ast child) { + child = this.getBody() or child = this.getACatchType() + } +} + +class CatchClauseCfgNode extends AstCfgNode { + override string getAPrimaryQlClass() { result = "CatchClauseCfgNode" } + + CatchClauseChildMapping s; + + CatchClause getCatchClause() { result = s } + + StmtCfgNode getBody() { s.hasCfgChild(s.getBody(), this, result) } + + TypeConstraint getCatchType(int i) { result = s.getCatchType(i) } + + TypeConstraint getACatchType() { result = this.getCatchType(_) } +} + module ExprNodes { private class ArrayExprChildMapping extends ExprChildMapping, ArrayExpr { override predicate relevantChild(Ast child) { @@ -371,7 +391,7 @@ module ExprNodes { ExprCfgNode getOperand() { e.hasCfgChild(e.getOperand(), this, result) } } - class ConstExprChildMapping extends ExprChildMapping, ConstExpr { + private class ConstExprChildMapping extends ExprChildMapping, ConstExpr { override predicate relevantChild(Ast child) { none() } } @@ -383,7 +403,7 @@ module ExprNodes { override ConstExpr getExpr() { result = e } } - class ConvertExprChildMapping extends ExprChildMapping, ConvertExpr { + private class ConvertExprChildMapping extends ExprChildMapping, ConvertExpr { override predicate relevantChild(Ast child) { child = this.getExpr() } } @@ -397,7 +417,7 @@ module ExprNodes { ExprCfgNode getSubExpr() { e.hasCfgChild(e.getExpr(), this, result) } } - class IndexExprChildMapping extends ExprChildMapping, IndexExpr { + private class IndexExprChildMapping extends ExprChildMapping, IndexExpr { override predicate relevantChild(Ast child) { child = this.getBase() or @@ -457,7 +477,7 @@ module ExprNodes { override IndexExprReadAccess getExpr() { result = e } } - class CallExprChildMapping extends ExprChildMapping, CallExpr { + private class CallExprChildMapping extends ExprChildMapping, CallExpr { override predicate relevantChild(Ast child) { child = this.getQualifier() or @@ -503,7 +523,7 @@ module ExprNodes { predicate isStatic() { this.getExpr().isStatic() } } - class ObjectCreationChildMapping extends CallExprChildMapping instanceof ObjectCreation { + private class ObjectCreationChildMapping extends CallExprChildMapping instanceof ObjectCreation { override predicate relevantChild(Ast child) { child = super.getConstructedTypeExpr() } } @@ -522,7 +542,7 @@ module ExprNodes { } } - class CallOperatorChildMapping extends CallExprChildMapping instanceof CallOperator { + private class CallOperatorChildMapping extends CallExprChildMapping instanceof CallOperator { override predicate relevantChild(Ast child) { none() } } @@ -536,7 +556,7 @@ module ExprNodes { ExprCfgNode getCommand() { result = this.getArgument(0) } } - class MemberExprChildMapping extends ExprChildMapping, MemberExpr { + private class MemberExprChildMapping extends ExprChildMapping, MemberExpr { override predicate relevantChild(Ast child) { child = this.getQualifier() or @@ -603,7 +623,7 @@ module ExprNodes { override MemberExprReadAccess getExpr() { result = e } } - class TypeNameExprChildMapping extends ExprChildMapping, TypeNameExpr { + private class TypeNameExprChildMapping extends ExprChildMapping, TypeNameExpr { override predicate relevantChild(Ast child) { none() } } @@ -631,7 +651,7 @@ module ExprNodes { override QualifiedTypeNameExpr getExpr() { result = e } } - class ErrorExprChildMapping extends ExprChildMapping, ErrorExpr { + private class ErrorExprChildMapping extends ExprChildMapping, ErrorExpr { override predicate relevantChild(Ast child) { none() } } @@ -643,7 +663,7 @@ module ExprNodes { override ErrorExpr getExpr() { result = e } } - class ScriptBlockExprChildMapping extends ExprChildMapping, ScriptBlockExpr { + private class ScriptBlockExprChildMapping extends ExprChildMapping, ScriptBlockExpr { override predicate relevantChild(Ast child) { child = this.getBody() } } @@ -657,7 +677,7 @@ module ExprNodes { ScriptBlockCfgNode getBody() { e.hasCfgChild(e.getBody(), this, result) } } - class StringLiteralExprChildMapping extends ExprChildMapping, StringConstExpr { + private class StringLiteralExprChildMapping extends ExprChildMapping, StringConstExpr { override predicate relevantChild(Ast child) { none() } } @@ -671,7 +691,7 @@ module ExprNodes { string getValueString() { result = e.getValueString() } } - class ExpandableStringExprChildMapping extends ExprChildMapping, ExpandableStringExpr { + private class ExpandableStringExprChildMapping extends ExprChildMapping, ExpandableStringExpr { override predicate relevantChild(Ast child) { child = this.getAnExpr() } } @@ -728,7 +748,7 @@ module ExprNodes { override VarReadAccess getExpr() { result = e } } - class HashTableExprChildMapping extends ExprChildMapping, HashTableExpr { + private class HashTableExprChildMapping extends ExprChildMapping, HashTableExpr { override predicate relevantChild(Ast child) { child = this.getAKey() or @@ -747,7 +767,7 @@ module ExprNodes { ExprCfgNode getAnKey() { result = this.getKey(_) } - ExprCfgNode getValue(int i) { e.hasCfgChild(e.getKey(i), this, result) } + ExprCfgNode getValue(int i) { e.hasCfgChild(e.getValue(i), this, result) } ExprCfgNode getValueFromKey(ExprCfgNode key) { exists(int i | @@ -759,7 +779,7 @@ module ExprNodes { ExprCfgNode getAValue() { result = this.getValue(_) } } - class PipelineChildMapping extends ExprChildMapping, Pipeline { + private class PipelineChildMapping extends ExprChildMapping, Pipeline { override predicate relevantChild(Ast child) { child = this.getAComponent() } } @@ -775,7 +795,7 @@ module ExprNodes { ExprCfgNode getAComponent() { result = this.getComponent(_) } } - class PipelineChainChildMapping extends ExprChildMapping, PipelineChain { + private class PipelineChainChildMapping extends ExprChildMapping, PipelineChain { override predicate relevantChild(Ast child) { child = this.getLeft() or child = this.getRight() } @@ -793,7 +813,7 @@ module ExprNodes { ExprCfgNode getRight() { e.hasCfgChild(e.getRight(), this, result) } } - class ConditionalExprChildMapping extends ExprChildMapping, ConditionalExpr { + private class ConditionalExprChildMapping extends ExprChildMapping, ConditionalExpr { override predicate relevantChild(Ast child) { child = this.getCondition() or @@ -827,7 +847,7 @@ module ExprNodes { ExprCfgNode getABranch() { result = this.getBranch(_) } } - class ExpandableSubExprChildMapping extends ExprChildMapping, ExpandableSubExpr { + private class ExpandableSubExprChildMapping extends ExprChildMapping, ExpandableSubExpr { override predicate relevantChild(Ast child) { child = this.getExpr() } } @@ -841,7 +861,7 @@ module ExprNodes { ExprCfgNode getSubExpr() { e.hasCfgChild(e.getExpr(), this, result) } } - class UsingExprChildMapping extends ExprChildMapping, UsingExpr { + private class UsingExprChildMapping extends ExprChildMapping, UsingExpr { override predicate relevantChild(Ast child) { child = this.getExpr() } } @@ -855,7 +875,7 @@ module ExprNodes { ExprCfgNode getSubExpr() { e.hasCfgChild(e.getExpr(), this, result) } } - class AttributedExprChildMapping extends ExprChildMapping, AttributedExpr { + private class AttributedExprChildMapping extends ExprChildMapping, AttributedExpr { override predicate relevantChild(Ast child) { child = this.getExpr() or child = this.getAttribute() @@ -874,7 +894,7 @@ module ExprNodes { ExprCfgNode getAttribute() { e.hasCfgChild(e.getAttribute(), this, result) } } - class IfChildMapping extends ExprChildMapping, If { + private class IfChildMapping extends ExprChildMapping, If { override predicate relevantChild(Ast child) { child = this.getACondition() or @@ -900,9 +920,19 @@ module ExprNodes { StmtCfgNode getAThen() { result = this.getThen(_) } StmtCfgNode getElse() { e.hasCfgChild(e.getElse(), this, result) } + + StmtCfgNode getABranch(boolean b) { + b = true and + result = this.getAThen() + or + b = false and + result = this.getElse() + } + + StmtCfgNode getABranch() { result = this.getABranch(_) } } - class LiteralChildMapping extends ExprChildMapping, Literal { + private class LiteralChildMapping extends ExprChildMapping, Literal { override predicate relevantChild(Ast child) { none() } } @@ -914,7 +944,7 @@ module ExprNodes { override Literal getExpr() { result = e } } - class BoolLiteralChildMapping extends ExprChildMapping, BoolLiteral { + private class BoolLiteralChildMapping extends ExprChildMapping, BoolLiteral { override predicate relevantChild(Ast child) { none() } } @@ -926,7 +956,7 @@ module ExprNodes { override BoolLiteral getExpr() { result = e } } - class NullLiteralChildMapping extends ExprChildMapping, NullLiteral { + private class NullLiteralChildMapping extends ExprChildMapping, NullLiteral { override predicate relevantChild(Ast child) { none() } } @@ -1004,7 +1034,7 @@ module StmtNodes { ExprCfgNode getRightHandSide() { s.hasCfgChild(s.getRightHandSide(), this, result) } } - class BreakStmtChildMapping extends NonExprChildMapping, BreakStmt { + private class BreakStmtChildMapping extends NonExprChildMapping, BreakStmt { override predicate relevantChild(Ast child) { none() } } @@ -1016,7 +1046,7 @@ module StmtNodes { override BreakStmt getStmt() { result = s } } - class ContinueStmtChildMapping extends NonExprChildMapping, ContinueStmt { + private class ContinueStmtChildMapping extends NonExprChildMapping, ContinueStmt { override predicate relevantChild(Ast child) { none() } } @@ -1028,7 +1058,7 @@ module StmtNodes { override ContinueStmt getStmt() { result = s } } - class DataStmtChildMapping extends NonExprChildMapping, DataStmt { + private class DataStmtChildMapping extends NonExprChildMapping, DataStmt { override predicate relevantChild(Ast child) { child = this.getACmdAllowed() or child = this.getBody() } @@ -1048,13 +1078,27 @@ module StmtNodes { StmtCfgNode getBody() { s.hasCfgChild(s.getBody(), this, result) } } - class DoUntilStmtChildMapping extends NonExprChildMapping, DoUntilStmt { + private class LoopStmtChildMapping extends NonExprChildMapping, LoopStmt { + override predicate relevantChild(Ast child) { child = this.getBody() } + } + + class LoopStmtCfgNode extends StmtCfgNode { + override string getAPrimaryQlClass() { result = "LoopStmtCfgNode" } + + override LoopStmtChildMapping s; + + override LoopStmt getStmt() { result = s } + + StmtCfgNode getBody() { s.hasCfgChild(s.getBody(), this, result) } + } + + private class DoUntilStmtChildMapping extends LoopStmtChildMapping, DoUntilStmt { override predicate relevantChild(Ast child) { - child = this.getCondition() or child = this.getBody() + child = this.getCondition() or super.relevantChild(child) } } - class DoUntilStmtCfgNode extends StmtCfgNode { + class DoUntilStmtCfgNode extends LoopStmtCfgNode { override string getAPrimaryQlClass() { result = "DoUntilStmtCfgNode" } override DoUntilStmtChildMapping s; @@ -1062,17 +1106,15 @@ module StmtNodes { override DoUntilStmt getStmt() { result = s } ExprCfgNode getCondition() { s.hasCfgChild(s.getCondition(), this, result) } - - StmtCfgNode getBody() { s.hasCfgChild(s.getBody(), this, result) } } - class DoWhileStmtChildMapping extends NonExprChildMapping, DoWhileStmt { + private class DoWhileStmtChildMapping extends LoopStmtChildMapping, DoWhileStmt { override predicate relevantChild(Ast child) { - child = this.getCondition() or child = this.getBody() + child = this.getCondition() or super.relevantChild(child) } } - class DoWhileStmtCfgNode extends StmtCfgNode { + class DoWhileStmtCfgNode extends LoopStmtCfgNode { override string getAPrimaryQlClass() { result = "DoWhileStmtCfgNode" } override DoWhileStmtChildMapping s; @@ -1080,11 +1122,9 @@ module StmtNodes { override DoWhileStmt getStmt() { result = s } ExprCfgNode getCondition() { s.hasCfgChild(s.getCondition(), this, result) } - - StmtCfgNode getBody() { s.hasCfgChild(s.getBody(), this, result) } } - class ErrorStmtChildMapping extends NonExprChildMapping, ErrorStmt { + private class ErrorStmtChildMapping extends NonExprChildMapping, ErrorStmt { override predicate relevantChild(Ast child) { none() } } @@ -1096,7 +1136,7 @@ module StmtNodes { override ErrorStmt getStmt() { result = s } } - class ExitStmtChildMapping extends NonExprChildMapping, ExitStmt { + private class ExitStmtChildMapping extends NonExprChildMapping, ExitStmt { override predicate relevantChild(Ast child) { child = this.getPipeline() } } @@ -1110,7 +1150,7 @@ module StmtNodes { ExprCfgNode getPipeline() { s.hasCfgChild(s.getPipeline(), this, result) } } - class DynamicStmtChildMapping extends NonExprChildMapping, DynamicStmt { + private class DynamicStmtChildMapping extends NonExprChildMapping, DynamicStmt { override predicate relevantChild(Ast child) { child = this.getName() or child = this.getScriptBlock() or child = this.getHashTableExpr() } @@ -1130,13 +1170,13 @@ module StmtNodes { ExprCfgNode getHashTableExpr() { s.hasCfgChild(s.getHashTableExpr(), this, result) } } - class ForEachStmtChildMapping extends NonExprChildMapping, ForEachStmt { + private class ForEachStmtChildMapping extends LoopStmtChildMapping, ForEachStmt { override predicate relevantChild(Ast child) { - child = this.getVarAccess() or child = this.getIterableExpr() or child = this.getBody() + child = this.getVarAccess() or child = this.getIterableExpr() or super.relevantChild(child) } } - class ForEachStmtCfgNode extends StmtCfgNode { + class ForEachStmtCfgNode extends LoopStmtCfgNode { override string getAPrimaryQlClass() { result = "ForEachStmtCfgNode" } override ForEachStmtChildMapping s; @@ -1146,20 +1186,18 @@ module StmtNodes { ExprCfgNode getVarAccess() { s.hasCfgChild(s.getVarAccess(), this, result) } ExprCfgNode getIterableExpr() { s.hasCfgChild(s.getIterableExpr(), this, result) } - - StmtCfgNode getBody() { s.hasCfgChild(s.getBody(), this, result) } } - class ForStmtChildMapping extends NonExprChildMapping, ForStmt { + private class ForStmtChildMapping extends LoopStmtChildMapping, ForStmt { override predicate relevantChild(Ast child) { child = this.getInitializer() or child = this.getCondition() or child = this.getIterator() or - child = this.getBody() + super.relevantChild(child) } } - class ForStmtCfgNode extends StmtCfgNode { + class ForStmtCfgNode extends LoopStmtCfgNode { override string getAPrimaryQlClass() { result = "ForStmtCfgNode" } override ForStmtChildMapping s; @@ -1171,11 +1209,9 @@ module StmtNodes { ExprCfgNode getCondition() { s.hasCfgChild(s.getCondition(), this, result) } AstCfgNode getIterator() { s.hasCfgChild(s.getIterator(), this, result) } - - StmtCfgNode getBody() { s.hasCfgChild(s.getBody(), this, result) } } - class GotoStmtChildMapping extends NonExprChildMapping, GotoStmt { + private class GotoStmtChildMapping extends NonExprChildMapping, GotoStmt { override predicate relevantChild(Ast child) { child = this.getLabel() } } @@ -1189,7 +1225,7 @@ module StmtNodes { ExprCfgNode getLabel() { s.hasCfgChild(s.getLabel(), this, result) } } - class ReturnStmtChildMapping extends NonExprChildMapping, ReturnStmt { + private class ReturnStmtChildMapping extends NonExprChildMapping, ReturnStmt { override predicate relevantChild(Ast child) { child = this.getPipeline() } } @@ -1203,7 +1239,7 @@ module StmtNodes { ExprCfgNode getPipeline() { s.hasCfgChild(s.getPipeline(), this, result) } } - class StmtBlockChildMapping extends NonExprChildMapping, StmtBlock { + private class StmtBlockChildMapping extends NonExprChildMapping, StmtBlock { override predicate relevantChild(Ast child) { child = this.getAStmt() } } @@ -1219,7 +1255,7 @@ module StmtNodes { StmtCfgNode getAStmt() { result = this.getStmt(_) } } - class SwitchStmtChildMapping extends NonExprChildMapping, SwitchStmt { + private class SwitchStmtChildMapping extends NonExprChildMapping, SwitchStmt { override predicate relevantChild(Ast child) { child = this.getCondition() or child = this.getDefault() or @@ -1248,7 +1284,7 @@ module StmtNodes { ExprCfgNode getAPattern() { result = this.getPattern(_) } } - class ThrowStmtChildMapping extends NonExprChildMapping, ThrowStmt { + private class ThrowStmtChildMapping extends NonExprChildMapping, ThrowStmt { override predicate relevantChild(Ast child) { child = this.getPipeline() } } @@ -1262,7 +1298,7 @@ module StmtNodes { ExprCfgNode getPipeline() { s.hasCfgChild(s.getPipeline(), this, result) } } - class TrapStmtChildMapping extends NonExprChildMapping, TrapStmt { + private class TrapStmtChildMapping extends NonExprChildMapping, TrapStmt { override predicate relevantChild(Ast child) { child = this.getBody() } } @@ -1276,7 +1312,7 @@ module StmtNodes { StmtCfgNode getBody() { s.hasCfgChild(s.getBody(), this, result) } } - class TryStmtChildMapping extends NonExprChildMapping, TryStmt { + private class TryStmtChildMapping extends NonExprChildMapping, TryStmt { override predicate relevantChild(Ast child) { child = this.getBody() or child = this.getFinally() or @@ -1298,7 +1334,7 @@ module StmtNodes { StmtCfgNode getCatchClause(int i) { s.hasCfgChild(s.getCatchClause(i), this, result) } } - class UsingStmtChildMapping extends NonExprChildMapping, UsingStmt { + private class UsingStmtChildMapping extends NonExprChildMapping, UsingStmt { override predicate relevantChild(Ast child) { none() } } @@ -1310,14 +1346,14 @@ module StmtNodes { override UsingStmt getStmt() { result = s } } - class WhileStmtChildMapping extends NonExprChildMapping, WhileStmt { + private class WhileStmtChildMapping extends LoopStmtChildMapping, WhileStmt { override predicate relevantChild(Ast child) { child = this.getCondition() or - child = this.getBody() + super.relevantChild(child) } } - class WhileStmtCfgNode extends StmtCfgNode { + class WhileStmtCfgNode extends LoopStmtCfgNode { override string getAPrimaryQlClass() { result = "WhileStmtCfgNode" } override WhileStmtChildMapping s; @@ -1325,11 +1361,9 @@ module StmtNodes { override WhileStmt getStmt() { result = s } ExprCfgNode getCondition() { s.hasCfgChild(s.getCondition(), this, result) } - - StmtCfgNode getBody() { s.hasCfgChild(s.getBody(), this, result) } } - class ConfigurationChildMapping extends NonExprChildMapping, Configuration { + private class ConfigurationChildMapping extends NonExprChildMapping, Configuration { override predicate relevantChild(Ast child) { child = this.getName() or child = this.getBody() } } @@ -1345,7 +1379,7 @@ module StmtNodes { StmtCfgNode getBody() { s.hasCfgChild(s.getBody(), this, result) } } - class TypeStmtChildMapping extends NonExprChildMapping, TypeDefinitionStmt { + private class TypeStmtChildMapping extends NonExprChildMapping, TypeDefinitionStmt { override predicate relevantChild(Ast child) { none() } } @@ -1369,7 +1403,7 @@ module StmtNodes { string getName() { result = s.getName() } } - class FunctionDefinitionChildMapping extends NonExprChildMapping, FunctionDefinitionStmt { + private class FunctionDefinitionChildMapping extends NonExprChildMapping, FunctionDefinitionStmt { override predicate relevantChild(Ast child) { none() } } @@ -1383,7 +1417,7 @@ module StmtNodes { FunctionBase getFunction() { result = s.getFunction() } } - class ExprStmtChildMapping extends NonExprChildMapping, ExprStmt { + private class ExprStmtChildMapping extends NonExprChildMapping, ExprStmt { override predicate relevantChild(Ast child) { child = this.getExpr() } } diff --git a/powershell/ql/lib/semmle/code/powershell/controlflow/internal/ControlFlowGraphImpl.qll b/powershell/ql/lib/semmle/code/powershell/controlflow/internal/ControlFlowGraphImpl.qll index 25d0773b0c07..f67a748068e4 100644 --- a/powershell/ql/lib/semmle/code/powershell/controlflow/internal/ControlFlowGraphImpl.qll +++ b/powershell/ql/lib/semmle/code/powershell/controlflow/internal/ControlFlowGraphImpl.qll @@ -151,6 +151,30 @@ module Trees { override predicate succ(AstNode pred, AstNode succ, Completion c) { this.succEntry(pred, c) and + ( + first(super.getThisParameter(), succ) + or + not exists(super.getThisParameter()) and + first(super.getParameter(0), succ) + or + not exists(super.getThisParameter()) and + not exists(super.getAParameter()) and + first(super.getBeginBlock(), succ) + or + not exists(super.getThisParameter()) and + not exists(super.getAParameter()) and + not exists(super.getBeginBlock()) and + first(super.getProcessBlock(), succ) + or + not exists(super.getThisParameter()) and + not exists(super.getAParameter()) and + not exists(super.getBeginBlock()) and + not exists(super.getProcessBlock()) and + first(super.getEndBlock(), succ) + ) + or + last(super.getThisParameter(), pred, c) and + completionIsNormal(c) and ( first(super.getParameter(0), succ) or diff --git a/powershell/ql/lib/semmle/code/powershell/dataflow/Ssa.qll b/powershell/ql/lib/semmle/code/powershell/dataflow/Ssa.qll index e023162d437f..212936b3643d 100644 --- a/powershell/ql/lib/semmle/code/powershell/dataflow/Ssa.qll +++ b/powershell/ql/lib/semmle/code/powershell/dataflow/Ssa.qll @@ -113,7 +113,7 @@ module Ssa { override ThisParameter getSourceVariable() { result = v } - final override string toString() { result = "self (" + v.getDeclaringScope() + ")" } + final override string toString() { result = "this (" + v.getDeclaringScope() + ")" } final override Location getLocation() { result = this.getControlFlowNode().getLocation() } } diff --git a/powershell/ql/lib/semmle/code/powershell/dataflow/internal/DataFlowPrivate.qll b/powershell/ql/lib/semmle/code/powershell/dataflow/internal/DataFlowPrivate.qll index c5aa29ceff98..46380b0a3e1b 100644 --- a/powershell/ql/lib/semmle/code/powershell/dataflow/internal/DataFlowPrivate.qll +++ b/powershell/ql/lib/semmle/code/powershell/dataflow/internal/DataFlowPrivate.qll @@ -8,6 +8,7 @@ private import DataFlowDispatch private import SsaImpl as SsaImpl private import FlowSummaryImpl as FlowSummaryImpl private import semmle.code.powershell.frameworks.data.ModelsAsData +private import PipelineReturns as PipelineReturns /** Gets the callable in which this node occurs. */ DataFlowCallable nodeGetEnclosingCallable(Node n) { result = n.(NodeImpl).getEnclosingCallable() } @@ -61,6 +62,8 @@ module SsaFlow { private ParameterNodeImpl toParameterNode(SsaImpl::ParameterExt p) { result = TNormalParameterNode(p.asParameter()) + or + result = TThisParameterNode(p.asThis()) } Impl::Node asNode(Node n) { @@ -85,6 +88,16 @@ module SsaFlow { } } +private module ArrayExprFlow { + private module Input implements PipelineReturns::InputSig { + predicate isSource(CfgNodes::AstCfgNode source) { + source = any(CfgNodes::ExprNodes::ArrayExprCfgNode ae).getStmtBlock() + } + } + + import PipelineReturns::Make +} + /** Provides predicates related to local data flow. */ module LocalFlow { pragma[nomagic] @@ -97,16 +110,13 @@ module LocalFlow { or nodeFrom.asExpr() = nodeTo.asExpr().(CfgNodes::ExprNodes::ParenExprCfgNode).getSubExpr() or - exists( - CfgNodes::ExprNodes::ArrayExprCfgNode arrayExpr, EscapeContainer::EscapeContainer container - | - nodeTo.asExpr() = arrayExpr and - container = arrayExpr.getStmtBlock().getAstNode() and - nodeFrom.(AstNode).getCfgNode() = container.getAnEscapingElement() and - not container.mayBeMultiReturned(_) - ) + nodeFrom.asExpr() = nodeTo.asExpr().(CfgNodes::ExprNodes::ArrayExprCfgNode) or - nodeFrom.(AstNode).getCfgNode() = nodeTo.(PreReturNodeImpl).getReturnedNode() + exists(CfgNodes::ExprCfgNode e | + e = nodeFrom.(AstNode).getCfgNode() and + isReturned(e) and + e.getScope() = nodeTo.(PreReturNodeImpl).getCfgScope() + ) or exists(CfgNode cfgNode | nodeFrom = TPreReturnNodeImpl(cfgNode, true) and @@ -118,10 +128,12 @@ module LocalFlow { nodeTo = TReturnNodeImpl(cfgNode.getScope()) ) or - exists(CfgNode cfgNode | - cfgNode = nodeFrom.(AstNode).getCfgNode() and - isUniqueReturned(cfgNode) and - nodeTo.(ReturnNodeImpl).getCfgScope() = cfgNode.getScope() + exists(CfgNodes::ExprCfgNode e, CfgNodes::ScriptBlockCfgNode scriptBlock | + e = nodeFrom.(AstNode).getCfgNode() and + isReturned(e) and + e.getScope() = scriptBlock.getAstNode() and + not blockMayReturnMultipleValues(scriptBlock) and + nodeTo.(ReturnNodeImpl).getCfgScope() = scriptBlock.getAstNode() ) } @@ -164,7 +176,6 @@ private module Cached { cached newtype TNode = TExprNode(CfgNodes::ExprCfgNode n) or - TStmtNode(CfgNodes::StmtCfgNode n) or TSsaNode(SsaImpl::DataFlowIntegration::SsaNode node) or TNormalParameterNode(SsaImpl::NormalParameter p) or TThisParameterNode(Method m) or @@ -183,8 +194,12 @@ private module Cached { n = any(CfgNodes::ExprNodes::IndexExprCfgNode index).getBase() } or TFlowSummaryNode(FlowSummaryImpl::Private::SummaryNode sn) or - TPreReturnNodeImpl(CfgNodes::AstCfgNode n, Boolean isArray) { isMultiReturned(n) } or - TImplicitWrapNode(CfgNodes::AstCfgNode n, Boolean shouldWrap) { isMultiReturned(n) } or + TPreReturnNodeImpl(CfgNodes::ScriptBlockCfgNode scriptBlock, Boolean isArray) { + blockMayReturnMultipleValues(scriptBlock) + } or + TImplicitWrapNode(CfgNodes::ScriptBlockCfgNode scriptBlock, Boolean shouldWrap) { + blockMayReturnMultipleValues(scriptBlock) + } or TReturnNodeImpl(CfgScope scope) or TProcessNode(ProcessBlock process) or TProcessPropertyByNameNode(PipelineByPropertyNameIteratorVariable iter) { @@ -549,7 +564,7 @@ private module ParameterNodes { ThisParameterNode() { this = TThisParameterNode(m) } - override Parameter getParameter() { none() } + override Parameter getParameter() { result = m.getThisParameter() } override predicate isParameterOf(DataFlowCallable c, ParameterPosition pos) { m.getBody() = c.asCfgScope() and @@ -751,64 +766,41 @@ abstract class ReturnNode extends Node { abstract ReturnKind getKind(); } -private module EscapeContainer { - private import semmle.code.powershell.internal.AstEscape::Private - - private module ReturnContainerInterpreter implements InterpretAstInputSig { - class T = CfgNodes::AstCfgNode; - - T interpret(Ast a) { result.(CfgNodes::ExprCfgNode).getExpr() = a } // TODO: Recutse into expr-to-stmt conversions - } - - class EscapeContainer extends AstEscape::Element { - /** Holds if `n` may be returned multiples times. */ - predicate mayBeMultiReturned(CfgNode n) { - n = this.getANode() and - n.getASuccessor+() = n - or - this.getAChild().(EscapeContainer).mayBeMultiReturned(n) - } - } - - private class SummaryReturnNode extends FlowSummaryNode, ReturnNode { - private ReturnKind rk; +private class SummaryReturnNode extends FlowSummaryNode, ReturnNode { + private ReturnKind rk; - SummaryReturnNode() { FlowSummaryImpl::Private::summaryReturnNode(this.getSummaryNode(), rk) } + SummaryReturnNode() { FlowSummaryImpl::Private::summaryReturnNode(this.getSummaryNode(), rk) } - override ReturnKind getKind() { result = rk } - } + override ReturnKind getKind() { result = rk } } private module ReturnNodes { - private import EscapeContainer + private CfgNodes::NamedBlockCfgNode getAReturnBlock(CfgNodes::ScriptBlockCfgNode sb) { + result = sb.getBeginBlock() + or + result = sb.getEndBlock() + or + result = sb.getProcessBlock() + } - private predicate isReturnedImpl(CfgNodes::AstCfgNode n, EscapeContainer container) { - container = n.getScope() and - n = container.getAnEscapingElement() + private module CfgScopeReturn implements PipelineReturns::InputSig { + predicate isSource(CfgNodes::AstCfgNode source) { source = getAReturnBlock(_) } } + private module P = PipelineReturns::Make; + /** * Holds if `n` may be returned, and there are possibly * more than one return value from the function. */ - predicate isMultiReturned(CfgNodes::AstCfgNode n) { - exists(EscapeContainer container | isReturnedImpl(n, container) | - strictcount(container.getAnEscapingElement()) > 1 - or - container.mayBeMultiReturned(n) - ) + predicate blockMayReturnMultipleValues(CfgNodes::ScriptBlockCfgNode scriptBlock) { + P::mayReturnMultipleValues(getAReturnBlock(scriptBlock)) } /** * Holds if `n` may be returned. */ - predicate isReturned(CfgNodes::AstCfgNode n) { isReturnedImpl(n, _) } - - /** - * Holds if `n` may be returned, and this is the only value that may be - * returned from the function. - */ - predicate isUniqueReturned(CfgNodes::AstCfgNode n) { isReturned(n) and not isMultiReturned(n) } + predicate isReturned(CfgNodes::AstCfgNode n) { n = P::getAReturn(_) } class NormalReturnNode extends ReturnNode instanceof ReturnNodeImpl { final override NormalReturnKind getKind() { any() } @@ -851,6 +843,24 @@ predicate jumpStep(Node pred, Node succ) { succ.(FlowSummaryNode).getSummaryNode()) } +private predicate arrayExprStore(Node node1, ContentSet cs, Node node2, CfgNodes::ExprCfgNode e) { + exists(CfgNodes::ExprNodes::ArrayExprCfgNode ae, CfgNodes::StmtNodes::StmtBlockCfgNode block | + e = node1.(AstNode).getCfgNode() and + ae = node2.asExpr() and + block = ae.getStmtBlock() + | + exists(Content::KnownElementContent ec, int index | + e = ArrayExprFlow::getReturn(block, index) and + cs.isKnownOrUnknownElement(ec) and + index = ec.getIndex().asInt() + ) + or + not ArrayExprFlow::eachValueIsReturnedOnce(block) and + e = ArrayExprFlow::getAReturn(block) and + cs.isAnyElement() + ) +} + /** * Holds if data can flow from `node1` to `node2` via an assignment to * content `c`. @@ -877,8 +887,10 @@ predicate storeStep(Node node1, ContentSet c, Node node2) { c.isAnyElement() ) or - exists(Content::KnownElementContent ec, int index | - node2.asExpr().(CfgNodes::ExprNodes::ArrayLiteralCfgNode).getExpr(index) = node1.asExpr() and + exists(Content::KnownElementContent ec, int index, CfgNodes::ExprCfgNode e | + e = node1.asExpr() and + not arrayExprStore(node1, _, _, e) and + node2.asExpr().(CfgNodes::ExprNodes::ArrayLiteralCfgNode).getExpr(index) = e and c.isKnownOrUnknownElement(ec) and index = ec.getIndex().asInt() ) @@ -895,15 +907,7 @@ predicate storeStep(Node node1, ContentSet c, Node node2) { c.isAnyElement() ) or - c.isAnyElement() and - exists( - CfgNodes::ExprNodes::ArrayExprCfgNode arrayExpr, EscapeContainer::EscapeContainer container - | - node2.asExpr() = arrayExpr and - container = arrayExpr.getStmtBlock().getAstNode() and - node1.(AstNode).getCfgNode() = container.getAnEscapingElement() and - container.mayBeMultiReturned(_) - ) + arrayExprStore(node1, c, node2, _) or c.isAnyElement() and exists(CfgNode cfgNode | @@ -1089,12 +1093,12 @@ private import PostUpdateNodes * (or statement) is being returned from a function. */ private class ImplicitWrapNode extends TImplicitWrapNode, NodeImpl { - private CfgNodes::AstCfgNode n; + private CfgNodes::ScriptBlockCfgNode n; private boolean shouldWrap; ImplicitWrapNode() { this = TImplicitWrapNode(n, shouldWrap) } - CfgNodes::AstCfgNode getReturnedNode() { result = n } + CfgNodes::ScriptBlockCfgNode getScriptBlock() { result = n } predicate shouldWrap() { shouldWrap = true } @@ -1112,12 +1116,12 @@ private class ImplicitWrapNode extends TImplicitWrapNode, NodeImpl { * has been performed. */ private class PreReturNodeImpl extends TPreReturnNodeImpl, NodeImpl { - private CfgNodes::AstCfgNode n; + private CfgNodes::ScriptBlockCfgNode n; private boolean isArray; PreReturNodeImpl() { this = TPreReturnNodeImpl(n, isArray) } - CfgNodes::AstCfgNode getReturnedNode() { result = n } + CfgNodes::AstCfgNode getScriptBlock() { result = n } override CfgScope getCfgScope() { result = n.getScope() } diff --git a/powershell/ql/lib/semmle/code/powershell/dataflow/internal/PipelineReturns.qll b/powershell/ql/lib/semmle/code/powershell/dataflow/internal/PipelineReturns.qll new file mode 100644 index 000000000000..fd14591617dc --- /dev/null +++ b/powershell/ql/lib/semmle/code/powershell/dataflow/internal/PipelineReturns.qll @@ -0,0 +1,199 @@ +private import semmle.code.powershell.controlflow.CfgNodes + +/** + * The input module which defines the set of sources for which to calculate + * "escaping expressions". + */ +signature module InputSig { + /** + * Holds if `source` is a relevant AST element that we want to compute + * which expressions are returned from. + */ + predicate isSource(AstCfgNode source); +} + +/** The output signature from the "escape analysis". */ +signature module OutputSig { + /** Gets an expression that escapes from `source` */ + ExprCfgNode getAReturn(AstCfgNode source); + + /** + * Gets the `i`'th expression that escapes from `source`, if an ordering can + * be determined statically. + */ + ExprCfgNode getReturn(AstCfgNode source, int i); + + /** Holds multiple value may escape from `source`. */ + predicate mayReturnMultipleValues(AstCfgNode source); + + /** + * Holds if each value escaping from `source` is guarenteed to only escape + * once. In particular, if `count(getAReturn(source)) = 1` and this predicate + * holds, then only one value can escape from `source`. + * + * If `count(getAReturn(source)) > 1` and this predicate holds, + * it means that a sequence of values may escape from `source`. + */ + predicate eachValueIsReturnedOnce(AstCfgNode source); +} + +module Make implements OutputSig { + private import Input + + private predicate step0(AstCfgNode pred, AstCfgNode succ) { + exists(NamedBlockCfgNode nb | + pred = nb and + succ = nb.getAStmt() + ) + or + exists(StmtNodes::StmtBlockCfgNode sb | + pred = sb and + succ = sb.getAStmt() + ) + or + exists(StmtNodes::ExprStmtCfgNode es | + pred = es and + succ = es.getExpr() + ) + or + exists(StmtNodes::ReturnStmtCfgNode es | + pred = es and + succ = es.getPipeline() + ) + or + exists(ExprNodes::ArrayLiteralCfgNode al | + pred = al and + succ = al.getAnExpr() + ) + or + exists(StmtNodes::LoopStmtCfgNode loop | + pred = loop and + succ = loop.getBody() + ) + or + exists(ExprNodes::IfCfgNode if_ | + pred = if_ and + succ = if_.getABranch() + ) + or + exists(StmtNodes::SwitchStmtCfgNode switch | + pred = switch and + succ = switch.getACase() + ) + or + exists(CatchClauseCfgNode catch | + pred = catch and + succ = catch.getBody() + ) + or + exists(StmtNodes::TryStmtCfgNode try | + pred = try and + succ = [try.getBody(), try.getFinally()] + ) + } + + private predicate fwd(AstCfgNode n) { + isSource(n) + or + exists(AstCfgNode pred | + fwd(pred) and + step0(pred, n) + ) + } + + private predicate isSink(AstCfgNode sink) { + fwd(sink) and + ( + sink instanceof ExprCfgNode and + // If is not really an expression + not sink instanceof ExprNodes::IfCfgNode and + // When `a, b, c` is returned it is flattened to returning a, and b, and c. + not sink instanceof ExprNodes::ArrayLiteralCfgNode + ) + } + + private predicate rev(AstCfgNode n) { + fwd(n) and + ( + isSink(n) + or + exists(AstCfgNode succ | + rev(succ) and + step0(n, succ) + ) + ) + } + + private predicate step(AstCfgNode n1, AstCfgNode n2) { + rev(n1) and + rev(n2) and + step0(n1, n2) + } + + private predicate stepPlus(AstCfgNode n1, AstCfgNode n2) = + doublyBoundedFastTC(step/2, isSource/1, isSink/1)(n1, n2) + + /** Gets a value that may be returned from `source`. */ + private ExprCfgNode getAReturn0(AstCfgNode source) { + isSource(source) and + isSink(result) and + stepPlus(source, result) + } + + private predicate inScopeOfSource(AstCfgNode n, AstCfgNode source) { + isSource(source) and + n.getAstNode().getParent*() = source.getAstNode() + } + + private predicate getASuccessor(AstCfgNode pred, AstCfgNode succ) { + exists(AstCfgNode source | + inScopeOfSource(pred, source) and + pred.getASuccessor() = succ and + inScopeOfSource(succ, source) + ) + } + + /** Holds if `e` may be returned multiple times from `source`. */ + private predicate mayBeReturnedMoreThanOnce(ExprCfgNode e, AstCfgNode source) { + e = getAReturn0(source) and getASuccessor+(e, e) + } + + predicate eachValueIsReturnedOnce(AstCfgNode source) { + isSource(source) and + not mayBeReturnedMoreThanOnce(_, source) + } + + private predicate isSourceForSingularReturn(AstCfgNode source) { + isSource(source) and + eachValueIsReturnedOnce(source) + } + + private predicate hasReturnOrderImpl0(int dist, ExprCfgNode e, AstCfgNode source) = + shortestDistances(isSourceForSingularReturn/1, getASuccessor/2)(source, e, dist) + + private predicate hasReturnOrderImpl(int dist, ExprCfgNode e) { + hasReturnOrderImpl0(dist, e, _) and + e = getAReturn0(_) + } + + private predicate hasReturnOrder(int i, ExprCfgNode e) { + e = rank[i + 1](ExprCfgNode e0, int i0 | hasReturnOrderImpl(i0, e0) | e0 order by i0) + } + + ExprCfgNode getReturn(AstCfgNode source, int i) { + result = getAReturn0(source) and + eachValueIsReturnedOnce(source) and + hasReturnOrder(i, result) + } + + ExprCfgNode getAReturn(AstCfgNode source) { result = getAReturn0(source) } + + /** + * Holds if `source` may return multiple values, and `n` is one of the values. + */ + predicate mayReturnMultipleValues(AstCfgNode source) { + strictcount(getAReturn0(source)) > 1 + or + mayBeReturnedMoreThanOnce(_, source) + } +} diff --git a/powershell/ql/lib/semmle/code/powershell/dataflow/internal/SsaImpl.qll b/powershell/ql/lib/semmle/code/powershell/dataflow/internal/SsaImpl.qll index d9dceb4d27a4..a1e34e36af34 100644 --- a/powershell/ql/lib/semmle/code/powershell/dataflow/internal/SsaImpl.qll +++ b/powershell/ql/lib/semmle/code/powershell/dataflow/internal/SsaImpl.qll @@ -27,12 +27,6 @@ module SsaInput implements SsaImplCommon::InputSig { */ predicate variableWrite(BasicBlock bb, int i, SourceVariable v, boolean certain) { ( - exists(Scope scope | scope = v.(ThisParameter).getDeclaringScope() | - // We consider the `this` variable to have a single write at the entry to a method block - scope = bb.(BasicBlocks::EntryBasicBlock).getScope() and - i = 0 - ) - or uninitializedWrite(bb, i, v) or variableWriteActual(bb, i, v, _) @@ -138,9 +132,7 @@ private module Cached { * AST write access is `write`. */ cached - predicate variableWriteActual( - Cfg::BasicBlock bb, int i, Variable v, VarWriteAccessCfgNode write - ) { + predicate variableWriteActual(Cfg::BasicBlock bb, int i, Variable v, VarWriteAccessCfgNode write) { exists(Cfg::CfgNode n | write.getVariable() = v and n = bb.getNode(i) @@ -277,7 +269,8 @@ private Parameter getANonPipelineParameter(FunctionBase f) { class NormalParameter extends Parameter { NormalParameter() { not this instanceof PipelineParameter and - not this instanceof PipelineByPropertyNameParameter + not this instanceof PipelineByPropertyNameParameter and + not this instanceof ThisParameter } int getIndexExcludingPipelines() { @@ -295,18 +288,18 @@ class NormalParameter extends Parameter { private newtype TParameterExt = TNormalParameter(NormalParameter p) or - TSelfMethodParameter(Method m) + TThisMethodParameter(Method m) -/** A normal parameter or an implicit `self` parameter. */ +/** A normal parameter or an implicit `this` parameter. */ class ParameterExt extends TParameterExt { NormalParameter asParameter() { this = TNormalParameter(result) } - Method asThis() { this = TSelfMethodParameter(result) } + Method asThis() { this = TThisMethodParameter(result) } predicate isInitializedBy(WriteDefinition def) { def = getParameterDef(this.asParameter()) or - def.(Ssa::ThisDefinition).getSourceVariable().getDeclaringScope() = this.asThis().(Scope) + def.(Ssa::ThisDefinition).getSourceVariable().getDeclaringScope() = this.asThis().getBody() } string toString() { result = [this.asParameter().toString(), this.asThis().toString()] } diff --git a/powershell/ql/lib/semmle/code/powershell/internal/AstEscape.qll b/powershell/ql/lib/semmle/code/powershell/internal/AstEscape.qll deleted file mode 100644 index 7e031d537bdb..000000000000 --- a/powershell/ql/lib/semmle/code/powershell/internal/AstEscape.qll +++ /dev/null @@ -1,84 +0,0 @@ -private import powershell as PS - -/** - * TODO: This whole computation cab be sped up by providing a set of "root"s and doing - * a forward/backwards traversal first. - */ -module Private { - signature module InterpretAstInputSig { - /** The type on which to translate `Ast` elements during escape calculations */ - class T; - - /** Interpret `a` into a `T` */ - T interpret(PS::Ast a); - } - - module AstEscape { - private import Interpret - - /** An AST element that may produce a value which can escape from this `Ast` when evaluated. */ - abstract private class ElementImpl instanceof PS::Ast { - string toString() { result = super.toString() } - - /** Gets a direct node that will may escape when evaluating this element. */ - T getANode() { none() } - - /** Gets a child that may produce more elements that may escape. */ - abstract Element getAChild(); - - /** - * Gets a (possibly transitive) element that may escape when evaluating - * this element. - */ - final T getAnEscapingElement() { - result = this.getANode() - or - result = this.getAChild().getAnEscapingElement() - } - } - - final class Element = ElementImpl; - - private class ScriptBlockElement extends ElementImpl instanceof PS::ScriptBlock { - final override Element getAChild() { result = super.getEndBlock() } - } - - private class NamedBlockElement extends ElementImpl instanceof PS::NamedBlock { - final override Element getAChild() { result = super.getAStmt() } - } - - private class ExprStmtElement extends ElementImpl instanceof PS::ExprStmt { - final override T getANode() { result = interpret(super.getExpr()) } - - final override Element getAChild() { none() } - } - - private class LoopStmtElement extends ElementImpl instanceof PS::LoopStmt { - final override Element getAChild() { result = super.getBody() } - } - - private class StmtBlockElement extends ElementImpl instanceof PS::StmtBlock { - final override Element getAChild() { result = super.getAStmt() } - } - - private class TryStmtElement extends ElementImpl instanceof PS::TryStmt { - final override Element getAChild() { - result = super.getBody() or result = super.getACatchClause() or result = super.getFinally() - } - } - - private class ReturnStmtElement extends ElementImpl instanceof PS::ReturnStmt { - final override Element getAChild() { result = super.getPipeline() } - } - - private class CatchClausElement extends ElementImpl instanceof PS::CatchClause { - final override Element getAChild() { result = super.getBody() } - } - - private class SwitchStmtElement extends ElementImpl instanceof PS::SwitchStmt { - final override Element getAChild() { result = super.getACase() } - } - } -} - -module Public { } diff --git a/powershell/ql/test/library-tests/controlflow/graph/Cfg.expected b/powershell/ql/test/library-tests/controlflow/graph/Cfg.expected index c2432f65b155..d4be5cad40bf 100644 --- a/powershell/ql/test/library-tests/controlflow/graph/Cfg.expected +++ b/powershell/ql/test/library-tests/controlflow/graph/Cfg.expected @@ -247,7 +247,9 @@ | functions.ps1:13:28:20:1 | enter {...} | functions.ps1:13:28:20:1 | {...} | | | functions.ps1:13:28:20:1 | exit {...} (normal) | functions.ps1:13:28:20:1 | exit {...} | | | functions.ps1:13:28:20:1 | name0 | functions.ps1:13:28:20:1 | name1 | | +| functions.ps1:13:28:20:1 | name1 | functions.ps1:13:28:20:1 | name2 | | | functions.ps1:13:28:20:1 | name1 | functions.ps1:16:24:16:24 | 0 | | +| functions.ps1:13:28:20:1 | name2 | functions.ps1:13:28:20:1 | [synth] pipeline | | | functions.ps1:13:28:20:1 | name2 | functions.ps1:17:24:17:29 | name1 | | | functions.ps1:13:28:20:1 | {...} | functions.ps1:16:24:16:24 | 0 | | | functions.ps1:14:5:19:18 | {...} | functions.ps1:19:5:19:18 | [Stmt] ...+... | | diff --git a/powershell/ql/test/library-tests/controlflow/graph/consistency.expected b/powershell/ql/test/library-tests/controlflow/graph/consistency.expected index 68e5304a2904..33994ab4888b 100644 --- a/powershell/ql/test/library-tests/controlflow/graph/consistency.expected +++ b/powershell/ql/test/library-tests/controlflow/graph/consistency.expected @@ -8,6 +8,10 @@ multipleSuccessors | functions.ps1:8:5:8:23 | [Stmt] ...+... | successor | functions.ps1:46:17:46:18 | __pipeline_iterator | | functions.ps1:8:5:8:23 | [Stmt] __pipeline_iterator | successor | functions.ps1:8:5:8:12 | number1 | | functions.ps1:8:5:8:23 | [Stmt] __pipeline_iterator | successor | functions.ps1:46:17:46:18 | __pipeline_iterator | +| functions.ps1:13:28:20:1 | name1 | successor | functions.ps1:13:28:20:1 | name2 | +| functions.ps1:13:28:20:1 | name1 | successor | functions.ps1:16:24:16:24 | 0 | +| functions.ps1:13:28:20:1 | name2 | successor | functions.ps1:13:28:20:1 | [synth] pipeline | +| functions.ps1:13:28:20:1 | name2 | successor | functions.ps1:17:24:17:29 | name1 | | functions.ps1:16:24:16:24 | 0 | successor | functions.ps1:13:28:20:1 | name2 | | functions.ps1:16:24:16:24 | 0 | successor | functions.ps1:17:24:17:29 | name1 | | functions.ps1:17:24:17:33 | ...+... | successor | functions.ps1:13:28:20:1 | [synth] pipeline | diff --git a/powershell/ql/test/library-tests/dataflow/fields/test.expected b/powershell/ql/test/library-tests/dataflow/fields/test.expected index 8916976e2a57..0908f093abc0 100644 --- a/powershell/ql/test/library-tests/dataflow/fields/test.expected +++ b/powershell/ql/test/library-tests/dataflow/fields/test.expected @@ -40,11 +40,33 @@ edges | test.ps1:40:6:40:10 | arr8 [element 2] | test.ps1:40:6:40:13 | ...[...] | provenance | | | test.ps1:41:6:41:10 | arr8 [element 2] | test.ps1:41:6:41:20 | ...[...] | provenance | | | test.ps1:43:6:43:16 | Call to Source | test.ps1:45:17:45:18 | y | provenance | | -| test.ps1:45:11:45:18 | ...,... [element 2] | test.ps1:48:6:48:10 | arr9 [element 2] | provenance | | -| test.ps1:45:11:45:18 | ...,... [element 2] | test.ps1:49:6:49:10 | arr9 [element 2] | provenance | | -| test.ps1:45:17:45:18 | y | test.ps1:45:11:45:18 | ...,... [element 2] | provenance | | +| test.ps1:45:9:45:19 | @(...) [element 2] | test.ps1:48:6:48:10 | arr9 [element 2] | provenance | | +| test.ps1:45:9:45:19 | @(...) [element 2] | test.ps1:49:6:49:10 | arr9 [element 2] | provenance | | +| test.ps1:45:17:45:18 | y | test.ps1:45:9:45:19 | @(...) [element 2] | provenance | | | test.ps1:48:6:48:10 | arr9 [element 2] | test.ps1:48:6:48:13 | ...[...] | provenance | | | test.ps1:49:6:49:10 | arr9 [element 2] | test.ps1:49:6:49:20 | ...[...] | provenance | | +| test.ps1:54:5:56:5 | this [field] | test.ps1:55:14:55:24 | this [field] | provenance | | +| test.ps1:55:14:55:24 | this [field] | test.ps1:55:14:55:24 | field | provenance | | +| test.ps1:61:1:61:8 | [post] myClass [field] | test.ps1:63:1:63:8 | myClass [field] | provenance | | +| test.ps1:61:18:61:28 | Call to Source | test.ps1:61:1:61:8 | [post] myClass [field] | provenance | | +| test.ps1:63:1:63:8 | myClass [field] | test.ps1:54:5:56:5 | this [field] | provenance | | +| test.ps1:66:10:66:20 | Call to Source | test.ps1:69:5:69:6 | x | provenance | | +| test.ps1:67:10:67:20 | Call to Source | test.ps1:70:5:70:6 | y | provenance | | +| test.ps1:68:10:68:20 | Call to Source | test.ps1:70:9:70:10 | z | provenance | | +| test.ps1:69:5:69:6 | x | test.ps1:73:6:73:12 | Call to produce [element] | provenance | | +| test.ps1:70:5:70:6 | y | test.ps1:73:6:73:12 | Call to produce [element] | provenance | | +| test.ps1:70:9:70:10 | z | test.ps1:73:6:73:12 | Call to produce [element] | provenance | | +| test.ps1:73:6:73:12 | Call to produce [element] | test.ps1:74:6:74:7 | x [element] | provenance | | +| test.ps1:73:6:73:12 | Call to produce [element] | test.ps1:75:6:75:7 | x [element] | provenance | | +| test.ps1:73:6:73:12 | Call to produce [element] | test.ps1:76:6:76:7 | x [element] | provenance | | +| test.ps1:74:6:74:7 | x [element] | test.ps1:74:6:74:10 | ...[...] | provenance | | +| test.ps1:75:6:75:7 | x [element] | test.ps1:75:6:75:10 | ...[...] | provenance | | +| test.ps1:76:6:76:7 | x [element] | test.ps1:76:6:76:10 | ...[...] | provenance | | +| test.ps1:78:9:81:1 | ${...} [element a] | test.ps1:83:6:83:10 | hash [element a] | provenance | | +| test.ps1:78:9:81:1 | ${...} [element a] | test.ps1:87:6:87:10 | hash [element a] | provenance | | +| test.ps1:79:7:79:17 | Call to Source | test.ps1:78:9:81:1 | ${...} [element a] | provenance | | +| test.ps1:83:6:83:10 | hash [element a] | test.ps1:83:6:83:15 | ...[...] | provenance | | +| test.ps1:87:6:87:10 | hash [element a] | test.ps1:87:6:87:15 | ...[...] | provenance | | | test.ps1:88:1:88:5 | [post] hash [b] | test.ps1:89:6:89:10 | hash [b] | provenance | | | test.ps1:88:11:88:21 | Call to Source | test.ps1:88:1:88:5 | [post] hash [b] | provenance | | | test.ps1:89:6:89:10 | hash [b] | test.ps1:89:6:89:12 | b | provenance | | @@ -98,30 +120,43 @@ nodes | test.ps1:41:6:41:10 | arr8 [element 2] | semmle.label | arr8 [element 2] | | test.ps1:41:6:41:20 | ...[...] | semmle.label | ...[...] | | test.ps1:43:6:43:16 | Call to Source | semmle.label | Call to Source | -| test.ps1:45:11:45:18 | ...,... [element 2] | semmle.label | ...,... [element 2] | +| test.ps1:45:9:45:19 | @(...) [element 2] | semmle.label | @(...) [element 2] | | test.ps1:45:17:45:18 | y | semmle.label | y | | test.ps1:48:6:48:10 | arr9 [element 2] | semmle.label | arr9 [element 2] | | test.ps1:48:6:48:13 | ...[...] | semmle.label | ...[...] | | test.ps1:49:6:49:10 | arr9 [element 2] | semmle.label | arr9 [element 2] | | test.ps1:49:6:49:20 | ...[...] | semmle.label | ...[...] | +| test.ps1:54:5:56:5 | this [field] | semmle.label | this [field] | +| test.ps1:55:14:55:24 | field | semmle.label | field | +| test.ps1:55:14:55:24 | this [field] | semmle.label | this [field] | +| test.ps1:61:1:61:8 | [post] myClass [field] | semmle.label | [post] myClass [field] | +| test.ps1:61:18:61:28 | Call to Source | semmle.label | Call to Source | +| test.ps1:63:1:63:8 | myClass [field] | semmle.label | myClass [field] | +| test.ps1:66:10:66:20 | Call to Source | semmle.label | Call to Source | +| test.ps1:67:10:67:20 | Call to Source | semmle.label | Call to Source | +| test.ps1:68:10:68:20 | Call to Source | semmle.label | Call to Source | +| test.ps1:69:5:69:6 | x | semmle.label | x | +| test.ps1:70:5:70:6 | y | semmle.label | y | +| test.ps1:70:9:70:10 | z | semmle.label | z | +| test.ps1:73:6:73:12 | Call to produce [element] | semmle.label | Call to produce [element] | +| test.ps1:74:6:74:7 | x [element] | semmle.label | x [element] | +| test.ps1:74:6:74:10 | ...[...] | semmle.label | ...[...] | +| test.ps1:75:6:75:7 | x [element] | semmle.label | x [element] | +| test.ps1:75:6:75:10 | ...[...] | semmle.label | ...[...] | +| test.ps1:76:6:76:7 | x [element] | semmle.label | x [element] | +| test.ps1:76:6:76:10 | ...[...] | semmle.label | ...[...] | +| test.ps1:78:9:81:1 | ${...} [element a] | semmle.label | ${...} [element a] | +| test.ps1:79:7:79:17 | Call to Source | semmle.label | Call to Source | +| test.ps1:83:6:83:10 | hash [element a] | semmle.label | hash [element a] | +| test.ps1:83:6:83:15 | ...[...] | semmle.label | ...[...] | +| test.ps1:87:6:87:10 | hash [element a] | semmle.label | hash [element a] | +| test.ps1:87:6:87:15 | ...[...] | semmle.label | ...[...] | | test.ps1:88:1:88:5 | [post] hash [b] | semmle.label | [post] hash [b] | | test.ps1:88:11:88:21 | Call to Source | semmle.label | Call to Source | | test.ps1:89:6:89:10 | hash [b] | semmle.label | hash [b] | | test.ps1:89:6:89:12 | b | semmle.label | b | subpaths testFailures -| test.ps1:55:26:55:44 | # $ hasValueFlow=12 | Missing result: hasValueFlow=12 | -| test.ps1:74:12:74:62 | # $ hasValueFlow=13 hasValueFlow=14 hasValueFlow=15 | Missing result: hasValueFlow=13 | -| test.ps1:74:12:74:62 | # $ hasValueFlow=13 hasValueFlow=14 hasValueFlow=15 | Missing result: hasValueFlow=14 | -| test.ps1:74:12:74:62 | # $ hasValueFlow=13 hasValueFlow=14 hasValueFlow=15 | Missing result: hasValueFlow=15 | -| test.ps1:75:12:75:62 | # $ hasValueFlow=13 hasValueFlow=14 hasValueFlow=15 | Missing result: hasValueFlow=13 | -| test.ps1:75:12:75:62 | # $ hasValueFlow=13 hasValueFlow=14 hasValueFlow=15 | Missing result: hasValueFlow=14 | -| test.ps1:75:12:75:62 | # $ hasValueFlow=13 hasValueFlow=14 hasValueFlow=15 | Missing result: hasValueFlow=15 | -| test.ps1:76:12:76:62 | # $ hasValueFlow=13 hasValueFlow=14 hasValueFlow=15 | Missing result: hasValueFlow=13 | -| test.ps1:76:12:76:62 | # $ hasValueFlow=13 hasValueFlow=14 hasValueFlow=15 | Missing result: hasValueFlow=14 | -| test.ps1:76:12:76:62 | # $ hasValueFlow=13 hasValueFlow=14 hasValueFlow=15 | Missing result: hasValueFlow=15 | -| test.ps1:83:17:83:35 | # $ hasValueFlow=16 | Missing result: hasValueFlow=16 | -| test.ps1:87:17:87:45 | # $ SPURIOUS: hasValueFlow=16 | Fixed spurious result: hasValueFlow=16 | #select | test.ps1:4:6:4:9 | f | test.ps1:3:8:3:17 | Call to Source | test.ps1:4:6:4:9 | f | $@ | test.ps1:3:8:3:17 | Call to Source | Call to Source | | test.ps1:11:6:11:13 | ...[...] | test.ps1:10:12:10:21 | Call to Source | test.ps1:11:6:11:13 | ...[...] | $@ | test.ps1:10:12:10:21 | Call to Source | Call to Source | @@ -136,4 +171,16 @@ testFailures | test.ps1:41:6:41:20 | ...[...] | test.ps1:35:6:35:16 | Call to Source | test.ps1:41:6:41:20 | ...[...] | $@ | test.ps1:35:6:35:16 | Call to Source | Call to Source | | test.ps1:48:6:48:13 | ...[...] | test.ps1:43:6:43:16 | Call to Source | test.ps1:48:6:48:13 | ...[...] | $@ | test.ps1:43:6:43:16 | Call to Source | Call to Source | | test.ps1:49:6:49:20 | ...[...] | test.ps1:43:6:43:16 | Call to Source | test.ps1:49:6:49:20 | ...[...] | $@ | test.ps1:43:6:43:16 | Call to Source | Call to Source | +| test.ps1:55:14:55:24 | field | test.ps1:61:18:61:28 | Call to Source | test.ps1:55:14:55:24 | field | $@ | test.ps1:61:18:61:28 | Call to Source | Call to Source | +| test.ps1:74:6:74:10 | ...[...] | test.ps1:66:10:66:20 | Call to Source | test.ps1:74:6:74:10 | ...[...] | $@ | test.ps1:66:10:66:20 | Call to Source | Call to Source | +| test.ps1:74:6:74:10 | ...[...] | test.ps1:67:10:67:20 | Call to Source | test.ps1:74:6:74:10 | ...[...] | $@ | test.ps1:67:10:67:20 | Call to Source | Call to Source | +| test.ps1:74:6:74:10 | ...[...] | test.ps1:68:10:68:20 | Call to Source | test.ps1:74:6:74:10 | ...[...] | $@ | test.ps1:68:10:68:20 | Call to Source | Call to Source | +| test.ps1:75:6:75:10 | ...[...] | test.ps1:66:10:66:20 | Call to Source | test.ps1:75:6:75:10 | ...[...] | $@ | test.ps1:66:10:66:20 | Call to Source | Call to Source | +| test.ps1:75:6:75:10 | ...[...] | test.ps1:67:10:67:20 | Call to Source | test.ps1:75:6:75:10 | ...[...] | $@ | test.ps1:67:10:67:20 | Call to Source | Call to Source | +| test.ps1:75:6:75:10 | ...[...] | test.ps1:68:10:68:20 | Call to Source | test.ps1:75:6:75:10 | ...[...] | $@ | test.ps1:68:10:68:20 | Call to Source | Call to Source | +| test.ps1:76:6:76:10 | ...[...] | test.ps1:66:10:66:20 | Call to Source | test.ps1:76:6:76:10 | ...[...] | $@ | test.ps1:66:10:66:20 | Call to Source | Call to Source | +| test.ps1:76:6:76:10 | ...[...] | test.ps1:67:10:67:20 | Call to Source | test.ps1:76:6:76:10 | ...[...] | $@ | test.ps1:67:10:67:20 | Call to Source | Call to Source | +| test.ps1:76:6:76:10 | ...[...] | test.ps1:68:10:68:20 | Call to Source | test.ps1:76:6:76:10 | ...[...] | $@ | test.ps1:68:10:68:20 | Call to Source | Call to Source | +| test.ps1:83:6:83:15 | ...[...] | test.ps1:79:7:79:17 | Call to Source | test.ps1:83:6:83:15 | ...[...] | $@ | test.ps1:79:7:79:17 | Call to Source | Call to Source | +| test.ps1:87:6:87:15 | ...[...] | test.ps1:79:7:79:17 | Call to Source | test.ps1:87:6:87:15 | ...[...] | $@ | test.ps1:79:7:79:17 | Call to Source | Call to Source | | test.ps1:89:6:89:12 | b | test.ps1:88:11:88:21 | Call to Source | test.ps1:89:6:89:12 | b | $@ | test.ps1:88:11:88:21 | Call to Source | Call to Source | diff --git a/powershell/ql/test/library-tests/dataflow/local/flow.expected b/powershell/ql/test/library-tests/dataflow/local/flow.expected index 080873884dfe..e7aa288529cb 100644 --- a/powershell/ql/test/library-tests/dataflow/local/flow.expected +++ b/powershell/ql/test/library-tests/dataflow/local/flow.expected @@ -1,51 +1,37 @@ | test.ps1:1:1:1:3 | a1 | test.ps1:2:6:2:8 | a1 | +| test.ps1:1:1:21:27 | implicit unwrapping of {...} | test.ps1:1:1:21:27 | return value for {...} | +| test.ps1:1:1:21:27 | pre-return value for {...} | test.ps1:1:1:21:27 | implicit unwrapping of {...} | | test.ps1:1:7:1:12 | Call to Source | test.ps1:1:1:1:3 | a1 | -| test.ps1:2:1:2:8 | Call to Sink | test.ps1:2:1:2:8 | pre-return value for Call to Sink | -| test.ps1:2:1:2:8 | Call to Sink | test.ps1:2:1:2:8 | pre-return value for Call to Sink | -| test.ps1:2:1:2:8 | implicit unwrapping of Call to Sink | test.ps1:1:1:21:27 | return value for {...} | -| test.ps1:2:1:2:8 | pre-return value for Call to Sink | test.ps1:2:1:2:8 | implicit unwrapping of Call to Sink | +| test.ps1:2:1:2:8 | Call to Sink | test.ps1:1:1:21:27 | pre-return value for {...} | +| test.ps1:2:1:2:8 | Call to Sink | test.ps1:1:1:21:27 | pre-return value for {...} | | test.ps1:4:1:4:2 | b | test.ps1:5:4:5:5 | b | | test.ps1:4:6:4:12 | Call to GetBool | test.ps1:4:1:4:2 | b | -| test.ps1:5:1:7:1 | if (...) {...} | test.ps1:5:1:7:1 | pre-return value for if (...) {...} | -| test.ps1:5:1:7:1 | if (...) {...} | test.ps1:5:1:7:1 | pre-return value for if (...) {...} | -| test.ps1:5:1:7:1 | implicit unwrapping of if (...) {...} | test.ps1:1:1:21:27 | return value for {...} | | test.ps1:5:1:7:1 | phi | test.ps1:8:6:8:8 | a2 | -| test.ps1:5:1:7:1 | pre-return value for if (...) {...} | test.ps1:5:1:7:1 | implicit unwrapping of if (...) {...} | | test.ps1:5:4:5:5 | b | test.ps1:10:14:10:15 | b | | test.ps1:6:5:6:7 | a2 | test.ps1:6:11:6:16 | [input] phi | | test.ps1:6:11:6:16 | Call to Source | test.ps1:6:5:6:7 | a2 | | test.ps1:6:11:6:16 | [input] phi | test.ps1:5:1:7:1 | phi | -| test.ps1:8:1:8:8 | Call to Sink | test.ps1:8:1:8:8 | pre-return value for Call to Sink | -| test.ps1:8:1:8:8 | Call to Sink | test.ps1:8:1:8:8 | pre-return value for Call to Sink | -| test.ps1:8:1:8:8 | implicit unwrapping of Call to Sink | test.ps1:1:1:21:27 | return value for {...} | -| test.ps1:8:1:8:8 | pre-return value for Call to Sink | test.ps1:8:1:8:8 | implicit unwrapping of Call to Sink | +| test.ps1:8:1:8:8 | Call to Sink | test.ps1:1:1:21:27 | pre-return value for {...} | +| test.ps1:8:1:8:8 | Call to Sink | test.ps1:1:1:21:27 | pre-return value for {...} | | test.ps1:10:1:10:2 | c | test.ps1:11:6:11:7 | c | | test.ps1:10:6:10:15 | [...]... | test.ps1:10:1:10:2 | c | | test.ps1:10:14:10:15 | b | test.ps1:10:6:10:15 | [...]... | -| test.ps1:11:1:11:7 | Call to Sink | test.ps1:11:1:11:7 | pre-return value for Call to Sink | -| test.ps1:11:1:11:7 | Call to Sink | test.ps1:11:1:11:7 | pre-return value for Call to Sink | -| test.ps1:11:1:11:7 | implicit unwrapping of Call to Sink | test.ps1:1:1:21:27 | return value for {...} | -| test.ps1:11:1:11:7 | pre-return value for Call to Sink | test.ps1:11:1:11:7 | implicit unwrapping of Call to Sink | +| test.ps1:11:1:11:7 | Call to Sink | test.ps1:1:1:21:27 | pre-return value for {...} | +| test.ps1:11:1:11:7 | Call to Sink | test.ps1:1:1:21:27 | pre-return value for {...} | | test.ps1:11:6:11:7 | [post] c | test.ps1:13:7:13:8 | c | | test.ps1:11:6:11:7 | c | test.ps1:13:7:13:8 | c | | test.ps1:13:1:13:2 | d | test.ps1:14:6:14:7 | d | | test.ps1:13:6:13:9 | (...) | test.ps1:13:1:13:2 | d | | test.ps1:13:7:13:8 | c | test.ps1:13:6:13:9 | (...) | -| test.ps1:14:1:14:7 | Call to Sink | test.ps1:14:1:14:7 | pre-return value for Call to Sink | -| test.ps1:14:1:14:7 | Call to Sink | test.ps1:14:1:14:7 | pre-return value for Call to Sink | -| test.ps1:14:1:14:7 | implicit unwrapping of Call to Sink | test.ps1:1:1:21:27 | return value for {...} | -| test.ps1:14:1:14:7 | pre-return value for Call to Sink | test.ps1:14:1:14:7 | implicit unwrapping of Call to Sink | +| test.ps1:14:1:14:7 | Call to Sink | test.ps1:1:1:21:27 | pre-return value for {...} | +| test.ps1:14:1:14:7 | Call to Sink | test.ps1:1:1:21:27 | pre-return value for {...} | | test.ps1:14:6:14:7 | [post] d | test.ps1:16:6:16:7 | d | | test.ps1:14:6:14:7 | d | test.ps1:16:6:16:7 | d | | test.ps1:16:1:16:2 | e | test.ps1:17:6:17:7 | e | | test.ps1:16:6:16:11 | ...+... | test.ps1:16:1:16:2 | e | -| test.ps1:17:1:17:7 | Call to Sink | test.ps1:17:1:17:7 | pre-return value for Call to Sink | -| test.ps1:17:1:17:7 | Call to Sink | test.ps1:17:1:17:7 | pre-return value for Call to Sink | -| test.ps1:17:1:17:7 | implicit unwrapping of Call to Sink | test.ps1:1:1:21:27 | return value for {...} | -| test.ps1:17:1:17:7 | pre-return value for Call to Sink | test.ps1:17:1:17:7 | implicit unwrapping of Call to Sink | +| test.ps1:17:1:17:7 | Call to Sink | test.ps1:1:1:21:27 | pre-return value for {...} | +| test.ps1:17:1:17:7 | Call to Sink | test.ps1:1:1:21:27 | pre-return value for {...} | | test.ps1:19:1:19:2 | f | test.ps1:21:25:21:26 | f | | test.ps1:19:6:19:11 | Call to Source | test.ps1:19:1:19:2 | f | -| test.ps1:21:1:21:27 | Call to Sink | test.ps1:21:1:21:27 | pre-return value for Call to Sink | -| test.ps1:21:1:21:27 | Call to Sink | test.ps1:21:1:21:27 | pre-return value for Call to Sink | -| test.ps1:21:1:21:27 | implicit unwrapping of Call to Sink | test.ps1:1:1:21:27 | return value for {...} | -| test.ps1:21:1:21:27 | pre-return value for Call to Sink | test.ps1:21:1:21:27 | implicit unwrapping of Call to Sink | +| test.ps1:21:1:21:27 | Call to Sink | test.ps1:1:1:21:27 | pre-return value for {...} | +| test.ps1:21:1:21:27 | Call to Sink | test.ps1:1:1:21:27 | pre-return value for {...} | diff --git a/powershell/ql/test/library-tests/dataflow/local/taint.expected b/powershell/ql/test/library-tests/dataflow/local/taint.expected index 5a68287270e1..381f685afbdf 100644 --- a/powershell/ql/test/library-tests/dataflow/local/taint.expected +++ b/powershell/ql/test/library-tests/dataflow/local/taint.expected @@ -1,61 +1,41 @@ | test.ps1:1:1:1:3 | a1 | test.ps1:2:6:2:8 | a1 | +| test.ps1:1:1:21:27 | implicit unwrapping of {...} | test.ps1:1:1:21:27 | return value for {...} | +| test.ps1:1:1:21:27 | pre-return value for {...} | test.ps1:1:1:21:27 | implicit unwrapping of {...} | +| test.ps1:1:1:21:27 | pre-return value for {...} | test.ps1:1:1:21:27 | implicit unwrapping of {...} | | test.ps1:1:7:1:12 | Call to Source | test.ps1:1:1:1:3 | a1 | -| test.ps1:2:1:2:8 | Call to Sink | test.ps1:2:1:2:8 | pre-return value for Call to Sink | -| test.ps1:2:1:2:8 | Call to Sink | test.ps1:2:1:2:8 | pre-return value for Call to Sink | -| test.ps1:2:1:2:8 | implicit unwrapping of Call to Sink | test.ps1:1:1:21:27 | return value for {...} | -| test.ps1:2:1:2:8 | pre-return value for Call to Sink | test.ps1:2:1:2:8 | implicit unwrapping of Call to Sink | -| test.ps1:2:1:2:8 | pre-return value for Call to Sink | test.ps1:2:1:2:8 | implicit unwrapping of Call to Sink | +| test.ps1:2:1:2:8 | Call to Sink | test.ps1:1:1:21:27 | pre-return value for {...} | +| test.ps1:2:1:2:8 | Call to Sink | test.ps1:1:1:21:27 | pre-return value for {...} | | test.ps1:4:1:4:2 | b | test.ps1:5:4:5:5 | b | | test.ps1:4:6:4:12 | Call to GetBool | test.ps1:4:1:4:2 | b | -| test.ps1:5:1:7:1 | if (...) {...} | test.ps1:5:1:7:1 | pre-return value for if (...) {...} | -| test.ps1:5:1:7:1 | if (...) {...} | test.ps1:5:1:7:1 | pre-return value for if (...) {...} | -| test.ps1:5:1:7:1 | implicit unwrapping of if (...) {...} | test.ps1:1:1:21:27 | return value for {...} | | test.ps1:5:1:7:1 | phi | test.ps1:8:6:8:8 | a2 | -| test.ps1:5:1:7:1 | pre-return value for if (...) {...} | test.ps1:5:1:7:1 | implicit unwrapping of if (...) {...} | -| test.ps1:5:1:7:1 | pre-return value for if (...) {...} | test.ps1:5:1:7:1 | implicit unwrapping of if (...) {...} | | test.ps1:5:4:5:5 | b | test.ps1:10:14:10:15 | b | | test.ps1:6:5:6:7 | a2 | test.ps1:6:11:6:16 | [input] phi | | test.ps1:6:11:6:16 | Call to Source | test.ps1:6:5:6:7 | a2 | | test.ps1:6:11:6:16 | [input] phi | test.ps1:5:1:7:1 | phi | -| test.ps1:8:1:8:8 | Call to Sink | test.ps1:8:1:8:8 | pre-return value for Call to Sink | -| test.ps1:8:1:8:8 | Call to Sink | test.ps1:8:1:8:8 | pre-return value for Call to Sink | -| test.ps1:8:1:8:8 | implicit unwrapping of Call to Sink | test.ps1:1:1:21:27 | return value for {...} | -| test.ps1:8:1:8:8 | pre-return value for Call to Sink | test.ps1:8:1:8:8 | implicit unwrapping of Call to Sink | -| test.ps1:8:1:8:8 | pre-return value for Call to Sink | test.ps1:8:1:8:8 | implicit unwrapping of Call to Sink | +| test.ps1:8:1:8:8 | Call to Sink | test.ps1:1:1:21:27 | pre-return value for {...} | +| test.ps1:8:1:8:8 | Call to Sink | test.ps1:1:1:21:27 | pre-return value for {...} | | test.ps1:10:1:10:2 | c | test.ps1:11:6:11:7 | c | | test.ps1:10:6:10:15 | [...]... | test.ps1:10:1:10:2 | c | | test.ps1:10:14:10:15 | b | test.ps1:10:6:10:15 | [...]... | -| test.ps1:11:1:11:7 | Call to Sink | test.ps1:11:1:11:7 | pre-return value for Call to Sink | -| test.ps1:11:1:11:7 | Call to Sink | test.ps1:11:1:11:7 | pre-return value for Call to Sink | -| test.ps1:11:1:11:7 | implicit unwrapping of Call to Sink | test.ps1:1:1:21:27 | return value for {...} | -| test.ps1:11:1:11:7 | pre-return value for Call to Sink | test.ps1:11:1:11:7 | implicit unwrapping of Call to Sink | -| test.ps1:11:1:11:7 | pre-return value for Call to Sink | test.ps1:11:1:11:7 | implicit unwrapping of Call to Sink | +| test.ps1:11:1:11:7 | Call to Sink | test.ps1:1:1:21:27 | pre-return value for {...} | +| test.ps1:11:1:11:7 | Call to Sink | test.ps1:1:1:21:27 | pre-return value for {...} | | test.ps1:11:6:11:7 | [post] c | test.ps1:13:7:13:8 | c | | test.ps1:11:6:11:7 | c | test.ps1:13:7:13:8 | c | | test.ps1:13:1:13:2 | d | test.ps1:14:6:14:7 | d | | test.ps1:13:6:13:9 | (...) | test.ps1:13:1:13:2 | d | | test.ps1:13:7:13:8 | c | test.ps1:13:6:13:9 | (...) | -| test.ps1:14:1:14:7 | Call to Sink | test.ps1:14:1:14:7 | pre-return value for Call to Sink | -| test.ps1:14:1:14:7 | Call to Sink | test.ps1:14:1:14:7 | pre-return value for Call to Sink | -| test.ps1:14:1:14:7 | implicit unwrapping of Call to Sink | test.ps1:1:1:21:27 | return value for {...} | -| test.ps1:14:1:14:7 | pre-return value for Call to Sink | test.ps1:14:1:14:7 | implicit unwrapping of Call to Sink | -| test.ps1:14:1:14:7 | pre-return value for Call to Sink | test.ps1:14:1:14:7 | implicit unwrapping of Call to Sink | +| test.ps1:14:1:14:7 | Call to Sink | test.ps1:1:1:21:27 | pre-return value for {...} | +| test.ps1:14:1:14:7 | Call to Sink | test.ps1:1:1:21:27 | pre-return value for {...} | | test.ps1:14:6:14:7 | [post] d | test.ps1:16:6:16:7 | d | | test.ps1:14:6:14:7 | d | test.ps1:16:6:16:7 | d | | test.ps1:16:1:16:2 | e | test.ps1:17:6:17:7 | e | | test.ps1:16:6:16:7 | d | test.ps1:16:6:16:11 | ...+... | | test.ps1:16:6:16:11 | ...+... | test.ps1:16:1:16:2 | e | | test.ps1:16:11:16:11 | 1 | test.ps1:16:6:16:11 | ...+... | -| test.ps1:17:1:17:7 | Call to Sink | test.ps1:17:1:17:7 | pre-return value for Call to Sink | -| test.ps1:17:1:17:7 | Call to Sink | test.ps1:17:1:17:7 | pre-return value for Call to Sink | -| test.ps1:17:1:17:7 | implicit unwrapping of Call to Sink | test.ps1:1:1:21:27 | return value for {...} | -| test.ps1:17:1:17:7 | pre-return value for Call to Sink | test.ps1:17:1:17:7 | implicit unwrapping of Call to Sink | -| test.ps1:17:1:17:7 | pre-return value for Call to Sink | test.ps1:17:1:17:7 | implicit unwrapping of Call to Sink | +| test.ps1:17:1:17:7 | Call to Sink | test.ps1:1:1:21:27 | pre-return value for {...} | +| test.ps1:17:1:17:7 | Call to Sink | test.ps1:1:1:21:27 | pre-return value for {...} | | test.ps1:19:1:19:2 | f | test.ps1:21:25:21:26 | f | | test.ps1:19:6:19:11 | Call to Source | test.ps1:19:1:19:2 | f | -| test.ps1:21:1:21:27 | Call to Sink | test.ps1:21:1:21:27 | pre-return value for Call to Sink | -| test.ps1:21:1:21:27 | Call to Sink | test.ps1:21:1:21:27 | pre-return value for Call to Sink | -| test.ps1:21:1:21:27 | implicit unwrapping of Call to Sink | test.ps1:1:1:21:27 | return value for {...} | -| test.ps1:21:1:21:27 | pre-return value for Call to Sink | test.ps1:21:1:21:27 | implicit unwrapping of Call to Sink | -| test.ps1:21:1:21:27 | pre-return value for Call to Sink | test.ps1:21:1:21:27 | implicit unwrapping of Call to Sink | +| test.ps1:21:1:21:27 | Call to Sink | test.ps1:1:1:21:27 | pre-return value for {...} | +| test.ps1:21:1:21:27 | Call to Sink | test.ps1:1:1:21:27 | pre-return value for {...} | | test.ps1:21:25:21:26 | f | test.ps1:21:6:21:27 | here is a string: $f | diff --git a/powershell/ql/test/library-tests/dataflow/returns/test.expected b/powershell/ql/test/library-tests/dataflow/returns/test.expected index ae3eefed04d1..cfd34b3abcdd 100644 --- a/powershell/ql/test/library-tests/dataflow/returns/test.expected +++ b/powershell/ql/test/library-tests/dataflow/returns/test.expected @@ -8,6 +8,16 @@ edges | test.ps1:13:6:13:20 | Call to callSourceTwice [element] | test.ps1:16:6:16:7 | x [element] | provenance | | | test.ps1:15:6:15:7 | x [element] | test.ps1:15:6:15:10 | ...[...] | provenance | | | test.ps1:16:6:16:7 | x [element] | test.ps1:16:6:16:10 | ...[...] | provenance | | +| test.ps1:19:12:19:21 | Call to Source | test.ps1:22:6:22:18 | Call to returnSource1 | provenance | | +| test.ps1:22:6:22:18 | Call to returnSource1 | test.ps1:23:6:23:7 | x | provenance | | +| test.ps1:26:10:26:19 | Call to Source | test.ps1:27:5:27:6 | x | provenance | | +| test.ps1:27:5:27:6 | x | test.ps1:32:6:32:18 | Call to returnSource2 [element] | provenance | | +| test.ps1:28:10:28:19 | Call to Source | test.ps1:29:12:29:13 | y | provenance | | +| test.ps1:29:12:29:13 | y | test.ps1:32:6:32:18 | Call to returnSource2 [element] | provenance | | +| test.ps1:32:6:32:18 | Call to returnSource2 [element] | test.ps1:33:6:33:7 | x [element] | provenance | | +| test.ps1:32:6:32:18 | Call to returnSource2 [element] | test.ps1:34:6:34:7 | x [element] | provenance | | +| test.ps1:33:6:33:7 | x [element] | test.ps1:33:6:33:10 | ...[...] | provenance | | +| test.ps1:34:6:34:7 | x [element] | test.ps1:34:6:34:10 | ...[...] | provenance | | | test.ps1:38:9:38:18 | Call to Source | test.ps1:42:6:42:21 | Call to callSourceInLoop [element] | provenance | | | test.ps1:42:6:42:21 | Call to callSourceInLoop [element] | test.ps1:43:6:43:7 | x [element] | provenance | | | test.ps1:42:6:42:21 | Call to callSourceInLoop [element] | test.ps1:44:6:44:7 | x [element] | provenance | | @@ -24,6 +34,18 @@ nodes | test.ps1:15:6:15:10 | ...[...] | semmle.label | ...[...] | | test.ps1:16:6:16:7 | x [element] | semmle.label | x [element] | | test.ps1:16:6:16:10 | ...[...] | semmle.label | ...[...] | +| test.ps1:19:12:19:21 | Call to Source | semmle.label | Call to Source | +| test.ps1:22:6:22:18 | Call to returnSource1 | semmle.label | Call to returnSource1 | +| test.ps1:23:6:23:7 | x | semmle.label | x | +| test.ps1:26:10:26:19 | Call to Source | semmle.label | Call to Source | +| test.ps1:27:5:27:6 | x | semmle.label | x | +| test.ps1:28:10:28:19 | Call to Source | semmle.label | Call to Source | +| test.ps1:29:12:29:13 | y | semmle.label | y | +| test.ps1:32:6:32:18 | Call to returnSource2 [element] | semmle.label | Call to returnSource2 [element] | +| test.ps1:33:6:33:7 | x [element] | semmle.label | x [element] | +| test.ps1:33:6:33:10 | ...[...] | semmle.label | ...[...] | +| test.ps1:34:6:34:7 | x [element] | semmle.label | x [element] | +| test.ps1:34:6:34:10 | ...[...] | semmle.label | ...[...] | | test.ps1:38:9:38:18 | Call to Source | semmle.label | Call to Source | | test.ps1:42:6:42:21 | Call to callSourceInLoop [element] | semmle.label | Call to callSourceInLoop [element] | | test.ps1:43:6:43:7 | x [element] | semmle.label | x [element] | @@ -32,16 +54,16 @@ nodes | test.ps1:44:6:44:10 | ...[...] | semmle.label | ...[...] | subpaths testFailures -| test.ps1:23:9:23:26 | # $ hasValueFlow=4 | Missing result: hasValueFlow=4 | -| test.ps1:33:12:33:54 | # $ hasValueFlow=5 SPURIOUS: hasValueFlow=6 | Fixed spurious result: hasValueFlow=6 | -| test.ps1:33:12:33:54 | # $ hasValueFlow=5 SPURIOUS: hasValueFlow=6 | Missing result: hasValueFlow=5 | -| test.ps1:34:12:34:54 | # $ hasValueFlow=6 SPURIOUS: hasValueFlow=5 | Fixed spurious result: hasValueFlow=5 | -| test.ps1:34:12:34:54 | # $ hasValueFlow=6 SPURIOUS: hasValueFlow=5 | Missing result: hasValueFlow=6 | #select | test.ps1:6:6:6:7 | x | test.ps1:2:5:2:14 | Call to Source | test.ps1:6:6:6:7 | x | $@ | test.ps1:2:5:2:14 | Call to Source | Call to Source | | test.ps1:15:6:15:10 | ...[...] | test.ps1:9:5:9:14 | Call to Source | test.ps1:15:6:15:10 | ...[...] | $@ | test.ps1:9:5:9:14 | Call to Source | Call to Source | | test.ps1:15:6:15:10 | ...[...] | test.ps1:10:5:10:14 | Call to Source | test.ps1:15:6:15:10 | ...[...] | $@ | test.ps1:10:5:10:14 | Call to Source | Call to Source | | test.ps1:16:6:16:10 | ...[...] | test.ps1:9:5:9:14 | Call to Source | test.ps1:16:6:16:10 | ...[...] | $@ | test.ps1:9:5:9:14 | Call to Source | Call to Source | | test.ps1:16:6:16:10 | ...[...] | test.ps1:10:5:10:14 | Call to Source | test.ps1:16:6:16:10 | ...[...] | $@ | test.ps1:10:5:10:14 | Call to Source | Call to Source | +| test.ps1:23:6:23:7 | x | test.ps1:19:12:19:21 | Call to Source | test.ps1:23:6:23:7 | x | $@ | test.ps1:19:12:19:21 | Call to Source | Call to Source | +| test.ps1:33:6:33:10 | ...[...] | test.ps1:26:10:26:19 | Call to Source | test.ps1:33:6:33:10 | ...[...] | $@ | test.ps1:26:10:26:19 | Call to Source | Call to Source | +| test.ps1:33:6:33:10 | ...[...] | test.ps1:28:10:28:19 | Call to Source | test.ps1:33:6:33:10 | ...[...] | $@ | test.ps1:28:10:28:19 | Call to Source | Call to Source | +| test.ps1:34:6:34:10 | ...[...] | test.ps1:26:10:26:19 | Call to Source | test.ps1:34:6:34:10 | ...[...] | $@ | test.ps1:26:10:26:19 | Call to Source | Call to Source | +| test.ps1:34:6:34:10 | ...[...] | test.ps1:28:10:28:19 | Call to Source | test.ps1:34:6:34:10 | ...[...] | $@ | test.ps1:28:10:28:19 | Call to Source | Call to Source | | test.ps1:43:6:43:10 | ...[...] | test.ps1:38:9:38:18 | Call to Source | test.ps1:43:6:43:10 | ...[...] | $@ | test.ps1:38:9:38:18 | Call to Source | Call to Source | | test.ps1:44:6:44:10 | ...[...] | test.ps1:38:9:38:18 | Call to Source | test.ps1:44:6:44:10 | ...[...] | $@ | test.ps1:38:9:38:18 | Call to Source | Call to Source |