From 63747add889c227e5c099fdf09c59ca21c263459 Mon Sep 17 00:00:00 2001 From: Mathew Payne Date: Thu, 19 Oct 2023 17:01:15 +0100 Subject: [PATCH 01/14] Add shared libs to pack --- ql/lib/qlpack.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/ql/lib/qlpack.yml b/ql/lib/qlpack.yml index f4cd7d5..9d76fc8 100644 --- a/ql/lib/qlpack.yml +++ b/ql/lib/qlpack.yml @@ -6,6 +6,9 @@ version: 0.2.0 dependencies: codeql/util: 0.1.2 codeql/yaml: ^0.1.2 + codeql/controlflow: ^0.1.1 + codeql/dataflow: ^0.1.1 + codeql/ssa: ^0.2.1 dbscheme: iac.dbscheme extractor: iac upgrades: upgrades From 7873dd806d266fa0310c3ee16f21f209aa8fdb80 Mon Sep 17 00:00:00 2001 From: Mathew Payne Date: Thu, 19 Oct 2023 17:01:56 +0100 Subject: [PATCH 02/14] Massive update to Ast --- ql/lib/bicep.qll | 2 +- ql/lib/codeql/bicep/AST.qll | 6 +- ql/lib/codeql/bicep/Ast.qll | 8 + ql/lib/codeql/bicep/ast/Ast.qll | 14 ++ ql/lib/codeql/bicep/ast/Calls.qll | 6 + ql/lib/codeql/bicep/ast/Expr.qll | 139 +++++------------- ql/lib/codeql/bicep/ast/Literal.qll | 62 ++------ ql/lib/codeql/bicep/ast/Loops.qll | 24 +++ ql/lib/codeql/bicep/ast/Object.qll | 57 +++---- ql/lib/codeql/bicep/ast/Resources.qll | 54 ++----- ql/lib/codeql/bicep/ast/Variables.qll | 0 .../bicep/ast/{ => internal}/AstNodes.qll | 12 -- ql/lib/codeql/bicep/ast/internal/Calls.qll | 25 ++++ ql/lib/codeql/bicep/ast/internal/Expr.qll | 126 ++++++++++++++++ ql/lib/codeql/bicep/ast/internal/Literal.qll | 74 ++++++++++ ql/lib/codeql/bicep/ast/internal/Loops.qll | 44 ++++++ ql/lib/codeql/bicep/ast/internal/Object.qll | 47 ++++++ .../codeql/bicep/ast/internal/Resources.qll | 91 ++++++++++++ 18 files changed, 544 insertions(+), 247 deletions(-) create mode 100644 ql/lib/codeql/bicep/Ast.qll create mode 100644 ql/lib/codeql/bicep/ast/Ast.qll create mode 100644 ql/lib/codeql/bicep/ast/Calls.qll create mode 100644 ql/lib/codeql/bicep/ast/Loops.qll create mode 100644 ql/lib/codeql/bicep/ast/Variables.qll rename ql/lib/codeql/bicep/ast/{ => internal}/AstNodes.qll (81%) create mode 100644 ql/lib/codeql/bicep/ast/internal/Calls.qll create mode 100644 ql/lib/codeql/bicep/ast/internal/Expr.qll create mode 100644 ql/lib/codeql/bicep/ast/internal/Literal.qll create mode 100644 ql/lib/codeql/bicep/ast/internal/Loops.qll create mode 100644 ql/lib/codeql/bicep/ast/internal/Object.qll create mode 100644 ql/lib/codeql/bicep/ast/internal/Resources.qll diff --git a/ql/lib/bicep.qll b/ql/lib/bicep.qll index f647508..1c4d963 100644 --- a/ql/lib/bicep.qll +++ b/ql/lib/bicep.qll @@ -1,6 +1,6 @@ import codeql.Locations import codeql.files.FileSystem -import codeql.bicep.AST +import codeql.bicep.Ast // Resources import codeql.bicep.microsoft.Compute import codeql.bicep.microsoft.Storage diff --git a/ql/lib/codeql/bicep/AST.qll b/ql/lib/codeql/bicep/AST.qll index 97e2427..88f1662 100644 --- a/ql/lib/codeql/bicep/AST.qll +++ b/ql/lib/codeql/bicep/AST.qll @@ -1,4 +1,8 @@ -import codeql.bicep.ast.AstNodes +import codeql.bicep.ast.Ast import codeql.bicep.ast.Expr +import codeql.bicep.ast.Calls +import codeql.bicep.ast.Loops +import codeql.bicep.ast.Object import codeql.bicep.ast.Literal +import codeql.bicep.ast.Variables import codeql.bicep.ast.Resources diff --git a/ql/lib/codeql/bicep/Ast.qll b/ql/lib/codeql/bicep/Ast.qll new file mode 100644 index 0000000..88f1662 --- /dev/null +++ b/ql/lib/codeql/bicep/Ast.qll @@ -0,0 +1,8 @@ +import codeql.bicep.ast.Ast +import codeql.bicep.ast.Expr +import codeql.bicep.ast.Calls +import codeql.bicep.ast.Loops +import codeql.bicep.ast.Object +import codeql.bicep.ast.Literal +import codeql.bicep.ast.Variables +import codeql.bicep.ast.Resources diff --git a/ql/lib/codeql/bicep/ast/Ast.qll b/ql/lib/codeql/bicep/ast/Ast.qll new file mode 100644 index 0000000..b48981d --- /dev/null +++ b/ql/lib/codeql/bicep/ast/Ast.qll @@ -0,0 +1,14 @@ +private import codeql.bicep.ast.internal.AstNodes +private import codeql.Locations + +final class AstNode instanceof BicepAstNode { + AstNode getAChild(string name) { result = super.getAChild(name) } + + AstNode getParent() { result.getAChild(_) = this } + + string toString() { result = super.toString() } + + string getAPrimaryQlClass() { result = super.getAPrimaryQlClass() } + + Location getLocation() { result = super.getLocation() } +} diff --git a/ql/lib/codeql/bicep/ast/Calls.qll b/ql/lib/codeql/bicep/ast/Calls.qll new file mode 100644 index 0000000..06f7274 --- /dev/null +++ b/ql/lib/codeql/bicep/ast/Calls.qll @@ -0,0 +1,6 @@ +private import codeql.bicep.ast.internal.AstNodes +private import codeql.bicep.ast.internal.Calls + +final class CallExpr extends BicepAstNode instanceof CallExprImpl { } + +final class LambdaExpr extends BicepAstNode instanceof LambdaExprImpl { } diff --git a/ql/lib/codeql/bicep/ast/Expr.qll b/ql/lib/codeql/bicep/ast/Expr.qll index 9e6f7d8..e13691d 100644 --- a/ql/lib/codeql/bicep/ast/Expr.qll +++ b/ql/lib/codeql/bicep/ast/Expr.qll @@ -1,122 +1,51 @@ -private import codeql.iac.ast.internal.Bicep -private import codeql.bicep.ast.AstNodes +private import codeql.bicep.ast.internal.AstNodes +private import codeql.bicep.ast.internal.Expr -class Expr extends BicepAstNode, TExpr { - override string getAPrimaryQlClass() { result = "Expr" } -} - -class Identifier extends Expr, TIdentifier { - private BICEP::Identifier identifier; - - override string getAPrimaryQlClass() { result = "Identifier" } - - Identifier() { this = TIdentifier(identifier) } - - override string toString() { result = this.getName() } - - string getName() { result = identifier.getValue() } -} - -class Expression extends Expr, TExpression { - private BICEP::Expression expression; - - override string getAPrimaryQlClass() { result = "Expression" } - - Expression() { this = TExpression(expression) } -} +/** + * A Bicep expression. + */ +final class Expr extends BicepAstNode instanceof ExprImpl { } -class AssignmentExpr extends Expr, TAssignmentExpression { - BICEP::AssignmentExpression aexpr; - - override string getAPrimaryQlClass() { result = "AssignmentExpr" } - - AssignmentExpr() { this = TAssignmentExpression(aexpr) } +/** + * A Bicep identifier. + */ +final class Identifier extends Expr instanceof IdentifierImpl { + string getName() { result = super.getName() } } -class BinaryExpr extends Expr, TBinaryExpression { - BICEP::BinaryExpression bexpr; - - override string getAPrimaryQlClass() { result = "BinaryExpr" } +/** + * A Bicep expression. + */ +final class Expression extends Expr instanceof ExpressionImpl { } - BinaryExpr() { this = TBinaryExpression(bexpr) } -} +/** + * A Binary assignment expression. + */ +final class AssignmentExpr extends Expr instanceof AssignmentExprImpl { } -class CallExpr extends Expr, TCallExpression { - BICEP::CallExpression cexpr; +final class BinaryExpr extends Expr instanceof BinaryExprImpl { } - override string getAPrimaryQlClass() { result = "CallExpr" } +final class MemberExpr extends Expr instanceof MemberExprImpl { + Expr getObject() { result = super.getObject() } - CallExpr() { this = TCallExpression(cexpr) } + PropertyIdentifier getProperty() { result = super.getProperty() } } -class LambdaExpr extends Expr, TLambdaExpression { - BICEP::LambdaExpression lexpr; - - override string getAPrimaryQlClass() { result = "LambdaExpr" } +final class ParenthesizedExpr extends Expr instanceof ParenthesizedExprImpl { } - LambdaExpr() { this = TLambdaExpression(lexpr) } -} +final class ResourceExpr extends Expr instanceof ResourceExprImpl { } -class MemberExpr extends Expr, TMemberExpression { - BICEP::MemberExpression mexpr; - - override string getAPrimaryQlClass() { result = "MemberExpr" } - - MemberExpr() { this = TMemberExpression(mexpr) } - - Expr getObject() { toBicepTreeSitter(result) = mexpr.getObject() } - - PropertyIdentifier getProperty() { toBicepTreeSitter(result) = mexpr.getProperty() } -} +final class SubscriptExpr extends Expr instanceof SubscriptExprImpl { } -class ParenthesizedExpr extends Expr, TParenthesizedExpression { - BICEP::ParenthesizedExpression pexpr; +final class TerenaryExpr extends Expr instanceof TerenaryExprImpl { } - override string getAPrimaryQlClass() { result = "ParenthesizedExpr" } +final class UnaryExpr extends Expr instanceof UnaryExprImpl { } - ParenthesizedExpr() { this = TParenthesizedExpression(pexpr) } +/** + * A Bicept Property Identifier. + */ +final class PropertyIdentifier extends Expr instanceof PropertyIdentifierImpl { + string getName() { result = super.getName() } } -class ResourceExpr extends Expr, TResourceExpression { - BICEP::ResourceExpression rexpr; - - override string getAPrimaryQlClass() { result = "ResourceExpr" } - - ResourceExpr() { this = TResourceExpression(rexpr) } -} - -class SubscriptExpr extends Expr, TSubscriptExpression { - BICEP::SubscriptExpression sexpr; - - override string getAPrimaryQlClass() { result = "SubscriptExpr" } - - SubscriptExpr() { this = TSubscriptExpression(sexpr) } -} - -class TerenaryExpr extends Expr, TTernaryExpression { - BICEP::TernaryExpression texpr; - - override string getAPrimaryQlClass() { result = "TerenaryExpr" } - - TerenaryExpr() { this = TTernaryExpression(texpr) } -} - -class UnaryExpr extends Expr, TUnaryExpression { - BICEP::UnaryExpression uexpr; - - override string getAPrimaryQlClass() { result = "UnaryExpr" } - - UnaryExpr() { this = TUnaryExpression(uexpr) } -} - -class PropertyIdentifier extends Expr, TPropertyIdentifier { - BICEP::PropertyIdentifier pidentifier; - - override string getAPrimaryQlClass() { result = "PropertyIdentifier" } - - PropertyIdentifier() { this = TPropertyIdentifier(pidentifier) } - - override string toString() { result = this.getName() } - - string getName() { result = pidentifier.getValue() } -} +final class Decorator extends Expr instanceof DecoratorImpl { } diff --git a/ql/lib/codeql/bicep/ast/Literal.qll b/ql/lib/codeql/bicep/ast/Literal.qll index d50c6eb..a3e5eab 100644 --- a/ql/lib/codeql/bicep/ast/Literal.qll +++ b/ql/lib/codeql/bicep/ast/Literal.qll @@ -1,60 +1,22 @@ -private import codeql.iac.ast.internal.Bicep -private import codeql.bicep.ast.AstNodes +private import codeql.bicep.ast.internal.AstNodes +private import codeql.bicep.ast.internal.Literal -class Literal extends BicepAstNode, TLiteral { - override string getAPrimaryQlClass() { result = "Literal" } +final class Literal extends BicepAstNode instanceof LiteralImpl { } - string getValue() { none() } +final class NullLiteral extends Literal instanceof NullLiteralImpl { } - override string toString() { result = this.getValue() } +final class NumberLiteral extends Literal instanceof NumberLiteralImpl { + int getNumber() { result = super.getNumber() } } -class NumberLiteral extends Literal, TNumber { - private BICEP::Number literal; - - override string getAPrimaryQlClass() { result = "NumberLiteral" } - - NumberLiteral() { this = TNumber(literal) } +final class BooleanLiteral extends Literal instanceof BooleanLiteralImpl { + boolean getBool() { result = super.getBool() } } -class NullLiteral extends Literal, TNull { - private BICEP::Null literal; - - override string getAPrimaryQlClass() { result = "NullLiteral" } - - NullLiteral() { this = TNull(literal) } -} - -class BooleanLiteral extends Literal, TBoolean { - private BICEP::Boolean literal; - - override string getAPrimaryQlClass() { result = "BooleanLiteral" } - - BooleanLiteral() { this = TBoolean(literal) } - - boolean getBool() { result.toString() = literal.getValue() } +final class StringLiteral extends Literal instanceof StringLiteralImpl { + string getValue() { result = super.getValue() } } -class StringLiteral extends Literal, TString { - private BICEP::String literal; - - override string getAPrimaryQlClass() { result = "StringLiteral" } - - StringLiteral() { this = TString(literal) } - - override string getValue() { - exists(StringContent c | toBicepTreeSitter(c) = literal.getAFieldOrChild() | - result = c.getValue() - ) - } -} - -class StringContent extends Literal, TStringContent { - private BICEP::StringContent literal; - - override string getAPrimaryQlClass() { result = "StringContent" } - - StringContent() { this = TStringContent(literal) } - - override string getValue() { result = literal.getValue() } +final class StringContent extends Literal instanceof StringContentImpl { + string getValue() { result = super.getValue() } } diff --git a/ql/lib/codeql/bicep/ast/Loops.qll b/ql/lib/codeql/bicep/ast/Loops.qll new file mode 100644 index 0000000..07a0fe9 --- /dev/null +++ b/ql/lib/codeql/bicep/ast/Loops.qll @@ -0,0 +1,24 @@ +private import codeql.bicep.ast.internal.AstNodes +private import codeql.bicep.ast.internal.Loops +private import codeql.bicep.ast.Expr + +/** + * A Bicep loop statement. + */ +final class ForStatement extends Expr instanceof ForStatementImpl { + Identifier getInitializer() { result = super.getInitializer() } + + Expr getCondition() { result = super.getCondition() } + + Expr getBody() { result = super.getBody() } +} + +/** + * A Bicep loop enumerator. + */ +final class LoopEnumerator extends Expr instanceof LoopEnumeratorImpl { } + +/** + * A Bicep loop variable. + */ +final class LoopVariable extends Expr instanceof LoopVariableImpl { } diff --git a/ql/lib/codeql/bicep/ast/Object.qll b/ql/lib/codeql/bicep/ast/Object.qll index 352f558..f335696 100644 --- a/ql/lib/codeql/bicep/ast/Object.qll +++ b/ql/lib/codeql/bicep/ast/Object.qll @@ -1,47 +1,30 @@ -private import codeql.iac.ast.internal.Bicep -private import codeql.bicep.ast.AstNodes -private import codeql.bicep.ast.Literal +private import codeql.bicep.ast.internal.AstNodes +private import codeql.bicep.ast.internal.Object private import codeql.bicep.ast.Expr -class Object extends Expr, TObject { - private BICEP::Object object; +/** + * A Bicep Object. + */ +final class Object extends BicepAstNode instanceof ObjectImpl { + ObjectProperty getProperties() { result = super.getProperties() } - override string getAPrimaryQlClass() { result = "Object" } - - Object() { this = TObject(object) } - - ObjectProperty getProperties() { toBicepTreeSitter(result) = object.getAFieldOrChild() } - - Expr getProperty(string name) { - exists(ObjectProperty prop | object.getAFieldOrChild() = toBicepTreeSitter(prop) | - prop.getKey().(Identifier).getName() = name and - result = prop.getValue() - ) - } + Expr getProperty(string name) { result = super.getProperty(name) } } -class ObjectProperty extends BicepAstNode, TObjectProperty { - private BICEP::ObjectProperty property; - - override string getAPrimaryQlClass() { result = "ObjectProperty" } - - ObjectProperty() { this = TObjectProperty(property) } +/** + * A Bicep Object property. + */ +final class ObjectProperty extends BicepAstNode instanceof ObjectPropertyImpl { + Identifier getKey() { result = super.getKey() } - override string toString() { result = this.getKey().getName() + " = " + this.getValue() } - - Identifier getKey() { toBicepTreeSitter(result) = property.getChild(0) } - - Expr getValue() { toBicepTreeSitter(result) = property.getChild(1) } + Expr getValue() { result = super.getValue() } } -class Array extends Expr, TArray { - private BICEP::Array array; - - override string getAPrimaryQlClass() { result = "Array" } - - Array() { this = TArray(array) } - - Expr getElements() { toBicepTreeSitter(result) = array.getAFieldOrChild() } +/** + * A Bicep Array. + */ +final class Array extends Expr instanceof ArrayImpl { + Expr getElements() { result = super.getElements() } - Expr getElement(int index) { toBicepTreeSitter(result) = array.getChild(index) } + Expr getElement(int index) { result = super.getElement(index) } } diff --git a/ql/lib/codeql/bicep/ast/Resources.qll b/ql/lib/codeql/bicep/ast/Resources.qll index 60e0eb2..f21b6b5 100644 --- a/ql/lib/codeql/bicep/ast/Resources.qll +++ b/ql/lib/codeql/bicep/ast/Resources.qll @@ -1,50 +1,22 @@ -private import codeql.iac.ast.internal.Bicep -private import codeql.bicep.ast.AstNodes -private import codeql.bicep.ast.Literal -private import codeql.bicep.ast.Object +private import codeql.bicep.ast.internal.AstNodes +private import codeql.bicep.ast.internal.Resources private import codeql.bicep.ast.Expr +private import codeql.bicep.ast.Object -Resource resolveResource(Expr expr) { - exists(Resource resource | - // Object having an id property needs to be resolved - // {resource.id}.id - exists(MemberExpr memexpr | - memexpr = expr.(Object).getProperty("id") and - memexpr.getObject().(Identifier).getName() = resource.getIdentifier().(Identifier).getName() - | - result = resource - ) - or - exists(Identifier ident | - ident = expr and - ident.getName() = resource.getIdentifier().(Identifier).getName() - | - result = resource - ) - ) +final class Infrastructure extends BicepAstNode instanceof InfrastructureImpl { + Statement getStatement(int i) { result = super.getStatement(i) } } -class Resource extends BicepAstNode, TResourceDeclaration { - private BICEP::ResourceDeclaration resource; - - override string getAPrimaryQlClass() { result = "ResourceDeclaration" } +final class Statement extends BicepAstNode instanceof StatementImpl { } - Resource() { this = TResourceDeclaration(resource) } +final class Resource extends BicepAstNode instanceof ResourceImpl { + string getResourceType() { result = super.getResourceType() } - string getResourceType() { - exists(StringLiteral s | toBicepTreeSitter(s) = resource.getAFieldOrChild() | - result = s.getValue() - ) - } + Identifier getIdentifier() { result = super.getIdentifier() } - /** - * A name given to the resource instance that is unique within the template. - */ - Identifier getIdentifier() { toBicepTreeSitter(result) = resource.getChild(0) } + Object getBody() { result = super.getBody() } - Object getBody() { toBicepTreeSitter(result) = resource.getAFieldOrChild() } - - Expr getProperty(string name) { result = this.getBody().getProperty(name) } - - override Resource getParent() { result = resolveResource(this.getProperty("parent")) } + Expr getProperty(string name) { result = super.getProperty(name) } } + +final class Comment extends BicepAstNode instanceof CommentImpl { } diff --git a/ql/lib/codeql/bicep/ast/Variables.qll b/ql/lib/codeql/bicep/ast/Variables.qll new file mode 100644 index 0000000..e69de29 diff --git a/ql/lib/codeql/bicep/ast/AstNodes.qll b/ql/lib/codeql/bicep/ast/internal/AstNodes.qll similarity index 81% rename from ql/lib/codeql/bicep/ast/AstNodes.qll rename to ql/lib/codeql/bicep/ast/internal/AstNodes.qll index 16847a2..5fe2ece 100644 --- a/ql/lib/codeql/bicep/ast/AstNodes.qll +++ b/ql/lib/codeql/bicep/ast/internal/AstNodes.qll @@ -53,15 +53,3 @@ class BicepAstNode extends TBicepAstNode { */ string getAPrimaryQlClass() { result = "???" } } - -class Comment extends BicepAstNode, TComment { - override string getAPrimaryQlClass() { result = "Comment" } -} - -class Infrastructure extends BicepAstNode, TInfrastructure { - private BICEP::Infrastructure infrastructure; - - override string getAPrimaryQlClass() { result = "Infrastructure" } - - Infrastructure() { this = TInfrastructure(infrastructure) } -} diff --git a/ql/lib/codeql/bicep/ast/internal/Calls.qll b/ql/lib/codeql/bicep/ast/internal/Calls.qll new file mode 100644 index 0000000..71bc27a --- /dev/null +++ b/ql/lib/codeql/bicep/ast/internal/Calls.qll @@ -0,0 +1,25 @@ +private import codeql.iac.ast.internal.Bicep +private import codeql.bicep.ast.internal.AstNodes +private import codeql.bicep.ast.internal.Expr + +/** + * A Bicep call expression. + */ +class CallExprImpl extends ExprImpl, TCallExpression { + BICEP::CallExpression cexpr; + + override string getAPrimaryQlClass() { result = "CallExpr" } + + CallExprImpl() { this = TCallExpression(cexpr) } +} + +/** + * A Bicep lambda expression. + */ +class LambdaExprImpl extends ExprImpl, TLambdaExpression { + BICEP::LambdaExpression lexpr; + + override string getAPrimaryQlClass() { result = "LambdaExpr" } + + LambdaExprImpl() { this = TLambdaExpression(lexpr) } +} diff --git a/ql/lib/codeql/bicep/ast/internal/Expr.qll b/ql/lib/codeql/bicep/ast/internal/Expr.qll new file mode 100644 index 0000000..eb48c7d --- /dev/null +++ b/ql/lib/codeql/bicep/ast/internal/Expr.qll @@ -0,0 +1,126 @@ +private import codeql.iac.ast.internal.Bicep +private import codeql.bicep.ast.internal.AstNodes + +/** + * A Bicep expression. + */ +class ExprImpl extends BicepAstNode, TExpr { + override string getAPrimaryQlClass() { result = "Expr" } +} + +/** + * A Bicep identifier. + */ +class IdentifierImpl extends ExprImpl, TIdentifier { + private BICEP::Identifier identifier; + + override string getAPrimaryQlClass() { result = "Identifier" } + + IdentifierImpl() { this = TIdentifier(identifier) } + + override string toString() { result = this.getName() } + + string getName() { result = identifier.getValue() } +} + +/** + * A Bicep expression. + */ +class ExpressionImpl extends ExprImpl, TExpression { + private BICEP::Expression expression; + + override string getAPrimaryQlClass() { result = "Expression" } + + ExpressionImpl() { this = TExpression(expression) } +} + +/** + * A Binary assignment expression. + */ +class AssignmentExprImpl extends ExprImpl, TAssignmentExpression { + BICEP::AssignmentExpression aexpr; + + override string getAPrimaryQlClass() { result = "AssignmentExpr" } + + AssignmentExprImpl() { this = TAssignmentExpression(aexpr) } +} + +class BinaryExprImpl extends ExprImpl, TBinaryExpression { + BICEP::BinaryExpression bexpr; + + override string getAPrimaryQlClass() { result = "BinaryExpr" } + + BinaryExprImpl() { this = TBinaryExpression(bexpr) } +} + +class MemberExprImpl extends ExprImpl, TMemberExpression { + BICEP::MemberExpression mexpr; + + override string getAPrimaryQlClass() { result = "MemberExpr" } + + MemberExprImpl() { this = TMemberExpression(mexpr) } + + ExprImpl getObject() { toBicepTreeSitter(result) = mexpr.getObject() } + + PropertyIdentifierImpl getProperty() { toBicepTreeSitter(result) = mexpr.getProperty() } +} + +class ParenthesizedExprImpl extends ExprImpl, TParenthesizedExpression { + BICEP::ParenthesizedExpression pexpr; + + override string getAPrimaryQlClass() { result = "ParenthesizedExpr" } + + ParenthesizedExprImpl() { this = TParenthesizedExpression(pexpr) } +} + +class ResourceExprImpl extends ExprImpl, TResourceExpression { + BICEP::ResourceExpression rexpr; + + override string getAPrimaryQlClass() { result = "ResourceExpr" } + + ResourceExprImpl() { this = TResourceExpression(rexpr) } +} + +class SubscriptExprImpl extends ExprImpl, TSubscriptExpression { + BICEP::SubscriptExpression sexpr; + + override string getAPrimaryQlClass() { result = "SubscriptExpr" } + + SubscriptExprImpl() { this = TSubscriptExpression(sexpr) } +} + +class TerenaryExprImpl extends ExprImpl, TTernaryExpression { + BICEP::TernaryExpression texpr; + + override string getAPrimaryQlClass() { result = "TerenaryExpr" } + + TerenaryExprImpl() { this = TTernaryExpression(texpr) } +} + +class UnaryExprImpl extends ExprImpl, TUnaryExpression { + BICEP::UnaryExpression uexpr; + + override string getAPrimaryQlClass() { result = "UnaryExpr" } + + UnaryExprImpl() { this = TUnaryExpression(uexpr) } +} + +class PropertyIdentifierImpl extends ExprImpl, TPropertyIdentifier { + BICEP::PropertyIdentifier pidentifier; + + override string getAPrimaryQlClass() { result = "PropertyIdentifier" } + + PropertyIdentifierImpl() { this = TPropertyIdentifier(pidentifier) } + + override string toString() { result = this.getName() } + + string getName() { result = pidentifier.getValue() } +} + +class DecoratorImpl extends ExprImpl, TDecorator { + private BICEP::Decorator decorator; + + override string getAPrimaryQlClass() { result = "Decorator" } + + DecoratorImpl() { this = TDecorator(decorator) } +} diff --git a/ql/lib/codeql/bicep/ast/internal/Literal.qll b/ql/lib/codeql/bicep/ast/internal/Literal.qll new file mode 100644 index 0000000..489c082 --- /dev/null +++ b/ql/lib/codeql/bicep/ast/internal/Literal.qll @@ -0,0 +1,74 @@ +private import codeql.iac.ast.internal.Bicep +private import codeql.bicep.ast.internal.AstNodes + +class LiteralImpl extends BicepAstNode, TLiteral { + override string getAPrimaryQlClass() { result = "Literal" } + + string getValue() { result = this.getValue() } + + override string toString() { result = this.getValue() } +} + +/** + * A Bicep literal number. + */ +class NumberLiteralImpl extends LiteralImpl, TNumber { + private BICEP::Number literal; + + override string getAPrimaryQlClass() { result = "NumberLiteral" } + + NumberLiteralImpl() { this = TNumber(literal) } + + int getNumber() { result = literal.getValue().toInt() } +} + +/** + * A Bicep literal null. + */ +class NullLiteralImpl extends LiteralImpl, TNull { + private BICEP::Null literal; + + override string getAPrimaryQlClass() { result = "NullLiteral" } + + NullLiteralImpl() { this = TNull(literal) } +} + +/** + * A Bicep literal boolean. + */ +class BooleanLiteralImpl extends LiteralImpl, TBoolean { + private BICEP::Boolean literal; + + override string getAPrimaryQlClass() { result = "BooleanLiteral" } + + BooleanLiteralImpl() { this = TBoolean(literal) } + + boolean getBool() { result.toString() = literal.getValue() } +} + +class StringLiteralImpl extends LiteralImpl, TString { + private BICEP::String literal; + + override string getAPrimaryQlClass() { result = "StringLiteral" } + + StringLiteralImpl() { this = TString(literal) } + + override string getValue() { + exists(StringContentImpl c | toBicepTreeSitter(c) = literal.getAFieldOrChild() | + result = c.getValue() + ) + } +} + +/** + * A Bicep literal string content. + */ +class StringContentImpl extends LiteralImpl, TStringContent { + private BICEP::StringContent literal; + + override string getAPrimaryQlClass() { result = "StringContent" } + + StringContentImpl() { this = TStringContent(literal) } + + override string getValue() { result = literal.getValue() } +} diff --git a/ql/lib/codeql/bicep/ast/internal/Loops.qll b/ql/lib/codeql/bicep/ast/internal/Loops.qll new file mode 100644 index 0000000..48f6f0c --- /dev/null +++ b/ql/lib/codeql/bicep/ast/internal/Loops.qll @@ -0,0 +1,44 @@ +private import codeql.iac.ast.internal.Bicep +private import codeql.bicep.ast.internal.AstNodes +private import codeql.bicep.ast.internal.Expr + +/** + * A Bicep for loop statement. + */ +class ForStatementImpl extends ExprImpl, TForExpression { + BICEP::ForStatement fexpr; + + override string getAPrimaryQlClass() { result = "ForStatement" } + + ForStatementImpl() { this = TForExpression(fexpr) } + + IdentifierImpl getInitializer() { toBicepTreeSitter(result) = fexpr.getInitializer() } + + ExprImpl getCondition() { + // TODO + none() + } + + ExprImpl getBody() { toBicepTreeSitter(result) = fexpr.getBody() } +} + +/** + */ +class LoopEnumeratorImpl extends ExprImpl, TLoopEnumerator { + private BICEP::LoopEnumerator lenum; + + override string getAPrimaryQlClass() { result = "LoopEnumerator" } + + LoopEnumeratorImpl() { this = TLoopEnumerator(lenum) } +} + +/** + * A Bicep loop variable. + */ +class LoopVariableImpl extends ExprImpl, TLoopVariable { + private BICEP::LoopVariable lvar; + + override string getAPrimaryQlClass() { result = "LoopVariable" } + + LoopVariableImpl() { this = TLoopVariable(lvar) } +} diff --git a/ql/lib/codeql/bicep/ast/internal/Object.qll b/ql/lib/codeql/bicep/ast/internal/Object.qll new file mode 100644 index 0000000..6a59725 --- /dev/null +++ b/ql/lib/codeql/bicep/ast/internal/Object.qll @@ -0,0 +1,47 @@ +private import codeql.iac.ast.internal.Bicep +private import codeql.bicep.ast.internal.AstNodes +private import codeql.bicep.ast.internal.Literal +private import codeql.bicep.ast.internal.Expr + +class ObjectImpl extends ExprImpl, TObject { + private BICEP::Object object; + + override string getAPrimaryQlClass() { result = "Object" } + + ObjectImpl() { this = TObject(object) } + + ObjectPropertyImpl getProperties() { toBicepTreeSitter(result) = object.getAFieldOrChild() } + + ExprImpl getProperty(string name) { + exists(ObjectPropertyImpl prop | object.getAFieldOrChild() = toBicepTreeSitter(prop) | + prop.getKey().(IdentifierImpl).getName() = name and + result = prop.getValue() + ) + } +} + +class ObjectPropertyImpl extends BicepAstNode, TObjectProperty { + private BICEP::ObjectProperty property; + + override string getAPrimaryQlClass() { result = "ObjectProperty" } + + ObjectPropertyImpl() { this = TObjectProperty(property) } + + override string toString() { result = this.getKey().getName() + " = " + this.getValue() } + + IdentifierImpl getKey() { toBicepTreeSitter(result) = property.getChild(0) } + + ExprImpl getValue() { toBicepTreeSitter(result) = property.getChild(1) } +} + +class ArrayImpl extends ExprImpl, TArray { + private BICEP::Array array; + + override string getAPrimaryQlClass() { result = "Array" } + + ArrayImpl() { this = TArray(array) } + + ExprImpl getElements() { toBicepTreeSitter(result) = array.getAFieldOrChild() } + + ExprImpl getElement(int index) { toBicepTreeSitter(result) = array.getChild(index) } +} diff --git a/ql/lib/codeql/bicep/ast/internal/Resources.qll b/ql/lib/codeql/bicep/ast/internal/Resources.qll new file mode 100644 index 0000000..915e6fb --- /dev/null +++ b/ql/lib/codeql/bicep/ast/internal/Resources.qll @@ -0,0 +1,91 @@ +private import codeql.Locations +private import codeql.files.FileSystem +private import codeql.iac.ast.internal.Bicep +private import codeql.bicep.ast.internal.AstNodes +private import codeql.bicep.ast.internal.Resources +private import codeql.bicep.ast.internal.Object +private import codeql.bicep.ast.internal.Literal +private import codeql.bicep.ast.internal.Expr + +/** + * A Bicep program. + */ +class InfrastructureImpl extends BicepAstNode, TInfrastructure { + private BICEP::Infrastructure infrastructure; + + override string getAPrimaryQlClass() { result = "Infrastructure" } + + override string toString() { result = "Infrastructure" } + + InfrastructureImpl() { this = TInfrastructure(infrastructure) } + + StatementImpl getStatement(int i) { toBicepTreeSitter(result) = infrastructure.getChild(i) } + + StatementImpl getStatements() { toBicepTreeSitter(result) = infrastructure.getChild(_) } +} + +ResourceImpl resolveResource(ExprImpl expr) { + exists(ResourceImpl resource | + // Object having an id property needs to be resolved + // {resource.id}.id + exists(MemberExprImpl memexpr | + memexpr = expr.(ObjectImpl).getProperty("id") and + memexpr.getObject().(IdentifierImpl).getName() = + resource.getIdentifier().(IdentifierImpl).getName() + | + result = resource + ) + or + exists(IdentifierImpl ident | + ident = expr and + ident.getName() = resource.getIdentifier().(IdentifierImpl).getName() + | + result = resource + ) + ) +} + +class ResourceImpl extends BicepAstNode, TResourceDeclaration { + private BICEP::ResourceDeclaration resource; + + override string getAPrimaryQlClass() { result = "ResourceDeclaration" } + + ResourceImpl() { this = TResourceDeclaration(resource) } + + string getResourceType() { + exists(StringLiteralImpl s | toBicepTreeSitter(s) = resource.getAFieldOrChild() | + result = s.getValue() + ) + } + + /** + * A name given to the resource instance that is unique within the template. + */ + IdentifierImpl getIdentifier() { toBicepTreeSitter(result) = resource.getChild(0) } + + ObjectImpl getBody() { toBicepTreeSitter(result) = resource.getAFieldOrChild() } + + ExprImpl getProperty(string name) { result = this.getBody().getProperty(name) } + + override ResourceImpl getParent() { result = resolveResource(this.getProperty("parent")) } +} + +/** + * A Bicep statement. + */ +class StatementImpl extends BicepAstNode, TStatement { + private BICEP::Statement statement; + + override string getAPrimaryQlClass() { result = "Statement" } + + StatementImpl() { this = TStatement(statement) } +} + +/** + * A Bicep comment + */ +class CommentImpl extends BicepAstNode, TComment { + override string getAPrimaryQlClass() { result = "Comment" } + + CommentImpl() { this = TComment(_) } +} From d7d5cb42dbbe700c2f8ad05b39cb903ed5e1986e Mon Sep 17 00:00:00 2001 From: Mathew Payne Date: Thu, 19 Oct 2023 17:07:05 +0100 Subject: [PATCH 03/14] Fix Statement issue --- ql/lib/codeql/bicep/ast/Resources.qll | 4 ++-- ql/lib/codeql/bicep/ast/internal/Resources.qll | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/ql/lib/codeql/bicep/ast/Resources.qll b/ql/lib/codeql/bicep/ast/Resources.qll index f21b6b5..5c8e9ba 100644 --- a/ql/lib/codeql/bicep/ast/Resources.qll +++ b/ql/lib/codeql/bicep/ast/Resources.qll @@ -7,8 +7,6 @@ final class Infrastructure extends BicepAstNode instanceof InfrastructureImpl { Statement getStatement(int i) { result = super.getStatement(i) } } -final class Statement extends BicepAstNode instanceof StatementImpl { } - final class Resource extends BicepAstNode instanceof ResourceImpl { string getResourceType() { result = super.getResourceType() } @@ -19,4 +17,6 @@ final class Resource extends BicepAstNode instanceof ResourceImpl { Expr getProperty(string name) { result = super.getProperty(name) } } +final class Statement extends Expr instanceof StatementImpl { } + final class Comment extends BicepAstNode instanceof CommentImpl { } diff --git a/ql/lib/codeql/bicep/ast/internal/Resources.qll b/ql/lib/codeql/bicep/ast/internal/Resources.qll index 915e6fb..264eacd 100644 --- a/ql/lib/codeql/bicep/ast/internal/Resources.qll +++ b/ql/lib/codeql/bicep/ast/internal/Resources.qll @@ -73,7 +73,7 @@ class ResourceImpl extends BicepAstNode, TResourceDeclaration { /** * A Bicep statement. */ -class StatementImpl extends BicepAstNode, TStatement { +class StatementImpl extends ExprImpl, TStatement { private BICEP::Statement statement; override string getAPrimaryQlClass() { result = "Statement" } From 9e43f80db378d363ccc3176dcf00506b764fcf85 Mon Sep 17 00:00:00 2001 From: Mathew Payne Date: Thu, 19 Oct 2023 17:07:21 +0100 Subject: [PATCH 04/14] Update Ast tests --- ql/test/codeql-pack.lock.yml | 6 + ql/test/library-tests/bicep/ast/AST.expected | 259 +++++++++++++++++++ ql/test/library-tests/bicep/ast/AST.ql | 2 +- ql/test/library-tests/bicep/ast/loops.bicep | 22 ++ 4 files changed, 288 insertions(+), 1 deletion(-) create mode 100644 ql/test/library-tests/bicep/ast/loops.bicep diff --git a/ql/test/codeql-pack.lock.yml b/ql/test/codeql-pack.lock.yml index bf05bf3..6a33199 100644 --- a/ql/test/codeql-pack.lock.yml +++ b/ql/test/codeql-pack.lock.yml @@ -1,6 +1,12 @@ --- lockVersion: 1.0.0 dependencies: + codeql/controlflow: + version: 0.0.1 + codeql/dataflow: + version: 0.0.1 + codeql/ssa: + version: 0.1.2 codeql/util: version: 0.1.2 codeql/yaml: diff --git a/ql/test/library-tests/bicep/ast/AST.expected b/ql/test/library-tests/bicep/ast/AST.expected index cd0a3d5..9a19659 100644 --- a/ql/test/library-tests/bicep/ast/AST.expected +++ b/ql/test/library-tests/bicep/ast/AST.expected @@ -1,3 +1,260 @@ +| loops.bicep:1:1:1:48 | Statement | +| loops.bicep:1:1:22:3 | Infrastructure | +| loops.bicep:1:7:1:14 | ??? | +| loops.bicep:1:7:1:14 | Expression | +| loops.bicep:1:7:1:14 | location | +| loops.bicep:1:25:1:37 | ??? | +| loops.bicep:1:25:1:37 | Expression | +| loops.bicep:1:25:1:37 | resourceGroup | +| loops.bicep:1:25:1:39 | ??? | +| loops.bicep:1:25:1:39 | CallExpr | +| loops.bicep:1:25:1:39 | Expression | +| loops.bicep:1:25:1:48 | ??? | +| loops.bicep:1:25:1:48 | Expression | +| loops.bicep:1:25:1:48 | MemberExpr | +| loops.bicep:1:41:1:48 | location | +| loops.bicep:2:1:2:23 | Statement | +| loops.bicep:2:7:2:15 | ??? | +| loops.bicep:2:7:2:15 | Expression | +| loops.bicep:2:7:2:15 | itemCount | +| loops.bicep:2:23:2:23 | (no string representation) | +| loops.bicep:2:23:2:23 | ??? | +| loops.bicep:2:23:2:23 | Expression | +| loops.bicep:3:1:3:26 | Statement | +| loops.bicep:3:7:3:18 | ??? | +| loops.bicep:3:7:3:18 | Expression | +| loops.bicep:3:7:3:18 | storageCount | +| loops.bicep:3:26:3:26 | (no string representation) | +| loops.bicep:3:26:3:26 | ??? | +| loops.bicep:3:26:3:26 | Expression | +| loops.bicep:5:1:5:66 | Statement | +| loops.bicep:5:5:5:15 | ??? | +| loops.bicep:5:5:5:15 | Expression | +| loops.bicep:5:5:5:15 | stringArray | +| loops.bicep:5:19:5:66 | ??? | +| loops.bicep:5:19:5:66 | Expression | +| loops.bicep:5:19:5:66 | ForStatement | +| loops.bicep:5:24:5:24 | ??? | +| loops.bicep:5:24:5:24 | Expression | +| loops.bicep:5:24:5:24 | i | +| loops.bicep:5:29:5:33 | ??? | +| loops.bicep:5:29:5:33 | Expression | +| loops.bicep:5:29:5:33 | range | +| loops.bicep:5:29:5:47 | ??? | +| loops.bicep:5:29:5:47 | CallExpr | +| loops.bicep:5:29:5:47 | Expression | +| loops.bicep:5:35:5:35 | (no string representation) | +| loops.bicep:5:35:5:35 | ??? | +| loops.bicep:5:35:5:35 | Expression | +| loops.bicep:5:38:5:46 | ??? | +| loops.bicep:5:38:5:46 | Expression | +| loops.bicep:5:38:5:46 | itemCount | +| loops.bicep:5:50:5:65 | ??? | +| loops.bicep:5:50:5:65 | Expression | +| loops.bicep:5:50:5:65 | item | +| loops.bicep:5:51:5:54 | item | +| loops.bicep:5:57:5:63 | ??? | +| loops.bicep:5:57:5:63 | Expression | +| loops.bicep:5:57:5:63 | ParenthesizedExpr | +| loops.bicep:5:58:5:58 | ??? | +| loops.bicep:5:58:5:58 | Expression | +| loops.bicep:5:58:5:58 | i | +| loops.bicep:5:58:5:62 | BinaryExpr | +| loops.bicep:5:58:5:62 | Expression | +| loops.bicep:5:62:5:62 | (no string representation) | +| loops.bicep:5:62:5:62 | ??? | +| loops.bicep:5:62:5:62 | Expression | +| loops.bicep:7:1:7:38 | Statement | +| loops.bicep:7:8:7:18 | ??? | +| loops.bicep:7:8:7:18 | Expression | +| loops.bicep:7:8:7:18 | arrayResult | +| loops.bicep:7:28:7:38 | ??? | +| loops.bicep:7:28:7:38 | Expression | +| loops.bicep:7:28:7:38 | stringArray | +| loops.bicep:9:1:16:2 | ResourceDeclaration | +| loops.bicep:9:1:16:2 | Statement | +| loops.bicep:9:10:9:20 | ??? | +| loops.bicep:9:10:9:20 | Expression | +| loops.bicep:9:10:9:20 | storageAcct | +| loops.bicep:9:22:9:67 | ??? | +| loops.bicep:9:22:9:67 | Expression | +| loops.bicep:9:22:9:67 | Microsoft.Storage/storageAccounts@2022-09-01 | +| loops.bicep:9:23:9:66 | Microsoft.Storage/storageAccounts@2022-09-01 | +| loops.bicep:9:71:16:2 | ??? | +| loops.bicep:9:71:16:2 | Expression | +| loops.bicep:9:71:16:2 | ForStatement | +| loops.bicep:9:76:9:76 | ??? | +| loops.bicep:9:76:9:76 | Expression | +| loops.bicep:9:76:9:76 | i | +| loops.bicep:9:81:9:85 | ??? | +| loops.bicep:9:81:9:85 | Expression | +| loops.bicep:9:81:9:85 | range | +| loops.bicep:9:81:9:102 | ??? | +| loops.bicep:9:81:9:102 | CallExpr | +| loops.bicep:9:81:9:102 | Expression | +| loops.bicep:9:87:9:87 | (no string representation) | +| loops.bicep:9:87:9:87 | ??? | +| loops.bicep:9:87:9:87 | Expression | +| loops.bicep:9:90:9:101 | ??? | +| loops.bicep:9:90:9:101 | Expression | +| loops.bicep:9:90:9:101 | storageCount | +| loops.bicep:9:105:16:1 | ??? | +| loops.bicep:9:105:16:1 | Expression | +| loops.bicep:9:105:16:1 | Object | +| loops.bicep:10:3:10:6 | ??? | +| loops.bicep:10:3:10:6 | Expression | +| loops.bicep:10:3:10:6 | name | +| loops.bicep:10:3:10:56 | name = Expression | +| loops.bicep:10:3:10:56 | name = storage | +| loops.bicep:10:9:10:56 | ??? | +| loops.bicep:10:9:10:56 | Expression | +| loops.bicep:10:9:10:56 | storage | +| loops.bicep:10:12:10:12 | ??? | +| loops.bicep:10:12:10:12 | Expression | +| loops.bicep:10:12:10:12 | i | +| loops.bicep:10:14:10:20 | storage | +| loops.bicep:10:23:10:34 | ??? | +| loops.bicep:10:23:10:34 | Expression | +| loops.bicep:10:23:10:34 | uniqueString | +| loops.bicep:10:23:10:54 | ??? | +| loops.bicep:10:23:10:54 | CallExpr | +| loops.bicep:10:23:10:54 | Expression | +| loops.bicep:10:36:10:48 | ??? | +| loops.bicep:10:36:10:48 | Expression | +| loops.bicep:10:36:10:48 | resourceGroup | +| loops.bicep:10:36:10:50 | ??? | +| loops.bicep:10:36:10:50 | CallExpr | +| loops.bicep:10:36:10:50 | Expression | +| loops.bicep:10:36:10:53 | ??? | +| loops.bicep:10:36:10:53 | Expression | +| loops.bicep:10:36:10:53 | MemberExpr | +| loops.bicep:10:52:10:53 | id | +| loops.bicep:11:3:11:10 | ??? | +| loops.bicep:11:3:11:10 | Expression | +| loops.bicep:11:3:11:10 | location | +| loops.bicep:11:3:11:20 | location = Expression | +| loops.bicep:11:3:11:20 | location = location | +| loops.bicep:11:13:11:20 | ??? | +| loops.bicep:11:13:11:20 | Expression | +| loops.bicep:11:13:11:20 | location | +| loops.bicep:12:3:12:5 | ??? | +| loops.bicep:12:3:12:5 | Expression | +| loops.bicep:12:3:12:5 | sku | +| loops.bicep:12:3:14:3 | sku = Expression | +| loops.bicep:12:3:14:3 | sku = Object | +| loops.bicep:12:8:14:3 | ??? | +| loops.bicep:12:8:14:3 | Expression | +| loops.bicep:12:8:14:3 | Object | +| loops.bicep:13:5:13:8 | ??? | +| loops.bicep:13:5:13:8 | Expression | +| loops.bicep:13:5:13:8 | name | +| loops.bicep:13:5:13:24 | name = Expression | +| loops.bicep:13:5:13:24 | name = Standard_LRS | +| loops.bicep:13:11:13:24 | ??? | +| loops.bicep:13:11:13:24 | Expression | +| loops.bicep:13:11:13:24 | Standard_LRS | +| loops.bicep:13:12:13:23 | Standard_LRS | +| loops.bicep:15:3:15:6 | ??? | +| loops.bicep:15:3:15:6 | Expression | +| loops.bicep:15:3:15:6 | kind | +| loops.bicep:15:3:15:17 | kind = Expression | +| loops.bicep:15:3:15:17 | kind = Storage | +| loops.bicep:15:9:15:17 | ??? | +| loops.bicep:15:9:15:17 | Expression | +| loops.bicep:15:9:15:17 | Storage | +| loops.bicep:15:10:15:16 | Storage | +| loops.bicep:18:1:22:2 | Statement | +| loops.bicep:18:8:18:18 | ??? | +| loops.bicep:18:8:18:18 | Expression | +| loops.bicep:18:8:18:18 | storageInfo | +| loops.bicep:18:28:22:2 | ??? | +| loops.bicep:18:28:22:2 | Expression | +| loops.bicep:18:28:22:2 | ForStatement | +| loops.bicep:18:33:18:33 | ??? | +| loops.bicep:18:33:18:33 | Expression | +| loops.bicep:18:33:18:33 | i | +| loops.bicep:18:38:18:42 | ??? | +| loops.bicep:18:38:18:42 | Expression | +| loops.bicep:18:38:18:42 | range | +| loops.bicep:18:38:18:59 | ??? | +| loops.bicep:18:38:18:59 | CallExpr | +| loops.bicep:18:38:18:59 | Expression | +| loops.bicep:18:44:18:44 | (no string representation) | +| loops.bicep:18:44:18:44 | ??? | +| loops.bicep:18:44:18:44 | Expression | +| loops.bicep:18:47:18:58 | ??? | +| loops.bicep:18:47:18:58 | Expression | +| loops.bicep:18:47:18:58 | storageCount | +| loops.bicep:18:62:22:1 | ??? | +| loops.bicep:18:62:22:1 | Expression | +| loops.bicep:18:62:22:1 | Object | +| loops.bicep:19:3:19:4 | ??? | +| loops.bicep:19:3:19:4 | Expression | +| loops.bicep:19:3:19:4 | id | +| loops.bicep:19:3:19:23 | id = Expression | +| loops.bicep:19:3:19:23 | id = MemberExpr | +| loops.bicep:19:7:19:17 | ??? | +| loops.bicep:19:7:19:17 | Expression | +| loops.bicep:19:7:19:17 | storageAcct | +| loops.bicep:19:7:19:20 | ??? | +| loops.bicep:19:7:19:20 | Expression | +| loops.bicep:19:7:19:20 | SubscriptExpr | +| loops.bicep:19:7:19:23 | ??? | +| loops.bicep:19:7:19:23 | Expression | +| loops.bicep:19:7:19:23 | MemberExpr | +| loops.bicep:19:19:19:19 | ??? | +| loops.bicep:19:19:19:19 | Expression | +| loops.bicep:19:19:19:19 | i | +| loops.bicep:19:22:19:23 | id | +| loops.bicep:20:3:20:14 | ??? | +| loops.bicep:20:3:20:14 | Expression | +| loops.bicep:20:3:20:14 | blobEndpoint | +| loops.bicep:20:3:20:63 | blobEndpoint = Expression | +| loops.bicep:20:3:20:63 | blobEndpoint = MemberExpr | +| loops.bicep:20:17:20:27 | ??? | +| loops.bicep:20:17:20:27 | Expression | +| loops.bicep:20:17:20:27 | storageAcct | +| loops.bicep:20:17:20:30 | ??? | +| loops.bicep:20:17:20:30 | Expression | +| loops.bicep:20:17:20:30 | SubscriptExpr | +| loops.bicep:20:17:20:41 | ??? | +| loops.bicep:20:17:20:41 | Expression | +| loops.bicep:20:17:20:41 | MemberExpr | +| loops.bicep:20:17:20:58 | ??? | +| loops.bicep:20:17:20:58 | Expression | +| loops.bicep:20:17:20:58 | MemberExpr | +| loops.bicep:20:17:20:63 | ??? | +| loops.bicep:20:17:20:63 | Expression | +| loops.bicep:20:17:20:63 | MemberExpr | +| loops.bicep:20:29:20:29 | ??? | +| loops.bicep:20:29:20:29 | Expression | +| loops.bicep:20:29:20:29 | i | +| loops.bicep:20:32:20:41 | properties | +| loops.bicep:20:43:20:58 | primaryEndpoints | +| loops.bicep:20:60:20:63 | blob | +| loops.bicep:21:3:21:8 | ??? | +| loops.bicep:21:3:21:8 | Expression | +| loops.bicep:21:3:21:8 | status | +| loops.bicep:21:3:21:51 | status = Expression | +| loops.bicep:21:3:21:51 | status = MemberExpr | +| loops.bicep:21:11:21:21 | ??? | +| loops.bicep:21:11:21:21 | Expression | +| loops.bicep:21:11:21:21 | storageAcct | +| loops.bicep:21:11:21:24 | ??? | +| loops.bicep:21:11:21:24 | Expression | +| loops.bicep:21:11:21:24 | SubscriptExpr | +| loops.bicep:21:11:21:35 | ??? | +| loops.bicep:21:11:21:35 | Expression | +| loops.bicep:21:11:21:35 | MemberExpr | +| loops.bicep:21:11:21:51 | ??? | +| loops.bicep:21:11:21:51 | Expression | +| loops.bicep:21:11:21:51 | MemberExpr | +| loops.bicep:21:23:21:23 | ??? | +| loops.bicep:21:23:21:23 | Expression | +| loops.bicep:21:23:21:23 | i | +| loops.bicep:21:26:21:35 | properties | +| loops.bicep:21:37:21:51 | statusOfPrimary | +| sample.bicep:1:1:1:48 | Statement | | sample.bicep:1:1:14:2 | Infrastructure | | sample.bicep:1:7:1:14 | ??? | | sample.bicep:1:7:1:14 | Expression | @@ -12,6 +269,7 @@ | sample.bicep:1:25:1:48 | Expression | | sample.bicep:1:25:1:48 | MemberExpr | | sample.bicep:1:41:1:48 | location | +| sample.bicep:2:1:2:80 | Statement | | sample.bicep:2:7:2:24 | ??? | | sample.bicep:2:7:2:24 | Expression | | sample.bicep:2:7:2:24 | storageAccountName | @@ -36,6 +294,7 @@ | sample.bicep:2:60:2:77 | MemberExpr | | sample.bicep:2:76:2:77 | id | | sample.bicep:4:1:14:1 | ResourceDeclaration | +| sample.bicep:4:1:14:1 | Statement | | sample.bicep:4:10:4:23 | ??? | | sample.bicep:4:10:4:23 | Expression | | sample.bicep:4:10:4:23 | storageAccount | diff --git a/ql/test/library-tests/bicep/ast/AST.ql b/ql/test/library-tests/bicep/ast/AST.ql index 7c6d206..3d449d0 100644 --- a/ql/test/library-tests/bicep/ast/AST.ql +++ b/ql/test/library-tests/bicep/ast/AST.ql @@ -1,3 +1,3 @@ private import codeql.iac.ast.Bicep -query predicate ast(BicepAstNode ast) { any() } +query predicate ast(AstNode ast) { any() } diff --git a/ql/test/library-tests/bicep/ast/loops.bicep b/ql/test/library-tests/bicep/ast/loops.bicep new file mode 100644 index 0000000..68076d8 --- /dev/null +++ b/ql/test/library-tests/bicep/ast/loops.bicep @@ -0,0 +1,22 @@ +param location string = resourceGroup().location +param itemCount int = 5 +param storageCount int = 2 + +var stringArray = [for i in range(0, itemCount): 'item${(i + 1)}'] + +output arrayResult array = stringArray + +resource storageAcct 'Microsoft.Storage/storageAccounts@2022-09-01' = [for i in range(0, storageCount): { + name: '${i}storage${uniqueString(resourceGroup().id)}' + location: location + sku: { + name: 'Standard_LRS' + } + kind: 'Storage' +}] + +output storageInfo array = [for i in range(0, storageCount): { + id: storageAcct[i].id + blobEndpoint: storageAcct[i].properties.primaryEndpoints.blob + status: storageAcct[i].properties.statusOfPrimary +}] From 4e87b2ef321c3ee1b0d7403b793a34032bd9e92c Mon Sep 17 00:00:00 2001 From: Mathew Payne Date: Thu, 19 Oct 2023 17:07:41 +0100 Subject: [PATCH 05/14] Remove Bicep from Azure --- ql/lib/codeql/iac/azure/Bicep.qll | 10 ---------- 1 file changed, 10 deletions(-) delete mode 100644 ql/lib/codeql/iac/azure/Bicep.qll diff --git a/ql/lib/codeql/iac/azure/Bicep.qll b/ql/lib/codeql/iac/azure/Bicep.qll deleted file mode 100644 index b292a7c..0000000 --- a/ql/lib/codeql/iac/azure/Bicep.qll +++ /dev/null @@ -1,10 +0,0 @@ -private import codeql.files.FileSystem - -module Bicep { - /** - * All extracted Bicep files. - */ - class BicepFile extends File { - BicepFile() { this.getExtension() = "bicep" } - } -} From 4643894d72922bc2637a22071c45c9d5d3517360 Mon Sep 17 00:00:00 2001 From: Mathew Payne Date: Thu, 19 Oct 2023 17:08:13 +0100 Subject: [PATCH 06/14] Update Iac module --- ql/lib/codeql/iac/AST.qll | 2 ++ ql/lib/codeql/iac/ast/Bicep.qll | 2 +- ql/lib/codeql/iac/ast/internal/Bicep.qll | 30 +++++++++++++++++++----- 3 files changed, 27 insertions(+), 7 deletions(-) create mode 100644 ql/lib/codeql/iac/AST.qll diff --git a/ql/lib/codeql/iac/AST.qll b/ql/lib/codeql/iac/AST.qll new file mode 100644 index 0000000..57132df --- /dev/null +++ b/ql/lib/codeql/iac/AST.qll @@ -0,0 +1,2 @@ +import codeql.iac.ast.Container +import codeql.iac.ast.Hcl diff --git a/ql/lib/codeql/iac/ast/Bicep.qll b/ql/lib/codeql/iac/ast/Bicep.qll index dc25f90..872b4ee 100644 --- a/ql/lib/codeql/iac/ast/Bicep.qll +++ b/ql/lib/codeql/iac/ast/Bicep.qll @@ -1 +1 @@ -import codeql.bicep.AST +import codeql.bicep.Ast diff --git a/ql/lib/codeql/iac/ast/internal/Bicep.qll b/ql/lib/codeql/iac/ast/internal/Bicep.qll index 6ffd895..88bc0b6 100644 --- a/ql/lib/codeql/iac/ast/internal/Bicep.qll +++ b/ql/lib/codeql/iac/ast/internal/Bicep.qll @@ -12,6 +12,8 @@ newtype TBicepAstNode = TStringContent(BICEP::StringContent s) or TMultilineStringContent(BICEP::MultilineStringContent m) or // Expressions + TStatement(BICEP::Statement s) or + TDecorator(BICEP::Decorator d) or TAssignmentExpression(BICEP::AssignmentExpression a) or TArray(BICEP::Array a) or TBinaryExpression(BICEP::BinaryExpression b) or @@ -30,20 +32,30 @@ newtype TBicepAstNode = TResourceDeclaration(BICEP::ResourceDeclaration r) or TObject(BICEP::Object o) or TObjectProperty(BICEP::ObjectProperty p) or - TIdentifier(BICEP::Identifier i) + TIdentifier(BICEP::Identifier i) or + // Loops + TForExpression(BICEP::ForStatement l) or + TLoopVariable(BICEP::LoopVariable l) or + TLoopEnumerator(BICEP::LoopEnumerator l) or + TForLoopParameters(BICEP::ForLoopParameters p) class TLiteral = TBoolean or TNull or TNumber or TString or TStringContent or TMultilineStringContent; +class TVariables = TLoopVariable; + class TDeclaration = TResourceDeclaration or TObject or TObjectProperty or TIdentifier; class TIdentifiers = TIdentifier or TPropertyIdentifier; +class TLoops = TForExpression or TLoopEnumerator; + class TExpr = - TLiteral or TArray or TAssignmentExpression or TBinaryExpression or TCallExpression or - TExpression or TLambdaExpression or TMemberExpression or TParenthesizedExpression or - TResourceExpression or TSubscriptExpression or TTernaryExpression or TUnaryExpression or - TIdentifiers or TObject or TObjectProperty; + TLiteral or TVariables or TStatement or TArray or TAssignmentExpression or TBinaryExpression or + TCallExpression or TExpression or TLambdaExpression or TMemberExpression or + TParenthesizedExpression or TResourceExpression or TSubscriptExpression or + TTernaryExpression or TUnaryExpression or TIdentifiers or TObject or TObjectProperty or + TLoops or TDecorator; cached BICEP::AstNode toBicepTreeSitter(TBicepAstNode n) { @@ -55,6 +67,8 @@ BICEP::AstNode toBicepTreeSitter(TBicepAstNode n) { n = TString(result) or n = TStringContent(result) or n = TMultilineStringContent(result) or + n = TStatement(result) or + n = TDecorator(result) or n = TAssignmentExpression(result) or n = TArray(result) or n = TBinaryExpression(result) or @@ -72,5 +86,9 @@ BICEP::AstNode toBicepTreeSitter(TBicepAstNode n) { n = TObject(result) or n = TObjectProperty(result) or n = TIdentifier(result) or - n = TPropertyIdentifier(result) + n = TPropertyIdentifier(result) or + n = TForExpression(result) or + n = TLoopEnumerator(result) or + n = TLoopVariable(result) or + n = TForLoopParameters(result) } From b39e3b290e6962c159a2960e78f323989db2287f Mon Sep 17 00:00:00 2001 From: Mathew Payne Date: Thu, 19 Oct 2023 17:17:18 +0100 Subject: [PATCH 07/14] Fix AstNode issue --- ql/lib/codeql/bicep/ast/Calls.qll | 6 +++--- ql/lib/codeql/bicep/ast/Expr.qll | 4 ++-- ql/lib/codeql/bicep/ast/Literal.qll | 4 ++-- ql/lib/codeql/bicep/ast/Loops.qll | 2 +- ql/lib/codeql/bicep/ast/Object.qll | 6 +++--- ql/lib/codeql/bicep/ast/Resources.qll | 8 ++++---- 6 files changed, 15 insertions(+), 15 deletions(-) diff --git a/ql/lib/codeql/bicep/ast/Calls.qll b/ql/lib/codeql/bicep/ast/Calls.qll index 06f7274..5ada3bf 100644 --- a/ql/lib/codeql/bicep/ast/Calls.qll +++ b/ql/lib/codeql/bicep/ast/Calls.qll @@ -1,6 +1,6 @@ -private import codeql.bicep.ast.internal.AstNodes +private import codeql.bicep.ast.Ast private import codeql.bicep.ast.internal.Calls -final class CallExpr extends BicepAstNode instanceof CallExprImpl { } +final class CallExpr extends AstNode instanceof CallExprImpl { } -final class LambdaExpr extends BicepAstNode instanceof LambdaExprImpl { } +final class LambdaExpr extends AstNode instanceof LambdaExprImpl { } diff --git a/ql/lib/codeql/bicep/ast/Expr.qll b/ql/lib/codeql/bicep/ast/Expr.qll index e13691d..338b19d 100644 --- a/ql/lib/codeql/bicep/ast/Expr.qll +++ b/ql/lib/codeql/bicep/ast/Expr.qll @@ -1,10 +1,10 @@ -private import codeql.bicep.ast.internal.AstNodes +private import codeql.bicep.ast.Ast private import codeql.bicep.ast.internal.Expr /** * A Bicep expression. */ -final class Expr extends BicepAstNode instanceof ExprImpl { } +final class Expr extends AstNode instanceof ExprImpl { } /** * A Bicep identifier. diff --git a/ql/lib/codeql/bicep/ast/Literal.qll b/ql/lib/codeql/bicep/ast/Literal.qll index a3e5eab..5d86725 100644 --- a/ql/lib/codeql/bicep/ast/Literal.qll +++ b/ql/lib/codeql/bicep/ast/Literal.qll @@ -1,7 +1,7 @@ -private import codeql.bicep.ast.internal.AstNodes +private import codeql.bicep.ast.Ast private import codeql.bicep.ast.internal.Literal -final class Literal extends BicepAstNode instanceof LiteralImpl { } +final class Literal extends AstNode instanceof LiteralImpl { } final class NullLiteral extends Literal instanceof NullLiteralImpl { } diff --git a/ql/lib/codeql/bicep/ast/Loops.qll b/ql/lib/codeql/bicep/ast/Loops.qll index 07a0fe9..7275ede 100644 --- a/ql/lib/codeql/bicep/ast/Loops.qll +++ b/ql/lib/codeql/bicep/ast/Loops.qll @@ -1,4 +1,4 @@ -private import codeql.bicep.ast.internal.AstNodes +private import codeql.bicep.ast.Ast private import codeql.bicep.ast.internal.Loops private import codeql.bicep.ast.Expr diff --git a/ql/lib/codeql/bicep/ast/Object.qll b/ql/lib/codeql/bicep/ast/Object.qll index f335696..dfbc925 100644 --- a/ql/lib/codeql/bicep/ast/Object.qll +++ b/ql/lib/codeql/bicep/ast/Object.qll @@ -1,11 +1,11 @@ -private import codeql.bicep.ast.internal.AstNodes +private import codeql.bicep.ast.Ast private import codeql.bicep.ast.internal.Object private import codeql.bicep.ast.Expr /** * A Bicep Object. */ -final class Object extends BicepAstNode instanceof ObjectImpl { +final class Object extends AstNode instanceof ObjectImpl { ObjectProperty getProperties() { result = super.getProperties() } Expr getProperty(string name) { result = super.getProperty(name) } @@ -14,7 +14,7 @@ final class Object extends BicepAstNode instanceof ObjectImpl { /** * A Bicep Object property. */ -final class ObjectProperty extends BicepAstNode instanceof ObjectPropertyImpl { +final class ObjectProperty extends AstNode instanceof ObjectPropertyImpl { Identifier getKey() { result = super.getKey() } Expr getValue() { result = super.getValue() } diff --git a/ql/lib/codeql/bicep/ast/Resources.qll b/ql/lib/codeql/bicep/ast/Resources.qll index 5c8e9ba..4d5d48d 100644 --- a/ql/lib/codeql/bicep/ast/Resources.qll +++ b/ql/lib/codeql/bicep/ast/Resources.qll @@ -1,13 +1,13 @@ -private import codeql.bicep.ast.internal.AstNodes +private import codeql.bicep.ast.Ast private import codeql.bicep.ast.internal.Resources private import codeql.bicep.ast.Expr private import codeql.bicep.ast.Object -final class Infrastructure extends BicepAstNode instanceof InfrastructureImpl { +final class Infrastructure extends AstNode instanceof InfrastructureImpl { Statement getStatement(int i) { result = super.getStatement(i) } } -final class Resource extends BicepAstNode instanceof ResourceImpl { +final class Resource extends AstNode instanceof ResourceImpl { string getResourceType() { result = super.getResourceType() } Identifier getIdentifier() { result = super.getIdentifier() } @@ -19,4 +19,4 @@ final class Resource extends BicepAstNode instanceof ResourceImpl { final class Statement extends Expr instanceof StatementImpl { } -final class Comment extends BicepAstNode instanceof CommentImpl { } +final class Comment extends AstNode instanceof CommentImpl { } From 3519a416701886579bb6598b616116f88a41ef49 Mon Sep 17 00:00:00 2001 From: Mathew Payne Date: Thu, 19 Oct 2023 17:21:49 +0100 Subject: [PATCH 08/14] Fix dep issues --- ql/lib/codeql-pack.lock.yml | 12 ++++++++++-- ql/lib/qlpack.yml | 8 ++++---- 2 files changed, 14 insertions(+), 6 deletions(-) diff --git a/ql/lib/codeql-pack.lock.yml b/ql/lib/codeql-pack.lock.yml index bf05bf3..653b167 100644 --- a/ql/lib/codeql-pack.lock.yml +++ b/ql/lib/codeql-pack.lock.yml @@ -1,8 +1,16 @@ --- lockVersion: 1.0.0 dependencies: + codeql/controlflow: + version: 0.1.0 + codeql/dataflow: + version: 0.1.0 + codeql/ssa: + version: 0.2.0 + codeql/typetracking: + version: 0.2.0 codeql/util: - version: 0.1.2 + version: 0.2.0 codeql/yaml: - version: 0.1.3 + version: 0.1.5 compiled: false diff --git a/ql/lib/qlpack.yml b/ql/lib/qlpack.yml index 9d76fc8..023fa4f 100644 --- a/ql/lib/qlpack.yml +++ b/ql/lib/qlpack.yml @@ -4,11 +4,11 @@ warnOnImplicitThis: false name: advanced-security/iac-all version: 0.2.0 dependencies: - codeql/util: 0.1.2 + codeql/util: ^0.2.0 codeql/yaml: ^0.1.2 - codeql/controlflow: ^0.1.1 - codeql/dataflow: ^0.1.1 - codeql/ssa: ^0.2.1 + codeql/controlflow: ^0.1.0 + codeql/dataflow: ^0.1.0 + codeql/ssa: ^0.2.0 dbscheme: iac.dbscheme extractor: iac upgrades: upgrades From 5e15f9cbf299070f0c369dbaca222ad8ca96dccd Mon Sep 17 00:00:00 2001 From: Mathew Payne Date: Thu, 19 Oct 2023 17:43:42 +0100 Subject: [PATCH 09/14] Add Bicep Types --- ql/lib/codeql/bicep/ast/Resources.qll | 9 ++++ .../codeql/bicep/ast/internal/Resources.qll | 50 +++++++++++++++++++ 2 files changed, 59 insertions(+) diff --git a/ql/lib/codeql/bicep/ast/Resources.qll b/ql/lib/codeql/bicep/ast/Resources.qll index 4d5d48d..2d451fc 100644 --- a/ql/lib/codeql/bicep/ast/Resources.qll +++ b/ql/lib/codeql/bicep/ast/Resources.qll @@ -19,4 +19,13 @@ final class Resource extends AstNode instanceof ResourceImpl { final class Statement extends Expr instanceof StatementImpl { } +// Types +final class Type extends AstNode instanceof TypeImpl { + string getType() { result = super.getType() } +} + +final class BuiltInType extends AstNode instanceof BuiltinTypeImpl { } + +final class PrimitiveType extends AstNode instanceof PrimitiveTypeImpl { } + final class Comment extends AstNode instanceof CommentImpl { } diff --git a/ql/lib/codeql/bicep/ast/internal/Resources.qll b/ql/lib/codeql/bicep/ast/internal/Resources.qll index 264eacd..e151c7a 100644 --- a/ql/lib/codeql/bicep/ast/internal/Resources.qll +++ b/ql/lib/codeql/bicep/ast/internal/Resources.qll @@ -81,6 +81,56 @@ class StatementImpl extends ExprImpl, TStatement { StatementImpl() { this = TStatement(statement) } } +class TypeImpl extends LiteralImpl, TType { + private BICEP::Type type; + + override string getAPrimaryQlClass() { result = "Type" } + + override string toString() { result = "Type: " + this.getType() } + + TypeImpl() { this = TType(type) } + + string getType() { result = getBuiltinType().getType() } + + BuiltinTypeImpl getBuiltinType() { toBicepTreeSitter(result) = type.getAFieldOrChild() } +} + +class BuiltinTypeImpl extends LiteralImpl, TBuiltinType { + private BICEP::BuiltinType builtinType; + + override string getAPrimaryQlClass() { result = "BuiltinType" } + + BuiltinTypeImpl() { this = TBuiltinType(builtinType) } + + string getType() { result = getPrimitiveType().getValue() } + + PrimitiveTypeImpl getPrimitiveType() { + toBicepTreeSitter(result) = builtinType.getAFieldOrChild() + } +} + +class PrimitiveTypeImpl extends LiteralImpl, TPrimitiveType { + private BICEP::PrimitiveType primitiveType; + + override string getAPrimaryQlClass() { result = "PrimitiveType" } + + PrimitiveTypeImpl() { this = TPrimitiveType(primitiveType) } + + override string getValue() { result = primitiveType.getValue() } +} + +class ParameterDeclarationImpl extends ExprImpl, TParameterDeclaration { + private BICEP::ParameterDeclaration parameter; + + override string getAPrimaryQlClass() { result = "ParameterDeclaration" } + + ParameterDeclarationImpl() { this = TParameterDeclaration(parameter) } + + IdentifierImpl getName() { toBicepTreeSitter(result) = parameter.getChild(0) } + + ExprImpl getDefaultValue() { toBicepTreeSitter(result) = parameter.getChild(1) } +} + /** * A Bicep comment */ From 49e51b29c5946dfd14a31254feab926bb5938e80 Mon Sep 17 00:00:00 2001 From: Mathew Payne Date: Thu, 19 Oct 2023 17:46:03 +0100 Subject: [PATCH 10/14] Import types --- ql/lib/codeql/bicep/ast/internal/Resources.qll | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/ql/lib/codeql/bicep/ast/internal/Resources.qll b/ql/lib/codeql/bicep/ast/internal/Resources.qll index e151c7a..86eb2f4 100644 --- a/ql/lib/codeql/bicep/ast/internal/Resources.qll +++ b/ql/lib/codeql/bicep/ast/internal/Resources.qll @@ -90,7 +90,7 @@ class TypeImpl extends LiteralImpl, TType { TypeImpl() { this = TType(type) } - string getType() { result = getBuiltinType().getType() } + string getType() { result = this.getBuiltinType().getType() } BuiltinTypeImpl getBuiltinType() { toBicepTreeSitter(result) = type.getAFieldOrChild() } } @@ -100,9 +100,11 @@ class BuiltinTypeImpl extends LiteralImpl, TBuiltinType { override string getAPrimaryQlClass() { result = "BuiltinType" } + override string toString() { result = "BuiltinType: " + this.getType() } + BuiltinTypeImpl() { this = TBuiltinType(builtinType) } - string getType() { result = getPrimitiveType().getValue() } + string getType() { result = this.getPrimitiveType().getValue() } PrimitiveTypeImpl getPrimitiveType() { toBicepTreeSitter(result) = builtinType.getAFieldOrChild() @@ -114,6 +116,8 @@ class PrimitiveTypeImpl extends LiteralImpl, TPrimitiveType { override string getAPrimaryQlClass() { result = "PrimitiveType" } + override string toString() { result = "PrimitiveType: " + this.getValue() } + PrimitiveTypeImpl() { this = TPrimitiveType(primitiveType) } override string getValue() { result = primitiveType.getValue() } From e886e8e55294df8cdb3c2f8329d3e9cccb0984dc Mon Sep 17 00:00:00 2001 From: Mathew Payne Date: Thu, 19 Oct 2023 17:49:49 +0100 Subject: [PATCH 11/14] Add Decorators support --- ql/lib/codeql/bicep/ast/Expr.qll | 6 ++++++ ql/lib/codeql/bicep/ast/internal/Expr.qll | 12 ++++++++++++ 2 files changed, 18 insertions(+) diff --git a/ql/lib/codeql/bicep/ast/Expr.qll b/ql/lib/codeql/bicep/ast/Expr.qll index 338b19d..481f018 100644 --- a/ql/lib/codeql/bicep/ast/Expr.qll +++ b/ql/lib/codeql/bicep/ast/Expr.qll @@ -48,4 +48,10 @@ final class PropertyIdentifier extends Expr instanceof PropertyIdentifierImpl { string getName() { result = super.getName() } } +final class Decorators extends Expr instanceof DecoratorsImpl { + Decorator getDecorator(int i) { result = super.getDecorator(i) } + + Decorator getDecorators() { result = super.getDecorators() } +} + final class Decorator extends Expr instanceof DecoratorImpl { } diff --git a/ql/lib/codeql/bicep/ast/internal/Expr.qll b/ql/lib/codeql/bicep/ast/internal/Expr.qll index eb48c7d..ccadc34 100644 --- a/ql/lib/codeql/bicep/ast/internal/Expr.qll +++ b/ql/lib/codeql/bicep/ast/internal/Expr.qll @@ -124,3 +124,15 @@ class DecoratorImpl extends ExprImpl, TDecorator { DecoratorImpl() { this = TDecorator(decorator) } } + +class DecoratorsImpl extends ExprImpl, TDecorators { + private BICEP::Decorators decorators; + + override string getAPrimaryQlClass() { result = "Decorator" } + + DecoratorsImpl() { this = TDecorators(decorators) } + + DecoratorImpl getDecorator(int i) { toBicepTreeSitter(result) = decorators.getChild(i) } + + DecoratorImpl getDecorators() { toBicepTreeSitter(result) = decorators.getChild(_) } +} From 9c2cd201f840b90d4e1d02584daaf79592b8e149 Mon Sep 17 00:00:00 2001 From: Mathew Payne Date: Thu, 19 Oct 2023 18:45:03 +0100 Subject: [PATCH 12/14] Update to Ast --- ql/lib/codeql/bicep/ast/Calls.qll | 13 +++- ql/lib/codeql/bicep/ast/Expr.qll | 8 --- ql/lib/codeql/bicep/ast/Resources.qll | 32 +++++++++- ql/lib/codeql/bicep/ast/internal/Calls.qll | 16 +++++ ql/lib/codeql/bicep/ast/internal/Expr.qll | 20 ------ .../codeql/bicep/ast/internal/Resources.qll | 61 ++++++++++++++----- 6 files changed, 102 insertions(+), 48 deletions(-) diff --git a/ql/lib/codeql/bicep/ast/Calls.qll b/ql/lib/codeql/bicep/ast/Calls.qll index 5ada3bf..9132e2b 100644 --- a/ql/lib/codeql/bicep/ast/Calls.qll +++ b/ql/lib/codeql/bicep/ast/Calls.qll @@ -1,6 +1,17 @@ private import codeql.bicep.ast.Ast private import codeql.bicep.ast.internal.Calls +private import codeql.bicep.ast.Expr -final class CallExpr extends AstNode instanceof CallExprImpl { } +final class CallExpr extends AstNode instanceof CallExprImpl { + Identifier getFunction() { result = super.getFunction() } + + Arguments getArguments() { result = super.getArguments() } +} + +final class Arguments extends AstNode instanceof ArgumentsImpl { + Expr getArgument(int i) { result = super.getArgument(i) } + + Expr getArguments() { result = super.getArguments() } +} final class LambdaExpr extends AstNode instanceof LambdaExprImpl { } diff --git a/ql/lib/codeql/bicep/ast/Expr.qll b/ql/lib/codeql/bicep/ast/Expr.qll index 481f018..1d47e74 100644 --- a/ql/lib/codeql/bicep/ast/Expr.qll +++ b/ql/lib/codeql/bicep/ast/Expr.qll @@ -47,11 +47,3 @@ final class UnaryExpr extends Expr instanceof UnaryExprImpl { } final class PropertyIdentifier extends Expr instanceof PropertyIdentifierImpl { string getName() { result = super.getName() } } - -final class Decorators extends Expr instanceof DecoratorsImpl { - Decorator getDecorator(int i) { result = super.getDecorator(i) } - - Decorator getDecorators() { result = super.getDecorators() } -} - -final class Decorator extends Expr instanceof DecoratorImpl { } diff --git a/ql/lib/codeql/bicep/ast/Resources.qll b/ql/lib/codeql/bicep/ast/Resources.qll index 2d451fc..7bf8b2e 100644 --- a/ql/lib/codeql/bicep/ast/Resources.qll +++ b/ql/lib/codeql/bicep/ast/Resources.qll @@ -4,10 +4,20 @@ private import codeql.bicep.ast.Expr private import codeql.bicep.ast.Object final class Infrastructure extends AstNode instanceof InfrastructureImpl { - Statement getStatement(int i) { result = super.getStatement(i) } + Stmt getStatement(int i) { result = super.getStatement(i) } + + Stmt getStatements() { result = super.getStatements() } +} + +final class Stmt extends AstNode instanceof StmtImpl { } + +final class Statement extends Stmt instanceof StatementImpl { + Expr getExpr() { result = super.getAChild() } } -final class Resource extends AstNode instanceof ResourceImpl { +final class Resource extends Stmt instanceof ResourceImpl { + string toString() { result = "Resource" } + string getResourceType() { result = super.getResourceType() } Identifier getIdentifier() { result = super.getIdentifier() } @@ -17,7 +27,23 @@ final class Resource extends AstNode instanceof ResourceImpl { Expr getProperty(string name) { result = super.getProperty(name) } } -final class Statement extends Expr instanceof StatementImpl { } +final class ParameterDeclaration extends Stmt instanceof ParameterDeclarationImpl { + Identifier getName() { result = super.getName() } + + Type getType() { result = super.getType() } + + Expr getDefaultValue() { result = super.getDefaultValue() } +} + +final class Decorators extends Stmt instanceof DecoratorsImpl { + string toString() { result = "Decorators" } + + Decorator getDecorator(int i) { result = super.getDecorator(i) } + + Decorator getDecorators() { result = super.getDecorators() } +} + +final class Decorator extends Stmt instanceof DecoratorImpl { } // Types final class Type extends AstNode instanceof TypeImpl { diff --git a/ql/lib/codeql/bicep/ast/internal/Calls.qll b/ql/lib/codeql/bicep/ast/internal/Calls.qll index 71bc27a..5b93fa0 100644 --- a/ql/lib/codeql/bicep/ast/internal/Calls.qll +++ b/ql/lib/codeql/bicep/ast/internal/Calls.qll @@ -11,6 +11,22 @@ class CallExprImpl extends ExprImpl, TCallExpression { override string getAPrimaryQlClass() { result = "CallExpr" } CallExprImpl() { this = TCallExpression(cexpr) } + + IdentifierImpl getFunction() { toBicepTreeSitter(result) = cexpr.getFunction() } + + ArgumentsImpl getArguments() { toBicepTreeSitter(result) = cexpr.getArguments() } +} + +class ArgumentsImpl extends BicepAstNode, TArguments { + private BICEP::Arguments args; + + override string getAPrimaryQlClass() { result = "Arguments" } + + ArgumentsImpl() { this = TArguments(args) } + + ExprImpl getArgument(int i) { toBicepTreeSitter(result) = args.getChild(i) } + + ExprImpl getArguments() { toBicepTreeSitter(result) = args.getChild(_) } } /** diff --git a/ql/lib/codeql/bicep/ast/internal/Expr.qll b/ql/lib/codeql/bicep/ast/internal/Expr.qll index ccadc34..2166eb9 100644 --- a/ql/lib/codeql/bicep/ast/internal/Expr.qll +++ b/ql/lib/codeql/bicep/ast/internal/Expr.qll @@ -116,23 +116,3 @@ class PropertyIdentifierImpl extends ExprImpl, TPropertyIdentifier { string getName() { result = pidentifier.getValue() } } - -class DecoratorImpl extends ExprImpl, TDecorator { - private BICEP::Decorator decorator; - - override string getAPrimaryQlClass() { result = "Decorator" } - - DecoratorImpl() { this = TDecorator(decorator) } -} - -class DecoratorsImpl extends ExprImpl, TDecorators { - private BICEP::Decorators decorators; - - override string getAPrimaryQlClass() { result = "Decorator" } - - DecoratorsImpl() { this = TDecorators(decorators) } - - DecoratorImpl getDecorator(int i) { toBicepTreeSitter(result) = decorators.getChild(i) } - - DecoratorImpl getDecorators() { toBicepTreeSitter(result) = decorators.getChild(_) } -} diff --git a/ql/lib/codeql/bicep/ast/internal/Resources.qll b/ql/lib/codeql/bicep/ast/internal/Resources.qll index 86eb2f4..c5236ef 100644 --- a/ql/lib/codeql/bicep/ast/internal/Resources.qll +++ b/ql/lib/codeql/bicep/ast/internal/Resources.qll @@ -6,6 +6,11 @@ private import codeql.bicep.ast.internal.Resources private import codeql.bicep.ast.internal.Object private import codeql.bicep.ast.internal.Literal private import codeql.bicep.ast.internal.Expr +private import codeql.bicep.ast.internal.Calls + +class StmtImpl extends BicepAstNode, TDeclaration { + override string getAPrimaryQlClass() { result = "Stmt" } +} /** * A Bicep program. @@ -19,9 +24,20 @@ class InfrastructureImpl extends BicepAstNode, TInfrastructure { InfrastructureImpl() { this = TInfrastructure(infrastructure) } - StatementImpl getStatement(int i) { toBicepTreeSitter(result) = infrastructure.getChild(i) } + StmtImpl getStatement(int i) { toBicepTreeSitter(result) = infrastructure.getChild(i) } - StatementImpl getStatements() { toBicepTreeSitter(result) = infrastructure.getChild(_) } + StmtImpl getStatements() { toBicepTreeSitter(result) = infrastructure.getChild(_) } +} + +/** + * A Bicep statement. + */ +class StatementImpl extends StmtImpl, TStatement { + private BICEP::Statement statement; + + override string getAPrimaryQlClass() { result = "Statement" } + + StatementImpl() { this = TStatement(statement) } } ResourceImpl resolveResource(ExprImpl expr) { @@ -45,7 +61,7 @@ ResourceImpl resolveResource(ExprImpl expr) { ) } -class ResourceImpl extends BicepAstNode, TResourceDeclaration { +class ResourceImpl extends StmtImpl, TResourceDeclaration { private BICEP::ResourceDeclaration resource; override string getAPrimaryQlClass() { result = "ResourceDeclaration" } @@ -70,17 +86,6 @@ class ResourceImpl extends BicepAstNode, TResourceDeclaration { override ResourceImpl getParent() { result = resolveResource(this.getProperty("parent")) } } -/** - * A Bicep statement. - */ -class StatementImpl extends ExprImpl, TStatement { - private BICEP::Statement statement; - - override string getAPrimaryQlClass() { result = "Statement" } - - StatementImpl() { this = TStatement(statement) } -} - class TypeImpl extends LiteralImpl, TType { private BICEP::Type type; @@ -123,7 +128,7 @@ class PrimitiveTypeImpl extends LiteralImpl, TPrimitiveType { override string getValue() { result = primitiveType.getValue() } } -class ParameterDeclarationImpl extends ExprImpl, TParameterDeclaration { +class ParameterDeclarationImpl extends StmtImpl, TParameterDeclaration { private BICEP::ParameterDeclaration parameter; override string getAPrimaryQlClass() { result = "ParameterDeclaration" } @@ -132,7 +137,31 @@ class ParameterDeclarationImpl extends ExprImpl, TParameterDeclaration { IdentifierImpl getName() { toBicepTreeSitter(result) = parameter.getChild(0) } - ExprImpl getDefaultValue() { toBicepTreeSitter(result) = parameter.getChild(1) } + TypeImpl getType() { toBicepTreeSitter(result) = parameter.getChild(1) } + + ExprImpl getDefaultValue() { toBicepTreeSitter(result) = parameter.getChild(2) } +} + +class DecoratorImpl extends StmtImpl, TDecorator { + private BICEP::Decorator decorator; + + override string getAPrimaryQlClass() { result = "Decorator" } + + DecoratorImpl() { this = TDecorator(decorator) } + + CallExprImpl getCallExpr() { toBicepTreeSitter(result) = decorator.getAFieldOrChild() } +} + +class DecoratorsImpl extends StmtImpl, TDecorators { + private BICEP::Decorators decorators; + + override string getAPrimaryQlClass() { result = "Decorator" } + + DecoratorsImpl() { this = TDecorators(decorators) } + + DecoratorImpl getDecorator(int i) { toBicepTreeSitter(result) = decorators.getChild(i) } + + DecoratorImpl getDecorators() { toBicepTreeSitter(result) = decorators.getChild(_) } } /** From e3bd32778070c0e8e389790b5e62f5da21ece04f Mon Sep 17 00:00:00 2001 From: Mathew Payne Date: Thu, 19 Oct 2023 18:45:43 +0100 Subject: [PATCH 13/14] Small update to Microsoft frameworks --- ql/lib/codeql/bicep/microsoft/Compute.qll | 12 +++++------- ql/lib/codeql/bicep/microsoft/Network.qll | 4 ++-- 2 files changed, 7 insertions(+), 9 deletions(-) diff --git a/ql/lib/codeql/bicep/microsoft/Compute.qll b/ql/lib/codeql/bicep/microsoft/Compute.qll index 2d9cfce..bca15dc 100644 --- a/ql/lib/codeql/bicep/microsoft/Compute.qll +++ b/ql/lib/codeql/bicep/microsoft/Compute.qll @@ -1,8 +1,6 @@ private import codeql.Locations -private import codeql.bicep.ast.Expr -private import codeql.bicep.ast.Object -private import codeql.bicep.ast.Resources -private import codeql.bicep.ast.Literal +private import codeql.bicep.Ast +private import codeql.bicep.ast.internal.Resources private import codeql.bicep.microsoft.Network /** @@ -22,7 +20,7 @@ module Compute { this.getResourceType().regexpMatch("^Microsoft.Compute/virtualMachines@.*") } - override string toString() { result = "VirtualMachines Resource" } + string toString() { result = "VirtualMachines Resource" } VirtualMachinesProperties::Properties getProperties() { result = this.getProperty("properties") @@ -65,7 +63,7 @@ module Compute { HardwareProfile() { this = properties.getProperty("hardwareProfile") } - override string toString() { result = "HardwareProfile" } + string toString() { result = "HardwareProfile" } Expr getVmSize() { result = this.getProperty("vmSize") } } @@ -78,7 +76,7 @@ module Compute { NetworkProfile() { this = properties.getProperty("networkProfile") } - override string toString() { result = "NetworkProfile" } + string toString() { result = "NetworkProfile" } Network::NetworkInterfaces getNetworkInterfaces() { result = resolveResource(this.getNetworkInterfacesObject()) diff --git a/ql/lib/codeql/bicep/microsoft/Network.qll b/ql/lib/codeql/bicep/microsoft/Network.qll index b8f72b2..84b49e1 100644 --- a/ql/lib/codeql/bicep/microsoft/Network.qll +++ b/ql/lib/codeql/bicep/microsoft/Network.qll @@ -20,7 +20,7 @@ module Network { this.getResourceType().regexpMatch("^Microsoft.Network/networkInterfaces@.*") } - override string toString() { result = "NetworkInterfaces Resource" } + string toString() { result = "NetworkInterfaces Resource" } NetworkInterfaceProperties::Properties getProperties() { result = this.getProperty("properties") @@ -65,7 +65,7 @@ module Network { this.getResourceType().regexpMatch("^Microsoft.Network/virtualNetworks@.*") } - override string toString() { result = "VirtualNetworks Resource" } + string toString() { result = "VirtualNetworks Resource" } /** * Get the properties object for the Microsoft.Network/virtualNetworks type From c7adcb9160069783447c9f5e6f313083790ca2c8 Mon Sep 17 00:00:00 2001 From: Mathew Payne Date: Thu, 19 Oct 2023 18:45:56 +0100 Subject: [PATCH 14/14] Initial CFG --- ql/lib/codeql/bicep/Cfg.qll | 4 + .../codeql/bicep/controlflow/BasicBlocks.qll | 440 ++++++++++++++++++ .../codeql/bicep/controlflow/internal/Cfg.qll | 188 ++++++++ ql/lib/ide-contextual-queries/printCfg.ql | 53 +++ 4 files changed, 685 insertions(+) create mode 100644 ql/lib/codeql/bicep/Cfg.qll create mode 100644 ql/lib/codeql/bicep/controlflow/BasicBlocks.qll create mode 100644 ql/lib/codeql/bicep/controlflow/internal/Cfg.qll create mode 100644 ql/lib/ide-contextual-queries/printCfg.ql diff --git a/ql/lib/codeql/bicep/Cfg.qll b/ql/lib/codeql/bicep/Cfg.qll new file mode 100644 index 0000000..dd8d9b6 --- /dev/null +++ b/ql/lib/codeql/bicep/Cfg.qll @@ -0,0 +1,4 @@ +private import codeql.bicep.controlflow.internal.Cfg as CfgInternal +import CfgInternal::Completion +import CfgInternal::CfgScope +import CfgInternal::CfgImpl diff --git a/ql/lib/codeql/bicep/controlflow/BasicBlocks.qll b/ql/lib/codeql/bicep/controlflow/BasicBlocks.qll new file mode 100644 index 0000000..272677e --- /dev/null +++ b/ql/lib/codeql/bicep/controlflow/BasicBlocks.qll @@ -0,0 +1,440 @@ +/** Provides classes representing basic blocks. */ + +private import codeql.bicep.Cfg +private import codeql.bicep.Ast +private import codeql.iac.ast.internal.Bicep +private import codeql.iac.ast.internal.TreeSitter +private import codeql.Locations + +/** + * A basic block, that is, a maximal straight-line sequence of control flow nodes + * without branches or joins. + */ +class BasicBlock extends TBasicBlockStart { + /** Gets the scope of this basic block. */ + final CfgScope getScope() { result = this.getFirstNode().getScope() } + + /** Gets an immediate successor of this basic block, if any. */ + BasicBlock getASuccessor() { result = this.getASuccessor(_) } + + /** Gets an immediate successor of this basic block of a given type, if any. */ + BasicBlock getASuccessor(SuccessorType t) { + result.getFirstNode() = this.getLastNode().getASuccessor(t) + } + + /** Gets an immediate predecessor of this basic block, if any. */ + BasicBlock getAPredecessor() { result.getASuccessor() = this } + + /** Gets an immediate predecessor of this basic block of a given type, if any. */ + BasicBlock getAPredecessor(SuccessorType t) { result.getASuccessor(t) = this } + + /** Gets the control flow node at a specific (zero-indexed) position in this basic block. */ + Node getNode(int pos) { bbIndex(this.getFirstNode(), result, pos) } + + /** Gets a control flow node in this basic block. */ + Node getANode() { result = this.getNode(_) } + + /** Gets the first control flow node in this basic block. */ + Node getFirstNode() { this = TBasicBlockStart(result) } + + /** Gets the last control flow node in this basic block. */ + Node getLastNode() { result = this.getNode(this.length() - 1) } + + /** Gets the length of this basic block. */ + int length() { result = strictcount(this.getANode()) } + + /** + * Holds if this basic block immediately dominates basic block `bb`. + * + * That is, all paths reaching basic block `bb` from some entry point + * basic block must go through this basic block (which is an immediate + * predecessor of `bb`). + * + * Example: + * + * ```rb + * def m b + * if b + * return 0 + * end + * return 1 + * end + * ``` + * + * The basic block starting on line 2 immediately dominates the + * basic block on line 5 (all paths from the entry point of `m` + * to `return 1` must go through the `if` block). + */ + predicate immediatelyDominates(BasicBlock bb) { bbIDominates(this, bb) } + + /** + * Holds if this basic block strictly dominates basic block `bb`. + * + * That is, all paths reaching basic block `bb` from some entry point + * basic block must go through this basic block (which must be different + * from `bb`). + * + * Example: + * + * ```rb + * def m b + * if b + * return 0 + * end + * return 1 + * end + * ``` + * + * The basic block starting on line 2 strictly dominates the + * basic block on line 5 (all paths from the entry point of `m` + * to `return 1` must go through the `if` block). + */ + predicate strictlyDominates(BasicBlock bb) { bbIDominates+(this, bb) } + + /** + * Holds if this basic block dominates basic block `bb`. + * + * That is, all paths reaching basic block `bb` from some entry point + * basic block must go through this basic block. + * + * Example: + * + * ```rb + * def m b + * if b + * return 0 + * end + * return 1 + * end + * ``` + * + * The basic block starting on line 2 dominates the basic + * basic block on line 5 (all paths from the entry point of `m` + * to `return 1` must go through the `if` block). + */ + predicate dominates(BasicBlock bb) { + bb = this or + this.strictlyDominates(bb) + } + + /** + * Holds if `df` is in the dominance frontier of this basic block. + * That is, this basic block dominates a predecessor of `df`, but + * does not dominate `df` itself. + * + * Example: + * + * ```rb + * def m x + * if x < 0 + * x = -x + * if x > 10 + * x = x - 1 + * end + * end + * puts x + * end + * ``` + * + * The basic block on line 8 is in the dominance frontier + * of the basic block starting on line 3 because that block + * dominates the basic block on line 4, which is a predecessor of + * `puts x`. Also, the basic block starting on line 3 does not + * dominate the basic block on line 8. + */ + predicate inDominanceFrontier(BasicBlock df) { + this.dominatesPredecessor(df) and + not this.strictlyDominates(df) + } + + /** + * Holds if this basic block dominates a predecessor of `df`. + */ + private predicate dominatesPredecessor(BasicBlock df) { this.dominates(df.getAPredecessor()) } + + /** + * Gets the basic block that immediately dominates this basic block, if any. + * + * That is, all paths reaching this basic block from some entry point + * basic block must go through the result, which is an immediate basic block + * predecessor of this basic block. + * + * Example: + * + * ```rb + * def m b + * if b + * return 0 + * end + * return 1 + * end + * ``` + * + * The basic block starting on line 2 is an immediate dominator of + * the basic block on line 5 (all paths from the entry point of `m` + * to `return 1` must go through the `if` block, and the `if` block + * is an immediate predecessor of `return 1`). + */ + BasicBlock getImmediateDominator() { bbIDominates(result, this) } + + /** + * Holds if this basic block strictly post-dominates basic block `bb`. + * + * That is, all paths reaching a normal exit point basic block from basic + * block `bb` must go through this basic block (which must be different + * from `bb`). + * + * Example: + * + * ```rb + * def m b + * if b + * puts "b" + * end + * puts "m" + * end + * ``` + * + * The basic block on line 5 strictly post-dominates the basic block on + * line 3 (all paths to the exit point of `m` from `puts "b"` must go + * through `puts "m"`). + */ + predicate strictlyPostDominates(BasicBlock bb) { bbIPostDominates+(this, bb) } + + /** + * Holds if this basic block post-dominates basic block `bb`. + * + * That is, all paths reaching a normal exit point basic block from basic + * block `bb` must go through this basic block. + * + * Example: + * + * ```rb + * def m b + * if b + * puts "b" + * end + * puts "m" + * end + * ``` + * + * The basic block on line 5 post-dominates the basic block on line 3 + * (all paths to the exit point of `m` from `puts "b"` must go through + * `puts "m"`). + */ + predicate postDominates(BasicBlock bb) { + this.strictlyPostDominates(bb) or + this = bb + } + + /** Holds if this basic block is in a loop in the control flow graph. */ + predicate inLoop() { this.getASuccessor+() = this } + + /** Gets a textual representation of this basic block. */ + string toString() { result = this.getFirstNode().toString() } + + /** Gets the location of this basic block. */ + Location getLocation() { result = this.getFirstNode().getLocation() } +} + +cached +private module Cached { + /** Internal representation of basic blocks. */ + cached + newtype TBasicBlock = TBasicBlockStart(Node cfn) { startsBB(cfn) } + + /** Holds if `cfn` starts a new basic block. */ + private predicate startsBB(Node cfn) { + not exists(cfn.getAPredecessor()) and exists(cfn.getASuccessor()) + or + cfn.isJoin() + or + cfn.getAPredecessor().isBranch() + or + /* + * In cases such as + * + * ```rb + * if x or y + * foo + * else + * bar + * ``` + * + * we have a CFG that looks like + * + * x --false--> [false] x or y --false--> bar + * \ | + * --true--> y --false-- + * \ + * --true--> [true] x or y --true--> foo + * + * and we want to ensure that both `foo` and `bar` start a new basic block, + * in order to get a `ConditionalBlock` out of the disjunction. + */ + + exists(cfn.getAPredecessor(any(BooleanSuccessor s))) + } + + /** + * Holds if `succ` is a control flow successor of `pred` within + * the same basic block. + */ + private predicate intraBBSucc(Node pred, Node succ) { + succ = pred.getASuccessor() and + not startsBB(succ) + } + + /** + * Holds if `cfn` is the `i`th node in basic block `bb`. + * + * In other words, `i` is the shortest distance from a node `bb` + * that starts a basic block to `cfn` along the `intraBBSucc` relation. + */ + cached + predicate bbIndex(Node bbStart, Node cfn, int i) = + shortestDistances(startsBB/1, intraBBSucc/2)(bbStart, cfn, i) + + /** + * Holds if the first node of basic block `succ` is a control flow + * successor of the last node of basic block `pred`. + */ + private predicate succBB(BasicBlock pred, BasicBlock succ) { succ = pred.getASuccessor() } + + /** Holds if `dom` is an immediate dominator of `bb`. */ + cached + predicate bbIDominates(BasicBlock dom, BasicBlock bb) = + idominance(entryBB/1, succBB/2)(_, dom, bb) + + /** Holds if `pred` is a basic block predecessor of `succ`. */ + private predicate predBB(BasicBlock succ, BasicBlock pred) { succBB(pred, succ) } + + /** Holds if `bb` is an exit basic block that represents normal exit. */ + private predicate normalExitBB(BasicBlock bb) { bb.getANode().(AnnotatedExitNode).isNormal() } + + /** Holds if `dom` is an immediate post-dominator of `bb`. */ + cached + predicate bbIPostDominates(BasicBlock dom, BasicBlock bb) = + idominance(normalExitBB/1, predBB/2)(_, dom, bb) + + /** + * Gets the `i`th predecessor of join block `jb`, with respect to some + * arbitrary order. + */ + cached + JoinBlockPredecessor getJoinBlockPredecessor(JoinBlock jb, int i) { + result = + rank[i + 1](JoinBlockPredecessor jbp | + jbp = jb.getAPredecessor() + | + jbp order by JoinBlockPredecessors::getId(jbp), JoinBlockPredecessors::getSplitString(jbp) + ) + } + + cached + predicate immediatelyControls(ConditionBlock cb, BasicBlock succ, BooleanSuccessor s) { + succ = cb.getASuccessor(s) and + forall(BasicBlock pred | pred = succ.getAPredecessor() and pred != cb | succ.dominates(pred)) + } + + cached + predicate controls(ConditionBlock cb, BasicBlock controlled, BooleanSuccessor s) { + exists(BasicBlock succ | cb.immediatelyControls(succ, s) | succ.dominates(controlled)) + } +} + +private import Cached + +/** Holds if `bb` is an entry basic block. */ +private predicate entryBB(BasicBlock bb) { bb.getFirstNode() instanceof EntryNode } + +/** + * An entry basic block, that is, a basic block whose first node is + * an entry node. + */ +class EntryBasicBlock extends BasicBlock { + EntryBasicBlock() { entryBB(this) } +} + +/** + * An annotated exit basic block, that is, a basic block whose last node is + * an annotated exit node. + */ +class AnnotatedExitBasicBlock extends BasicBlock { + private boolean normal; + + AnnotatedExitBasicBlock() { + exists(AnnotatedExitNode n | + n = this.getANode() and + if n.isNormal() then normal = true else normal = false + ) + } + + /** Holds if this block represent a normal exit. */ + final predicate isNormal() { normal = true } +} + +/** + * An exit basic block, that is, a basic block whose last node is + * an exit node. + */ +class ExitBasicBlock extends BasicBlock { + ExitBasicBlock() { this.getLastNode() instanceof ExitNode } +} + +private module JoinBlockPredecessors { + private predicate id(BICEP::AstNode x, BICEP::AstNode y) { x = y } + + private predicate idOf(BICEP::AstNode x, int y) = equivalenceRelation(id/2)(x, y) + + int getId(JoinBlockPredecessor jbp) { + idOf(toBicepTreeSitter(jbp.getFirstNode().(AstCfgNode).getAstNode()), result) + or + idOf(toBicepTreeSitter(jbp.(EntryBasicBlock).getScope()), result) + } + + string getSplitString(JoinBlockPredecessor jbp) { + result = jbp.getFirstNode().(AstCfgNode).getSplitsString() + or + not exists(jbp.getFirstNode().(AstCfgNode).getSplitsString()) and + result = "" + } +} + +/** A basic block with more than one predecessor. */ +class JoinBlock extends BasicBlock { + JoinBlock() { this.getFirstNode().isJoin() } + + /** + * Gets the `i`th predecessor of this join block, with respect to some + * arbitrary order. + */ + JoinBlockPredecessor getJoinBlockPredecessor(int i) { result = getJoinBlockPredecessor(this, i) } +} + +/** A basic block that is an immediate predecessor of a join block. */ +class JoinBlockPredecessor extends BasicBlock { + JoinBlockPredecessor() { this.getASuccessor() instanceof JoinBlock } +} + +/** A basic block that terminates in a condition, splitting the subsequent control flow. */ +class ConditionBlock extends BasicBlock { + ConditionBlock() { this.getLastNode().isCondition() } + + /** + * Holds if basic block `succ` is immediately controlled by this basic + * block with conditional value `s`. That is, `succ` is an immediate + * successor of this block, and `succ` can only be reached from + * the callable entry point by going via the `s` edge out of this basic block. + */ + predicate immediatelyControls(BasicBlock succ, BooleanSuccessor s) { + immediatelyControls(this, succ, s) + } + + /** + * Holds if basic block `controlled` is controlled by this basic block with + * conditional value `s`. That is, `controlled` can only be reached from + * the callable entry point by going via the `s` edge out of this basic block. + */ + predicate controls(BasicBlock controlled, BooleanSuccessor s) { controls(this, controlled, s) } +} diff --git a/ql/lib/codeql/bicep/controlflow/internal/Cfg.qll b/ql/lib/codeql/bicep/controlflow/internal/Cfg.qll new file mode 100644 index 0000000..a759319 --- /dev/null +++ b/ql/lib/codeql/bicep/controlflow/internal/Cfg.qll @@ -0,0 +1,188 @@ +private import codeql.bicep.Ast +private import codeql.controlflow.Cfg as CfgShared +private import codeql.Locations + +module Completion { + private newtype TCompletion = + TSimpleCompletion() or + TBooleanCompletion(boolean b) { b in [false, true] } or + TReturnCompletion() + + abstract class Completion extends TCompletion { + abstract string toString(); + + predicate isValidForSpecific(AstNode e) { none() } + + predicate isValidFor(AstNode e) { this.isValidForSpecific(e) } + + abstract SuccessorType getAMatchingSuccessorType(); + } + + abstract class NormalCompletion extends Completion { } + + class SimpleCompletion extends NormalCompletion, TSimpleCompletion { + override string toString() { result = "SimpleCompletion" } + + override predicate isValidFor(AstNode e) { not any(Completion c).isValidForSpecific(e) } + + override NormalSuccessor getAMatchingSuccessorType() { any() } + } + + class BooleanCompletion extends NormalCompletion, TBooleanCompletion { + boolean value; + + BooleanCompletion() { this = TBooleanCompletion(value) } + + override string toString() { result = "BooleanCompletion(" + value + ")" } + + override predicate isValidForSpecific(AstNode e) { e = any(ForStatement c).getCondition() } + + override BooleanSuccessor getAMatchingSuccessorType() { result.getValue() = value } + + final boolean getValue() { result = value } + } + + class ReturnCompletion extends Completion, TReturnCompletion { + override string toString() { result = "ReturnCompletion" } + + override predicate isValidForSpecific(AstNode e) { none() } + + override ReturnSuccessor getAMatchingSuccessorType() { any() } + } + + cached + private newtype TSuccessorType = + TNormalSuccessor() or + TBooleanSuccessor(boolean b) { b in [false, true] } or + TReturnSuccessor() + + class SuccessorType extends TSuccessorType { + string toString() { none() } + } + + class NormalSuccessor extends SuccessorType, TNormalSuccessor { + override string toString() { result = "successor" } + } + + class BooleanSuccessor extends SuccessorType, TBooleanSuccessor { + boolean value; + + BooleanSuccessor() { this = TBooleanSuccessor(value) } + + override string toString() { result = value.toString() } + + boolean getValue() { result = value } + } + + class ReturnSuccessor extends SuccessorType, TReturnSuccessor { + override string toString() { result = "return" } + } +} + +module CfgScope { + abstract class CfgScope extends AstNode { } + + private class InfrastructureScope extends CfgScope, Infrastructure { } +} + +private module Implementation implements CfgShared::InputSig { + import codeql.bicep.ast.Ast + import Completion + import CfgScope + + predicate completionIsNormal(Completion c) { not c instanceof ReturnCompletion } + + // Not using CFG splitting, so the following are just dummy types. + private newtype TUnit = Unit() + + class SplitKindBase = TUnit; + + class Split extends TUnit { + abstract string toString(); + } + + predicate completionIsSimple(Completion c) { c instanceof SimpleCompletion } + + predicate completionIsValidFor(Completion c, AstNode e) { c.isValidFor(e) } + + CfgScope getCfgScope(AstNode e) { + exists(AstNode p | p = e.getParent() | + result = p + or + not p instanceof CfgScope and result = getCfgScope(p) + ) + } + + int maxSplits() { result = 0 } + + predicate scopeFirst(CfgScope scope, AstNode e) { first(scope.(Infrastructure), e) } + + predicate scopeLast(CfgScope scope, AstNode e, Completion c) { + last(scope.(Infrastructure), e, c) + } + + predicate successorTypeIsSimple(SuccessorType t) { t instanceof NormalSuccessor } + + predicate successorTypeIsCondition(SuccessorType t) { t instanceof BooleanSuccessor } + + SuccessorType getAMatchingSuccessorType(Completion c) { result = c.getAMatchingSuccessorType() } + + predicate isAbnormalExitType(SuccessorType t) { none() } +} + +module CfgImpl = CfgShared::Make; + +private import CfgImpl +private import Completion +private import CfgScope + +// Program / Infrastructure +private class InfrastructureTree extends StandardPreOrderTree instanceof Infrastructure { + override ControlFlowTree getChildNode(int i) { result = super.getStatement(i) } +} + +// Literals +private class NumberLiteralTree extends LeafTree, NumberLiteral { } + +private class NullLiteralTree extends LeafTree, NullLiteral { } + +private class BooleanLiteralTree extends LeafTree, BooleanLiteral { } + +private class StringLiteralTree extends LeafTree, StringLiteral { } + +private class StringContentTree extends LeafTree, StringContent { } + +private class IdentifierTree extends LeafTree, Identifier { } + +private class TypeTree extends LeafTree, Type { } + +private class DecoratorTree extends LeafTree instanceof Decorator { } + +private class DecoratorsTree extends StandardPreOrderTree, Decorators { + override ControlFlowTree getChildNode(int i) { result = super.getDecorator(i) } +} + +private class StmtTree extends StandardPreOrderTree, Stmt { + override ControlFlowTree getChildNode(int i) { result = this.getChildNode(i) } +} + +// Declarations +private class ResourceDeclarationTree extends StandardPreOrderTree, Resource { + override ControlFlowTree getChildNode(int i) { + result = this.getIdentifier() and i = 0 + or + result = this.getBody() and i = 1 + } +} + +// TODO(geekmasher): update +private class ObjectTree extends LeafTree, Object { } + +// private class ObjectPropertyTree extends LeafTree, ObjectProperty { } +private class ParameterDeclarationTree extends StandardPreOrderTree, ParameterDeclaration { + override ControlFlowTree getChildNode(int i) { + result = this.getDefaultValue() and i = 0 + or + result = this.getName() and i = 1 + } +} diff --git a/ql/lib/ide-contextual-queries/printCfg.ql b/ql/lib/ide-contextual-queries/printCfg.ql new file mode 100644 index 0000000..6dfdb6c --- /dev/null +++ b/ql/lib/ide-contextual-queries/printCfg.ql @@ -0,0 +1,53 @@ +/** + * @name Print CFG + * @description Produces a representation of a file's Control Flow Graph. + * This query is used by the VS Code extension. + * @id kd/print-cfg + * @kind graph + * @tags ide-contextual-queries/print-cfg + */ + +private import codeql.bicep.Cfg +private import codeql.bicep.Cfg::TestOutput +private import codeql.iac.ideContextual.IDEContextual +private import codeql.Locations + +/** + * Gets the source file to generate a CFG from. + */ +external string selectedSourceFile(); + +external string selectedSourceLine(); + +external string selectedSourceColumn(); + +bindingset[file, line, column] +private CfgScope smallestEnclosingScope(File file, int line, int column) { + result = + min(Location loc, CfgScope scope | + loc = scope.getLocation() and + ( + loc.getStartLine() < line + or + loc.getStartLine() = line and loc.getStartColumn() <= column + ) and + ( + loc.getEndLine() > line + or + loc.getEndLine() = line and loc.getEndColumn() >= column + ) and + loc.getFile() = file + | + scope + order by + loc.getStartLine() desc, loc.getStartColumn() desc, loc.getEndLine(), loc.getEndColumn() + ) +} + +class MyRelevantNode extends RelevantNode { + MyRelevantNode() { + this.getScope() = + smallestEnclosingScope(getFileBySourceArchiveName(selectedSourceFile()), + selectedSourceLine().toInt(), selectedSourceColumn().toInt()) + } +}