diff --git a/ql/lib/codeql/bicep/ast/Calls.qll b/ql/lib/codeql/bicep/ast/Calls.qll index 097f913..65a7f28 100644 --- a/ql/lib/codeql/bicep/ast/Calls.qll +++ b/ql/lib/codeql/bicep/ast/Calls.qll @@ -6,6 +6,8 @@ private import Misc private import internal.Calls private import internal.CallExpression private import internal.LambdaExpression +private import internal.UserDefinedFunction +private import internal.Type /** * Represents a callable expression in the AST, such as a function or method call. @@ -105,8 +107,8 @@ class CallExpression extends Call instanceof CallExpressionImpl { * filters, or other functional programming patterns. A lambda expression * consists of parameters and a body that defines the computation to be performed. */ -class LambdaExpression extends Expr instanceof LambdaExpressionImpl { - Idents getIdentifier() { none() } +class LambdaExpression extends Callable instanceof LambdaExpressionImpl { + override Idents getIdentifier() { none() } /** * Gets the parameters of this lambda expression. @@ -137,3 +139,55 @@ class LambdaExpression extends Expr instanceof LambdaExpressionImpl { */ int getNumberOfParameters() { result = LambdaExpressionImpl.super.getNumberOfParameters() } } + +/** + * A user-defined function in the AST. + * + * Represents a function declaration in Bicep, which can be called by name + * from other parts of the code. User-defined functions consist of a name, + * parameters, return type, and a body that defines the computation. + */ +class UserDefinedFunction extends Callable instanceof UserDefinedFunctionImpl { + /** + * Gets the identifier of this user-defined function. + * + * @return The identifier node of the function + */ + override Idents getIdentifier() { result = UserDefinedFunctionImpl.super.getIdentifier() } + + /** + * Gets the parameters of this user-defined function. + * + * @return The parameters node of the function + */ + Expr getParameters() { result = UserDefinedFunctionImpl.super.getParameters() } + + /** + * Gets the parameter at the specified index. + * + * @param n The index of the parameter to retrieve + * @return The parameter at the specified index + */ + Expr getParameter(int n) { result = UserDefinedFunctionImpl.super.getParameter(n) } + + /** + * Gets the return type of this user-defined function. + * + * @return The return type node of the function + */ + Type getReturnType() { result = UserDefinedFunctionImpl.super.getReturnType() } + + /** + * Gets the body of this user-defined function. + * + * @return The body node of the function + */ + Stmts getBody() { result = UserDefinedFunctionImpl.super.getBody() } + + /** + * Gets the number of parameters in this user-defined function. + * + * @return The number of parameters + */ + int getNumberOfParameters() { result = UserDefinedFunctionImpl.super.getNumberOfParameters() } +} diff --git a/ql/lib/codeql/bicep/ast/Stmts.qll b/ql/lib/codeql/bicep/ast/Stmts.qll index ff4c324..abb526a 100644 --- a/ql/lib/codeql/bicep/ast/Stmts.qll +++ b/ql/lib/codeql/bicep/ast/Stmts.qll @@ -21,7 +21,6 @@ private import internal.Parameter private import internal.Parameters private import internal.ParameterDeclaration private import internal.OutputDeclaration -private import internal.UserDefinedFunction // CFG private import codeql.bicep.CFG private import codeql.bicep.controlflow.internal.ControlFlowGraphImpl as CfgImpl @@ -262,78 +261,6 @@ class OutputDeclaration extends Stmts instanceof OutputDeclarationImpl { Expr getValue() { result = OutputDeclarationImpl.super.getValue() } } -/** - * Represents a user-defined function in the AST. - * - * User-defined functions allow creating reusable pieces of logic in Bicep templates. - * They encapsulate calculations or transformations that can be called from multiple - * places in the template, promoting code reuse and maintainability. - */ -class UserDefinedFunction extends Stmts instanceof UserDefinedFunctionImpl { - /** - * Gets the identifier of the user-defined function. - * - * This is the name token of the function as it appears in the source code. - * - * @return The identifier node of the function - */ - Identifier getIdentifier() { result = UserDefinedFunctionImpl.super.getName() } - - /** - * Gets the name of the user-defined function as a string. - * - * This is a convenience method that returns the name from the identifier. - * - * @return The name of the function - */ - string getName() { result = this.getIdentifier().getName() } - - /** - * Gets the return type of the user-defined function. - * - * This specifies what kind of value the function returns, - * such as 'string', 'int', 'bool', or more complex types. - * - * @return The return type node of the function - */ - Type getReturnType() { result = UserDefinedFunctionImpl.super.getReturnType() } - - /** - * Gets the declared parameters node of the user-defined function. - * - * This contains all the parameter declarations for the function. - * - * @return The parameters node of the function - */ - Parameters getDeclaredParameters() { result = UserDefinedFunctionImpl.super.getParameters() } - - /** - * Gets all individual parameters of the user-defined function. - * - * @return All parameter nodes of the function - */ - Parameter getParameters() { result = this.getDeclaredParameters().getParameter(_) } - - /** - * Gets the parameter at the specified index. - * - * @param index The index of the parameter to retrieve - * @return The parameter at the specified index - */ - Parameter getParameter(int index) { result = this.getDeclaredParameters().getParameter(index) } - - /** - * Gets the body of the user-defined function. - * - * This is the expression that defines the computation performed by the function. - * When the function is called, this expression is evaluated with the provided - * argument values bound to the function parameters. - * - * @return The body expression of the function - */ - Expr getBody() { result = UserDefinedFunctionImpl.super.getBody() } -} - /** * Represents a function parameter node in the AST. * diff --git a/ql/lib/codeql/bicep/ast/internal/UserDefinedFunction.qll b/ql/lib/codeql/bicep/ast/internal/UserDefinedFunction.qll index ff816f8..d0060e4 100644 --- a/ql/lib/codeql/bicep/ast/internal/UserDefinedFunction.qll +++ b/ql/lib/codeql/bicep/ast/internal/UserDefinedFunction.qll @@ -1,23 +1,22 @@ /** * Internal implementation for UserDefinedFunction - * - * WARNING: this file is generated, do not edit manually */ private import AstNodes private import TreeSitter private import codeql.bicep.ast.AstNodes +private import Calls private import Stmts private import Identifier -private import Stmts private import Type private import Parameter private import Parameters +private import Expr /** * A UserDefinedFunction AST Node. */ -class UserDefinedFunctionImpl extends TUserDefinedFunction, StmtsImpl { +class UserDefinedFunctionImpl extends TUserDefinedFunction, CallableImpl { private BICEP::UserDefinedFunction ast; override string getAPrimaryQlClass() { result = "UserDefinedFunction" } @@ -26,11 +25,20 @@ class UserDefinedFunctionImpl extends TUserDefinedFunction, StmtsImpl { override string toString() { result = ast.toString() } - IdentifierImpl getName() { toTreeSitter(result) = ast.getName() } + override IdentifierImpl getIdentifier() { toTreeSitter(result) = ast.getName() } + + override ParametersImpl getParameters() { toTreeSitter(result) = ast.getChild(0) } - ParametersImpl getParameters() { toTreeSitter(result) = ast.getChild(0) } + override ParameterImpl getParameter(int n) { + exists(ParametersImpl params | + params = this.getParameters() and + result = params.getParameter(n) + ) + } TypeImpl getReturnType() { toTreeSitter(result) = ast.getReturns() } - StmtsImpl getBody() { toTreeSitter(result) = ast.getChild(1) } + override StmtSequenceImpl getBody() { toTreeSitter(result) = ast.getChild(1) } + + override TypeImpl getType() { result = this.getReturnType() } } diff --git a/ql/lib/codeql/bicep/controlflow/CfgNodes.qll b/ql/lib/codeql/bicep/controlflow/CfgNodes.qll index 1532bae..8d078db 100644 --- a/ql/lib/codeql/bicep/controlflow/CfgNodes.qll +++ b/ql/lib/codeql/bicep/controlflow/CfgNodes.qll @@ -1,10 +1,58 @@ /** Provides classes representing nodes in a control flow graph. */ private import bicep +private import codeql.bicep.ast.Calls +private import codeql.bicep.ast.Literals +private import codeql.bicep.ast.Resources private import BasicBlocks private import ControlFlowGraph import internal.ControlFlowGraphImpl +/** + * A class for mapping parent-child AST nodes to parent-child CFG nodes. + */ +abstract private class ChildMapping extends AstNode { + /** + * Holds if `child` is a (possibly nested) child of this expression + * for which we would like to find a matching CFG child. + */ + abstract predicate relevantChild(AstNode child); + + pragma[nomagic] + abstract predicate reachesBasicBlock(AstNode child, CfgNode cfn, BasicBlock bb); + + /** + * Holds if there is a control-flow path from `cfn` to `cfnChild`, where `cfn` + * is a control-flow node for this expression, and `cfnChild` is a control-flow + * node for `child`. + * + * The path never escapes the syntactic scope of this expression. + */ + cached + predicate hasCfgChild(AstNode child, CfgNode cfn, CfgNode cfnChild) { + this.reachesBasicBlock(child, cfn, cfnChild.getBasicBlock()) and + cfnChild.getAstNode() = child + } +} + +/** + * A class for mapping parent-child AST nodes to parent-child CFG nodes. + */ +abstract private class ExprChildMapping extends Expr, ChildMapping { + pragma[nomagic] + override predicate reachesBasicBlock(AstNode child, CfgNode cfn, BasicBlock bb) { + this.relevantChild(child) and + cfn = this.getAControlFlowNode() and + bb.getANode() = cfn + or + exists(BasicBlock mid | + this.reachesBasicBlock(child, cfn, mid) and + bb = mid.getAPredecessor() and + not mid.getANode().getAstNode() = child + ) + } +} + /** A control-flow node that wraps an AST expression. */ class ExprCfgNode extends AstCfgNode { string getAPrimaryQlClass() { result = "ExprCfgNode" } @@ -29,8 +77,155 @@ class StmtsCfgNode extends AstCfgNode { Stmts getStmt() { result = s } } +/** A control-flow node that wraps an AST literal. */ +class LiteralsCfgNode extends AstCfgNode { + string getAPrimaryQlClass() { result = "LiteralsCfgNode" } + + Literals l; + + LiteralsCfgNode() { l = this.getAstNode() } + + /** Gets the underlying literal. */ + Literals getLiteral() { result = l } +} + +class VariableCfgNode extends AstCfgNode { + string getAPrimaryQlClass() { result = "VariableCfgNode" } + + Variable v; + + VariableCfgNode() { v.getAstNode() = this.getAstNode() } + + /** + * Gets the underlying `Variable` expression. + */ + Variable getExpr() { result = v } + + /** Gets the name of the variable. */ + string getName() { result = v.getName() } +} + +/** A control-flow node that wraps a `VariableAccess` AST expression. */ +class VariableAccessCfgNode extends AstCfgNode { + string getAPrimaryQlClass() { result = "VariableAccessCfgNode" } + + VariableAccess v; + + VariableAccessCfgNode() { v.getAstNode() = this.getAstNode() } + + /** + * Gets the underlying `VariableAccess` expression. + */ + VariableAccess getExpr() { result = v } + + /** Gets the variable */ + VariableCfgNode getTarget() { result.getExpr() = v.getVariable() } +} + +/** A control-flow node that wraps a `VariableReadAccess` AST expression. */ +class VariableReadAccessCfgNode extends VariableAccessCfgNode { + override string getAPrimaryQlClass() { result = "VariableReadAccessCfgNode" } + + override VariableReadAccess v; + + override VariableReadAccess getExpr() { result = super.getExpr() } + + Variable getVariable() { result = v.getVariable() } +} + +class VariableWriteAccessCfgNode extends VariableAccessCfgNode { + override string getAPrimaryQlClass() { result = "VariableWriteAccessCfgNode" } + + override VariableWriteAccess v; + + override VariableWriteAccess getExpr() { result.asExpr() = v.asExpr() } + + Variable getVariable() { result = v.getVariable() } + + final ExprCfgNode getReceiver() { result.getExpr() = v.asExpr() } +} + /** Provides classes for control-flow nodes that wrap AST expressions. */ -module ExprNodes { +module ExprNodes { + /** A mapping from a child of a literal to a literal. */ + abstract class LiteralChildMapping extends AstNode { + /** Holds if `n` is a relevant child of this literal. */ + abstract predicate relevantChild(AstNode n); + } + + /** A control-flow node that wraps an Array AST node. */ + class ArrayCfgNode extends LiteralsCfgNode { + override string getAPrimaryQlClass() { result = "ArrayCfgNode" } + + Array e; + + ArrayCfgNode() { l = this.getLiteral() } + + /** Gets the underlying array literal. */ + Array getArray() { result = l } + } + + /** A control-flow node that wraps a Boolean AST node. */ + class BooleanCfgNode extends LiteralsCfgNode { + override string getAPrimaryQlClass() { result = "BooleanCfgNode" } + + Boolean e; + + BooleanCfgNode() { l = this.getLiteral() } + + /** Gets the underlying boolean literal. */ + Boolean getBoolean() { result = l } + } + + /** A control-flow node that wraps a NullLiteral AST node. */ + class NullLiteralCfgNode extends LiteralsCfgNode { + override string getAPrimaryQlClass() { result = "NullLiteralCfgNode" } + + NullLiteral e; + + NullLiteralCfgNode() { l = this.getLiteral() } + + /** Gets the underlying null literal. */ + NullLiteral getNullLiteral() { result = l } + } + + /** A control-flow node that wraps a Number AST node. */ + class NumberCfgNode extends LiteralsCfgNode { + override string getAPrimaryQlClass() { result = "NumberCfgNode" } + + Number e; + + NumberCfgNode() { l = this.getLiteral() } + + /** Gets the underlying number literal. */ + Number getNumber() { result = l } + } + + /** A control-flow node that wraps a StringLiteral AST node. */ + class StringLiteralCfgNode extends LiteralsCfgNode { + override string getAPrimaryQlClass() { result = "StringLiteralCfgNode" } + + StringLiteral e; + + StringLiteralCfgNode() { l = this.getLiteral() } + + /** Gets the underlying string literal. */ + StringLiteral getStringLiteral() { result = l } + } + + /** A control-flow node that wraps a StringContentLiteral AST node. */ + class StringContentLiteralCfgNode extends LiteralsCfgNode { + override string getAPrimaryQlClass() { result = "StringContentLiteralCfgNode" } + + StringContentLiteral e; + + StringContentLiteralCfgNode() { l = this.getLiteral() } + + /** Gets the underlying string content literal. */ + StringContentLiteral getStringContentLiteral() { result = l } + } + + /** A control-flow node that wraps a CallExpression AST node. */ class CallCfgNode extends ExprCfgNode { override string getAPrimaryQlClass() { result = "CallCfgNode" } @@ -38,10 +233,347 @@ module ExprNodes { override CallExpression getExpr() { result = super.getExpr() } } + + /** A mapping from a child of an Arguments expression to the expression. */ + abstract class ArgumentsChildMapping extends ExprChildMapping, Arguments { + override predicate relevantChild(AstNode n) { n = this.getArgument(_) } + } + + /** A control-flow node that wraps an Arguments AST node. */ + class ArgumentsCfgNode extends ExprCfgNode { + override string getAPrimaryQlClass() { result = "ArgumentsCfgNode" } + + override Arguments e; + + override Arguments getExpr() { result = super.getExpr() } + } + + /** A mapping from a child of an AssignmentExpression to the expression. */ + abstract class AssignmentExpressionChildMapping extends ExprChildMapping, + AssignmentExpression + { + override predicate relevantChild(AstNode n) { n = this.getLeft() or n = this.getRight() } + } + + /** A control-flow node that wraps an AssignmentExpression AST node. */ + class AssignmentExpressionCfgNode extends ExprCfgNode { + override string getAPrimaryQlClass() { result = "AssignmentExpressionCfgNode" } + + override AssignmentExpression e; + + override AssignmentExpression getExpr() { result = super.getExpr() } + } + + /** A mapping from a child of a BinaryExpression to the expression. */ + abstract class BinaryExpressionChildMapping extends ExprChildMapping, BinaryExpression { + override predicate relevantChild(AstNode n) { n = this.getLeft() or n = this.getRight() } + } + + /** A control-flow node that wraps a BinaryExpression AST node. */ + class BinaryExpressionCfgNode extends ExprCfgNode { + override string getAPrimaryQlClass() { result = "BinaryExpressionCfgNode" } + + override BinaryExpression e; + + override BinaryExpression getExpr() { result = super.getExpr() } + + /** Gets the left operand CFG node */ + final ExprCfgNode getLeftOperand() { + result = this.getAPredecessor().(ExprCfgNode) and + result.getExpr() = this.getExpr().getLeft() + } + + /** Gets the right operand CFG node */ + final ExprCfgNode getRightOperand() { + result = this.getAPredecessor().(ExprCfgNode) and + result.getExpr() = this.getExpr().getRight() + } + } + + /** A mapping from a child of an Interpolation to the expression. */ + abstract class InterpolationChildMapping extends ExprChildMapping, Interpolation { + override predicate relevantChild(AstNode n) { n = this.getExpression() } + } + + /** A control-flow node that wraps an Interpolation AST node. */ + class InterpolationCfgNode extends ExprCfgNode { + override string getAPrimaryQlClass() { result = "InterpolationCfgNode" } + + override Interpolation e; + + override Interpolation getExpr() { result = super.getExpr() } + } + + /** A mapping from a child of a LambdaExpression to the expression. */ + abstract class LambdaExpressionChildMapping extends ExprChildMapping, LambdaExpression { + override predicate relevantChild(AstNode n) { n = this.getAChild() } + } + + /** A control-flow node that wraps a LambdaExpression AST node. */ + class LambdaExpressionCfgNode extends ExprCfgNode { + override string getAPrimaryQlClass() { result = "LambdaExpressionCfgNode" } + + override LambdaExpression e; + + override LambdaExpression getExpr() { result = super.getExpr() } + } + + /** A mapping from a child of a MemberExpression to the expression. */ + abstract class MemberExpressionChildMapping extends ExprChildMapping, MemberExpression { + override predicate relevantChild(AstNode n) { n = this.getNamespace() or n = this.getName() } + } + + /** A control-flow node that wraps a MemberExpression AST node. */ + class MemberExpressionCfgNode extends ExprCfgNode { + override string getAPrimaryQlClass() { result = "MemberExpressionCfgNode" } + + override MemberExpression e; + + override MemberExpression getExpr() { result = super.getExpr() } + } + + /** A mapping from a child of a NullableType to the expression. */ + abstract class NullableTypeChildMapping extends ExprChildMapping, NullableType { + override predicate relevantChild(AstNode n) { n = this.getAChild() } + } + + /** A control-flow node that wraps a NullableType AST node. */ + class NullableTypeCfgNode extends ExprCfgNode { + override string getAPrimaryQlClass() { result = "NullableTypeCfgNode" } + + override NullableType e; + + override NullableType getExpr() { result = super.getExpr() } + } + + /** A mapping from a child of a ParenthesizedExpression to the expression. */ + abstract class ParenthesizedExpressionChildMapping extends ExprChildMapping, + ParenthesizedExpression + { + override predicate relevantChild(AstNode n) { n = this.getExpression(_) } + } + + /** A control-flow node that wraps a ParenthesizedExpression AST node. */ + class ParenthesizedExpressionCfgNode extends ExprCfgNode { + override string getAPrimaryQlClass() { result = "ParenthesizedExpressionCfgNode" } + + override ParenthesizedExpression e; + + override ParenthesizedExpression getExpr() { result = super.getExpr() } + } + + /** A mapping from a child of a ResourceExpression to the expression. */ + abstract class ResourceExpressionChildMapping extends ExprChildMapping, ResourceExpression { + override predicate relevantChild(AstNode n) { n = this.getAChild() } + } + + /** A control-flow node that wraps a ResourceExpression AST node. */ + class ResourceExpressionCfgNode extends ExprCfgNode { + override string getAPrimaryQlClass() { result = "ResourceExpressionCfgNode" } + + override ResourceExpression e; + + override ResourceExpression getExpr() { result = super.getExpr() } + } + + /** A mapping from a child of a TernaryExpression to the expression. */ + abstract class TernaryExpressionChildMapping extends ExprChildMapping, TernaryExpression { + override predicate relevantChild(AstNode n) { n = this.getAChild() } + } + + /** A control-flow node that wraps a TernaryExpression AST node. */ + class TernaryExpressionCfgNode extends ExprCfgNode { + override string getAPrimaryQlClass() { result = "TernaryExpressionCfgNode" } + + override TernaryExpression e; + + override TernaryExpression getExpr() { result = super.getExpr() } + } + + /** A mapping from a child of a UnaryExpression to the expression. */ + abstract class UnaryExpressionChildMapping extends ExprChildMapping, UnaryExpression { + override predicate relevantChild(AstNode n) { n = this.getAChild() } + } + + /** A control-flow node that wraps a UnaryExpression AST node. */ + class UnaryExpressionCfgNode extends ExprCfgNode { + override string getAPrimaryQlClass() { result = "UnaryExpressionCfgNode" } + + override UnaryExpression e; + + override UnaryExpression getExpr() { result = super.getExpr() } + } + + /** A mapping from a child of a Parameter to the expression. */ + abstract class ParameterChildMapping extends ExprChildMapping, Parameter { + override predicate relevantChild(AstNode n) { n = this.getIdentifier() or n = this.getType() } + } + + /** A control-flow node that wraps a Parameter AST node. */ + class ParameterCfgNode extends ExprCfgNode { + override string getAPrimaryQlClass() { result = "ParameterCfgNode" } + + override Parameter e; + + override Parameter getExpr() { result = super.getExpr() } + } + + /** A mapping from a child of Parameters to the expression. */ + abstract class ParametersChildMaping extends ExprChildMapping, Parameters { + override predicate relevantChild(AstNode n) { n = this.getParameter(_) } + } + + /** A control-flow node that wraps a Parameters AST node. */ + class ParametersCfgNode extends ExprCfgNode { + override string getAPrimaryQlClass() { result = "ParametersCfgNode" } + + override Parameters e; + + override Parameters getExpr() { result = super.getExpr() } + } } +/** Provides classes for control-flow nodes that wrap AST statements. */ module StmtNodes { - /** A control-flow node that wraps a `Cmd` AST expression. */ + /** A mapping from a child of a statement to a statement. */ + abstract class StmtChildMapping extends Stmts { + /** Holds if `n` is a relevant child of this statement. */ + abstract predicate relevantChild(AstNode n); + } + + /** A control-flow node that wraps an AssertStatement AST node. */ + class AssertStatementCfgNode extends StmtsCfgNode { + override string getAPrimaryQlClass() { result = "AssertStatementCfgNode" } + + override AssertStatementStmt s; + + override AssertStatementStmt getStmt() { result = super.getStmt() } + } + + /** A control-flow node that wraps a ForStatement AST node. */ + class ForStatementCfgNode extends StmtsCfgNode { + override string getAPrimaryQlClass() { result = "ForStatementCfgNode" } + + override ForStatementStmt s; + + override ForStatementStmt getStmt() { result = super.getStmt() } + } + + /** A mapping from a child of an IfStatement to the statement. */ + abstract class IfStatementChildMapping extends StmtChildMapping, IfStatement { + override predicate relevantChild(AstNode n) { n = this.getCondition() or n = this.getBody() } + } + + /** A control-flow node that wraps an IfStatement AST node. */ + class IfStatementCfgNode extends StmtsCfgNode { + override string getAPrimaryQlClass() { result = "IfStatementCfgNode" } + + override IfStatement s; + + override IfStatement getStmt() { result = super.getStmt() } + + /** Gets the condition CFG node */ + final ExprCfgNode getCondition() { + result = this.getAPredecessor().(ExprCfgNode) and + result.getExpr() = this.getStmt().getCondition() + } + + /** Gets the body CFG node */ + final ExprCfgNode getBody() { + result = this.getAPredecessor().(ExprCfgNode) and + result.getExpr() = this.getStmt().getBody() + } + } + + /** A control-flow node that wraps an ImportStatement AST node. */ + class ImportStatementCfgNode extends StmtsCfgNode { + override string getAPrimaryQlClass() { result = "ImportStatementCfgNode" } + + override ImportStatementStmt s; + + override ImportStatementStmt getStmt() { result = super.getStmt() } + } + + /** A control-flow node that wraps an Infrastructure AST node. */ + class InfrastructureCfgNode extends AstCfgNode { + string getAPrimaryQlClass() { result = "InfrastructureCfgNode" } + + Infrastructure i; + + InfrastructureCfgNode() { i = this.getAstNode() } + + /** Gets the underlying Infrastructure. */ + Infrastructure getInfrastructure() { result = i } + } + + /** A mapping from a child of a ParameterDeclaration to the statement. */ + abstract class ParameterDeclarationChildMapping extends StmtChildMapping, ParameterDeclaration { + override predicate relevantChild(AstNode n) { + n = this.getIdentifier() or n = this.getDefaultValue() + } + } + + /** A control-flow node that wraps a ParameterDeclaration AST node. */ + class ParameterDeclarationCfgNode extends StmtsCfgNode { + override string getAPrimaryQlClass() { result = "ParameterDeclarationCfgNode" } + + override ParameterDeclaration s; + + override ParameterDeclaration getStmt() { result = super.getStmt() } + } + + /** A mapping from a child of an OutputDeclaration to the statement. */ + abstract class OutputDeclarationChildMapping extends StmtChildMapping, OutputDeclaration { + override predicate relevantChild(AstNode n) { n = this.getIdentifier() or n = this.getValue() } + } + + /** A control-flow node that wraps an OutputDeclaration AST node. */ + class OutputDeclarationCfgNode extends StmtsCfgNode { + override string getAPrimaryQlClass() { result = "OutputDeclarationCfgNode" } + + override OutputDeclaration s; + + override OutputDeclaration getStmt() { result = super.getStmt() } + } + + /** A mapping from a child of a UserDefinedFunction to the statement. */ + abstract class UserDefinedFunctionChildMapping extends StmtChildMapping, UserDefinedFunction { + override predicate relevantChild(AstNode n) { + n = this.getIdentifier() or + n = this.getParameters() or + n = this.getReturnType() or + n = this.getBody() + } + } + + /** A control-flow node that wraps a UserDefinedFunction AST node. */ + class UserDefinedFunctionCfgNode extends StmtsCfgNode { + override string getAPrimaryQlClass() { result = "UserDefinedFunctionCfgNode" } + + override UserDefinedFunction s; + + override UserDefinedFunction getStmt() { result = super.getStmt() } + } + + /** A control-flow node that wraps an ImportWithStatement AST node. */ + class ImportWithStatementCfgNode extends StmtsCfgNode { + override string getAPrimaryQlClass() { result = "ImportWithStatementCfgNode" } + + override ImportWithStatementStmt s; + + override ImportWithStatementStmt getStmt() { result = super.getStmt() } + } + + /** A control-flow node that wraps a UsingStatement AST node. */ + class UsingStatementCfgNode extends StmtsCfgNode { + override string getAPrimaryQlClass() { result = "UsingStatementCfgNode" } + + override UsingStatementStmt s; + + override UsingStatementStmt getStmt() { result = super.getStmt() } + } + + /** A control-flow node that wraps a UserDefinedFunction AST node. */ class CallCfgNode extends StmtsCfgNode { override string getAPrimaryQlClass() { result = "CallCfgNode" } @@ -49,4 +581,4 @@ module StmtNodes { override UserDefinedFunction getStmt() { result = super.getStmt() } } -} \ No newline at end of file +} diff --git a/ql/lib/codeql/bicep/controlflow/internal/ControlFlowGraphImpl.qll b/ql/lib/codeql/bicep/controlflow/internal/ControlFlowGraphImpl.qll index 1f0d74d..d2dcde6 100644 --- a/ql/lib/codeql/bicep/controlflow/internal/ControlFlowGraphImpl.qll +++ b/ql/lib/codeql/bicep/controlflow/internal/ControlFlowGraphImpl.qll @@ -63,89 +63,304 @@ private module CfgImpl = Make; import CfgImpl -class InfrastructureScopeTree extends StandardTree, PreOrderTree, PostOrderTree, Scope::InfrastructureScope { - override AstNode getChildNode(int i) { result = super.getStmt(i) } -} +module Trees { + private class InfrastructureScopeTree extends StandardTree, PreOrderTree, PostOrderTree, + Scope::InfrastructureScope + { + override AstNode getChildNode(int i) { result = super.getStmt(i) } + } -class StmtsTree extends StandardPostOrderTree instanceof Stmts { - override AstNode getChildNode(int i) { - // - i = 0 and result = super.getAChild() + private class StmtsTree extends StandardPostOrderTree instanceof Stmts { + override AstNode getChildNode(int i) { + // + i = 0 and result = super.getAChild() + } } -} -class ExprTree extends StandardPostOrderTree instanceof Expr { - override AstNode getChildNode(int i) { - i = 0 and result = super.getAChild() + private class ExprTree extends StandardPostOrderTree instanceof Expr { + override AstNode getChildNode(int i) { i = 0 and result = super.getAChild() } } -} -/** - * A literal value in a Bicep program. - */ -class LiteralTree extends LeafTree instanceof Literals { } + /** + * A tree for Arguments in a Bicep program. + */ + private class ArgumentsTree extends StandardPostOrderTree instanceof Arguments { + override AstNode getChildNode(int i) { result = super.getArgument(i) } + } -/** - * A Null literal value in a Bicep program. - */ -class NullLiteralTree extends LeafTree instanceof NullLiteral { } + /** + * A tree for AssignmentExpression in a Bicep program. + */ + private class AssignmentExpressionTree extends StandardPostOrderTree instanceof AssignmentExpression { + override AstNode getChildNode(int i) { + i = 0 and result = super.getLeft() + or + i = 1 and result = super.getRight() + } + } -/** - * A NullableReturnType literal value in a Bicep program. - */ -class NullableReturnTypeLiteralTree extends LeafTree instanceof NullableReturnTypeLiteral { } + /** + * A tree for BinaryExpression in a Bicep program. + */ + private class BinaryExpressionTree extends StandardPostOrderTree instanceof BinaryExpression { + override AstNode getChildNode(int i) { + i = 0 and result = super.getLeft() + or + i = 1 and result = super.getRight() + } + } -/** - * A String literal value in a Bicep program. - */ -class StringLiteralTree extends LeafTree instanceof StringLiteral { } + /** + * A tree for Interpolation in a Bicep program. + */ + private class InterpolationTree extends StandardPostOrderTree instanceof Interpolation { + override AstNode getChildNode(int i) { i = 0 and result = super.getExpression() } + } -/** - * A StringContent literal value in a Bicep program. - */ -class StringContentLiteralTree extends LeafTree instanceof StringContentLiteral { } + /** + * A tree for LambdaExpression in a Bicep program. + */ + private class LambdaExpressionTree extends StandardPostOrderTree instanceof LambdaExpression { + override AstNode getChildNode(int i) { i = 0 and result = super.getAChild() } + } -/** - * ParameterDeclarationTree represents a parameter declaration in a Bicep program. - */ -class ParameterDeclarationTree extends PreOrderTree instanceof ParameterDeclaration { - final override predicate propagatesAbnormal(AstNode child) { child = super.getIdentifier() } + /** + * A tree for MemberExpression in a Bicep program. + */ + private class MemberExpressionTree extends StandardPostOrderTree instanceof MemberExpression { + override AstNode getChildNode(int i) { + i = 0 and result = super.getNamespace() + or + i = 1 and result = super.getName() + } + } - override predicate succ(AstNode pred, AstNode succ, Completion c) { - // Start with the identifier - pred = this and first(super.getIdentifier(), succ) and completionIsSimple(c) - or - last(super.getIdentifier(), pred, c) and first(super.getDefaultValue(), succ) and completionIsNormal(c) + /** + * A tree for NullableType in a Bicep program. + */ + private class NullableTypeTree extends StandardPostOrderTree instanceof NullableType { + override AstNode getChildNode(int i) { i = 0 and result = super.getAChild() } } - override predicate last(AstNode node, Completion c) { - node = super.getDefaultValue() and completionIsNormal(c) + /** + * A tree for ParenthesizedExpression in a Bicep program. + */ + private class ParenthesizedExpressionTree extends StandardPostOrderTree instanceof ParenthesizedExpression + { + override AstNode getChildNode(int i) { result = super.getExpression(i) } } -} -class UserDefinedFunctionTree extends StandardPostOrderTree instanceof UserDefinedFunction { - override AstNode getChildNode(int i) { - i = 0 and result = super.getIdentifier() - or - i = 1 and result = super.getParameters() - or - i = 2 and result = super.getReturnType() - or - i = 3 and result = super.getBody() + /** + * A tree for ResourceExpression in a Bicep program. + */ + private class ResourceExpressionTree extends StandardPostOrderTree instanceof ResourceExpression { + override AstNode getChildNode(int i) { i = 0 and result = super.getAChild() } + } + + /** + * A tree for TernaryExpression in a Bicep program. + */ + private class TernaryExpressionTree extends StandardPostOrderTree instanceof TernaryExpression { + override AstNode getChildNode(int i) { i = 0 and result = super.getAChild() } + } + + /** + * A unary expression in the CFG. + */ + private class UnaryExpressionTree extends StandardPostOrderTree instanceof UnaryExpression { + override AstNode getChildNode(int i) { i = 0 and result = super.getAChild() } } -} -class OutputDeclarationTree extends PreOrderTree instanceof OutputDeclaration { - final override predicate propagatesAbnormal(AstNode child) { child = super.getIdentifier() } + /** + * A tree for Object in a Bicep program. + */ + private class ObjectTree extends StandardPostOrderTree instanceof Object { + override AstNode getChildNode(int i) { result = super.getProp(i) } + } + + /** + * A tree for ObjectProperty in a Bicep program. + */ + private class ObjectPropertyTree extends StandardPostOrderTree instanceof ObjectProperty { + override AstNode getChildNode(int i) { + i = 0 and result = super.getName() + or + i = 1 and result = super.getValue() + } + } + + /** + * A tree for SubscriptExpression in a Bicep program. + */ + private class SubscriptExpressionTree extends StandardPostOrderTree instanceof SubscriptExpression { + override AstNode getChildNode(int i) { + i = 0 and result = super.getIdentifier() + or + i = 1 and result = super.getIndex() + } + } + + /** + * A tree for Array in a Bicep program. + */ + private class ArrayTree extends StandardPostOrderTree instanceof Array { + override AstNode getChildNode(int i) { result = super.getElement(i) } + } - override predicate succ(AstNode pred, AstNode succ, Completion c) { - // Start with the identifier - pred = this and first(super.getIdentifier(), succ) and completionIsSimple(c) - or - last(super.getIdentifier(), pred, c) and first(super.getValue(), succ) and completionIsNormal(c) + /** + * A tree for Boolean literals in a Bicep program. + */ + private class BooleanTree extends LeafTree instanceof Boolean { } + + /** + * A tree for NullLiteral in a Bicep program. + */ + private class NullLiteralTree extends LeafTree instanceof NullLiteral { } + + /** + * A tree for Number literals in a Bicep program. + */ + private class NumberTree extends LeafTree instanceof Number { } + + /** + * A tree for String literals in a Bicep program. + */ + private class StringLiteralTree extends StandardPostOrderTree instanceof StringLiteral { + override AstNode getChildNode(int i) { result = super.getInterpolation(i) } } - override predicate last(AstNode node, Completion c) { - node = super.getValue() and completionIsNormal(c) + /** + * A tree for StringContentLiteral in a Bicep program. + */ + private class StringContentLiteralTree extends LeafTree instanceof StringContentLiteral { } + + /** + * A literal value in a Bicep program. + */ + private class LiteralTree extends LeafTree instanceof Literals { } + + /** + * A NullableReturnType literal value in a Bicep program. + */ + private class NullableReturnTypeLiteralTree extends LeafTree instanceof NullableReturnTypeLiteral { } + + /** + * A tree for AssertStatement in a Bicep program. + */ + private class AssertStatementTree extends StandardPostOrderTree instanceof AssertStatementStmt { + override AstNode getChildNode(int i) { i = 0 and result = super.getAChild() } + } + + /** + * A tree for ForStatement in a Bicep program. + */ + private class ForStatementTree extends StandardPostOrderTree instanceof ForStatementStmt { + override AstNode getChildNode(int i) { i = 0 and result = super.getAChild() } + } + + /** + * A tree for IfStatement in a Bicep program. + */ + private class IfStatementTree extends PreOrderTree instanceof IfStatement { + final override predicate propagatesAbnormal(AstNode child) { child = super.getCondition() } + + override predicate succ(AstNode pred, AstNode succ, Completion c) { + // Start with the condition + pred = this and first(super.getCondition(), succ) and completionIsSimple(c) + or + // If condition is normal, go to the body + last(super.getCondition(), pred, c) and + first(super.getBody(), succ) and + completionIsNormal(c) + } + + override predicate last(AstNode node, Completion c) { + node = super.getBody() and completionIsNormal(c) + } + } + + /** + * A tree for ImportStatement in a Bicep program. + */ + private class ImportStatementTree extends StandardPostOrderTree instanceof ImportStatementStmt { + override AstNode getChildNode(int i) { i = 0 and result = super.getAChild() } + } + + /** + * A tree for ImportWithStatement in a Bicep program. + */ + private class ImportWithStatementTree extends StandardPostOrderTree instanceof ImportWithStatementStmt { + override AstNode getChildNode(int i) { i = 0 and result = super.getAChild() } + } + + /** + * A tree for UsingStatement in a Bicep program. + */ + private class UsingStatementTree extends StandardPostOrderTree instanceof UsingStatementStmt { + override AstNode getChildNode(int i) { i = 0 and result = super.getAChild() } + } + + private class ParameterDeclarationTree extends PreOrderTree instanceof ParameterDeclaration { + final override predicate propagatesAbnormal(AstNode child) { child = super.getIdentifier() } + + override predicate succ(AstNode pred, AstNode succ, Completion c) { + // Start with the identifier + pred = this and first(super.getIdentifier(), succ) and completionIsSimple(c) + or + last(super.getIdentifier(), pred, c) and + first(super.getDefaultValue(), succ) and + completionIsNormal(c) + } + + override predicate last(AstNode node, Completion c) { + node = super.getDefaultValue() and completionIsNormal(c) + } + } + + private class UserDefinedFunctionTree extends StandardPostOrderTree instanceof UserDefinedFunction { + override AstNode getChildNode(int i) { + i = 0 and result = super.getIdentifier() + or + i = 1 and result = super.getParameters() + or + i = 2 and result = super.getReturnType() + or + i = 3 and result = super.getBody() + } + } + + private class OutputDeclarationTree extends PreOrderTree instanceof OutputDeclaration { + final override predicate propagatesAbnormal(AstNode child) { child = super.getIdentifier() } + + override predicate succ(AstNode pred, AstNode succ, Completion c) { + // Start with the identifier + pred = this and first(super.getIdentifier(), succ) and completionIsSimple(c) + or + last(super.getIdentifier(), pred, c) and + first(super.getValue(), succ) and + completionIsNormal(c) + } + + override predicate last(AstNode node, Completion c) { + node = super.getValue() and completionIsNormal(c) + } + } + + /** + * A tree for Parameter in a Bicep program. + */ + private class ParameterTree extends StandardPostOrderTree instanceof Parameter { + override AstNode getChildNode(int i) { + i = 0 and result = super.getIdentifier() + or + i = 1 and result = super.getType() + } + } + + /** + * A tree for Parameters in a Bicep program. + */ + private class ParametersTree extends StandardPostOrderTree instanceof Parameters { + override AstNode getChildNode(int i) { result = super.getParameter(i) } } } diff --git a/ql/test/library-tests/cfg/BasicBlocks.expected b/ql/test/library-tests/cfg/basic/BasicBlocks.expected similarity index 100% rename from ql/test/library-tests/cfg/BasicBlocks.expected rename to ql/test/library-tests/cfg/basic/BasicBlocks.expected diff --git a/ql/test/library-tests/cfg/BasicBlocks.ql b/ql/test/library-tests/cfg/basic/BasicBlocks.ql similarity index 100% rename from ql/test/library-tests/cfg/BasicBlocks.ql rename to ql/test/library-tests/cfg/basic/BasicBlocks.ql diff --git a/ql/test/library-tests/cfg/Cfg.expected b/ql/test/library-tests/cfg/basic/Cfg.expected similarity index 100% rename from ql/test/library-tests/cfg/Cfg.expected rename to ql/test/library-tests/cfg/basic/Cfg.expected diff --git a/ql/test/library-tests/cfg/Cfg.ql b/ql/test/library-tests/cfg/basic/Cfg.ql similarity index 100% rename from ql/test/library-tests/cfg/Cfg.ql rename to ql/test/library-tests/cfg/basic/Cfg.ql diff --git a/ql/test/library-tests/cfg/sample.bicep b/ql/test/library-tests/cfg/basic/sample.bicep similarity index 100% rename from ql/test/library-tests/cfg/sample.bicep rename to ql/test/library-tests/cfg/basic/sample.bicep