diff --git a/powershell/ql/lib/powershell.qll b/powershell/ql/lib/powershell.qll index 59fd70c14e9..60d74ef591d 100644 --- a/powershell/ql/lib/powershell.qll +++ b/powershell/ql/lib/powershell.qll @@ -22,6 +22,7 @@ import semmle.code.powershell.ScriptBlock import semmle.code.powershell.StringLiteral import semmle.code.powershell.AssignmentStatement import semmle.code.powershell.BinaryExpression +import semmle.code.powershell.UnaryExpression import semmle.code.powershell.ScriptBlockExpr import semmle.code.powershell.TernaryExpression import semmle.code.powershell.UsingExpression @@ -47,7 +48,7 @@ import semmle.code.powershell.UsingStmt import semmle.code.powershell.Type import semmle.code.powershell.Member import semmle.code.powershell.PropertyMember -import semmle.code.powershell.FunctionMember +import semmle.code.powershell.Function import semmle.code.powershell.TryStmt import semmle.code.powershell.IfStmt import semmle.code.powershell.ExitStmt @@ -65,6 +66,5 @@ import semmle.code.powershell.ParenExpression import semmle.code.powershell.Chainable import semmle.code.powershell.Pipeline import semmle.code.powershell.StringConstantExpression -import semmle.code.powershell.FunctionDefinition import semmle.code.powershell.InvokeMemberExpression import semmle.code.powershell.CommentEntity diff --git a/powershell/ql/lib/semmle/code/powershell/ArrayLiteral.qll b/powershell/ql/lib/semmle/code/powershell/ArrayLiteral.qll index a5fd2fd177f..447d26b45e3 100644 --- a/powershell/ql/lib/semmle/code/powershell/ArrayLiteral.qll +++ b/powershell/ql/lib/semmle/code/powershell/ArrayLiteral.qll @@ -7,5 +7,5 @@ class ArrayLiteral extends @array_literal, Expr { Expr getAnElement() { array_literal_element(this, _, result) } - override string toString() { result = "ArrayLiteral at: " + this.getLocation().toString() } + override string toString() { result = "...,..." } } diff --git a/powershell/ql/lib/semmle/code/powershell/BinaryExpression.qll b/powershell/ql/lib/semmle/code/powershell/BinaryExpression.qll index 4f3667e42db..c17d1f0bcef 100644 --- a/powershell/ql/lib/semmle/code/powershell/BinaryExpression.qll +++ b/powershell/ql/lib/semmle/code/powershell/BinaryExpression.qll @@ -1,15 +1,255 @@ import powershell class BinaryExpr extends @binary_expression, Expr { - override string toString() { - result = "...+..." // TODO - } - override SourceLocation getLocation() { binary_expression_location(this, result) } - private int getKind() { binary_expression(this, result, _, _) } + int getKind() { binary_expression(this, result, _, _) } Expr getLeft() { binary_expression(this, _, result, _) } Expr getRight() { binary_expression(this, _, _, result) } } + +abstract private class AbstractArithmeticExpr extends BinaryExpr { } + +final class ArithmeticExpr = AbstractArithmeticExpr; + +class AddExpr extends AbstractArithmeticExpr { + AddExpr() { this.getKind() = 40 } + + final override string toString() { result = "...+..." } +} + +class SubExpr extends AbstractArithmeticExpr { + SubExpr() { this.getKind() = 41 } + + final override string toString() { result = "...-..." } +} + +class MulExpr extends AbstractArithmeticExpr { + MulExpr() { this.getKind() = 37 } + + final override string toString() { result = "...*..." } +} + +class DivExpr extends AbstractArithmeticExpr { + DivExpr() { this.getKind() = 38 } + + final override string toString() { result = ".../..." } +} + +class RemExpr extends AbstractArithmeticExpr { + RemExpr() { this.getKind() = 39 } + + final override string toString() { result = "...%..." } +} + +abstract private class AbstractBitwiseExpr extends BinaryExpr { } + +final class BitwiseExpr = AbstractBitwiseExpr; + +class BitwiseAndExpr extends AbstractBitwiseExpr { + BitwiseAndExpr() { this.getKind() = 56 } + + final override string toString() { result = "...&..." } +} + +class BitwiseOrExpr extends AbstractBitwiseExpr { + BitwiseOrExpr() { this.getKind() = 57 } + + final override string toString() { result = "...|..." } +} + +class BitwiseXorExpr extends AbstractBitwiseExpr { + BitwiseXorExpr() { this.getKind() = 58 } + + final override string toString() { result = "...^..." } +} + +class ShiftLeftExpr extends AbstractBitwiseExpr { + ShiftLeftExpr() { this.getKind() = 97 } + + final override string toString() { result = "...<<..." } +} + +class ShiftRightExpr extends AbstractBitwiseExpr { + ShiftRightExpr() { this.getKind() = 98 } + + final override string toString() { result = "...>>..." } +} + +abstract private class AbstractComparisonExpr extends BinaryExpr { } + +final class ComparisonExpr = AbstractComparisonExpr; + +abstract private class AbstractCaseInsensitiveComparisonExpr extends AbstractComparisonExpr { } + +final class CaseInsensitiveComparisonExpr = AbstractCaseInsensitiveComparisonExpr; + +class EqExpr extends AbstractCaseInsensitiveComparisonExpr { + EqExpr() { this.getKind() = 60 } + + final override string toString() { result = "... -eq ..." } +} + +class NeExpr extends AbstractCaseInsensitiveComparisonExpr { + NeExpr() { this.getKind() = 61 } + + final override string toString() { result = "... -ne ..." } +} + +abstract private class AbstractRelationalExpr extends AbstractComparisonExpr { } + +final class RelationalExpr = AbstractRelationalExpr; + +abstract private class AbstractCaseInsensitiveRelationalExpr extends AbstractRelationalExpr { } + +class GeExpr extends AbstractCaseInsensitiveRelationalExpr { + GeExpr() { this.getKind() = 62 } + + final override string toString() { result = "... -ge ..." } +} + +class GtExpr extends AbstractCaseInsensitiveRelationalExpr { + GtExpr() { this.getKind() = 63 } + + final override string toString() { result = "... -gt ..." } +} + +class LtExpr extends AbstractCaseInsensitiveRelationalExpr { + LtExpr() { this.getKind() = 64 } + + final override string toString() { result = "... -lt ..." } +} + +class LeExpr extends AbstractCaseInsensitiveRelationalExpr { + LeExpr() { this.getKind() = 65 } + + final override string toString() { result = "... -le ..." } +} + +class LikeExpr extends AbstractCaseInsensitiveComparisonExpr { + LikeExpr() { this.getKind() = 66 } + + final override string toString() { result = "... -like ..." } +} + +class NotLikeExpr extends AbstractCaseInsensitiveComparisonExpr { + NotLikeExpr() { this.getKind() = 67 } + + final override string toString() { result = "... -notlike ..." } +} + +class MatchExpr extends AbstractCaseInsensitiveComparisonExpr { + MatchExpr() { this.getKind() = 68 } + + final override string toString() { result = "... -match ..." } +} + +class NotMatchExpr extends AbstractCaseInsensitiveComparisonExpr { + NotMatchExpr() { this.getKind() = 69 } + + final override string toString() { result = "... -notmatch ..." } +} + +class ReplaceExpr extends AbstractCaseInsensitiveComparisonExpr { + ReplaceExpr() { this.getKind() = 70 } + + final override string toString() { result = "... -replace ..." } +} + +abstract class AbstractTypeExpr extends BinaryExpr { } + +final class TypeExpr = AbstractTypeExpr; + +abstract class AbstractTypeComparisonExpr extends AbstractTypeExpr { } + +final class TypeComparisonExpr = AbstractTypeComparisonExpr; + +class IsExpr extends AbstractTypeComparisonExpr { + IsExpr() { this.getKind() = 92 } + + final override string toString() { result = "... -is ..." } +} + +class IsNotExpr extends AbstractTypeComparisonExpr { + IsNotExpr() { this.getKind() = 93 } + + final override string toString() { result = "... -isnot ..." } +} + +class AsExpr extends AbstractTypeExpr { + AsExpr() { this.getKind() = 94 } + + final override string toString() { result = "... -as ..." } +} + +abstract private class AbstractContainmentExpr extends BinaryExpr { } + +final class ContainmentExpr = AbstractContainmentExpr; + +abstract private class AbstractCaseInsensitiveContainmentExpr extends AbstractContainmentExpr { } + +final class CaseInsensitiveContainmentExpr = AbstractCaseInsensitiveContainmentExpr; + +class ContainsExpr extends AbstractCaseInsensitiveContainmentExpr { + ContainsExpr() { this.getKind() = 71 } + + final override string toString() { result = "... -contains ..." } +} + +class NotContainsExpr extends AbstractCaseInsensitiveContainmentExpr { + NotContainsExpr() { this.getKind() = 72 } + + final override string toString() { result = "... -notcontains ..." } +} + +class InExpr extends AbstractCaseInsensitiveContainmentExpr { + InExpr() { this.getKind() = 73 } + + final override string toString() { result = "... -in ..." } +} + +class NotInExpr extends AbstractCaseInsensitiveContainmentExpr { + NotInExpr() { this.getKind() = 74 } + + final override string toString() { result = "... -notin ..." } +} + +abstract private class AbstractLogicalBinaryExpr extends BinaryExpr { } + +final class LogicalBinaryExpr = AbstractLogicalBinaryExpr; + +class LogicalAndExpr extends AbstractLogicalBinaryExpr { + LogicalAndExpr() { this.getKind() = 53 } + + final override string toString() { result = "... -and ..." } +} + +class LogicalOrExpr extends AbstractLogicalBinaryExpr { + LogicalOrExpr() { this.getKind() = 54 } + + final override string toString() { result = "... -or ..." } +} + +class LogicalXorExpr extends AbstractLogicalBinaryExpr { + LogicalXorExpr() { this.getKind() = 55 } + + final override string toString() { result = "... -xor ..." } +} + +abstract private class AbstractStringExpr extends BinaryExpr { } + +final class StringExpr = AbstractStringExpr; + +class JoinExpr extends AbstractStringExpr { + JoinExpr() { this.getKind() = 59 } + + final override string toString() { result = "... -join ..." } +} + +class SplitExpr extends AbstractStringExpr { + SplitExpr() { this.getKind() = 75 } + + final override string toString() { result = "... -split ..." } +} diff --git a/powershell/ql/lib/semmle/code/powershell/BreakStmt.qll b/powershell/ql/lib/semmle/code/powershell/BreakStmt.qll index dbec183d7b0..abd74f3e682 100644 --- a/powershell/ql/lib/semmle/code/powershell/BreakStmt.qll +++ b/powershell/ql/lib/semmle/code/powershell/BreakStmt.qll @@ -1,7 +1,7 @@ import powershell -class BreakStmt extends GotoStmt, Stmt { +class BreakStmt extends GotoStmt, @break_statement { override SourceLocation getLocation() { break_statement_location(this, result) } - override string toString() { result = "continue" } + override string toString() { result = "break" } } diff --git a/powershell/ql/lib/semmle/code/powershell/ContinueStmt.qll b/powershell/ql/lib/semmle/code/powershell/ContinueStmt.qll index 79cd8053f77..98fe1ba3fcb 100644 --- a/powershell/ql/lib/semmle/code/powershell/ContinueStmt.qll +++ b/powershell/ql/lib/semmle/code/powershell/ContinueStmt.qll @@ -1,6 +1,6 @@ import powershell -class ContinueStmt extends GotoStmt, Stmt { +class ContinueStmt extends GotoStmt, @continue_statement { override SourceLocation getLocation() { continue_statement_location(this, result) } override string toString() { result = "continue" } diff --git a/powershell/ql/lib/semmle/code/powershell/DoUntilStmt.qll b/powershell/ql/lib/semmle/code/powershell/DoUntilStmt.qll index a0b4f40c03a..6bb3b5c48fd 100644 --- a/powershell/ql/lib/semmle/code/powershell/DoUntilStmt.qll +++ b/powershell/ql/lib/semmle/code/powershell/DoUntilStmt.qll @@ -7,5 +7,5 @@ class DoUntilStmt extends @do_until_statement, LoopStmt { PipelineBase getCondition() { do_until_statement_condition(this, result) } // TODO: Change @ast to @pipeline_base in dbscheme - StmtBlock getBody() { do_until_statement(this, result) } // TODO: Change @ast to @stmt_block in dbscheme + final override StmtBlock getBody() { do_until_statement(this, result) } // TODO: Change @ast to @stmt_block in dbscheme } diff --git a/powershell/ql/lib/semmle/code/powershell/DoWhileStmt.qll b/powershell/ql/lib/semmle/code/powershell/DoWhileStmt.qll index 8076e202347..1882ae35a78 100644 --- a/powershell/ql/lib/semmle/code/powershell/DoWhileStmt.qll +++ b/powershell/ql/lib/semmle/code/powershell/DoWhileStmt.qll @@ -7,5 +7,5 @@ class DoWhileStmt extends @do_while_statement, LoopStmt { PipelineBase getCondition() { do_while_statement_condition(this, result) } // TODO: Change @ast to @pipeline_base in dbscheme - StmtBlock getBody() { do_while_statement(this, result) } // TODO: Change @ast to @stmt_block in dbscheme + final override StmtBlock getBody() { do_while_statement(this, result) } // TODO: Change @ast to @stmt_block in dbscheme } diff --git a/powershell/ql/lib/semmle/code/powershell/ForEachStmt.qll b/powershell/ql/lib/semmle/code/powershell/ForEachStmt.qll index 9a32a6dc7cb..abe61126998 100644 --- a/powershell/ql/lib/semmle/code/powershell/ForEachStmt.qll +++ b/powershell/ql/lib/semmle/code/powershell/ForEachStmt.qll @@ -5,12 +5,11 @@ class ForEachStmt extends @foreach_statement, LoopStmt { override string toString() { result = "forach(... in ...)" } - StmtBlock getBody() { foreach_statement(this, _, _, result, _) } // TODO: Change @ast to @stmt_block in dbscheme + final override StmtBlock getBody() { foreach_statement(this, _, _, result, _) } // TODO: Change @ast to @stmt_block in dbscheme VarAccess getVariable() { foreach_statement(this, result, _, _, _) } // TODO: Change @ast to @variable_expression in dbscheme - /** ..., if any. */ - PipelineBase getCondition() { foreach_statement(this, _, result, _, _) } // TODO: Change @ast to @pipeline_base in dbscheme + PipelineBase getIterableExpr() { foreach_statement(this, _, result, _, _) } // TODO: Change @ast to @pipeline_base in dbscheme predicate isParallel() { foreach_statement(this, _, _, _, 1) } } diff --git a/powershell/ql/lib/semmle/code/powershell/ForStmt.qll b/powershell/ql/lib/semmle/code/powershell/ForStmt.qll index 3a89532e537..402a72d5469 100644 --- a/powershell/ql/lib/semmle/code/powershell/ForStmt.qll +++ b/powershell/ql/lib/semmle/code/powershell/ForStmt.qll @@ -11,5 +11,5 @@ class ForStmt extends @for_statement, LoopStmt { PipelineBase getIterator() { for_statement_iterator(this, result) } // TODO: Change @ast to @pipeline_base in dbscheme - StmtBlock getBody() { for_statement(this, result) } // TODO: Change @ast to @stmt_block in dbscheme + final override StmtBlock getBody() { for_statement(this, result) } // TODO: Change @ast to @stmt_block in dbscheme } diff --git a/powershell/ql/lib/semmle/code/powershell/Function.qll b/powershell/ql/lib/semmle/code/powershell/Function.qll new file mode 100644 index 00000000000..ac438c20429 --- /dev/null +++ b/powershell/ql/lib/semmle/code/powershell/Function.qll @@ -0,0 +1,69 @@ +import powershell + +abstract private class AbstractFunction extends Ast { + abstract string getName(); + + abstract ScriptBlock getBody(); + + abstract Parameter getFunctionParameter(int i); + + final Parameter getAFunctionParameter() { result = this.getFunctionParameter(_) } + + final int getNumberOfFunctionParameters() { result = count(this.getAFunctionParameter()) } + + final int getNumberOfParameters() { result = count(this.getAParameter()) } + + final Parameter getParameter(int i) { + result = this.getFunctionParameter(i) + or + result = this.getBody().getParamBlock().getParameter(i) + } + + final Parameter getAParameter() { result = this.getParameter(_) } +} + +class NonMemberFunction extends @function_definition, Stmt, AbstractFunction { + override string toString() { result = this.getName() } + + override SourceLocation getLocation() { function_definition_location(this, result) } + + override string getName() { function_definition(this, _, result, _, _) } + + override ScriptBlock getBody() { function_definition(this, result, _, _, _) } + + predicate isFilter() { function_definition(this, _, _, true, _) } + + predicate isWorkflow() { function_definition(this, _, _, _, true) } + + override Parameter getFunctionParameter(int i) { function_definition_parameter(this, i, result) } +} + +class MemberFunction extends @function_member, Member, AbstractFunction { + override string getName() { function_member(this, _, _, _, _, _, _, result, _) } + + override SourceLocation getLocation() { function_member_location(this, result) } + + override string toString() { result = this.getName() } + + override ScriptBlock getBody() { function_member(this, result, _, _, _, _, _, _, _) } + + override predicate isHidden() { function_member(this, _, _, true, _, _, _, _, _) } + + override predicate isPrivate() { function_member(this, _, _, _, true, _, _, _, _) } + + override predicate isPublic() { function_member(this, _, _, _, _, true, _, _, _) } + + override predicate isStatic() { function_member(this, _, _, _, _, _, true, _, _) } + + predicate isConstructor() { function_member(this, _, true, _, _, _, _, _, _) } + + override Parameter getFunctionParameter(int i) { function_member_parameter(this, i, result) } + + TypeConstraint getTypeConstraint() { function_member_return_type(this, result) } +} + +class Constructor extends MemberFunction { + Constructor() { this.isConstructor() } +} + +final class Function = AbstractFunction; diff --git a/powershell/ql/lib/semmle/code/powershell/FunctionDefinition.qll b/powershell/ql/lib/semmle/code/powershell/FunctionDefinition.qll deleted file mode 100644 index 68867cbd845..00000000000 --- a/powershell/ql/lib/semmle/code/powershell/FunctionDefinition.qll +++ /dev/null @@ -1,19 +0,0 @@ -import powershell - -class Function extends @function_definition, Stmt { - override string toString() { result = "FunctionDefinition at: " + this.getLocation().toString() } - - override SourceLocation getLocation() { function_definition_location(this, result) } - - string getName() { function_definition(this, _, result, _, _) } - - ScriptBlock getBody() { function_definition(this, result, _, _, _) } - - predicate isFilter() { function_definition(this, _, _, true, _) } - - predicate isWorkflow() { function_definition(this, _, _, _, true) } - - Parameter getParameter(int i) { function_definition_parameter(this, i, result) } - - Parameter getAParameter() { result = this.getParameter(_) } -} diff --git a/powershell/ql/lib/semmle/code/powershell/FunctionMember.qll b/powershell/ql/lib/semmle/code/powershell/FunctionMember.qll deleted file mode 100644 index e3498be9e0f..00000000000 --- a/powershell/ql/lib/semmle/code/powershell/FunctionMember.qll +++ /dev/null @@ -1,31 +0,0 @@ -import powershell - -class FunctionMember extends @function_member, Member { - override string getName() { function_member(this, _, _, _, _, _, _, result, _) } - - override SourceLocation getLocation() { function_member_location(this, result) } - - override string toString() { result = this.getName() } - - ScriptBlock getBody() { function_member(this, result, _, _, _, _, _, _, _) } - - override predicate isHidden() { function_member(this, _, _, true, _, _, _, _, _) } - - override predicate isPrivate() { function_member(this, _, _, _, true, _, _, _, _) } - - override predicate isPublic() { function_member(this, _, _, _, _, true, _, _, _) } - - override predicate isStatic() { function_member(this, _, _, _, _, _, true, _, _) } - - predicate isConstructor() { function_member(this, _, true, _, _, _, _, _, _) } - - Parameter getParameter(int i) { function_member_parameter(this, i, result) } - - Parameter getAParameter() { result = this.getParameter(_) } - - TypeConstraint getTypeConstraint() { function_member_return_type(this, result) } -} - -class Constructor extends FunctionMember { - Constructor() { this.isConstructor() } -} diff --git a/powershell/ql/lib/semmle/code/powershell/IfStmt.qll b/powershell/ql/lib/semmle/code/powershell/IfStmt.qll index 21ca8730cd5..019a811fa6d 100644 --- a/powershell/ql/lib/semmle/code/powershell/IfStmt.qll +++ b/powershell/ql/lib/semmle/code/powershell/IfStmt.qll @@ -9,6 +9,8 @@ class IfStmt extends @if_statement, Stmt { PipelineBase getCondition(int i) { if_statement_clause(this, i, result, _) } // TODO: Change @ast to @pipeline_base in dbscheme + PipelineBase getACondition() { result = this.getCondition(_) } + StmtBlock getThen(int i) { if_statement_clause(this, i, _, result) } // TODO: Change @ast to @statement_block in dbscheme /** ..., if any. */ diff --git a/powershell/ql/lib/semmle/code/powershell/LoopStmt.qll b/powershell/ql/lib/semmle/code/powershell/LoopStmt.qll index b8ac1e47206..edd9417a495 100644 --- a/powershell/ql/lib/semmle/code/powershell/LoopStmt.qll +++ b/powershell/ql/lib/semmle/code/powershell/LoopStmt.qll @@ -1,3 +1,5 @@ import powershell -class LoopStmt extends @loop_statement, LabeledStmt { } +class LoopStmt extends @loop_statement, LabeledStmt { + StmtBlock getBody() { none() } +} diff --git a/powershell/ql/lib/semmle/code/powershell/ParamBlock.qll b/powershell/ql/lib/semmle/code/powershell/ParamBlock.qll index 5fd95828883..65baf93bfdd 100644 --- a/powershell/ql/lib/semmle/code/powershell/ParamBlock.qll +++ b/powershell/ql/lib/semmle/code/powershell/ParamBlock.qll @@ -1,7 +1,7 @@ import powershell class ParamBlock extends @param_block, Ast { - override string toString() { result = "ParamBlock" } + override string toString() { result = "param(...)" } override SourceLocation getLocation() { param_block_location(this, result) } diff --git a/powershell/ql/lib/semmle/code/powershell/Pipeline.qll b/powershell/ql/lib/semmle/code/powershell/Pipeline.qll index 0e37c658115..f630a117e54 100644 --- a/powershell/ql/lib/semmle/code/powershell/Pipeline.qll +++ b/powershell/ql/lib/semmle/code/powershell/Pipeline.qll @@ -1,11 +1,15 @@ import powershell class Pipeline extends @pipeline, Chainable { - override string toString() { result = "...|..." } + override string toString() { + if this.getNumberOfComponents() = 1 + then result = this.getComponent(0).toString() + else result = "...|..." + } override SourceLocation getLocation() { pipeline_location(this, result) } - int getNumComponents() { pipeline(this, result) } + int getNumberOfComponents() { result = count(this.getAComponent()) } CmdBase getComponent(int i) { pipeline_component(this, i, result) } diff --git a/powershell/ql/lib/semmle/code/powershell/ScriptBlock.qll b/powershell/ql/lib/semmle/code/powershell/ScriptBlock.qll index d2793e076ae..975525c6cbc 100644 --- a/powershell/ql/lib/semmle/code/powershell/ScriptBlock.qll +++ b/powershell/ql/lib/semmle/code/powershell/ScriptBlock.qll @@ -1,7 +1,13 @@ import powershell class ScriptBlock extends @script_block, Ast { - override string toString() { result = this.getLocation().getFile().getBaseName() } + predicate isTopLevel() { not exists(this.getParent()) } + + override string toString() { + if this.isTopLevel() + then result = this.getLocation().getFile().getBaseName() + else result = "{...}" + } override SourceLocation getLocation() { script_block_location(this, result) } diff --git a/powershell/ql/lib/semmle/code/powershell/StatementBlock.qll b/powershell/ql/lib/semmle/code/powershell/StatementBlock.qll index 4ea36e1e9ac..74d2d36c5b2 100644 --- a/powershell/ql/lib/semmle/code/powershell/StatementBlock.qll +++ b/powershell/ql/lib/semmle/code/powershell/StatementBlock.qll @@ -3,17 +3,17 @@ import powershell class StmtBlock extends @statement_block, Ast { override SourceLocation getLocation() { statement_block_location(this, result) } - int getNumStatements() { statement_block(this, result, _) } + int getNumberOfStmts() { statement_block(this, result, _) } int getNumTraps() { statement_block(this, _, result) } - Stmt getStatement(int index) { statement_block_statement(this, index, result) } + Stmt getStmt(int index) { statement_block_statement(this, index, result) } - Stmt getAStatement() { result = this.getStatement(_) } + Stmt getAStmt() { result = this.getStmt(_) } - TrapStmt getTrapStatement(int index) { statement_block_trap(this, index, result) } + TrapStmt getTrapStmt(int index) { statement_block_trap(this, index, result) } - TrapStmt getATrapStatement() { result = this.getTrapStatement(_) } + TrapStmt getATrapStmt() { result = this.getTrapStmt(_) } - override string toString() { result = "StatementBlock at: " + this.getLocation().toString() } + override string toString() { result = "{...}" } } diff --git a/powershell/ql/lib/semmle/code/powershell/TernaryExpression.qll b/powershell/ql/lib/semmle/code/powershell/TernaryExpression.qll index 9df624d2d6c..19af31478fc 100644 --- a/powershell/ql/lib/semmle/code/powershell/TernaryExpression.qll +++ b/powershell/ql/lib/semmle/code/powershell/TernaryExpression.qll @@ -10,4 +10,14 @@ class ConditionalExpr extends @ternary_expression, Expr { Expr getIfFalse() { ternary_expression(this, _, result, _) } Expr getIfTrue() { ternary_expression(this, _, _, result) } + + Expr getBranch(boolean value) { + value = true and + result = this.getIfTrue() + or + value = false and + result = this.getIfFalse() + } + + Expr getABranch() { result = this.getBranch(_) } } diff --git a/powershell/ql/lib/semmle/code/powershell/UnaryExpression.qll b/powershell/ql/lib/semmle/code/powershell/UnaryExpression.qll new file mode 100644 index 00000000000..d7b649e85ee --- /dev/null +++ b/powershell/ql/lib/semmle/code/powershell/UnaryExpression.qll @@ -0,0 +1,15 @@ +import powershell + +class UnaryExpr extends @unary_expression, Expr { + override SourceLocation getLocation() { unary_expression_location(this, result) } + + int getKind() { unary_expression(this, _, result, _) } + + Expr getOperand() { unary_expression(this, result, _, _) } +} + +class NotExpr extends UnaryExpr { + NotExpr() { this.getKind() = 36 } + + final override string toString() { result = "!..." } +} diff --git a/powershell/ql/lib/semmle/code/powershell/WhileStmt.qll b/powershell/ql/lib/semmle/code/powershell/WhileStmt.qll index 1b34927956e..4f716f6be04 100644 --- a/powershell/ql/lib/semmle/code/powershell/WhileStmt.qll +++ b/powershell/ql/lib/semmle/code/powershell/WhileStmt.qll @@ -7,5 +7,5 @@ class WhileStmt extends @while_statement, LoopStmt { PipelineBase getCondition() { while_statement_condition(this, result) } // TODO: Change @ast to @pipeline_base in dbscheme - StmtBlock getBody() { while_statement(this, result) } // TODO: Change @ast to @stmt_block in dbscheme + final override StmtBlock getBody() { while_statement(this, result) } // TODO: Change @ast to @stmt_block in dbscheme } diff --git a/powershell/ql/lib/semmle/code/powershell/controlflow/ControlFlowGraph.qll b/powershell/ql/lib/semmle/code/powershell/controlflow/ControlFlowGraph.qll index 18c55a3fa2f..8c04fe644c8 100644 --- a/powershell/ql/lib/semmle/code/powershell/controlflow/ControlFlowGraph.qll +++ b/powershell/ql/lib/semmle/code/powershell/controlflow/ControlFlowGraph.qll @@ -90,6 +90,10 @@ module SuccessorTypes { final override string toString() { result = "break" } } + class ContinueSuccessor extends SuccessorType, CfgImpl::TContinueSuccessor { + final override string toString() { result = "continue" } + } + class RaiseSuccessor extends SuccessorType, CfgImpl::TRaiseSuccessor { final override string toString() { result = "raise" } } diff --git a/powershell/ql/lib/semmle/code/powershell/controlflow/internal/Completion.qll b/powershell/ql/lib/semmle/code/powershell/controlflow/internal/Completion.qll index a7726d1a6f3..858779d50cd 100644 --- a/powershell/ql/lib/semmle/code/powershell/controlflow/internal/Completion.qll +++ b/powershell/ql/lib/semmle/code/powershell/controlflow/internal/Completion.qll @@ -15,11 +15,18 @@ private newtype TCompletion = TBooleanCompletion(boolean b) { b in [false, true] } or TReturnCompletion() or TBreakCompletion() or + TContinueCompletion() or TRaiseCompletion() or TExitCompletion() pragma[noinline] -private predicate completionIsValidForStmt(Ast n, Completion c) { none() } +private predicate completionIsValidForStmt(Ast n, Completion c) { + n instanceof BreakStmt and + c instanceof BreakCompletion + or + n instanceof ContinueStmt and + c instanceof ContinueCompletion +} /** A completion of a statement or an expression. */ abstract class Completion extends TCompletion { @@ -49,19 +56,10 @@ abstract class Completion extends TCompletion { * Holds if this completion will continue a loop when it is the completion * of a loop body. */ - predicate continuesLoop() { this instanceof NormalCompletion } - - /** - * Gets the inner completion. This is either the inner completion, - * when the completion is nested, or the completion itself. - */ - Completion getInnerCompletion() { result = this } - - /** - * Gets the outer completion. This is either the outer completion, - * when the completion is nested, or the completion itself. - */ - Completion getOuterCompletion() { result = this } + predicate continuesLoop() { + this instanceof NormalCompletion or + this instanceof ContinueCompletion + } /** Gets a successor type that matches this completion. */ abstract SuccessorType getAMatchingSuccessorType(); @@ -85,7 +83,50 @@ private predicate mustHaveBooleanCompletion(Ast n) { inBooleanContext(n) } * Holds if `n` is used in a Boolean context. That is, the value * that `n` evaluates to determines a true/false branch successor. */ -private predicate inBooleanContext(Ast n) { none() } +private predicate inBooleanContext(Ast n) { + n = any(IfStmt ifStmt).getACondition() + or + n = any(WhileStmt whileStmt).getCondition() + or + n = any(DoWhileStmt doWhileStmt).getCondition() + or + n = any(ForStmt forStmt).getCondition() + or + n = any(DoUntilStmt doUntilStmt).getCondition() + or + exists(ConditionalExpr cond | + n = cond.getCondition() + or + inBooleanContext(cond) and + n = cond.getABranch() + ) + or + exists(LogicalAndExpr parent | + n = parent.getLeft() + or + inBooleanContext(parent) and + n = parent.getRight() + ) + or + exists(LogicalOrExpr parent | + n = parent.getLeft() + or + inBooleanContext(parent) and + n = parent.getRight() + ) + or + n = any(NotExpr parent | inBooleanContext(parent)).getOperand() + or + exists(Pipeline pipeline | + inBooleanContext(pipeline) and + n = pipeline.getComponent(pipeline.getNumberOfComponents() - 1) + ) + or + exists(CmdExpr cmdExpr | + inBooleanContext(cmdExpr) and + n = cmdExpr.getExpr() + ) +} /** * A completion that represents normal evaluation of a statement or an @@ -159,6 +200,16 @@ class BreakCompletion extends Completion, TBreakCompletion { override string toString() { result = "break" } } +/** + * A completion that represents evaluation of a statement or an + * expression resulting in a continuation of a loop. + */ +class ContinueCompletion extends Completion, TContinueCompletion { + override ContinueSuccessor getAMatchingSuccessorType() { any() } + + override string toString() { result = "continue" } +} + /** * A completion that represents evaluation of a statement or an * expression resulting in a thrown exception. 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 8f19015426f..6d6dac7603f 100644 --- a/powershell/ql/lib/semmle/code/powershell/controlflow/internal/ControlFlowGraphImpl.qll +++ b/powershell/ql/lib/semmle/code/powershell/controlflow/internal/ControlFlowGraphImpl.qll @@ -75,8 +75,29 @@ predicate succExit(CfgScope scope, Ast last, Completion c) { scope.exit(last, c) /** Defines the CFG by dispatch on the various AST types. */ module Trees { - class ScriptBlockTree extends PreOrderTree instanceof ScriptBlock { - final override predicate last(AstNode last, Completion c) { + class NonDefaultParameterTree extends LeafTree instanceof Parameter { + NonDefaultParameterTree() { not exists(super.getDefaultValue()) } + } + + class DefaultParameterTree extends StandardPostOrderTree instanceof Parameter { + DefaultParameterTree() { exists(super.getDefaultValue()) } + + override AstNode getChildNode(int i) { + i = 0 and + result = super.getDefaultValue() + } + + final override predicate propagatesAbnormal(AstNode child) { child = super.getDefaultValue() } + } + + class ParameterBlockTree extends StandardPostOrderTree instanceof ParamBlock { + override AstNode getChildNode(int i) { result = super.getParameter(i) } + } + + abstract class ScriptBlockTree extends ControlFlowTree instanceof ScriptBlock { + abstract predicate succEntry(AstNode n, Completion c); + + override predicate last(AstNode last, Completion c) { last(super.getEndBlock(), last, c) or not exists(super.getEndBlock()) and @@ -89,16 +110,36 @@ module Trees { not exists(super.getEndBlock()) and not exists(super.getProcessBlock()) and not exists(super.getBeginBlock()) and - last = this and - completionIsSimple(c) + last(super.getParamBlock(), last, c) + or + not exists(super.getEndBlock()) and + not exists(super.getProcessBlock()) and + not exists(super.getBeginBlock()) and + not exists(super.getParamBlock()) and + // No blocks at all. We end where we started + this.succEntry(last, c) } - final override predicate propagatesAbnormal(AstNode child) { - child = [super.getBeginBlock(), super.getProcessBlock(), super.getEndBlock()] - } - - final override predicate succ(AstNode pred, AstNode succ, Completion c) { - pred = this and + override predicate succ(AstNode pred, AstNode succ, Completion c) { + this.succEntry(pred, c) and + ( + first(super.getParamBlock(), succ) + or + not exists(super.getParamBlock()) and + first(super.getBeginBlock(), succ) + or + not exists(super.getParamBlock()) and + not exists(super.getBeginBlock()) and + first(super.getProcessBlock(), succ) + or + not exists(super.getParamBlock()) and + not exists(super.getBeginBlock()) and + not exists(super.getProcessBlock()) and + first(super.getEndBlock(), succ) + ) + or + last(super.getParamBlock(), pred, c) and + completionIsNormal(c) and ( first(super.getBeginBlock(), succ) or @@ -108,24 +149,19 @@ module Trees { not exists(super.getBeginBlock()) and not exists(super.getProcessBlock()) and first(super.getEndBlock(), succ) - ) and - completionIsSimple(c) + ) or last(super.getBeginBlock(), pred, c) and - c instanceof NormalCompletion and + completionIsNormal(c) and ( first(super.getProcessBlock(), succ) or not exists(super.getProcessBlock()) and first(super.getEndBlock(), succ) - or - not exists(super.getProcessBlock()) and - not exists(super.getEndBlock()) and - succ = this ) or last(super.getProcessBlock(), pred, c) and - c instanceof NormalCompletion and + completionIsNormal(c) and ( // If we process multiple items we will loop back to the process block first(super.getProcessBlock(), succ) @@ -134,6 +170,57 @@ module Trees { first(super.getEndBlock(), succ) ) } + + final override predicate propagatesAbnormal(AstNode child) { + child = super.getParamBlock() or + child = super.getBeginBlock() or + child = super.getProcessBlock() or + child = super.getEndBlock() + } + } + + class FunctionScriptBlockTree extends PreOrderTree, ScriptBlockTree { + Function func; + + FunctionScriptBlockTree() { func.getBody() = this } + + AstNode getParameter(int i) { result = func.getFunctionParameter(i) } + + int getNumberOfParameters() { result = func.getNumberOfFunctionParameters() } + + override predicate succ(AstNode pred, AstNode succ, Completion c) { + // Step to the first parameter + pred = this and + first(this.getParameter(0), succ) and + completionIsSimple(c) + or + // Step to the next parameter + exists(int i | + last(this.getParameter(i), pred, c) and + completionIsNormal(c) and + first(this.getParameter(i + 1), succ) + ) + or + // Body steps + super.succ(pred, succ, c) + } + + final override predicate succEntry(AstNode n, Completion c) { + // If there are no paramters we enter the body directly + not exists(this.getParameter(0)) and + n = this and + completionIsSimple(c) + or + // Once we are done with the last parameter we enter the body + last(this.getParameter(this.getNumberOfParameters() - 1), n, c) and + completionIsNormal(c) + } + } + + class TopLevelScriptBlockTree extends PreOrderTree, ScriptBlockTree { + TopLevelScriptBlockTree() { this.(ScriptBlock).isTopLevel() } + + final override predicate succEntry(Ast n, Completion c) { n = this and completionIsSimple(c) } } class NamedBlockTree extends StandardPostOrderTree instanceof NamedBlock { @@ -149,6 +236,192 @@ module Trees { } } + abstract class LoopStmtTree extends ControlFlowTree instanceof LoopStmt { + final AstNode getBody() { result = super.getBody() } + + override predicate last(AstNode last, Completion c) { + // Exit the loop body when we encounter a break + last(this.getBody(), last, c) and + c instanceof BreakCompletion + or + // Body exits abnormally + last(this.getBody(), last, c) and + not c instanceof BreakCompletion and + not c.continuesLoop() + } + } + + abstract class ConditionalLoopStmtTree extends PreOrderTree, LoopStmtTree { + abstract AstNode getCondition(); + + abstract predicate entersLoopWhenConditionIs(boolean value); + + final override predicate propagatesAbnormal(AstNode child) { child = this.getCondition() } + + override predicate last(AstNode last, Completion c) { + // Exit the loop body when the condition is false + last(this.getCondition(), last, c) and + this.entersLoopWhenConditionIs(c.(BooleanCompletion).getValue().booleanNot()) + or + super.last(last, c) + } + + override predicate succ(AstNode pred, AstNode succ, Completion c) { + // Condition -> body + last(this.getCondition(), pred, c) and + this.entersLoopWhenConditionIs(c.(BooleanCompletion).getValue()) and + first(this.getBody(), succ) + or + // Body -> condition + last(this.getBody(), pred, c) and + c.continuesLoop() and + first(this.getCondition(), succ) + } + } + + class WhileStmtTree extends ConditionalLoopStmtTree instanceof WhileStmt { + override predicate entersLoopWhenConditionIs(boolean value) { value = true } + + override AstNode getCondition() { result = WhileStmt.super.getCondition() } + + final override predicate succ(AstNode pred, AstNode succ, Completion c) { + // Start with the condition + this = pred and + first(this.getCondition(), succ) and + completionIsSimple(c) + or + super.succ(pred, succ, c) + } + } + + abstract class DoBasedLoopStmtTree extends ConditionalLoopStmtTree { + final override predicate succ(AstNode pred, AstNode succ, Completion c) { + // Start with the body + this = pred and + first(this.getBody(), succ) and + completionIsSimple(c) + or + super.succ(pred, succ, c) + } + } + + class DoWhileStmtTree extends DoBasedLoopStmtTree instanceof DoWhileStmt { + override AstNode getCondition() { result = DoWhileStmt.super.getCondition() } + + override predicate entersLoopWhenConditionIs(boolean value) { value = true } + } + + class DoUntilStmtTree extends DoBasedLoopStmtTree instanceof DoUntilStmt { + override AstNode getCondition() { result = DoUntilStmt.super.getCondition() } + + override predicate entersLoopWhenConditionIs(boolean value) { value = false } + } + + class ForStmtTree extends PreOrderTree, LoopStmtTree instanceof ForStmt { + final override predicate propagatesAbnormal(AstNode child) { + child = [super.getInitializer(), super.getIterator()] + } + + override predicate last(AstNode last, Completion c) { + // Condition returns false + last(super.getCondition(), last, c) and + c instanceof FalseCompletion + or + super.last(last, c) + } + + final override predicate succ(AstNode pred, AstNode succ, Completion c) { + // Start with initialization + this = pred and + first(super.getInitializer(), succ) and + completionIsSimple(c) + or + // Initialization -> condition + last(super.getInitializer(), pred, c) and + completionIsNormal(c) and + first(super.getCondition(), succ) + or + // Condition -> body + last(super.getCondition(), pred, c) and + c instanceof TrueCompletion and + first(this.getBody(), succ) + or + // Body -> iterator + last(this.getBody(), pred, c) and + completionIsNormal(c) and + first(super.getIterator(), succ) + } + } + + class ForEachStmtTree extends LoopStmtTree instanceof ForEachStmt { + final override predicate propagatesAbnormal(AstNode child) { child = super.getIterableExpr() } + + final override predicate first(AstNode first) { + // Unlike most other statements, `foreach` statements are not modeled in + // pre-order, because we use the `foreach` node itself to represent the + // emptiness test that determines whether to execute the loop body + first(super.getIterableExpr(), first) + } + + final override predicate last(AstNode last, Completion c) { + // Emptiness test exits with no more elements + last = this and + completionIsSimple(c) + or + super.last(last, c) + } + + final override predicate succ(AstNode pred, AstNode succ, Completion c) { + // Emptiness test + last(super.getIterableExpr(), pred, c) and + completionIsNormal(c) and + succ = this + or + // Emptiness test to variable declaration + pred = this and + first(super.getVariable(), succ) and + completionIsSimple(c) + or + // Variable declaration to body + last(super.getVariable(), succ, c) and + completionIsNormal(c) and + first(this.getBody(), succ) + or + // Body to emptiness test + last(this.getBody(), pred, c) and + c.continuesLoop() and + succ = this + } + } + + class StmtBlockTree extends PreOrderTree instanceof StmtBlock { + final override predicate propagatesAbnormal(AstNode child) { child = super.getAStmt() } + + final override predicate last(AstNode last, Completion c) { + last(super.getStmt(super.getNumberOfStmts() - 1), last, c) + or + not exists(super.getAStmt()) and + last = this and + completionIsSimple(c) + } + + final override predicate succ(AstNode pred, AstNode succ, Completion c) { + this = pred and + first(super.getStmt(0), succ) and + completionIsSimple(c) + or + exists(int i | + last(super.getStmt(i), pred, c) and + completionIsNormal(c) and + first(super.getStmt(i + 1), succ) + ) + } + } + + class GotoStmtTree extends LeafTree instanceof GotoStmt { } + + class FunctionStmtTree extends LeafTree instanceof Function { } + class VarAccessTree extends LeafTree instanceof VarAccess { } class BinaryExprTree extends StandardPostOrderTree instanceof BinaryExpr { @@ -159,11 +432,23 @@ module Trees { } } + class UnaryExprTree extends StandardPostOrderTree instanceof UnaryExpr { + override AstNode getChildNode(int i) { i = 0 and result = super.getOperand() } + } + + class ArrayLiteralTree extends StandardPostOrderTree instanceof ArrayLiteral { + override AstNode getChildNode(int i) { result = super.getElement(i) } + } + class ConstExprTree extends LeafTree instanceof ConstExpr { } class CmdExprTree extends StandardPreOrderTree instanceof CmdExpr { override AstNode getChildNode(int i) { i = 0 and result = super.getExpr() } } + + class PipelineTree extends StandardPreOrderTree instanceof Pipeline { + override AstNode getChildNode(int i) { result = super.getComponent(i) } + } } private import Scope @@ -188,6 +473,7 @@ private module Cached { TBooleanSuccessor(boolean b) { b in [false, true] } or TReturnSuccessor() or TBreakSuccessor() or + TContinueSuccessor() or TRaiseSuccessor() or TExitSuccessor() } diff --git a/powershell/ql/test/library-tests/controlflow/graph/Cfg.expected b/powershell/ql/test/library-tests/controlflow/graph/Cfg.expected index 306b95b03ac..cf644265bbd 100644 --- a/powershell/ql/test/library-tests/controlflow/graph/Cfg.expected +++ b/powershell/ql/test/library-tests/controlflow/graph/Cfg.expected @@ -1,3 +1,260 @@ +functions.ps1: +# 1| Add-Numbers-Arguments +#-----| -> foo + +# 1| {...} +#-----| -> exit functions.ps1 (normal) + +# 1| enter functions.ps1 +#-----| -> functions.ps1 + +# 1| exit functions.ps1 + +# 1| exit functions.ps1 (normal) +#-----| -> exit functions.ps1 + +# 1| functions.ps1 +#-----| -> Add-Numbers-Arguments + +# 1| enter {...} +#-----| -> {...} + +# 1| exit {...} + +# 1| exit {...} (normal) +#-----| -> exit {...} + +# 1| {...} +#-----| -> number1 + +# 3| param(...) +#-----| -> ...+... + +# 3| {...} +#-----| -> exit {...} (normal) + +# 4| number1 +#-----| -> number2 + +# 5| number2 +#-----| -> param(...) + +# 8| number1 +#-----| -> number2 + +# 8| ...+... +#-----| -> number1 + +# 8| ...+... +#-----| -> {...} + +# 8| ...+... +#-----| -> ...+... + +# 8| number2 +#-----| -> ...+... + +# 11| foo +#-----| -> Default-Arguments + +# 11| enter {...} +#-----| -> {...} + +# 11| exit {...} + +# 11| exit {...} (normal) +#-----| -> exit {...} + +# 11| {...} +#-----| -> a + +# 11| param(...) +#-----| -> {...} + +# 11| {...} +#-----| -> exit {...} (normal) + +# 11| a +#-----| -> param(...) + +# 13| Default-Arguments +#-----| -> Add-Numbers-From-Array + +# 13| enter {...} +#-----| -> {...} + +# 13| exit {...} + +# 13| exit {...} (normal) +#-----| -> exit {...} + +# 13| {...} +#-----| -> name0 + +# 14| param(...) +#-----| -> ...+... + +# 14| {...} +#-----| -> exit {...} (normal) + +# 15| name0 +#-----| -> 0 + +# 16| name1 +#-----| -> name1 + +# 16| 0 +#-----| -> name1 + +# 17| name2 +#-----| -> param(...) + +# 17| name1 +#-----| -> 1 + +# 17| ...+... +#-----| -> name2 + +# 17| 1 +#-----| -> ...+... + +# 19| name +#-----| -> name2 + +# 19| ...+... +#-----| -> name + +# 19| ...+... +#-----| -> {...} + +# 19| ...+... +#-----| -> ...+... + +# 19| name2 +#-----| -> ...+... + +# 22| Add-Numbers-From-Array +#-----| -> Add-Numbers-From-Pipeline + +# 22| enter {...} +#-----| -> {...} + +# 22| exit {...} + +# 22| exit {...} (normal) +#-----| -> exit {...} + +# 22| {...} +#-----| -> numbers + +# 24| param(...) +#-----| -> sum + +# 24| {...} +#-----| -> exit {...} (normal) + +# 25| numbers +#-----| -> param(...) + +# 28| sum +#-----| -> 0 + +# 28| ...=... +#-----| -> numbers + +# 28| 0 +#-----| -> ...=... + +# 28| 0 +#-----| -> 0 + +# 29| forach(... in ...) +#-----| -> number +#-----| -> sum + +# 29| number + +# 29| numbers +#-----| -> numbers + +# 29| numbers +#-----| -> forach(... in ...) + +# 29| numbers +#-----| -> numbers + +# 33| sum +#-----| -> sum + +# 33| sum +#-----| -> {...} + +# 33| sum +#-----| -> sum + +# 36| Add-Numbers-From-Pipeline +#-----| -> {...} + +# 36| enter {...} +#-----| -> {...} + +# 36| exit {...} + +# 36| exit {...} (normal) +#-----| -> exit {...} + +# 36| {...} +#-----| -> numbers + +# 38| param(...) +#-----| -> sum + +# 39| numbers +#-----| -> param(...) + +# 41| {...} +#-----| -> sum + +# 42| sum +#-----| -> 0 + +# 42| ...=... +#-----| -> {...} + +# 42| 0 +#-----| -> ...=... + +# 42| 0 +#-----| -> 0 + +# 44| {...} +#-----| -> sum +#-----| -> sum + +# 46| sum +#-----| -> _ + +# 46| ...=... +#-----| -> {...} + +# 46| _ +#-----| -> ...=... + +# 46| _ +#-----| -> _ + +# 48| {...} +#-----| -> exit {...} (normal) + +# 50| sum +#-----| -> sum + +# 50| sum +#-----| -> {...} + +# 50| sum +#-----| -> sum + global.ps1: # 1| {...} #-----| -> c @@ -57,3 +314,445 @@ global.ps1: # 6| b #-----| -> ...+... + +loops.ps1: +# 1| Test-While +#-----| -> Test-Break + +# 1| enter loops.ps1 +#-----| -> loops.ps1 + +# 1| exit loops.ps1 + +# 1| exit loops.ps1 (normal) +#-----| -> exit loops.ps1 + +# 1| loops.ps1 +#-----| -> Test-While + +# 1| {...} +#-----| -> exit loops.ps1 (normal) + +# 1| enter {...} +#-----| -> {...} + +# 1| exit {...} + +# 1| exit {...} (normal) +#-----| -> exit {...} + +# 1| {...} +#-----| -> a + +# 2| a +#-----| -> 0 + +# 2| ...=... +#-----| -> while(...) {...} + +# 2| {...} +#-----| -> exit {...} (normal) + +# 2| 0 +#-----| -> ...=... + +# 2| 0 +#-----| -> 0 + +# 4| while(...) {...} +#-----| -> ... -le ... + +# 4| a +#-----| -> 10 + +# 4| ... -le ... +#-----| -> a + +# 4| ... -le ... +#-----| false -> {...} +#-----| true -> {...} + +# 4| ... -le ... +#-----| -> ... -le ... + +# 4| 10 +#-----| -> ... -le ... + +# 4| {...} +#-----| -> a + +# 5| a +#-----| -> ...+... + +# 5| ...=... +#-----| -> ... -le ... + +# 5| a +#-----| -> 1 + +# 5| ...+... +#-----| -> ...=... + +# 5| ...+... +#-----| -> a + +# 5| 1 +#-----| -> ...+... + +# 9| Test-Break +#-----| -> Test-Continue + +# 9| enter {...} +#-----| -> {...} + +# 9| exit {...} + +# 9| exit {...} (normal) +#-----| -> exit {...} + +# 9| {...} +#-----| -> a + +# 10| a +#-----| -> 0 + +# 10| ...=... +#-----| -> while(...) {...} + +# 10| {...} +#-----| -> exit {...} (normal) + +# 10| 0 +#-----| -> ...=... + +# 10| 0 +#-----| -> 0 + +# 11| while(...) {...} +#-----| -> ... -le ... + +# 11| a +#-----| -> 10 + +# 11| ... -le ... +#-----| -> a + +# 11| ... -le ... +#-----| false -> {...} +#-----| true -> {...} + +# 11| ... -le ... +#-----| -> ... -le ... + +# 11| 10 +#-----| -> ... -le ... + +# 11| {...} +#-----| -> break + +# 12| break +#-----| break -> exit {...} (normal) + +# 17| Test-Continue +#-----| -> Test-DoWhile + +# 17| enter {...} +#-----| -> {...} + +# 17| exit {...} + +# 17| exit {...} (normal) +#-----| -> exit {...} + +# 17| {...} +#-----| -> a + +# 18| a +#-----| -> 0 + +# 18| ...=... +#-----| -> while(...) {...} + +# 18| {...} +#-----| -> exit {...} (normal) + +# 18| 0 +#-----| -> ...=... + +# 18| 0 +#-----| -> 0 + +# 19| while(...) {...} +#-----| -> ... -le ... + +# 19| a +#-----| -> 10 + +# 19| ... -le ... +#-----| -> a + +# 19| ... -le ... +#-----| false -> {...} +#-----| true -> {...} + +# 19| ... -le ... +#-----| -> ... -le ... + +# 19| 10 +#-----| -> ... -le ... + +# 19| {...} +#-----| -> continue + +# 20| continue +#-----| continue -> ... -le ... + +# 25| Test-DoWhile +#-----| -> Test-DoUntil + +# 25| enter {...} +#-----| -> {...} + +# 25| exit {...} + +# 25| exit {...} (normal) +#-----| -> exit {...} + +# 25| {...} +#-----| -> a + +# 26| a +#-----| -> 0 + +# 26| ...=... +#-----| -> DoWhile + +# 26| {...} +#-----| -> exit {...} (normal) + +# 26| 0 +#-----| -> ...=... + +# 26| 0 +#-----| -> 0 + +# 28| DoWhile +#-----| -> {...} + +# 28| {...} +#-----| -> a + +# 29| a +#-----| -> ...+... + +# 29| ...=... +#-----| -> ... -le ... + +# 29| a +#-----| -> 1 + +# 29| ...+... +#-----| -> ...=... + +# 29| ...+... +#-----| -> a + +# 29| 1 +#-----| -> ...+... + +# 30| a +#-----| -> 10 + +# 30| ... -le ... +#-----| -> a + +# 30| ... -le ... +#-----| false -> {...} +#-----| true -> {...} + +# 30| ... -le ... +#-----| -> ... -le ... + +# 30| 10 +#-----| -> ... -le ... + +# 33| Test-DoUntil +#-----| -> Tet-For + +# 33| enter {...} +#-----| -> {...} + +# 33| exit {...} + +# 33| exit {...} (normal) +#-----| -> exit {...} + +# 33| {...} +#-----| -> a + +# 34| a +#-----| -> 0 + +# 34| ...=... +#-----| -> DoUntil + +# 34| {...} +#-----| -> exit {...} (normal) + +# 34| 0 +#-----| -> ...=... + +# 34| 0 +#-----| -> 0 + +# 36| DoUntil +#-----| -> {...} + +# 36| {...} +#-----| -> a + +# 37| a +#-----| -> ...+... + +# 37| ...=... +#-----| -> ... -ge ... + +# 37| a +#-----| -> 1 + +# 37| ...+... +#-----| -> ...=... + +# 37| ...+... +#-----| -> a + +# 37| 1 +#-----| -> ...+... + +# 38| a +#-----| -> 10 + +# 38| ... -ge ... +#-----| -> a + +# 38| ... -ge ... +#-----| true -> {...} +#-----| false -> {...} + +# 38| ... -ge ... +#-----| -> ... -ge ... + +# 38| 10 +#-----| -> ... -ge ... + +# 41| Tet-For +#-----| -> Test-ForEach + +# 41| enter {...} +#-----| -> {...} + +# 41| exit {...} + +# 41| exit {...} (normal) +#-----| -> exit {...} + +# 41| {...} +#-----| -> a + +# 42| a +#-----| -> 0 + +# 42| ...=... +#-----| -> for(...;...;...) + +# 42| {...} +#-----| -> exit {...} (normal) + +# 42| 0 +#-----| -> ...=... + +# 42| 0 +#-----| -> 0 + +# 44| for(...;...;...) +#-----| -> i + +# 44| i +#-----| -> 0 + +# 44| ...=... +#-----| -> ... -le ... + +# 44| 0 +#-----| -> ...=... + +# 44| 0 +#-----| -> 0 + +# 44| i +#-----| -> 10 + +# 44| ... -le ... +#-----| -> i + +# 44| ... -le ... +#-----| false -> {...} +#-----| true -> {...} + +# 44| ... -le ... +#-----| -> ... -le ... + +# 44| 10 +#-----| -> ... -le ... + +# 44| i +#-----| -> ...+... + +# 44| ...=... + +# 44| i +#-----| -> 1 + +# 44| ...+... +#-----| -> ...=... + +# 44| ...+... +#-----| -> i + +# 44| 1 +#-----| -> ...+... + +# 44| {...} +#-----| -> a + +# 45| a +#-----| -> ...+... + +# 45| ...=... +#-----| -> i + +# 45| a +#-----| -> 1 + +# 45| ...+... +#-----| -> ...=... + +# 45| ...+... +#-----| -> a + +# 45| 1 +#-----| -> ...+... + +# 49| Test-ForEach +#-----| -> {...} + +# 49| enter {...} +#-----| -> {...} + +# 49| {...} +#-----| -> letterArray + +# 50| letterArray +#-----| -> ...,... + +# 50| ...,... diff --git a/powershell/ql/test/library-tests/controlflow/graph/functions.ps1 b/powershell/ql/test/library-tests/controlflow/graph/functions.ps1 new file mode 100644 index 00000000000..e9020634925 --- /dev/null +++ b/powershell/ql/test/library-tests/controlflow/graph/functions.ps1 @@ -0,0 +1,53 @@ +Function Add-Numbers-Arguments { + # We take in two numbers + param( + [int] $number1, + [int] $number2 + ) + # We add them together + $number1 + $number2 +} + +function foo() { param($a) } + +Function Default-Arguments { + param( + [int] $name0, + [int] $name1 = 0, + [int] $name2 = $name1 + 1 + ) + $name + $name2 +} + +Function Add-Numbers-From-Array { + # We take in a list of numbers + param( + [int[]] $numbers + ) + + $sum = 0 + foreach ($number in $numbers) { + # We add each number to the sum + $sum += $number + } + $sum +} + +Function Add-Numbers-From-Pipeline { + # We take in a list of numbers + param( + [int[]] $numbers + ) + Begin { + $sum = 0 + } + Process { + # We add each number to the sum + $sum += $_ + } + End { + # We return the sum + $sum + } +} + diff --git a/powershell/ql/test/library-tests/controlflow/graph/loops.ps1 b/powershell/ql/test/library-tests/controlflow/graph/loops.ps1 new file mode 100644 index 00000000000..6f0f5eded6e --- /dev/null +++ b/powershell/ql/test/library-tests/controlflow/graph/loops.ps1 @@ -0,0 +1,56 @@ +function Test-While { + $a = 0 + + while($a -le 10) { + $a = $a + 1 + } +} + +function Test-Break { + $a = 0 + while($a -le 10) { + break + $a = $a + 1 + } +} + +function Test-Continue { + $a = 0 + while($a -le 10) { + continue + $a = $a + 1 + } +} + +function Test-DoWhile { + $a = 0 + + do { + $a = $a + 1 + } while ($a -le 10) +} + +function Test-DoUntil { + $a = 0 + + do { + $a = $a + 1 + } until ($a -ge 10) +} + +function Tet-For { + $a = 0 + + for ($i = 0; $i -le 10; $i = $i + 1) { + $a = $a + 1 + } +} + +function Test-ForEach { + $letterArray = 'a','b','c','d' + $a = 0 + foreach ($letter in $letterArray) + { + $a = $a + 1 + } +} \ No newline at end of file