From 2bb09d2438a34d4ba01f4ac3419f89a6b3335374 Mon Sep 17 00:00:00 2001 From: GeekMasher Date: Fri, 13 Jun 2025 13:44:42 +0100 Subject: [PATCH 1/8] feat(ast): Update Calls and Outputs --- ql/lib/codeql/bicep/ast/Calls.qll | 112 +++++++----------- ql/lib/codeql/bicep/ast/Misc.qll | 5 - .../bicep/ast/internal/OutputDeclaration.qll | 12 ++ 3 files changed, 56 insertions(+), 73 deletions(-) diff --git a/ql/lib/codeql/bicep/ast/Calls.qll b/ql/lib/codeql/bicep/ast/Calls.qll index 6ff336e..7f4cc36 100644 --- a/ql/lib/codeql/bicep/ast/Calls.qll +++ b/ql/lib/codeql/bicep/ast/Calls.qll @@ -7,6 +7,7 @@ private import internal.CallExpression private import internal.Parameter private import internal.Parameters private import internal.ParameterDeclaration +private import internal.OutputDeclaration private import internal.UserDefinedFunction abstract class Callable extends Expr { @@ -29,109 +30,84 @@ abstract class Callable extends Expr { /** * A CallExpression expression in the AST. */ -class CallExpression extends Callable instanceof CallExpressionImpl { - override Idents getIdentifier() { - result = CallExpressionImpl.super.getIdentifier() - } - - Expr getArgument(int index) { - result = this.getDeclaredArguments().getArgument(index) - } - - Expr getArguments() { - result = this.getDeclaredArguments().getArguments() - } - - Arguments getDeclaredArguments() { - result = CallExpressionImpl.super.getArguments() - } -} +class CallExpression extends Expr instanceof CallExpressionImpl { + Idents getIdentifier() { result = CallExpressionImpl.super.getIdentifier() } + + string getName() { result = this.getIdentifier().getName() } + Expr getArgument(int index) { result = this.getDeclaredArguments().getArgument(index) } + + Expr getArguments() { result = this.getDeclaredArguments().getArguments() } + + Arguments getDeclaredArguments() { result = CallExpressionImpl.super.getArguments() } +} /** * A Arguments unknown AST node. */ class Arguments extends AstNode instanceof ArgumentsImpl { + Expr getArgument(int index) { result = ArgumentsImpl.super.getArgument(index) } - Expr getArgument(int index) { - result = ArgumentsImpl.super.getArgument(index) - } - - Expr getArguments() { - result = ArgumentsImpl.super.getArguments() - } + Expr getArguments() { result = ArgumentsImpl.super.getArguments() } } /** * A Parameter unknown AST node. */ class Parameter extends AstNode instanceof ParameterImpl { + Idents getName() { result = ParameterImpl.super.getName() } - Idents getName() { - result = ParameterImpl.super.getName() - } - - Type getType() { - result = ParameterImpl.super.getType() - } + Type getType() { result = ParameterImpl.super.getType() } } /** * A Parameters unknown AST node. */ class Parameters extends AstNode instanceof ParametersImpl { - Parameter getParameter(int index) { - result = ParametersImpl.super.getParameter(index) - } + Parameter getParameter(int index) { result = ParametersImpl.super.getParameter(index) } } - /** * A ParameterDeclaration unknown AST node. */ -class ParameterDeclaration extends AstNode instanceof ParameterDeclarationImpl { - Identifier getName() { - result = ParameterDeclarationImpl.super.getName() - } - - Type getType() { - result = ParameterDeclarationImpl.super.getType() - } - - Expr getDefaultValue() { - result = ParameterDeclarationImpl.super.getDefaultValue() - } +class ParameterDeclaration extends AstNode instanceof ParameterDeclarationImpl { + Identifier getIdentifier() { result = ParameterDeclarationImpl.super.getName() } + + string getName() { result = this.getIdentifier().getName() } + + Type getType() { result = ParameterDeclarationImpl.super.getType() } + + Expr getDefaultValue() { result = ParameterDeclarationImpl.super.getDefaultValue() } +} + +/** + * A OutputDeclaration unknown AST node. + */ +class OutputDeclaration extends AstNode instanceof OutputDeclarationImpl { + Identifier getIdentifier() { result = OutputDeclarationImpl.super.getIdentifier() } + + string getName() { result = this.getIdentifier().getName() } + + Type getType() { result = OutputDeclarationImpl.super.getType() } + + Expr getValue() { result = OutputDeclarationImpl.super.getValue() } } /** * A UserDefinedFunction unknown AST node. */ class UserDefinedFunction extends AstNode instanceof UserDefinedFunctionImpl { - Identifier getIdentifier() { - result = UserDefinedFunctionImpl.super.getName() - } + Identifier getIdentifier() { result = UserDefinedFunctionImpl.super.getName() } - string getName() { - result = this.getIdentifier().getName() - } + string getName() { result = this.getIdentifier().getName() } - Type getReturnType() { - result = UserDefinedFunctionImpl.super.getReturnType() - } + Type getReturnType() { result = UserDefinedFunctionImpl.super.getReturnType() } - Parameters getDeclaredParameters() { - result = UserDefinedFunctionImpl.super.getParameters() - } + Parameters getDeclaredParameters() { result = UserDefinedFunctionImpl.super.getParameters() } - Parameter getParameters() { - result = this.getDeclaredParameters().getParameter(_) - } + Parameter getParameters() { result = this.getDeclaredParameters().getParameter(_) } - Parameter getParameter(int index) { - result = this.getDeclaredParameters().getParameter(index) - } + Parameter getParameter(int index) { result = this.getDeclaredParameters().getParameter(index) } - Expr getBody() { - result = UserDefinedFunctionImpl.super.getBody() - } + Expr getBody() { result = UserDefinedFunctionImpl.super.getBody() } } diff --git a/ql/lib/codeql/bicep/ast/Misc.qll b/ql/lib/codeql/bicep/ast/Misc.qll index 5b8dfb6..2741699 100644 --- a/ql/lib/codeql/bicep/ast/Misc.qll +++ b/ql/lib/codeql/bicep/ast/Misc.qll @@ -18,7 +18,6 @@ private import internal.MetadataDeclaration private import internal.ModuleDeclaration private import internal.NegatedType private import internal.ObjectProperty -private import internal.OutputDeclaration private import internal.ParameterizedType private import internal.ParenthesizedType private import internal.PrimitiveType @@ -102,10 +101,6 @@ class ModuleDeclaration extends AstNode instanceof ModuleDeclarationImpl { } */ class NegatedType extends AstNode instanceof NegatedTypeImpl { } -/** - * A OutputDeclaration unknown AST node. - */ -class OutputDeclaration extends AstNode instanceof OutputDeclarationImpl { } /** * A ParameterizedType unknown AST node. diff --git a/ql/lib/codeql/bicep/ast/internal/OutputDeclaration.qll b/ql/lib/codeql/bicep/ast/internal/OutputDeclaration.qll index f5e27b8..2bdcb49 100644 --- a/ql/lib/codeql/bicep/ast/internal/OutputDeclaration.qll +++ b/ql/lib/codeql/bicep/ast/internal/OutputDeclaration.qll @@ -6,6 +6,9 @@ private import AstNodes private import TreeSitter private import codeql.bicep.ast.AstNodes +private import Idents +private import Expr +private import Type /** * A OutputDeclaration AST Node. @@ -19,6 +22,15 @@ class OutputDeclarationImpl extends TOutputDeclaration, AstNode { override string toString() { result = ast.toString() } + IdentsImpl getIdentifier() { + toTreeSitter(result) = ast.getChild(0) + } + TypeImpl getType() { + toTreeSitter(result) = ast.getChild(1) + } + ExprImpl getValue() { + toTreeSitter(result) = ast.getChild(2) + } } \ No newline at end of file From 405cdf2e7fbd1467758a0bb88dd39093a6c23e9d Mon Sep 17 00:00:00 2001 From: GeekMasher Date: Fri, 13 Jun 2025 13:45:05 +0100 Subject: [PATCH 2/8] feat(cfg): Update CFG --- .../internal/ControlFlowGraphImpl.qll | 39 +++++++++++++++++++ .../bicep/controlflow/internal/Scope.qll | 13 ++++++- 2 files changed, 51 insertions(+), 1 deletion(-) diff --git a/ql/lib/codeql/bicep/controlflow/internal/ControlFlowGraphImpl.qll b/ql/lib/codeql/bicep/controlflow/internal/ControlFlowGraphImpl.qll index 591b533..99e8ec8 100644 --- a/ql/lib/codeql/bicep/controlflow/internal/ControlFlowGraphImpl.qll +++ b/ql/lib/codeql/bicep/controlflow/internal/ControlFlowGraphImpl.qll @@ -63,6 +63,10 @@ private module CfgImpl = Make; import CfgImpl +class InfrastructureScopeTree extends StandardTree, PreOrderTree, PostOrderTree, Scope::InfrastructureScope { + override AstNode getChildNode(int i) { result = super.getStatement(i) } +} + /** * A literal value in a Bicep program. */ @@ -87,3 +91,38 @@ class StringLiteralTree extends LeafTree instanceof StringLiteral { } * A StringContent literal value in a Bicep program. */ class StringContentLiteralTree extends LeafTree instanceof StringContentLiteral { } + +/** + * ParameterDeclarationTree represents a parameter declaration in a Bicep program. + */ +class ParameterDeclarationTree extends StandardPostOrderTree instanceof ParameterDeclaration { + override AstNode getChildNode(int i) { + i = 0 and result = super.getIdentifier() + or + i = 1 and result = super.getType() + or + i = 2 and result = super.getDefaultValue() + } +} + +class UserDefinedFunctionTree extends StandardPostOrderTree instanceof UserDefinedFunction { + override AstNode getChildNode(int i) { + i = 0 and result = super.getIdentifier() + or + i = 1 and result = super.getParameters() + or + i = 2 and result = super.getReturnType() + or + i = 3 and result = super.getBody() + } +} + +class OutputDeclarationTree extends StandardPostOrderTree instanceof OutputDeclaration { + override AstNode getChildNode(int i) { + i = 0 and result = super.getIdentifier() + or + i = 1 and result = super.getType() + or + i = 2 and result = super.getValue() + } +} diff --git a/ql/lib/codeql/bicep/controlflow/internal/Scope.qll b/ql/lib/codeql/bicep/controlflow/internal/Scope.qll index 9ebdd27..ff77006 100644 --- a/ql/lib/codeql/bicep/controlflow/internal/Scope.qll +++ b/ql/lib/codeql/bicep/controlflow/internal/Scope.qll @@ -3,10 +3,21 @@ private import codeql.bicep.AST private import Completion private import ControlFlowGraphImpl -abstract class CfgScope extends AstNode { +abstract private class CfgScopeImpl extends AstNode { /** Holds if `first` is executed first when entering scope. */ abstract predicate scopeFirst(AstNode first); /** Holds if scope is exited when `last` finishes with completion `c`. */ abstract predicate scopeLast(AstNode last, Completion c); } + +final class CfgScope = CfgScopeImpl; + +/** + * A Infrastructure is a Sequence of statements. + */ +final class InfrastructureScope extends CfgScopeImpl, Infrastructure { + override predicate scopeFirst(AstNode first) { first(this.getStatement(0), first) } + + override predicate scopeLast(AstNode last, Completion c) { last(this, last, c) } +} From 63b2f1281963cd985f5463af2601d0767312e680 Mon Sep 17 00:00:00 2001 From: GeekMasher Date: Fri, 13 Jun 2025 13:46:27 +0100 Subject: [PATCH 3/8] docs: Add documentation for Calls --- ql/lib/codeql/bicep/ast/Calls.qll | 55 +++++++++++++++++++++++++++---- 1 file changed, 48 insertions(+), 7 deletions(-) diff --git a/ql/lib/codeql/bicep/ast/Calls.qll b/ql/lib/codeql/bicep/ast/Calls.qll index 7f4cc36..b1ff703 100644 --- a/ql/lib/codeql/bicep/ast/Calls.qll +++ b/ql/lib/codeql/bicep/ast/Calls.qll @@ -10,6 +10,10 @@ private import internal.ParameterDeclaration private import internal.OutputDeclaration private import internal.UserDefinedFunction +/** + * Represents a callable expression in the AST, such as a function or method call. + * Provides access to the identifier and name of the call expression. + */ abstract class Callable extends Expr { /** * Gets the identifier of the call expression. @@ -18,96 +22,133 @@ abstract class Callable extends Expr { /** * Gets the name of the call expression. + * + * @return The name as a string. */ string getName() { result = this.getIdentifier().getName() } /** * Checks if the call expression has a specific name. + * + * @param name The name to check against. + * @return True if the call expression has the given name. */ predicate hasName(string name) { this.getName() = name } } /** - * A CallExpression expression in the AST. + * Represents a call expression node in the AST. + * Provides access to the identifier, arguments, and declared arguments. */ class CallExpression extends Expr instanceof CallExpressionImpl { + /** Gets the identifier of the call expression. */ Idents getIdentifier() { result = CallExpressionImpl.super.getIdentifier() } + /** Gets the name of the call expression. */ string getName() { result = this.getIdentifier().getName() } + /** Gets the argument at the specified index. */ Expr getArgument(int index) { result = this.getDeclaredArguments().getArgument(index) } + /** Gets all arguments of the call expression. */ Expr getArguments() { result = this.getDeclaredArguments().getArguments() } + /** Gets the declared arguments node. */ Arguments getDeclaredArguments() { result = CallExpressionImpl.super.getArguments() } } /** - * A Arguments unknown AST node. + * Represents the arguments node in the AST. + * Provides access to individual and all arguments. */ class Arguments extends AstNode instanceof ArgumentsImpl { + /** Gets the argument at the specified index. */ Expr getArgument(int index) { result = ArgumentsImpl.super.getArgument(index) } + /** Gets all arguments. */ Expr getArguments() { result = ArgumentsImpl.super.getArguments() } } /** - * A Parameter unknown AST node. + * Represents a parameter node in the AST. + * Provides access to the parameter's name and type. */ class Parameter extends AstNode instanceof ParameterImpl { + /** Gets the name of the parameter. */ Idents getName() { result = ParameterImpl.super.getName() } + /** Gets the type of the parameter. */ Type getType() { result = ParameterImpl.super.getType() } } /** - * A Parameters unknown AST node. + * Represents a parameters node in the AST. + * Provides access to individual parameters by index. */ class Parameters extends AstNode instanceof ParametersImpl { + /** Gets the parameter at the specified index. */ Parameter getParameter(int index) { result = ParametersImpl.super.getParameter(index) } } /** - * A ParameterDeclaration unknown AST node. + * Represents a parameter declaration node in the AST. + * Provides access to the identifier, name, type, and default value of the parameter. */ class ParameterDeclaration extends AstNode instanceof ParameterDeclarationImpl { + /** Gets the identifier of the parameter declaration. */ Identifier getIdentifier() { result = ParameterDeclarationImpl.super.getName() } + /** Gets the name of the parameter declaration. */ string getName() { result = this.getIdentifier().getName() } + /** Gets the type of the parameter declaration. */ Type getType() { result = ParameterDeclarationImpl.super.getType() } + /** Gets the default value of the parameter declaration, if any. */ Expr getDefaultValue() { result = ParameterDeclarationImpl.super.getDefaultValue() } } /** - * A OutputDeclaration unknown AST node. + * Represents an output declaration node in the AST. + * Provides access to the identifier, name, type, and value of the output. */ class OutputDeclaration extends AstNode instanceof OutputDeclarationImpl { + /** Gets the identifier of the output declaration. */ Identifier getIdentifier() { result = OutputDeclarationImpl.super.getIdentifier() } + /** Gets the name of the output declaration. */ string getName() { result = this.getIdentifier().getName() } + /** Gets the type of the output declaration. */ Type getType() { result = OutputDeclarationImpl.super.getType() } + /** Gets the value of the output declaration. */ Expr getValue() { result = OutputDeclarationImpl.super.getValue() } } /** - * A UserDefinedFunction unknown AST node. + * Represents a user-defined function node in the AST. + * Provides access to the identifier, name, return type, parameters, and body of the function. */ class UserDefinedFunction extends AstNode instanceof UserDefinedFunctionImpl { + /** Gets the identifier of the user-defined function. */ Identifier getIdentifier() { result = UserDefinedFunctionImpl.super.getName() } + /** Gets the name of the user-defined function. */ string getName() { result = this.getIdentifier().getName() } + /** Gets the return type of the user-defined function. */ Type getReturnType() { result = UserDefinedFunctionImpl.super.getReturnType() } + /** Gets the declared parameters of the user-defined function. */ Parameters getDeclaredParameters() { result = UserDefinedFunctionImpl.super.getParameters() } + /** Gets all parameters of the user-defined function. */ Parameter getParameters() { result = this.getDeclaredParameters().getParameter(_) } + /** Gets the parameter at the specified index. */ Parameter getParameter(int index) { result = this.getDeclaredParameters().getParameter(index) } + /** Gets the body of the user-defined function. */ Expr getBody() { result = UserDefinedFunctionImpl.super.getBody() } } From 0678889ec5e70baa1cbeb021e1d9b29a05d983ba Mon Sep 17 00:00:00 2001 From: GeekMasher Date: Fri, 13 Jun 2025 13:51:32 +0100 Subject: [PATCH 4/8] fix: Print CFG query --- ql/lib/ide-contextual-queries/printCfg.ql | 66 +++++++++++------------ 1 file changed, 33 insertions(+), 33 deletions(-) diff --git a/ql/lib/ide-contextual-queries/printCfg.ql b/ql/lib/ide-contextual-queries/printCfg.ql index c7afc5e..48b4dd3 100644 --- a/ql/lib/ide-contextual-queries/printCfg.ql +++ b/ql/lib/ide-contextual-queries/printCfg.ql @@ -2,40 +2,40 @@ * @name Print CFG * @description Produces a representation of a file's Control Flow Graph. * This query is used by the VS Code extension. - * @id rb/print-cfg + * @id bicep/print-cfg * @kind graph * @tags ide-contextual-queries/print-cfg */ -private import codeql.Locations -private import codeql.bicep.controlflow.internal.ControlFlowGraphImpl -private import codeql.bicep.controlflow.ControlFlowGraph - -external string selectedSourceFile(); - -private predicate selectedSourceFileAlias = selectedSourceFile/0; - -external int selectedSourceLine(); - -private predicate selectedSourceLineAlias = selectedSourceLine/0; - -external int selectedSourceColumn(); - -private predicate selectedSourceColumnAlias = selectedSourceColumn/0; - -module ViewCfgQueryInput implements ViewCfgQueryInputSig { - predicate selectedSourceFile = selectedSourceFileAlias/0; - - predicate selectedSourceLine = selectedSourceLineAlias/0; - - predicate selectedSourceColumn = selectedSourceColumnAlias/0; - - predicate cfgScopeSpan( - CfgScope scope, File file, int startLine, int startColumn, int endLine, int endColumn - ) { - file = scope.getFile() and - scope.getLocation().hasLocationInfo(_, startLine, startColumn, endLine, endColumn) - } -} - -import ViewCfgQuery \ No newline at end of file + private import codeql.Locations + private import codeql.bicep.controlflow.internal.ControlFlowGraphImpl + private import codeql.bicep.controlflow.ControlFlowGraph + + external string selectedSourceFile(); + + private predicate selectedSourceFileAlias = selectedSourceFile/0; + + external int selectedSourceLine(); + + private predicate selectedSourceLineAlias = selectedSourceLine/0; + + external int selectedSourceColumn(); + + private predicate selectedSourceColumnAlias = selectedSourceColumn/0; + + module ViewCfgQueryInput implements ViewCfgQueryInputSig { + predicate selectedSourceFile = selectedSourceFileAlias/0; + + predicate selectedSourceLine = selectedSourceLineAlias/0; + + predicate selectedSourceColumn = selectedSourceColumnAlias/0; + + predicate cfgScopeSpan( + CfgScope scope, File file, int startLine, int startColumn, int endLine, int endColumn + ) { + file = scope.getFile() and + scope.getLocation().hasLocationInfo(_, startLine, startColumn, endLine, endColumn) + } + } + + import ViewCfgQuery \ No newline at end of file From eec07f21fa57d4b8846eac8453bffcd415201637 Mon Sep 17 00:00:00 2001 From: GeekMasher Date: Fri, 13 Jun 2025 16:16:20 +0100 Subject: [PATCH 5/8] fix(tests): Update Calls test --- ql/test/library-tests/calls/Calls.expected | 2 +- ql/test/library-tests/calls/Calls.ql | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/ql/test/library-tests/calls/Calls.expected b/ql/test/library-tests/calls/Calls.expected index a3bbbfd..9cea492 100644 --- a/ql/test/library-tests/calls/Calls.expected +++ b/ql/test/library-tests/calls/Calls.expected @@ -1,4 +1,4 @@ -callable +call | calls.bicep:1:32:1:63 | CallExpression | | calls.bicep:1:45:1:59 | CallExpression | | calls.bicep:4:111:4:121 | CallExpression | diff --git a/ql/test/library-tests/calls/Calls.ql b/ql/test/library-tests/calls/Calls.ql index ce4a48c..a5e4bc4 100644 --- a/ql/test/library-tests/calls/Calls.ql +++ b/ql/test/library-tests/calls/Calls.ql @@ -1,5 +1,5 @@ private import bicep -query predicate callable(Callable callable) { any() } +query predicate call(CallExpression callable) { any() } query predicate callArguments(CallExpression call, Expr argument) { call.getArguments() = argument } From 545ead47e67bbdf20f3ff49d32c8fd82719c6a3f Mon Sep 17 00:00:00 2001 From: GeekMasher Date: Fri, 13 Jun 2025 16:17:04 +0100 Subject: [PATCH 6/8] feat(ast): Move Call nodes to Stmts --- ql/lib/codeql/bicep/ast/Calls.qll | 89 ----------------------------- ql/lib/codeql/bicep/ast/Stmts.qll | 94 +++++++++++++++++++++++++++++++ 2 files changed, 94 insertions(+), 89 deletions(-) diff --git a/ql/lib/codeql/bicep/ast/Calls.qll b/ql/lib/codeql/bicep/ast/Calls.qll index b1ff703..3fb6551 100644 --- a/ql/lib/codeql/bicep/ast/Calls.qll +++ b/ql/lib/codeql/bicep/ast/Calls.qll @@ -4,11 +4,6 @@ private import Idents private import Misc private import internal.Arguments private import internal.CallExpression -private import internal.Parameter -private import internal.Parameters -private import internal.ParameterDeclaration -private import internal.OutputDeclaration -private import internal.UserDefinedFunction /** * Represents a callable expression in the AST, such as a function or method call. @@ -68,87 +63,3 @@ class Arguments extends AstNode instanceof ArgumentsImpl { /** Gets all arguments. */ Expr getArguments() { result = ArgumentsImpl.super.getArguments() } } - -/** - * Represents a parameter node in the AST. - * Provides access to the parameter's name and type. - */ -class Parameter extends AstNode instanceof ParameterImpl { - /** Gets the name of the parameter. */ - Idents getName() { result = ParameterImpl.super.getName() } - - /** Gets the type of the parameter. */ - Type getType() { result = ParameterImpl.super.getType() } -} - -/** - * Represents a parameters node in the AST. - * Provides access to individual parameters by index. - */ -class Parameters extends AstNode instanceof ParametersImpl { - /** Gets the parameter at the specified index. */ - Parameter getParameter(int index) { result = ParametersImpl.super.getParameter(index) } -} - -/** - * Represents a parameter declaration node in the AST. - * Provides access to the identifier, name, type, and default value of the parameter. - */ -class ParameterDeclaration extends AstNode instanceof ParameterDeclarationImpl { - /** Gets the identifier of the parameter declaration. */ - Identifier getIdentifier() { result = ParameterDeclarationImpl.super.getName() } - - /** Gets the name of the parameter declaration. */ - string getName() { result = this.getIdentifier().getName() } - - /** Gets the type of the parameter declaration. */ - Type getType() { result = ParameterDeclarationImpl.super.getType() } - - /** Gets the default value of the parameter declaration, if any. */ - Expr getDefaultValue() { result = ParameterDeclarationImpl.super.getDefaultValue() } -} - -/** - * Represents an output declaration node in the AST. - * Provides access to the identifier, name, type, and value of the output. - */ -class OutputDeclaration extends AstNode instanceof OutputDeclarationImpl { - /** Gets the identifier of the output declaration. */ - Identifier getIdentifier() { result = OutputDeclarationImpl.super.getIdentifier() } - - /** Gets the name of the output declaration. */ - string getName() { result = this.getIdentifier().getName() } - - /** Gets the type of the output declaration. */ - Type getType() { result = OutputDeclarationImpl.super.getType() } - - /** Gets the value of the output declaration. */ - Expr getValue() { result = OutputDeclarationImpl.super.getValue() } -} - -/** - * Represents a user-defined function node in the AST. - * Provides access to the identifier, name, return type, parameters, and body of the function. - */ -class UserDefinedFunction extends AstNode instanceof UserDefinedFunctionImpl { - /** Gets the identifier of the user-defined function. */ - Identifier getIdentifier() { result = UserDefinedFunctionImpl.super.getName() } - - /** Gets the name of the user-defined function. */ - string getName() { result = this.getIdentifier().getName() } - - /** Gets the return type of the user-defined function. */ - Type getReturnType() { result = UserDefinedFunctionImpl.super.getReturnType() } - - /** Gets the declared parameters of the user-defined function. */ - Parameters getDeclaredParameters() { result = UserDefinedFunctionImpl.super.getParameters() } - - /** Gets all parameters of the user-defined function. */ - Parameter getParameters() { result = this.getDeclaredParameters().getParameter(_) } - - /** Gets the parameter at the specified index. */ - Parameter getParameter(int index) { result = this.getDeclaredParameters().getParameter(index) } - - /** Gets the body of the user-defined function. */ - Expr getBody() { result = UserDefinedFunctionImpl.super.getBody() } -} diff --git a/ql/lib/codeql/bicep/ast/Stmts.qll b/ql/lib/codeql/bicep/ast/Stmts.qll index 68be165..ca37d16 100644 --- a/ql/lib/codeql/bicep/ast/Stmts.qll +++ b/ql/lib/codeql/bicep/ast/Stmts.qll @@ -3,6 +3,9 @@ */ private import AstNodes +private import Idents +private import Expr +private import Misc private import internal.AstNodes private import internal.TreeSitter private import internal.Stmts @@ -14,6 +17,11 @@ private import internal.ImportWithStatement private import internal.Infrastructure private import internal.Statement private import internal.UsingStatement +private import internal.Parameter +private import internal.Parameters +private import internal.ParameterDeclaration +private import internal.OutputDeclaration +private import internal.UserDefinedFunction // CFG private import codeql.bicep.CFG private import codeql.bicep.controlflow.internal.ControlFlowGraphImpl as CfgImpl @@ -57,6 +65,92 @@ class Infrastructure extends AstNode instanceof InfrastructureImpl { Stmts getStatement(int index) { result = InfrastructureImpl.super.getStatement(index) } } +/** + * Represents a parameter declaration node in the AST. + * Provides access to the identifier, name, type, and default value of the parameter. + */ +class ParameterDeclaration extends AstNode instanceof ParameterDeclarationImpl { + /** Gets the identifier of the parameter declaration. */ + Identifier getIdentifier() { result = ParameterDeclarationImpl.super.getName() } + + /** Gets the name of the parameter declaration. */ + string getName() { result = this.getIdentifier().getName() } + + /** Gets the type of the parameter declaration. */ + Type getType() { result = ParameterDeclarationImpl.super.getType() } + + /** Gets the default value of the parameter declaration, if any. */ + Expr getDefaultValue() { result = ParameterDeclarationImpl.super.getDefaultValue() } +} + + +/** + * Represents an output declaration node in the AST. + * Provides access to the identifier, name, type, and value of the output. + */ +class OutputDeclaration extends AstNode instanceof OutputDeclarationImpl { + /** Gets the identifier of the output declaration. */ + Identifier getIdentifier() { result = OutputDeclarationImpl.super.getIdentifier() } + + /** Gets the name of the output declaration. */ + string getName() { result = this.getIdentifier().getName() } + + /** Gets the type of the output declaration. */ + Type getType() { result = OutputDeclarationImpl.super.getType() } + + /** Gets the value of the output declaration. */ + Expr getValue() { result = OutputDeclarationImpl.super.getValue() } +} + +/** + * Represents a user-defined function node in the AST. + * Provides access to the identifier, name, return type, parameters, and body of the function. + */ +class UserDefinedFunction extends AstNode instanceof UserDefinedFunctionImpl { + /** Gets the identifier of the user-defined function. */ + Identifier getIdentifier() { result = UserDefinedFunctionImpl.super.getName() } + + /** Gets the name of the user-defined function. */ + string getName() { result = this.getIdentifier().getName() } + + /** Gets the return type of the user-defined function. */ + Type getReturnType() { result = UserDefinedFunctionImpl.super.getReturnType() } + + /** Gets the declared parameters of the user-defined function. */ + Parameters getDeclaredParameters() { result = UserDefinedFunctionImpl.super.getParameters() } + + /** Gets all parameters of the user-defined function. */ + Parameter getParameters() { result = this.getDeclaredParameters().getParameter(_) } + + /** Gets the parameter at the specified index. */ + Parameter getParameter(int index) { result = this.getDeclaredParameters().getParameter(index) } + + /** Gets the body of the user-defined function. */ + Expr getBody() { result = UserDefinedFunctionImpl.super.getBody() } +} + +/** + * Represents a parameter node in the AST. + * Provides access to the parameter's name and type. + */ +class Parameter extends AstNode instanceof ParameterImpl { + /** Gets the name of the parameter. */ + Idents getName() { result = ParameterImpl.super.getName() } + + /** Gets the type of the parameter. */ + Type getType() { result = ParameterImpl.super.getType() } +} + +/** + * Represents a parameters node in the AST. + * Provides access to individual parameters by index. + */ +class Parameters extends AstNode instanceof ParametersImpl { + /** Gets the parameter at the specified index. */ + Parameter getParameter(int index) { result = ParametersImpl.super.getParameter(index) } +} + + /** * A ImportWithStatement statement */ From 4f5886d9808cc60552537a06cc68956bc0d0a65e Mon Sep 17 00:00:00 2001 From: GeekMasher Date: Fri, 13 Jun 2025 16:27:54 +0100 Subject: [PATCH 7/8] feat(cfg): Update CFG --- .../internal/ControlFlowGraphImpl.qll | 47 ++++++++++++++----- 1 file changed, 35 insertions(+), 12 deletions(-) diff --git a/ql/lib/codeql/bicep/controlflow/internal/ControlFlowGraphImpl.qll b/ql/lib/codeql/bicep/controlflow/internal/ControlFlowGraphImpl.qll index 99e8ec8..42889f7 100644 --- a/ql/lib/codeql/bicep/controlflow/internal/ControlFlowGraphImpl.qll +++ b/ql/lib/codeql/bicep/controlflow/internal/ControlFlowGraphImpl.qll @@ -67,6 +67,19 @@ class InfrastructureScopeTree extends StandardTree, PreOrderTree, PostOrderTree, override AstNode getChildNode(int i) { result = super.getStatement(i) } } +class StmtsTree extends StandardPostOrderTree instanceof Stmts { + override AstNode getChildNode(int i) { + // + i = 0 and result = super.getAChild() + } +} + +class ExprTree extends StandardPostOrderTree instanceof Expr { + override AstNode getChildNode(int i) { + i = 0 and result = super.getAChild() + } +} + /** * A literal value in a Bicep program. */ @@ -95,13 +108,18 @@ class StringContentLiteralTree extends LeafTree instanceof StringContentLiteral /** * ParameterDeclarationTree represents a parameter declaration in a Bicep program. */ -class ParameterDeclarationTree extends StandardPostOrderTree instanceof ParameterDeclaration { - override AstNode getChildNode(int i) { - i = 0 and result = super.getIdentifier() - or - i = 1 and result = super.getType() +class ParameterDeclarationTree extends PreOrderTree instanceof ParameterDeclaration { + final override predicate propagatesAbnormal(AstNode child) { child = super.getIdentifier() } + + override predicate succ(AstNode pred, AstNode succ, Completion c) { + // Start with the identifier + pred = this and first(super.getIdentifier(), succ) and completionIsSimple(c) or - i = 2 and result = super.getDefaultValue() + last(super.getIdentifier(), pred, c) and first(super.getDefaultValue(), succ) and completionIsNormal(c) + } + + override predicate last(AstNode node, Completion c) { + node = super.getDefaultValue() and completionIsNormal(c) } } @@ -117,12 +135,17 @@ class UserDefinedFunctionTree extends StandardPostOrderTree instanceof UserDefin } } -class OutputDeclarationTree extends StandardPostOrderTree instanceof OutputDeclaration { - override AstNode getChildNode(int i) { - i = 0 and result = super.getIdentifier() - or - i = 1 and result = super.getType() +class OutputDeclarationTree extends PreOrderTree instanceof OutputDeclaration { + final override predicate propagatesAbnormal(AstNode child) { child = super.getIdentifier() } + + override predicate succ(AstNode pred, AstNode succ, Completion c) { + // Start with the identifier + pred = this and first(super.getIdentifier(), succ) and completionIsSimple(c) or - i = 2 and result = super.getValue() + last(super.getIdentifier(), pred, c) and first(super.getValue(), succ) and completionIsNormal(c) + } + + override predicate last(AstNode node, Completion c) { + node = super.getValue() and completionIsNormal(c) } } From 38b6ae9917cf7e91efcf5acf7122018c8538e401 Mon Sep 17 00:00:00 2001 From: GeekMasher Date: Fri, 13 Jun 2025 16:28:04 +0100 Subject: [PATCH 8/8] feat(tests): Add CFG Tests --- .../library-tests/cfg/BasicBlocks.expected | 5 + ql/test/library-tests/cfg/BasicBlocks.ql | 11 ++ ql/test/library-tests/cfg/Cfg.expected | 44 ++++++++ ql/test/library-tests/cfg/Cfg.ql | 2 + ql/test/library-tests/cfg/sample.bicep | 103 ++++++++++++++++++ 5 files changed, 165 insertions(+) create mode 100644 ql/test/library-tests/cfg/BasicBlocks.expected create mode 100644 ql/test/library-tests/cfg/BasicBlocks.ql create mode 100644 ql/test/library-tests/cfg/Cfg.expected create mode 100644 ql/test/library-tests/cfg/Cfg.ql create mode 100644 ql/test/library-tests/cfg/sample.bicep diff --git a/ql/test/library-tests/cfg/BasicBlocks.expected b/ql/test/library-tests/cfg/BasicBlocks.expected new file mode 100644 index 0000000..b6cb600 --- /dev/null +++ b/ql/test/library-tests/cfg/BasicBlocks.expected @@ -0,0 +1,5 @@ +dominates +| sample.bicep:1:1:103:1 | enter Infrastructure | sample.bicep:1:1:103:1 | enter Infrastructure | +postDominance +| sample.bicep:1:1:103:1 | enter Infrastructure | sample.bicep:1:1:103:1 | enter Infrastructure | +immediateDominator diff --git a/ql/test/library-tests/cfg/BasicBlocks.ql b/ql/test/library-tests/cfg/BasicBlocks.ql new file mode 100644 index 0000000..3c0f694 --- /dev/null +++ b/ql/test/library-tests/cfg/BasicBlocks.ql @@ -0,0 +1,11 @@ +import bicep +import codeql.bicep.controlflow.BasicBlocks +import codeql.bicep.controlflow.ControlFlowGraph + +query predicate dominates(BasicBlock bb1, BasicBlock bb2) { bb1.dominates(bb2) } + +query predicate postDominance(BasicBlock bb1, BasicBlock bb2) { bb1.postDominates(bb2) } + +query predicate immediateDominator(BasicBlock bb1, BasicBlock bb2) { + bb1.getImmediateDominator() = bb2 +} diff --git a/ql/test/library-tests/cfg/Cfg.expected b/ql/test/library-tests/cfg/Cfg.expected new file mode 100644 index 0000000..a651fb0 --- /dev/null +++ b/ql/test/library-tests/cfg/Cfg.expected @@ -0,0 +1,44 @@ +| sample.bicep:1:1:1:48 | ParameterDeclaration | sample.bicep:1:1:103:1 | Infrastructure | | +| sample.bicep:1:1:103:1 | Infrastructure | sample.bicep:1:1:103:1 | exit Infrastructure (normal) | | +| sample.bicep:1:1:103:1 | enter Infrastructure | sample.bicep:1:7:1:14 | location | | +| sample.bicep:1:1:103:1 | enter Infrastructure | sample.bicep:1:7:1:14 | location | | +| sample.bicep:1:1:103:1 | enter Infrastructure | sample.bicep:1:7:1:14 | location | | +| sample.bicep:1:1:103:1 | enter Infrastructure | sample.bicep:1:25:1:37 | resourceGroup | | +| sample.bicep:1:1:103:1 | enter Infrastructure | sample.bicep:1:25:1:37 | resourceGroup | | +| sample.bicep:1:1:103:1 | enter Infrastructure | sample.bicep:1:25:1:37 | resourceGroup | | +| sample.bicep:1:1:103:1 | enter Infrastructure | sample.bicep:1:41:1:48 | location | | +| sample.bicep:1:1:103:1 | exit Infrastructure (normal) | sample.bicep:1:1:103:1 | exit Infrastructure | | +| sample.bicep:1:7:1:14 | location | sample.bicep:1:1:1:48 | ParameterDeclaration | | +| sample.bicep:1:7:1:14 | location | sample.bicep:1:1:1:48 | ParameterDeclaration | | +| sample.bicep:1:7:1:14 | location | sample.bicep:1:1:1:48 | ParameterDeclaration | | +| sample.bicep:1:7:1:14 | location | sample.bicep:1:25:1:37 | resourceGroup | | +| sample.bicep:1:7:1:14 | location | sample.bicep:1:25:1:37 | resourceGroup | | +| sample.bicep:1:7:1:14 | location | sample.bicep:1:25:1:37 | resourceGroup | | +| sample.bicep:1:7:1:14 | location | sample.bicep:1:41:1:48 | location | | +| sample.bicep:1:25:1:37 | resourceGroup | sample.bicep:1:25:1:39 | CallExpression | | +| sample.bicep:1:25:1:37 | resourceGroup | sample.bicep:1:25:1:39 | CallExpression | | +| sample.bicep:1:25:1:37 | resourceGroup | sample.bicep:1:25:1:39 | CallExpression | | +| sample.bicep:1:25:1:37 | resourceGroup | sample.bicep:1:25:1:39 | CallExpression | | +| sample.bicep:1:25:1:37 | resourceGroup | sample.bicep:1:25:1:39 | CallExpression | | +| sample.bicep:1:25:1:37 | resourceGroup | sample.bicep:1:25:1:39 | CallExpression | | +| sample.bicep:1:25:1:37 | resourceGroup | sample.bicep:1:25:1:39 | CallExpression | | +| sample.bicep:1:25:1:37 | resourceGroup | sample.bicep:1:25:1:39 | CallExpression | | +| sample.bicep:1:25:1:37 | resourceGroup | sample.bicep:1:25:1:39 | CallExpression | | +| sample.bicep:1:25:1:39 | CallExpression | sample.bicep:1:25:1:48 | MemberExpression | | +| sample.bicep:1:25:1:39 | CallExpression | sample.bicep:1:25:1:48 | MemberExpression | | +| sample.bicep:1:25:1:39 | CallExpression | sample.bicep:1:25:1:48 | MemberExpression | | +| sample.bicep:1:25:1:39 | CallExpression | sample.bicep:1:25:1:48 | MemberExpression | | +| sample.bicep:1:25:1:39 | CallExpression | sample.bicep:1:25:1:48 | MemberExpression | | +| sample.bicep:1:25:1:39 | CallExpression | sample.bicep:1:25:1:48 | MemberExpression | | +| sample.bicep:1:25:1:39 | CallExpression | sample.bicep:1:25:1:48 | MemberExpression | | +| sample.bicep:1:25:1:39 | CallExpression | sample.bicep:1:25:1:48 | MemberExpression | | +| sample.bicep:1:25:1:39 | CallExpression | sample.bicep:1:25:1:48 | MemberExpression | | +| sample.bicep:1:25:1:48 | MemberExpression | sample.bicep:1:1:1:48 | ParameterDeclaration | | +| sample.bicep:1:25:1:48 | MemberExpression | sample.bicep:1:1:1:48 | ParameterDeclaration | | +| sample.bicep:1:25:1:48 | MemberExpression | sample.bicep:1:1:1:48 | ParameterDeclaration | | +| sample.bicep:1:25:1:48 | MemberExpression | sample.bicep:1:1:103:1 | Infrastructure | , BooleanSuccessor, BooleanSuccessor | +| sample.bicep:1:25:1:48 | MemberExpression | sample.bicep:1:1:103:1 | Infrastructure | , BooleanSuccessor, BooleanSuccessor | +| sample.bicep:1:25:1:48 | MemberExpression | sample.bicep:1:1:103:1 | Infrastructure | , BooleanSuccessor, BooleanSuccessor | +| sample.bicep:1:41:1:48 | location | sample.bicep:1:25:1:48 | MemberExpression | | +| sample.bicep:1:41:1:48 | location | sample.bicep:1:25:1:48 | MemberExpression | | +| sample.bicep:1:41:1:48 | location | sample.bicep:1:25:1:48 | MemberExpression | | diff --git a/ql/test/library-tests/cfg/Cfg.ql b/ql/test/library-tests/cfg/Cfg.ql new file mode 100644 index 0000000..b0e5604 --- /dev/null +++ b/ql/test/library-tests/cfg/Cfg.ql @@ -0,0 +1,2 @@ +import codeql.bicep.CFG +import codeql.bicep.controlflow.internal.ControlFlowGraphImpl::TestOutput \ No newline at end of file diff --git a/ql/test/library-tests/cfg/sample.bicep b/ql/test/library-tests/cfg/sample.bicep new file mode 100644 index 0000000..8189e57 --- /dev/null +++ b/ql/test/library-tests/cfg/sample.bicep @@ -0,0 +1,103 @@ +param location string = resourceGroup().location +param storageAccountName string = 'toylaunch${uniqueString(resourceGroup().id)}' +param vmName string = 'myVM' +param adminUsername string = 'azureuser' +param adminPassword string = 'P@ssw0rd123!' +param vnetName string = 'myVnet' +param subnetName string = 'mySubnet' +param publicIpName string = 'myPublicIP' +param nicName string = 'myNIC' + +resource storageAccount 'Microsoft.Storage/storageAccounts@2021-06-01' = { + name: storageAccountName + location: location + sku: { + name: 'Standard_LRS' + } + kind: 'StorageV2' + properties: { + accessTier: 'Hot' + } +} + +resource vnet 'Microsoft.Network/virtualNetworks@2021-05-01' = { + name: vnetName + location: location + properties: { + addressSpace: { + addressPrefixes: [ + '10.0.0.0/16' + ] + } + subnets: [ + { + name: subnetName + properties: { + addressPrefix: '10.0.0.0/24' + } + } + ] + } +} + +resource publicIp 'Microsoft.Network/publicIPAddresses@2021-05-01' = { + name: publicIpName + location: location + properties: { + publicIPAllocationMethod: 'Dynamic' + } +} + +resource nic 'Microsoft.Network/networkInterfaces@2021-05-01' = { + name: nicName + location: location + properties: { + ipConfigurations: [ + { + name: 'ipconfig1' + properties: { + subnet: { + id: vnet.properties.subnets[0].id + } + privateIPAllocationMethod: 'Dynamic' + publicIPAddress: { + id: publicIp.id + } + } + } + ] + } +} + +resource vm 'Microsoft.Compute/virtualMachines@2021-07-01' = { + name: vmName + location: location + properties: { + hardwareProfile: { + vmSize: 'Standard_DS1_v2' + } + osProfile: { + computerName: vmName + adminUsername: adminUsername + adminPassword: adminPassword + } + storageProfile: { + imageReference: { + publisher: 'Canonical' + offer: 'UbuntuServer' + sku: '18.04-LTS' + version: 'latest' + } + osDisk: { + createOption: 'FromImage' + } + } + networkProfile: { + networkInterfaces: [ + { + id: nic.id + } + ] + } + } +} \ No newline at end of file