From 980c271be37889b49fbc1fe29bf22fdea931af12 Mon Sep 17 00:00:00 2001 From: Mathew Payne Date: Fri, 15 Sep 2023 16:17:39 +0100 Subject: [PATCH 01/16] Add Bicep support to Extractor and init ast support --- Cargo.lock | 64 +- codeql-extractor.yml | 14 +- extractor/Cargo.toml | 1 + extractor/src/autobuilder.rs | 1 + extractor/src/extractor.rs | 6 + extractor/src/generator.rs | 4 + ql/lib/codeql/iac/ast/Bicep.qll | 55 ++ ql/lib/codeql/iac/ast/internal/Bicep.qll | 38 + ql/lib/codeql/iac/ast/internal/TreeSitter.qll | 727 ++++++++++++++++++ ql/lib/iac.dbscheme | 477 ++++++++++++ 10 files changed, 1345 insertions(+), 42 deletions(-) create mode 100644 ql/lib/codeql/iac/ast/Bicep.qll create mode 100644 ql/lib/codeql/iac/ast/internal/Bicep.qll diff --git a/Cargo.lock b/Cargo.lock index d28db38..9e4a349 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -48,9 +48,9 @@ dependencies = [ [[package]] name = "anstyle" -version = "1.0.2" +version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "15c4c2c83f81532e5845a733998b6971faca23490340a418e9b72a3ec9de12ea" +checksum = "b84bf0a05bbb2a83e5eb6fa36bb6e87baa08193c35ff52bbf6b38d8af2890e46" [[package]] name = "anstyle-parse" @@ -98,9 +98,9 @@ dependencies = [ [[package]] name = "bumpalo" -version = "3.13.0" +version = "3.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a3e2c3daef883ecc1b5d58c15adae93470a91d425f3532ba1695849656af3fc1" +checksum = "7f30e7476521f6f8af1a1c4c0b8cc94f0bee37d91763d0ca2665f299b6cd8aec" [[package]] name = "cc" @@ -119,25 +119,24 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" [[package]] name = "chrono" -version = "0.4.28" +version = "0.4.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "95ed24df0632f708f5f6d8082675bef2596f7084dee3dd55f632290bf35bfe0f" +checksum = "7f2c685bad3eb3d45a01354cedb7d5faa66194d1d58ba6e267a8de788f79db38" dependencies = [ "android-tzdata", "iana-time-zone", "js-sys", "num-traits", "serde", - "time", "wasm-bindgen", "windows-targets", ] [[package]] name = "clap" -version = "4.4.2" +version = "4.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6a13b88d2c62ff462f88e4a121f17a82c1af05693a2f192b5c38d14de73c19f6" +checksum = "84ed82781cea27b43c9b106a979fe450a13a31aab0500595fb3fc06616de08e6" dependencies = [ "clap_builder", "clap_derive", @@ -204,6 +203,7 @@ dependencies = [ "tracing", "tracing-subscriber", "tree-sitter", + "tree-sitter-bicep", "tree-sitter-dockerfile", "tree-sitter-hcl", ] @@ -429,9 +429,9 @@ checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" [[package]] name = "libc" -version = "0.2.147" +version = "0.2.148" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b4668fb0ea861c1df094127ac5f1da3409a82116a4ba74fca2e58ef927159bb3" +checksum = "9cdc71e17332e86d2e1d38c1f99edcb6288ee11b815fb1a4b049eaa2114d369b" [[package]] name = "log" @@ -521,9 +521,9 @@ checksum = "8afb450f006bf6385ca15ef45d71d2288452bc3683ce2e2cacc0d18e4be60b58" [[package]] name = "proc-macro2" -version = "1.0.66" +version = "1.0.67" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "18fb31db3f9bddb2ea821cde30a9f70117e3f119938b5ee630b7403aa6e2ead9" +checksum = "3d433d9f1a3e8c1263d9456598b16fec66f4acc9a74dacffd35c7bb09b3a1328" dependencies = [ "unicode-ident", ] @@ -637,9 +637,9 @@ dependencies = [ [[package]] name = "serde_json" -version = "1.0.105" +version = "1.0.107" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "693151e1ac27563d6dbcec9dee9fbd5da8539b20fa14ad3752b2e6d363ace360" +checksum = "6b420ce6e3d8bd882e9b243c6eed35dbc9a6110c9769e74b584e0d68d1f20c65" dependencies = [ "itoa", "ryu", @@ -669,9 +669,9 @@ checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" [[package]] name = "syn" -version = "2.0.31" +version = "2.0.33" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "718fa2415bcb8d8bd775917a1bf12a7931b6dfa890753378538118181e0cb398" +checksum = "9caece70c63bfba29ec2fed841a09851b14a235c60010fa4de58089b6c025668" dependencies = [ "proc-macro2", "quote", @@ -688,17 +688,6 @@ dependencies = [ "once_cell", ] -[[package]] -name = "time" -version = "0.1.45" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1b797afad3f312d1c66a56d11d0316f916356d11bd158fbc6ca6389ff6bf805a" -dependencies = [ - "libc", - "wasi", - "winapi", -] - [[package]] name = "tracing" version = "0.1.37" @@ -771,6 +760,15 @@ dependencies = [ "regex", ] +[[package]] +name = "tree-sitter-bicep" +version = "1.0.0" +source = "git+https://github.com/GeekMasher/tree-sitter-bicep?rev=3604d8c961ab129d2bfc6dfca56419c236ccdb83#3604d8c961ab129d2bfc6dfca56419c236ccdb83" +dependencies = [ + "cc", + "tree-sitter", +] + [[package]] name = "tree-sitter-dockerfile" version = "0.1.0" @@ -791,9 +789,9 @@ dependencies = [ [[package]] name = "unicode-ident" -version = "1.0.11" +version = "1.0.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "301abaae475aa91687eb82514b328ab47a211a533026cb25fc3e519b86adfc3c" +checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" [[package]] name = "utf8parse" @@ -807,12 +805,6 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "830b7e5d4d90034032940e4ace0d9a9a057e7a45cd94e6c007832e39edb82f6d" -[[package]] -name = "wasi" -version = "0.10.0+wasi-snapshot-preview1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1a143597ca7c7793eff794def352d41792a93c481eb1042423ff7ff72ba2c31f" - [[package]] name = "wasm-bindgen" version = "0.2.87" diff --git a/codeql-extractor.yml b/codeql-extractor.yml index e3d91be..0d02bfe 100644 --- a/codeql-extractor.yml +++ b/codeql-extractor.yml @@ -1,14 +1,16 @@ name: "iac" display_name: "IAC" -version: 0.0.3 +version: 0.0.4 column_kind: "utf8" legacy_qltest_extraction: true github_api_languages: - HCL - Docker + - Bicep scc_languages: - HCL - Docker + - Bicep # File types file_types: @@ -16,14 +18,14 @@ file_types: display_name: HCL extensions: - .tf - - .ftvars + - .tfvars - .hcl - - name: json - display_name: JSON - extensions: - - .json - name: dockerfile display_name: Dockerfile extensions: - .Dockerfile - .Containerfile + - name: + display_name: Bicep + extensions: + - .bicep diff --git a/extractor/Cargo.toml b/extractor/Cargo.toml index bdbfaed..99a5fd7 100644 --- a/extractor/Cargo.toml +++ b/extractor/Cargo.toml @@ -12,6 +12,7 @@ flate2 = "1.0" tree-sitter = ">= 0.20, < 0.21" tree-sitter-hcl = { git = "https://github.com/GeekMasher/tree-sitter-hcl", rev = "5e045dd1ff7852511c249c4c5d919d9556751d98" } tree-sitter-dockerfile = { git = "https://github.com/GeekMasher/tree-sitter-dockerfile", rev = "c0a9d694d9bf8ab79a919f5f9c7bc9c169caf321" } +tree-sitter-bicep = { git = "https://github.com/GeekMasher/tree-sitter-bicep", rev = "3604d8c961ab129d2bfc6dfca56419c236ccdb83" } clap = { version = "4.2", features = ["derive"] } tracing = "0.1" tracing-subscriber = { version = "0.3.17", features = ["env-filter"] } diff --git a/extractor/src/autobuilder.rs b/extractor/src/autobuilder.rs index 9b69ea5..ac24127 100644 --- a/extractor/src/autobuilder.rs +++ b/extractor/src/autobuilder.rs @@ -19,6 +19,7 @@ pub fn run(_: Options) -> std::io::Result<()> { ".tf", ".ftvars", // Terraform / HCL files ".Dockerfile", // Docker files + ".bicep", // Bicep files ]) .include_globs(&[ "**/Dockerfile", diff --git a/extractor/src/extractor.rs b/extractor/src/extractor.rs index 7eb47a1..414bd27 100644 --- a/extractor/src/extractor.rs +++ b/extractor/src/extractor.rs @@ -42,6 +42,12 @@ pub fn run(options: Options) -> std::io::Result<()> { node_types: tree_sitter_dockerfile::NODE_TYPES, file_globs: vec!["*Dockerfile".into(), "*Containerfile".into()], }, + simple::LanguageSpec { + prefix: "bicep", + ts_language: tree_sitter_bicep::language(), + node_types: tree_sitter_bicep::NODE_TYPES, + file_globs: vec!["*.bicep".into()], + }, ], trap_dir: options.output_dir, trap_compression: trap::Compression::from_env("CODEQL_IAC_TRAP_COMPRESSION"), diff --git a/extractor/src/generator.rs b/extractor/src/generator.rs index 538055c..14d453f 100644 --- a/extractor/src/generator.rs +++ b/extractor/src/generator.rs @@ -31,6 +31,10 @@ pub fn run(options: Options) -> std::io::Result<()> { name: "DOCKERFILE".to_owned(), node_types: tree_sitter_dockerfile::NODE_TYPES, }, + Language { + name: "BICEP".to_owned(), + node_types: tree_sitter_bicep::NODE_TYPES, + }, ]; generate(languages, options.dbscheme, options.library) diff --git a/ql/lib/codeql/iac/ast/Bicep.qll b/ql/lib/codeql/iac/ast/Bicep.qll new file mode 100644 index 0000000..5fe2ece --- /dev/null +++ b/ql/lib/codeql/iac/ast/Bicep.qll @@ -0,0 +1,55 @@ +private import codeql.Locations +private import codeql.files.FileSystem +private import codeql.iac.ast.internal.Bicep + +/** An AST node of a Bicep program */ +class BicepAstNode extends TBicepAstNode { + string toString() { result = this.getAPrimaryQlClass() } + + /** Gets the location of the AST node. */ + cached + Location getLocation() { result = this.getFullLocation() } // overridden in some subclasses + + /** Gets the file containing this AST node. */ + cached + File getFile() { result = this.getFullLocation().getFile() } + + /** Gets the location that spans the entire AST node. */ + cached + final Location getFullLocation() { result = toBicepTreeSitter(this).getLocation() } + + predicate hasLocationInfo( + string filepath, int startline, int startcolumn, int endline, int endcolumn + ) { + if exists(this.getLocation()) + then this.getLocation().hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn) + else ( + filepath = "" and + startline = 0 and + startcolumn = 0 and + endline = 0 and + endcolumn = 0 + ) + } + + /** + * Gets the parent in the AST for this node. + */ + cached + BicepAstNode getParent() { result.getAChild(_) = this } + + /** + * Gets a child of this node, which can also be retrieved using a predicate + * named `pred`. + */ + cached + BicepAstNode getAChild(string pred) { none() } + + /** Gets any child of this node. */ + BicepAstNode getAChild() { result = this.getAChild(_) } + + /** + * Gets the primary QL class for the ast node. + */ + string getAPrimaryQlClass() { result = "???" } +} diff --git a/ql/lib/codeql/iac/ast/internal/Bicep.qll b/ql/lib/codeql/iac/ast/internal/Bicep.qll new file mode 100644 index 0000000..83a5368 --- /dev/null +++ b/ql/lib/codeql/iac/ast/internal/Bicep.qll @@ -0,0 +1,38 @@ +import TreeSitter + +cached +newtype TBicepAstNode = + TComment(BICEP::Comment c) or + // Literals + TBoolean(BICEP::Boolean b) or + TNull(BICEP::Null n) or + TNumber(BICEP::Number n) or + TString(BICEP::String s) or + TMultilineStringContent(BICEP::MultilineStringContent m) or + // Expressions + TAssignmentExpression(BICEP::AssignmentExpression a) or + TBinaryExpression(BICEP::BinaryExpression b) or + TCallExpression(BICEP::CallExpression c) or + TExpression(BICEP::Expression e) or + TLambdaExpression(BICEP::LambdaExpression l) or + TMemberExpression(BICEP::MemberExpression m) or + TParenthesizedExpression(BICEP::ParenthesizedExpression p) or + TPrimaryExpression(BICEP::PrimaryExpression p) or + TResourceExpression(BICEP::ResourceExpression r) or + TSubscriptExpression(BICEP::SubscriptExpression s) or + TTernaryExpression(BICEP::TernaryExpression t) or + TUnaryExpression(BICEP::UnaryExpression u) + +class TLiteral = TBoolean or TNull or TNumber or TString or TMultilineStringContent; + +class TExpr = + TAssignmentExpression or TBinaryExpression or TCallExpression or TExpression or + TLambdaExpression or TMemberExpression or TParenthesizedExpression or TPrimaryExpression or + TResourceExpression or TSubscriptExpression or TTernaryExpression or TUnaryExpression; + +cached +BICEP::AstNode toBicepTreeSitter(TBicepAstNode n) { + n = TComment(result) or + n = TBoolean(result) or + n = TNull(result) +} diff --git a/ql/lib/codeql/iac/ast/internal/TreeSitter.qll b/ql/lib/codeql/iac/ast/internal/TreeSitter.qll index d74777c..4dedfda 100644 --- a/ql/lib/codeql/iac/ast/internal/TreeSitter.qll +++ b/ql/lib/codeql/iac/ast/internal/TreeSitter.qll @@ -1192,3 +1192,730 @@ module DOCKERFILE { final override AstNode getAFieldOrChild() { dockerfile_workdir_instruction_def(this, result) } } } + +module BICEP { + /** The base class for all AST nodes */ + class AstNode extends @bicep_ast_node { + /** Gets a string representation of this element. */ + string toString() { result = this.getAPrimaryQlClass() } + + /** Gets the location of this element. */ + final L::Location getLocation() { bicep_ast_node_info(this, _, _, result) } + + /** Gets the parent of this element. */ + final AstNode getParent() { bicep_ast_node_info(this, result, _, _) } + + /** Gets the index of this node among the children of its parent. */ + final int getParentIndex() { bicep_ast_node_info(this, _, result, _) } + + /** Gets a field or child node of this node. */ + AstNode getAFieldOrChild() { none() } + + /** Gets the name of the primary QL class for this element. */ + string getAPrimaryQlClass() { result = "???" } + + /** Gets a comma-separated list of the names of the primary CodeQL classes to which this element belongs. */ + string getPrimaryQlClasses() { result = concat(this.getAPrimaryQlClass(), ",") } + } + + /** A token. */ + class Token extends @bicep_token, AstNode { + /** Gets the value of this token. */ + final string getValue() { bicep_tokeninfo(this, _, result) } + + /** Gets a string representation of this element. */ + final override string toString() { result = this.getValue() } + + /** Gets the name of the primary QL class for this element. */ + override string getAPrimaryQlClass() { result = "Token" } + } + + /** A reserved word. */ + class ReservedWord extends @bicep_reserved_word, Token { + /** Gets the name of the primary QL class for this element. */ + final override string getAPrimaryQlClass() { result = "ReservedWord" } + } + + /** A class representing `arguments` nodes. */ + class Arguments extends @bicep_arguments, AstNode { + /** Gets the name of the primary QL class for this element. */ + final override string getAPrimaryQlClass() { result = "Arguments" } + + /** Gets the `i`th child of this node. */ + final Expression getChild(int i) { bicep_arguments_child(this, i, result) } + + /** Gets a field or child node of this node. */ + final override AstNode getAFieldOrChild() { bicep_arguments_child(this, _, result) } + } + + /** A class representing `array` nodes. */ + class Array extends @bicep_array, AstNode { + /** Gets the name of the primary QL class for this element. */ + final override string getAPrimaryQlClass() { result = "Array" } + + /** Gets the `i`th child of this node. */ + final AstNode getChild(int i) { bicep_array_child(this, i, result) } + + /** Gets a field or child node of this node. */ + final override AstNode getAFieldOrChild() { bicep_array_child(this, _, result) } + } + + /** A class representing `array_type` nodes. */ + class ArrayType extends @bicep_array_type, AstNode { + /** Gets the name of the primary QL class for this element. */ + final override string getAPrimaryQlClass() { result = "ArrayType" } + + /** Gets the child of this node. */ + final Expression getChild() { bicep_array_type_def(this, result) } + + /** Gets a field or child node of this node. */ + final override AstNode getAFieldOrChild() { bicep_array_type_def(this, result) } + } + + /** A class representing `assignment_expression` nodes. */ + class AssignmentExpression extends @bicep_assignment_expression, AstNode { + /** Gets the name of the primary QL class for this element. */ + final override string getAPrimaryQlClass() { result = "AssignmentExpression" } + + /** Gets the node corresponding to the field `left`. */ + final AstNode getLeft() { bicep_assignment_expression_def(this, result, _) } + + /** Gets the node corresponding to the field `right`. */ + final Expression getRight() { bicep_assignment_expression_def(this, _, result) } + + /** Gets a field or child node of this node. */ + final override AstNode getAFieldOrChild() { + bicep_assignment_expression_def(this, result, _) or + bicep_assignment_expression_def(this, _, result) + } + } + + /** A class representing `binary_expression` nodes. */ + class BinaryExpression extends @bicep_binary_expression, AstNode { + /** Gets the name of the primary QL class for this element. */ + final override string getAPrimaryQlClass() { result = "BinaryExpression" } + + /** Gets the node corresponding to the field `left`. */ + final Expression getLeft() { bicep_binary_expression_def(this, result, _, _) } + + /** Gets the node corresponding to the field `operator`. */ + final string getOperator() { + exists(int value | bicep_binary_expression_def(this, _, value, _) | + result = "!=" and value = 0 + or + result = "!~" and value = 1 + or + result = "%" and value = 2 + or + result = "&&" and value = 3 + or + result = "*" and value = 4 + or + result = "+" and value = 5 + or + result = "-" and value = 6 + or + result = "/" and value = 7 + or + result = "<" and value = 8 + or + result = "<=" and value = 9 + or + result = "==" and value = 10 + or + result = "=~" and value = 11 + or + result = ">" and value = 12 + or + result = ">=" and value = 13 + or + result = "??" and value = 14 + or + result = "|" and value = 15 + or + result = "||" and value = 16 + ) + } + + /** Gets the node corresponding to the field `right`. */ + final Expression getRight() { bicep_binary_expression_def(this, _, _, result) } + + /** Gets a field or child node of this node. */ + final override AstNode getAFieldOrChild() { + bicep_binary_expression_def(this, result, _, _) or + bicep_binary_expression_def(this, _, _, result) + } + } + + /** A class representing `boolean` tokens. */ + class Boolean extends @bicep_token_boolean, Token { + /** Gets the name of the primary QL class for this element. */ + final override string getAPrimaryQlClass() { result = "Boolean" } + } + + /** A class representing `builtin_type` nodes. */ + class BuiltinType extends @bicep_builtin_type, AstNode { + /** Gets the name of the primary QL class for this element. */ + final override string getAPrimaryQlClass() { result = "BuiltinType" } + + /** Gets the child of this node. */ + final PrimitiveType getChild() { bicep_builtin_type_def(this, result) } + + /** Gets a field or child node of this node. */ + final override AstNode getAFieldOrChild() { bicep_builtin_type_def(this, result) } + } + + /** A class representing `call_expression` nodes. */ + class CallExpression extends @bicep_call_expression, AstNode { + /** Gets the name of the primary QL class for this element. */ + final override string getAPrimaryQlClass() { result = "CallExpression" } + + /** Gets the node corresponding to the field `arguments`. */ + final Arguments getArguments() { bicep_call_expression_def(this, result, _) } + + /** Gets the node corresponding to the field `function`. */ + final Expression getFunction() { bicep_call_expression_def(this, _, result) } + + /** Gets the child of this node. */ + final NullableReturnType getChild() { bicep_call_expression_child(this, result) } + + /** Gets a field or child node of this node. */ + final override AstNode getAFieldOrChild() { + bicep_call_expression_def(this, result, _) or + bicep_call_expression_def(this, _, result) or + bicep_call_expression_child(this, result) + } + } + + /** A class representing `comment` tokens. */ + class Comment extends @bicep_token_comment, Token { + /** Gets the name of the primary QL class for this element. */ + final override string getAPrimaryQlClass() { result = "Comment" } + } + + /** A class representing `compatible_identifier` nodes. */ + class CompatibleIdentifier extends @bicep_compatible_identifier, AstNode { + /** Gets the name of the primary QL class for this element. */ + final override string getAPrimaryQlClass() { result = "CompatibleIdentifier" } + + /** Gets the child of this node. */ + final Identifier getChild() { bicep_compatible_identifier_def(this, result) } + + /** Gets a field or child node of this node. */ + final override AstNode getAFieldOrChild() { bicep_compatible_identifier_def(this, result) } + } + + class Declaration extends @bicep_declaration, AstNode { } + + /** A class representing `decorator` nodes. */ + class Decorator extends @bicep_decorator, AstNode { + /** Gets the name of the primary QL class for this element. */ + final override string getAPrimaryQlClass() { result = "Decorator" } + + /** Gets the child of this node. */ + final CallExpression getChild() { bicep_decorator_def(this, result) } + + /** Gets a field or child node of this node. */ + final override AstNode getAFieldOrChild() { bicep_decorator_def(this, result) } + } + + /** A class representing `decorators` nodes. */ + class Decorators extends @bicep_decorators, AstNode { + /** Gets the name of the primary QL class for this element. */ + final override string getAPrimaryQlClass() { result = "Decorators" } + + /** Gets the `i`th child of this node. */ + final Decorator getChild(int i) { bicep_decorators_child(this, i, result) } + + /** Gets a field or child node of this node. */ + final override AstNode getAFieldOrChild() { bicep_decorators_child(this, _, result) } + } + + /** A class representing `diagnostic_comment` tokens. */ + class DiagnosticComment extends @bicep_token_diagnostic_comment, Token { + /** Gets the name of the primary QL class for this element. */ + final override string getAPrimaryQlClass() { result = "DiagnosticComment" } + } + + /** A class representing `escape_sequence` tokens. */ + class EscapeSequence extends @bicep_token_escape_sequence, Token { + /** Gets the name of the primary QL class for this element. */ + final override string getAPrimaryQlClass() { result = "EscapeSequence" } + } + + class Expression extends @bicep_expression, AstNode { } + + /** A class representing `for_loop_parameters` nodes. */ + class ForLoopParameters extends @bicep_for_loop_parameters, AstNode { + /** Gets the name of the primary QL class for this element. */ + final override string getAPrimaryQlClass() { result = "ForLoopParameters" } + + /** Gets the `i`th child of this node. */ + final AstNode getChild(int i) { bicep_for_loop_parameters_child(this, i, result) } + + /** Gets a field or child node of this node. */ + final override AstNode getAFieldOrChild() { bicep_for_loop_parameters_child(this, _, result) } + } + + /** A class representing `for_statement` nodes. */ + class ForStatement extends @bicep_for_statement, AstNode { + /** Gets the name of the primary QL class for this element. */ + final override string getAPrimaryQlClass() { result = "ForStatement" } + + /** Gets the node corresponding to the field `body`. */ + final AstNode getBody() { bicep_for_statement_def(this, result) } + + /** Gets the node corresponding to the field `initializer`. */ + final Identifier getInitializer() { bicep_for_statement_initializer(this, result) } + + /** Gets the `i`th child of this node. */ + final AstNode getChild(int i) { bicep_for_statement_child(this, i, result) } + + /** Gets a field or child node of this node. */ + final override AstNode getAFieldOrChild() { + bicep_for_statement_def(this, result) or + bicep_for_statement_initializer(this, result) or + bicep_for_statement_child(this, _, result) + } + } + + /** A class representing `identifier` tokens. */ + class Identifier extends @bicep_token_identifier, Token { + /** Gets the name of the primary QL class for this element. */ + final override string getAPrimaryQlClass() { result = "Identifier" } + } + + /** A class representing `if_statement` nodes. */ + class IfStatement extends @bicep_if_statement, AstNode { + /** Gets the name of the primary QL class for this element. */ + final override string getAPrimaryQlClass() { result = "IfStatement" } + + /** Gets the `i`th child of this node. */ + final AstNode getChild(int i) { bicep_if_statement_child(this, i, result) } + + /** Gets a field or child node of this node. */ + final override AstNode getAFieldOrChild() { bicep_if_statement_child(this, _, result) } + } + + /** A class representing `import_name` tokens. */ + class ImportName extends @bicep_token_import_name, Token { + /** Gets the name of the primary QL class for this element. */ + final override string getAPrimaryQlClass() { result = "ImportName" } + } + + /** A class representing `import_statement` nodes. */ + class ImportStatement extends @bicep_import_statement, AstNode { + /** Gets the name of the primary QL class for this element. */ + final override string getAPrimaryQlClass() { result = "ImportStatement" } + + /** Gets the child of this node. */ + final ImportString getChild() { bicep_import_statement_def(this, result) } + + /** Gets a field or child node of this node. */ + final override AstNode getAFieldOrChild() { bicep_import_statement_def(this, result) } + } + + /** A class representing `import_string` nodes. */ + class ImportString extends @bicep_import_string, AstNode { + /** Gets the name of the primary QL class for this element. */ + final override string getAPrimaryQlClass() { result = "ImportString" } + + /** Gets the `i`th child of this node. */ + final AstNode getChild(int i) { bicep_import_string_child(this, i, result) } + + /** Gets a field or child node of this node. */ + final override AstNode getAFieldOrChild() { bicep_import_string_child(this, _, result) } + } + + /** A class representing `import_version` tokens. */ + class ImportVersion extends @bicep_token_import_version, Token { + /** Gets the name of the primary QL class for this element. */ + final override string getAPrimaryQlClass() { result = "ImportVersion" } + } + + /** A class representing `import_with_statement` nodes. */ + class ImportWithStatement extends @bicep_import_with_statement, AstNode { + /** Gets the name of the primary QL class for this element. */ + final override string getAPrimaryQlClass() { result = "ImportWithStatement" } + + /** Gets the `i`th child of this node. */ + final AstNode getChild(int i) { bicep_import_with_statement_child(this, i, result) } + + /** Gets a field or child node of this node. */ + final override AstNode getAFieldOrChild() { bicep_import_with_statement_child(this, _, result) } + } + + /** A class representing `infrastructure` nodes. */ + class Infrastructure extends @bicep_infrastructure, AstNode { + /** Gets the name of the primary QL class for this element. */ + final override string getAPrimaryQlClass() { result = "Infrastructure" } + + /** Gets the `i`th child of this node. */ + final Statement getChild(int i) { bicep_infrastructure_child(this, i, result) } + + /** Gets a field or child node of this node. */ + final override AstNode getAFieldOrChild() { bicep_infrastructure_child(this, _, result) } + } + + /** A class representing `interpolation` nodes. */ + class Interpolation extends @bicep_interpolation, AstNode { + /** Gets the name of the primary QL class for this element. */ + final override string getAPrimaryQlClass() { result = "Interpolation" } + + /** Gets the child of this node. */ + final Expression getChild() { bicep_interpolation_def(this, result) } + + /** Gets a field or child node of this node. */ + final override AstNode getAFieldOrChild() { bicep_interpolation_def(this, result) } + } + + /** A class representing `lambda_expression` nodes. */ + class LambdaExpression extends @bicep_lambda_expression, AstNode { + /** Gets the name of the primary QL class for this element. */ + final override string getAPrimaryQlClass() { result = "LambdaExpression" } + + /** Gets the `i`th child of this node. */ + final Expression getChild(int i) { bicep_lambda_expression_child(this, i, result) } + + /** Gets a field or child node of this node. */ + final override AstNode getAFieldOrChild() { bicep_lambda_expression_child(this, _, result) } + } + + /** A class representing `loop_enumerator` tokens. */ + class LoopEnumerator extends @bicep_token_loop_enumerator, Token { + /** Gets the name of the primary QL class for this element. */ + final override string getAPrimaryQlClass() { result = "LoopEnumerator" } + } + + /** A class representing `loop_variable` tokens. */ + class LoopVariable extends @bicep_token_loop_variable, Token { + /** Gets the name of the primary QL class for this element. */ + final override string getAPrimaryQlClass() { result = "LoopVariable" } + } + + /** A class representing `member_expression` nodes. */ + class MemberExpression extends @bicep_member_expression, AstNode { + /** Gets the name of the primary QL class for this element. */ + final override string getAPrimaryQlClass() { result = "MemberExpression" } + + /** Gets the node corresponding to the field `object`. */ + final Expression getObject() { bicep_member_expression_def(this, result, _) } + + /** Gets the node corresponding to the field `property`. */ + final PropertyIdentifier getProperty() { bicep_member_expression_def(this, _, result) } + + /** Gets a field or child node of this node. */ + final override AstNode getAFieldOrChild() { + bicep_member_expression_def(this, result, _) or bicep_member_expression_def(this, _, result) + } + } + + /** A class representing `metadata_declaration` nodes. */ + class MetadataDeclaration extends @bicep_metadata_declaration, AstNode { + /** Gets the name of the primary QL class for this element. */ + final override string getAPrimaryQlClass() { result = "MetadataDeclaration" } + + /** Gets the `i`th child of this node. */ + final AstNode getChild(int i) { bicep_metadata_declaration_child(this, i, result) } + + /** Gets a field or child node of this node. */ + final override AstNode getAFieldOrChild() { bicep_metadata_declaration_child(this, _, result) } + } + + /** A class representing `module_declaration` nodes. */ + class ModuleDeclaration extends @bicep_module_declaration, AstNode { + /** Gets the name of the primary QL class for this element. */ + final override string getAPrimaryQlClass() { result = "ModuleDeclaration" } + + /** Gets the `i`th child of this node. */ + final AstNode getChild(int i) { bicep_module_declaration_child(this, i, result) } + + /** Gets a field or child node of this node. */ + final override AstNode getAFieldOrChild() { bicep_module_declaration_child(this, _, result) } + } + + /** A class representing `multiline_string_content` tokens. */ + class MultilineStringContent extends @bicep_token_multiline_string_content, Token { + /** Gets the name of the primary QL class for this element. */ + final override string getAPrimaryQlClass() { result = "MultilineStringContent" } + } + + /** A class representing `null` tokens. */ + class Null extends @bicep_token_null, Token { + /** Gets the name of the primary QL class for this element. */ + final override string getAPrimaryQlClass() { result = "Null" } + } + + /** A class representing `nullable_return_type` tokens. */ + class NullableReturnType extends @bicep_token_nullable_return_type, Token { + /** Gets the name of the primary QL class for this element. */ + final override string getAPrimaryQlClass() { result = "NullableReturnType" } + } + + /** A class representing `nullable_type` nodes. */ + class NullableType extends @bicep_nullable_type, AstNode { + /** Gets the name of the primary QL class for this element. */ + final override string getAPrimaryQlClass() { result = "NullableType" } + + /** Gets the child of this node. */ + final AstNode getChild() { bicep_nullable_type_def(this, result) } + + /** Gets a field or child node of this node. */ + final override AstNode getAFieldOrChild() { bicep_nullable_type_def(this, result) } + } + + /** A class representing `number` tokens. */ + class Number extends @bicep_token_number, Token { + /** Gets the name of the primary QL class for this element. */ + final override string getAPrimaryQlClass() { result = "Number" } + } + + /** A class representing `object` nodes. */ + class Object extends @bicep_object, AstNode { + /** Gets the name of the primary QL class for this element. */ + final override string getAPrimaryQlClass() { result = "Object" } + + /** Gets the `i`th child of this node. */ + final AstNode getChild(int i) { bicep_object_child(this, i, result) } + + /** Gets a field or child node of this node. */ + final override AstNode getAFieldOrChild() { bicep_object_child(this, _, result) } + } + + /** A class representing `object_property` nodes. */ + class ObjectProperty extends @bicep_object_property, AstNode { + /** Gets the name of the primary QL class for this element. */ + final override string getAPrimaryQlClass() { result = "ObjectProperty" } + + /** Gets the `i`th child of this node. */ + final AstNode getChild(int i) { bicep_object_property_child(this, i, result) } + + /** Gets a field or child node of this node. */ + final override AstNode getAFieldOrChild() { bicep_object_property_child(this, _, result) } + } + + /** A class representing `output_declaration` nodes. */ + class OutputDeclaration extends @bicep_output_declaration, AstNode { + /** Gets the name of the primary QL class for this element. */ + final override string getAPrimaryQlClass() { result = "OutputDeclaration" } + + /** Gets the `i`th child of this node. */ + final AstNode getChild(int i) { bicep_output_declaration_child(this, i, result) } + + /** Gets a field or child node of this node. */ + final override AstNode getAFieldOrChild() { bicep_output_declaration_child(this, _, result) } + } + + /** A class representing `parameter_declaration` nodes. */ + class ParameterDeclaration extends @bicep_parameter_declaration, AstNode { + /** Gets the name of the primary QL class for this element. */ + final override string getAPrimaryQlClass() { result = "ParameterDeclaration" } + + /** Gets the `i`th child of this node. */ + final AstNode getChild(int i) { bicep_parameter_declaration_child(this, i, result) } + + /** Gets a field or child node of this node. */ + final override AstNode getAFieldOrChild() { bicep_parameter_declaration_child(this, _, result) } + } + + /** A class representing `parenthesized_expression` nodes. */ + class ParenthesizedExpression extends @bicep_parenthesized_expression, AstNode { + /** Gets the name of the primary QL class for this element. */ + final override string getAPrimaryQlClass() { result = "ParenthesizedExpression" } + + /** Gets the `i`th child of this node. */ + final Expression getChild(int i) { bicep_parenthesized_expression_child(this, i, result) } + + /** Gets a field or child node of this node. */ + final override AstNode getAFieldOrChild() { + bicep_parenthesized_expression_child(this, _, result) + } + } + + class PrimaryExpression extends @bicep_primary_expression, AstNode { } + + /** A class representing `primitive_type` tokens. */ + class PrimitiveType extends @bicep_token_primitive_type, Token { + /** Gets the name of the primary QL class for this element. */ + final override string getAPrimaryQlClass() { result = "PrimitiveType" } + } + + /** A class representing `property_identifier` tokens. */ + class PropertyIdentifier extends @bicep_token_property_identifier, Token { + /** Gets the name of the primary QL class for this element. */ + final override string getAPrimaryQlClass() { result = "PropertyIdentifier" } + } + + /** A class representing `resource_declaration` nodes. */ + class ResourceDeclaration extends @bicep_resource_declaration, AstNode { + /** Gets the name of the primary QL class for this element. */ + final override string getAPrimaryQlClass() { result = "ResourceDeclaration" } + + /** Gets the `i`th child of this node. */ + final AstNode getChild(int i) { bicep_resource_declaration_child(this, i, result) } + + /** Gets a field or child node of this node. */ + final override AstNode getAFieldOrChild() { bicep_resource_declaration_child(this, _, result) } + } + + /** A class representing `resource_expression` nodes. */ + class ResourceExpression extends @bicep_resource_expression, AstNode { + /** Gets the name of the primary QL class for this element. */ + final override string getAPrimaryQlClass() { result = "ResourceExpression" } + + /** Gets the node corresponding to the field `object`. */ + final Expression getObject() { bicep_resource_expression_def(this, result, _) } + + /** Gets the node corresponding to the field `resource`. */ + final Identifier getResource() { bicep_resource_expression_def(this, _, result) } + + /** Gets a field or child node of this node. */ + final override AstNode getAFieldOrChild() { + bicep_resource_expression_def(this, result, _) or + bicep_resource_expression_def(this, _, result) + } + } + + class Statement extends @bicep_statement, AstNode { } + + /** A class representing `string` nodes. */ + class String extends @bicep_string__, AstNode { + /** Gets the name of the primary QL class for this element. */ + final override string getAPrimaryQlClass() { result = "String" } + + /** Gets the `i`th child of this node. */ + final AstNode getChild(int i) { bicep_string_child(this, i, result) } + + /** Gets a field or child node of this node. */ + final override AstNode getAFieldOrChild() { bicep_string_child(this, _, result) } + } + + /** A class representing `string_content` tokens. */ + class StringContent extends @bicep_token_string_content, Token { + /** Gets the name of the primary QL class for this element. */ + final override string getAPrimaryQlClass() { result = "StringContent" } + } + + /** A class representing `subscript_expression` nodes. */ + class SubscriptExpression extends @bicep_subscript_expression, AstNode { + /** Gets the name of the primary QL class for this element. */ + final override string getAPrimaryQlClass() { result = "SubscriptExpression" } + + /** Gets the node corresponding to the field `index`. */ + final Expression getIndex() { bicep_subscript_expression_def(this, result, _) } + + /** Gets the node corresponding to the field `object`. */ + final Expression getObject() { bicep_subscript_expression_def(this, _, result) } + + /** Gets a field or child node of this node. */ + final override AstNode getAFieldOrChild() { + bicep_subscript_expression_def(this, result, _) or + bicep_subscript_expression_def(this, _, result) + } + } + + /** A class representing `target_scope_assignment` nodes. */ + class TargetScopeAssignment extends @bicep_target_scope_assignment, AstNode { + /** Gets the name of the primary QL class for this element. */ + final override string getAPrimaryQlClass() { result = "TargetScopeAssignment" } + + /** Gets the child of this node. */ + final String getChild() { bicep_target_scope_assignment_def(this, result) } + + /** Gets a field or child node of this node. */ + final override AstNode getAFieldOrChild() { bicep_target_scope_assignment_def(this, result) } + } + + /** A class representing `ternary_expression` nodes. */ + class TernaryExpression extends @bicep_ternary_expression, AstNode { + /** Gets the name of the primary QL class for this element. */ + final override string getAPrimaryQlClass() { result = "TernaryExpression" } + + /** Gets the node corresponding to the field `alternative`. */ + final Expression getAlternative() { bicep_ternary_expression_def(this, result, _, _) } + + /** Gets the node corresponding to the field `condition`. */ + final Expression getCondition() { bicep_ternary_expression_def(this, _, result, _) } + + /** Gets the node corresponding to the field `consequence`. */ + final Expression getConsequence() { bicep_ternary_expression_def(this, _, _, result) } + + /** Gets a field or child node of this node. */ + final override AstNode getAFieldOrChild() { + bicep_ternary_expression_def(this, result, _, _) or + bicep_ternary_expression_def(this, _, result, _) or + bicep_ternary_expression_def(this, _, _, result) + } + } + + /** A class representing `type` nodes. */ + class Type extends @bicep_type__, AstNode { + /** Gets the name of the primary QL class for this element. */ + final override string getAPrimaryQlClass() { result = "Type" } + + /** Gets the node corresponding to the field `left`. */ + final AstNode getLeft() { bicep_type_left(this, result) } + + /** Gets the node corresponding to the field `operator`. */ + final AstNode getOperator() { bicep_type_operator(this, result) } + + /** Gets the node corresponding to the field `right`. */ + final AstNode getRight() { bicep_type_right(this, result) } + + /** Gets the child of this node. */ + final AstNode getChild() { bicep_type_child(this, result) } + + /** Gets a field or child node of this node. */ + final override AstNode getAFieldOrChild() { + bicep_type_left(this, result) or + bicep_type_operator(this, result) or + bicep_type_right(this, result) or + bicep_type_child(this, result) + } + } + + /** A class representing `type_declaration` nodes. */ + class TypeDeclaration extends @bicep_type_declaration, AstNode { + /** Gets the name of the primary QL class for this element. */ + final override string getAPrimaryQlClass() { result = "TypeDeclaration" } + + /** Gets the `i`th child of this node. */ + final AstNode getChild(int i) { bicep_type_declaration_child(this, i, result) } + + /** Gets a field or child node of this node. */ + final override AstNode getAFieldOrChild() { bicep_type_declaration_child(this, _, result) } + } + + /** A class representing `unary_expression` nodes. */ + class UnaryExpression extends @bicep_unary_expression, AstNode { + /** Gets the name of the primary QL class for this element. */ + final override string getAPrimaryQlClass() { result = "UnaryExpression" } + + /** Gets the node corresponding to the field `argument`. */ + final Expression getArgument() { bicep_unary_expression_def(this, result, _) } + + /** Gets the node corresponding to the field `operator`. */ + final string getOperator() { + exists(int value | bicep_unary_expression_def(this, _, value) | + result = "!" and value = 0 + or + result = "-" and value = 1 + ) + } + + /** Gets a field or child node of this node. */ + final override AstNode getAFieldOrChild() { bicep_unary_expression_def(this, result, _) } + } + + /** A class representing `variable_declaration` nodes. */ + class VariableDeclaration extends @bicep_variable_declaration, AstNode { + /** Gets the name of the primary QL class for this element. */ + final override string getAPrimaryQlClass() { result = "VariableDeclaration" } + + /** Gets the `i`th child of this node. */ + final AstNode getChild(int i) { bicep_variable_declaration_child(this, i, result) } + + /** Gets a field or child node of this node. */ + final override AstNode getAFieldOrChild() { bicep_variable_declaration_child(this, _, result) } + } +} diff --git a/ql/lib/iac.dbscheme b/ql/lib/iac.dbscheme index 74fb276..c301edb 100644 --- a/ql/lib/iac.dbscheme +++ b/ql/lib/iac.dbscheme @@ -909,3 +909,480 @@ dockerfile_ast_node_info( int loc: @location_default ref ); +/*- BICEP dbscheme -*/ +#keyset[bicep_arguments, index] +bicep_arguments_child( + int bicep_arguments: @bicep_arguments ref, + int index: int ref, + unique int child: @bicep_expression ref +); + +bicep_arguments_def( + unique int id: @bicep_arguments +); + +@bicep_array_child_type = @bicep_decorators | @bicep_expression + +#keyset[bicep_array, index] +bicep_array_child( + int bicep_array: @bicep_array ref, + int index: int ref, + unique int child: @bicep_array_child_type ref +); + +bicep_array_def( + unique int id: @bicep_array +); + +bicep_array_type_def( + unique int id: @bicep_array_type, + int child: @bicep_expression ref +); + +@bicep_assignment_expression_left_type = @bicep_member_expression | @bicep_parenthesized_expression | @bicep_resource_expression | @bicep_subscript_expression | @bicep_token_identifier + +bicep_assignment_expression_def( + unique int id: @bicep_assignment_expression, + int left: @bicep_assignment_expression_left_type ref, + int right: @bicep_expression ref +); + +case @bicep_binary_expression.operator of + 0 = @bicep_binary_expression_bangequal +| 1 = @bicep_binary_expression_bangtilde +| 2 = @bicep_binary_expression_percent +| 3 = @bicep_binary_expression_ampersandampersand +| 4 = @bicep_binary_expression_star +| 5 = @bicep_binary_expression_plus +| 6 = @bicep_binary_expression_minus +| 7 = @bicep_binary_expression_slash +| 8 = @bicep_binary_expression_langle +| 9 = @bicep_binary_expression_langleequal +| 10 = @bicep_binary_expression_equalequal +| 11 = @bicep_binary_expression_equaltilde +| 12 = @bicep_binary_expression_rangle +| 13 = @bicep_binary_expression_rangleequal +| 14 = @bicep_binary_expression_questionquestion +| 15 = @bicep_binary_expression_pipe +| 16 = @bicep_binary_expression_pipepipe +; + + +bicep_binary_expression_def( + unique int id: @bicep_binary_expression, + int left: @bicep_expression ref, + int operator: int ref, + int right: @bicep_expression ref +); + +bicep_builtin_type_def( + unique int id: @bicep_builtin_type, + int child: @bicep_token_primitive_type ref +); + +bicep_call_expression_child( + unique int bicep_call_expression: @bicep_call_expression ref, + unique int child: @bicep_token_nullable_return_type ref +); + +bicep_call_expression_def( + unique int id: @bicep_call_expression, + int arguments: @bicep_arguments ref, + int function: @bicep_expression ref +); + +bicep_compatible_identifier_def( + unique int id: @bicep_compatible_identifier, + int child: @bicep_token_identifier ref +); + +@bicep_declaration = @bicep_metadata_declaration | @bicep_module_declaration | @bicep_output_declaration | @bicep_parameter_declaration | @bicep_resource_declaration | @bicep_type_declaration | @bicep_variable_declaration + +bicep_decorator_def( + unique int id: @bicep_decorator, + int child: @bicep_call_expression ref +); + +#keyset[bicep_decorators, index] +bicep_decorators_child( + int bicep_decorators: @bicep_decorators ref, + int index: int ref, + unique int child: @bicep_decorator ref +); + +bicep_decorators_def( + unique int id: @bicep_decorators +); + +@bicep_expression = @bicep_assignment_expression | @bicep_binary_expression | @bicep_lambda_expression | @bicep_primary_expression | @bicep_ternary_expression | @bicep_unary_expression + +@bicep_for_loop_parameters_child_type = @bicep_token_loop_enumerator | @bicep_token_loop_variable + +#keyset[bicep_for_loop_parameters, index] +bicep_for_loop_parameters_child( + int bicep_for_loop_parameters: @bicep_for_loop_parameters ref, + int index: int ref, + unique int child: @bicep_for_loop_parameters_child_type ref +); + +bicep_for_loop_parameters_def( + unique int id: @bicep_for_loop_parameters +); + +@bicep_for_statement_body_type = @bicep_expression | @bicep_if_statement + +bicep_for_statement_initializer( + unique int bicep_for_statement: @bicep_for_statement ref, + unique int initializer: @bicep_token_identifier ref +); + +@bicep_for_statement_child_type = @bicep_expression | @bicep_for_loop_parameters + +#keyset[bicep_for_statement, index] +bicep_for_statement_child( + int bicep_for_statement: @bicep_for_statement ref, + int index: int ref, + unique int child: @bicep_for_statement_child_type ref +); + +bicep_for_statement_def( + unique int id: @bicep_for_statement, + int body: @bicep_for_statement_body_type ref +); + +@bicep_if_statement_child_type = @bicep_object | @bicep_parenthesized_expression + +#keyset[bicep_if_statement, index] +bicep_if_statement_child( + int bicep_if_statement: @bicep_if_statement ref, + int index: int ref, + unique int child: @bicep_if_statement_child_type ref +); + +bicep_if_statement_def( + unique int id: @bicep_if_statement +); + +bicep_import_statement_def( + unique int id: @bicep_import_statement, + int child: @bicep_import_string ref +); + +@bicep_import_string_child_type = @bicep_token_import_name | @bicep_token_import_version + +#keyset[bicep_import_string, index] +bicep_import_string_child( + int bicep_import_string: @bicep_import_string ref, + int index: int ref, + unique int child: @bicep_import_string_child_type ref +); + +bicep_import_string_def( + unique int id: @bicep_import_string +); + +@bicep_import_with_statement_child_type = @bicep_expression | @bicep_import_string + +#keyset[bicep_import_with_statement, index] +bicep_import_with_statement_child( + int bicep_import_with_statement: @bicep_import_with_statement ref, + int index: int ref, + unique int child: @bicep_import_with_statement_child_type ref +); + +bicep_import_with_statement_def( + unique int id: @bicep_import_with_statement +); + +#keyset[bicep_infrastructure, index] +bicep_infrastructure_child( + int bicep_infrastructure: @bicep_infrastructure ref, + int index: int ref, + unique int child: @bicep_statement ref +); + +bicep_infrastructure_def( + unique int id: @bicep_infrastructure +); + +bicep_interpolation_def( + unique int id: @bicep_interpolation, + int child: @bicep_expression ref +); + +#keyset[bicep_lambda_expression, index] +bicep_lambda_expression_child( + int bicep_lambda_expression: @bicep_lambda_expression ref, + int index: int ref, + unique int child: @bicep_expression ref +); + +bicep_lambda_expression_def( + unique int id: @bicep_lambda_expression +); + +bicep_member_expression_def( + unique int id: @bicep_member_expression, + int object: @bicep_expression ref, + int property: @bicep_token_property_identifier ref +); + +@bicep_metadata_declaration_child_type = @bicep_expression | @bicep_token_identifier + +#keyset[bicep_metadata_declaration, index] +bicep_metadata_declaration_child( + int bicep_metadata_declaration: @bicep_metadata_declaration ref, + int index: int ref, + unique int child: @bicep_metadata_declaration_child_type ref +); + +bicep_metadata_declaration_def( + unique int id: @bicep_metadata_declaration +); + +@bicep_module_declaration_child_type = @bicep_for_statement | @bicep_if_statement | @bicep_object | @bicep_string__ | @bicep_token_identifier + +#keyset[bicep_module_declaration, index] +bicep_module_declaration_child( + int bicep_module_declaration: @bicep_module_declaration ref, + int index: int ref, + unique int child: @bicep_module_declaration_child_type ref +); + +bicep_module_declaration_def( + unique int id: @bicep_module_declaration +); + +@bicep_nullable_type_child_type = @bicep_array_type | @bicep_builtin_type | @bicep_expression + +bicep_nullable_type_def( + unique int id: @bicep_nullable_type, + int child: @bicep_nullable_type_child_type ref +); + +@bicep_object_child_type = @bicep_decorators | @bicep_object_property + +#keyset[bicep_object, index] +bicep_object_child( + int bicep_object: @bicep_object ref, + int index: int ref, + unique int child: @bicep_object_child_type ref +); + +bicep_object_def( + unique int id: @bicep_object +); + +@bicep_object_property_child_type = @bicep_array_type | @bicep_builtin_type | @bicep_compatible_identifier | @bicep_expression | @bicep_nullable_type | @bicep_resource_declaration | @bicep_string__ | @bicep_token_identifier + +#keyset[bicep_object_property, index] +bicep_object_property_child( + int bicep_object_property: @bicep_object_property ref, + int index: int ref, + unique int child: @bicep_object_property_child_type ref +); + +bicep_object_property_def( + unique int id: @bicep_object_property +); + +@bicep_output_declaration_child_type = @bicep_expression | @bicep_token_identifier | @bicep_type__ + +#keyset[bicep_output_declaration, index] +bicep_output_declaration_child( + int bicep_output_declaration: @bicep_output_declaration ref, + int index: int ref, + unique int child: @bicep_output_declaration_child_type ref +); + +bicep_output_declaration_def( + unique int id: @bicep_output_declaration +); + +@bicep_parameter_declaration_child_type = @bicep_expression | @bicep_token_identifier | @bicep_type__ + +#keyset[bicep_parameter_declaration, index] +bicep_parameter_declaration_child( + int bicep_parameter_declaration: @bicep_parameter_declaration ref, + int index: int ref, + unique int child: @bicep_parameter_declaration_child_type ref +); + +bicep_parameter_declaration_def( + unique int id: @bicep_parameter_declaration +); + +#keyset[bicep_parenthesized_expression, index] +bicep_parenthesized_expression_child( + int bicep_parenthesized_expression: @bicep_parenthesized_expression ref, + int index: int ref, + unique int child: @bicep_expression ref +); + +bicep_parenthesized_expression_def( + unique int id: @bicep_parenthesized_expression +); + +@bicep_primary_expression = @bicep_array | @bicep_call_expression | @bicep_for_statement | @bicep_member_expression | @bicep_object | @bicep_parenthesized_expression | @bicep_resource_expression | @bicep_string__ | @bicep_subscript_expression | @bicep_token_boolean | @bicep_token_identifier | @bicep_token_null | @bicep_token_number + +@bicep_resource_declaration_child_type = @bicep_for_statement | @bicep_if_statement | @bicep_object | @bicep_string__ | @bicep_token_identifier + +#keyset[bicep_resource_declaration, index] +bicep_resource_declaration_child( + int bicep_resource_declaration: @bicep_resource_declaration ref, + int index: int ref, + unique int child: @bicep_resource_declaration_child_type ref +); + +bicep_resource_declaration_def( + unique int id: @bicep_resource_declaration +); + +bicep_resource_expression_def( + unique int id: @bicep_resource_expression, + int object: @bicep_expression ref, + int resource: @bicep_token_identifier ref +); + +@bicep_statement = @bicep_declaration | @bicep_decorators | @bicep_import_statement | @bicep_import_with_statement | @bicep_target_scope_assignment + +@bicep_string_child_type = @bicep_interpolation | @bicep_token_escape_sequence | @bicep_token_multiline_string_content | @bicep_token_string_content + +#keyset[bicep_string__, index] +bicep_string_child( + int bicep_string__: @bicep_string__ ref, + int index: int ref, + unique int child: @bicep_string_child_type ref +); + +bicep_string_def( + unique int id: @bicep_string__ +); + +bicep_subscript_expression_def( + unique int id: @bicep_subscript_expression, + int index: @bicep_expression ref, + int object: @bicep_expression ref +); + +bicep_target_scope_assignment_def( + unique int id: @bicep_target_scope_assignment, + int child: @bicep_string__ ref +); + +bicep_ternary_expression_def( + unique int id: @bicep_ternary_expression, + int alternative: @bicep_expression ref, + int condition: @bicep_expression ref, + int consequence: @bicep_expression ref +); + +@bicep_type_left_type = @bicep_array_type | @bicep_builtin_type | @bicep_object | @bicep_token_identifier + +bicep_type_left( + unique int bicep_type__: @bicep_type__ ref, + unique int left: @bicep_type_left_type ref +); + +@bicep_type_operator_type = @bicep_reserved_word + +bicep_type_operator( + unique int bicep_type__: @bicep_type__ ref, + unique int operator: @bicep_type_operator_type ref +); + +@bicep_type_right_type = @bicep_array_type | @bicep_builtin_type | @bicep_object | @bicep_token_identifier + +bicep_type_right( + unique int bicep_type__: @bicep_type__ ref, + unique int right: @bicep_type_right_type ref +); + +@bicep_type_child_type = @bicep_array_type | @bicep_builtin_type | @bicep_member_expression | @bicep_object | @bicep_token_identifier + +bicep_type_child( + unique int bicep_type__: @bicep_type__ ref, + unique int child: @bicep_type_child_type ref +); + +bicep_type_def( + unique int id: @bicep_type__ +); + +@bicep_type_declaration_child_type = @bicep_array_type | @bicep_builtin_type | @bicep_expression | @bicep_token_identifier + +#keyset[bicep_type_declaration, index] +bicep_type_declaration_child( + int bicep_type_declaration: @bicep_type_declaration ref, + int index: int ref, + unique int child: @bicep_type_declaration_child_type ref +); + +bicep_type_declaration_def( + unique int id: @bicep_type_declaration +); + +case @bicep_unary_expression.operator of + 0 = @bicep_unary_expression_bang +| 1 = @bicep_unary_expression_minus +; + + +bicep_unary_expression_def( + unique int id: @bicep_unary_expression, + int argument: @bicep_expression ref, + int operator: int ref +); + +@bicep_variable_declaration_child_type = @bicep_expression | @bicep_token_identifier + +#keyset[bicep_variable_declaration, index] +bicep_variable_declaration_child( + int bicep_variable_declaration: @bicep_variable_declaration ref, + int index: int ref, + unique int child: @bicep_variable_declaration_child_type ref +); + +bicep_variable_declaration_def( + unique int id: @bicep_variable_declaration +); + +bicep_tokeninfo( + unique int id: @bicep_token, + int kind: int ref, + string value: string ref +); + +case @bicep_token.kind of + 0 = @bicep_reserved_word +| 1 = @bicep_token_boolean +| 2 = @bicep_token_comment +| 3 = @bicep_token_diagnostic_comment +| 4 = @bicep_token_escape_sequence +| 5 = @bicep_token_identifier +| 6 = @bicep_token_import_name +| 7 = @bicep_token_import_version +| 8 = @bicep_token_loop_enumerator +| 9 = @bicep_token_loop_variable +| 10 = @bicep_token_multiline_string_content +| 11 = @bicep_token_null +| 12 = @bicep_token_nullable_return_type +| 13 = @bicep_token_number +| 14 = @bicep_token_primitive_type +| 15 = @bicep_token_property_identifier +| 16 = @bicep_token_string_content +; + + +@bicep_ast_node = @bicep_arguments | @bicep_array | @bicep_array_type | @bicep_assignment_expression | @bicep_binary_expression | @bicep_builtin_type | @bicep_call_expression | @bicep_compatible_identifier | @bicep_decorator | @bicep_decorators | @bicep_for_loop_parameters | @bicep_for_statement | @bicep_if_statement | @bicep_import_statement | @bicep_import_string | @bicep_import_with_statement | @bicep_infrastructure | @bicep_interpolation | @bicep_lambda_expression | @bicep_member_expression | @bicep_metadata_declaration | @bicep_module_declaration | @bicep_nullable_type | @bicep_object | @bicep_object_property | @bicep_output_declaration | @bicep_parameter_declaration | @bicep_parenthesized_expression | @bicep_resource_declaration | @bicep_resource_expression | @bicep_string__ | @bicep_subscript_expression | @bicep_target_scope_assignment | @bicep_ternary_expression | @bicep_token | @bicep_type__ | @bicep_type_declaration | @bicep_unary_expression | @bicep_variable_declaration + +@bicep_ast_node_parent = @bicep_ast_node | @file + +#keyset[parent, parent_index] +bicep_ast_node_info( + unique int node: @bicep_ast_node ref, + int parent: @bicep_ast_node_parent ref, + int parent_index: int ref, + int loc: @location_default ref +); + From cd8339eac41fa6bf48ab67226bc8523285dcf6f5 Mon Sep 17 00:00:00 2001 From: Mathew Payne Date: Fri, 15 Sep 2023 16:32:30 +0100 Subject: [PATCH 02/16] Update tools --- tools/pre-finalize.cmd | 3 +++ tools/qltest.cmd | 2 +- tools/qltest.sh | 4 +++- 3 files changed, 7 insertions(+), 2 deletions(-) diff --git a/tools/pre-finalize.cmd b/tools/pre-finalize.cmd index df65ff5..20ca437 100755 --- a/tools/pre-finalize.cmd +++ b/tools/pre-finalize.cmd @@ -3,6 +3,9 @@ type NUL && "%CODEQL_DIST%\codeql" database index-files ^ --include=*.yml ^ --include=*.yaml ^ + --include=*.json ^ + --include=*.jsonc ^ + --include=*.jsonl ^ --size-limit=5m ^ --language yaml ^ -- ^ diff --git a/tools/qltest.cmd b/tools/qltest.cmd index dd757c9..c960b58 100755 --- a/tools/qltest.cmd +++ b/tools/qltest.cmd @@ -3,7 +3,7 @@ type NUL && "%CODEQL_DIST%\codeql.exe" database index-files ^ --prune=**/*.testproj ^ --include-extension=.hcl ^ - --include-extension=.yml ^ + --include-extension=.tf ^ --size-limit=5m ^ --language=hcl ^ --working-dir=. ^ diff --git a/tools/qltest.sh b/tools/qltest.sh index a8d78a5..62bb316 100755 --- a/tools/qltest.sh +++ b/tools/qltest.sh @@ -5,7 +5,9 @@ set -eu "${CODEQL_DIST}/codeql" database index-files \ --prune="**/*.testproj" \ --include-extension=.hcl \ - --include-extension=.dbscheme \ + --include-extension=.tf \ + --include-extension=.tfvars \ + --include-extension=.bicep \ --size-limit=5m \ --language=iac \ --working-dir=.\ From 0868bbd194b2f17a4556c7e4256a748fb856316d Mon Sep 17 00:00:00 2001 From: Mathew Payne Date: Fri, 15 Sep 2023 16:54:40 +0100 Subject: [PATCH 03/16] Add language loading check --- .github/workflows/build.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 87bbb66..cc4bbc0 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -37,6 +37,8 @@ jobs: ./scripts/create-extractor-pack.sh + gh codeql resolve languages --format=json --search-path ./extractor-pack + - name: "Run Tests" if: steps.changes.outputs.src == 'true' run: | From d2e3bb84b4e48b233d1d35ad5309ab0e62178438 Mon Sep 17 00:00:00 2001 From: Mathew Payne Date: Fri, 15 Sep 2023 16:57:05 +0100 Subject: [PATCH 04/16] Update Docs --- docs/Q&A.md | 3 ++- extractor/README.md | 1 + 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/docs/Q&A.md b/docs/Q&A.md index eae4c38..42377d0 100644 --- a/docs/Q&A.md +++ b/docs/Q&A.md @@ -25,6 +25,7 @@ The `codeql-extractor-iac` supports the following languages: | JSON | `.json`, `.jsonl`, `.jsonc` | | YAML | `.yaml`, `.yml` | | Container files | `*Dockerfile`, `*Containerfile` | +| Bicep | `.bicep` | ## Q: What frameworks and technologies does the `codeql-extractor-iac` support? @@ -41,7 +42,7 @@ The `codeql-extractor-iac` is a community extractor and supports a number of fra | GitHub Actions | 2 | extractor and library | | HelmChart (Kubernetes) | 2 | extractor and library | | OpenAPI / Swagger | 2 | extractor and library | -| Azure Bicep | 0 | currently unsupported | +| Azure Bicep | 1 | extractor | _levels grades are based on completeness, higher the grade the better its supported._ diff --git a/extractor/README.md b/extractor/README.md index 998e1ea..c69211a 100644 --- a/extractor/README.md +++ b/extractor/README.md @@ -8,3 +8,4 @@ | JSON | `.json`, `.jsonl`, `.jsonc` | | YAML | `.yaml`, `.yml` | | Container files | `Dockerfile`, `Containerfile` | +| Bicep | `.bicep` | From 06865ce6ef1767c536e81f89f29ee32db902af29 Mon Sep 17 00:00:00 2001 From: Mathew Payne Date: Fri, 15 Sep 2023 17:14:50 +0100 Subject: [PATCH 05/16] Add initial test --- ql/test/library-tests/azure/bicep/AST.expected | 0 ql/test/library-tests/azure/bicep/AST.ql | 4 ++++ ql/test/library-tests/azure/bicep/sample.bicep | 14 ++++++++++++++ 3 files changed, 18 insertions(+) create mode 100644 ql/test/library-tests/azure/bicep/AST.expected create mode 100644 ql/test/library-tests/azure/bicep/AST.ql create mode 100644 ql/test/library-tests/azure/bicep/sample.bicep diff --git a/ql/test/library-tests/azure/bicep/AST.expected b/ql/test/library-tests/azure/bicep/AST.expected new file mode 100644 index 0000000..e69de29 diff --git a/ql/test/library-tests/azure/bicep/AST.ql b/ql/test/library-tests/azure/bicep/AST.ql new file mode 100644 index 0000000..7342538 --- /dev/null +++ b/ql/test/library-tests/azure/bicep/AST.ql @@ -0,0 +1,4 @@ +private import codeql.iac.ast.Bicep +private import codeql.iac.azure.Bicep + +query predicate ast(BicepAstNode ast) { any() } diff --git a/ql/test/library-tests/azure/bicep/sample.bicep b/ql/test/library-tests/azure/bicep/sample.bicep new file mode 100644 index 0000000..ca9508e --- /dev/null +++ b/ql/test/library-tests/azure/bicep/sample.bicep @@ -0,0 +1,14 @@ +param location string = resourceGroup().location +param storageAccountName string = 'toylaunch${uniqueString(resourceGroup().id)}' + +resource storageAccount 'Microsoft.Storage/storageAccounts@2021-06-01' = { + name: storageAccountName + location: location + sku: { + name: 'Standard_LRS' + } + kind: 'StorageV2' + properties: { + accessTier: 'Hot' + } +} From 8d8d2342f4a6cfa421ade10cdbd777e0174e728b Mon Sep 17 00:00:00 2001 From: Mathew Payne Date: Tue, 19 Sep 2023 16:26:16 +0100 Subject: [PATCH 06/16] Fix codeql-extractor file --- codeql-extractor.yml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/codeql-extractor.yml b/codeql-extractor.yml index 0d02bfe..f1c66b2 100644 --- a/codeql-extractor.yml +++ b/codeql-extractor.yml @@ -20,12 +20,14 @@ file_types: - .tf - .tfvars - .hcl + - name: dockerfile display_name: Dockerfile extensions: - .Dockerfile - .Containerfile - - name: + + - name: bicep display_name: Bicep extensions: - .bicep From a8249fe4de10909ff775042c730a04044d1b7926 Mon Sep 17 00:00:00 2001 From: Mathew Payne Date: Tue, 19 Sep 2023 17:11:17 +0100 Subject: [PATCH 07/16] Update Bicep Lib --- ql/lib/bicep.qll | 3 + ql/lib/codeql/bicep/AST.qll | 3 + ql/lib/codeql/bicep/ast/AstNodes.qll | 59 +++++++++++++++ ql/lib/codeql/bicep/ast/Expr.qll | 94 ++++++++++++++++++++++++ ql/lib/codeql/bicep/ast/Literal.qll | 42 +++++++++++ ql/lib/codeql/iac/ast/Bicep.qll | 56 +------------- ql/lib/codeql/iac/ast/internal/Bicep.qll | 17 ++++- ql/lib/codeql/iac/azure/Bicep.qll | 9 ++- 8 files changed, 226 insertions(+), 57 deletions(-) create mode 100644 ql/lib/bicep.qll create mode 100644 ql/lib/codeql/bicep/AST.qll create mode 100644 ql/lib/codeql/bicep/ast/AstNodes.qll create mode 100644 ql/lib/codeql/bicep/ast/Expr.qll create mode 100644 ql/lib/codeql/bicep/ast/Literal.qll diff --git a/ql/lib/bicep.qll b/ql/lib/bicep.qll new file mode 100644 index 0000000..2545b4d --- /dev/null +++ b/ql/lib/bicep.qll @@ -0,0 +1,3 @@ +import codeql.Locations +import codeql.files.FileSystem +import codeql.bicep.AST diff --git a/ql/lib/codeql/bicep/AST.qll b/ql/lib/codeql/bicep/AST.qll new file mode 100644 index 0000000..ff9dc73 --- /dev/null +++ b/ql/lib/codeql/bicep/AST.qll @@ -0,0 +1,3 @@ +import codeql.bicep.ast.AstNodes +import codeql.bicep.ast.Expr +import codeql.bicep.ast.Literal diff --git a/ql/lib/codeql/bicep/ast/AstNodes.qll b/ql/lib/codeql/bicep/ast/AstNodes.qll new file mode 100644 index 0000000..ba2593a --- /dev/null +++ b/ql/lib/codeql/bicep/ast/AstNodes.qll @@ -0,0 +1,59 @@ +private import codeql.Locations +private import codeql.files.FileSystem +private import codeql.iac.ast.internal.Bicep + +/** An AST node of a Bicep program */ +class BicepAstNode extends TBicepAstNode { + string toString() { result = this.getAPrimaryQlClass() } + + /** Gets the location of the AST node. */ + cached + Location getLocation() { result = this.getFullLocation() } // overridden in some subclasses + + /** Gets the file containing this AST node. */ + cached + File getFile() { result = this.getFullLocation().getFile() } + + /** Gets the location that spans the entire AST node. */ + cached + final Location getFullLocation() { result = toBicepTreeSitter(this).getLocation() } + + predicate hasLocationInfo( + string filepath, int startline, int startcolumn, int endline, int endcolumn + ) { + if exists(this.getLocation()) + then this.getLocation().hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn) + else ( + filepath = "" and + startline = 0 and + startcolumn = 0 and + endline = 0 and + endcolumn = 0 + ) + } + + /** + * Gets the parent in the AST for this node. + */ + cached + BicepAstNode getParent() { result.getAChild(_) = this } + + /** + * Gets a child of this node, which can also be retrieved using a predicate + * named `pred`. + */ + cached + BicepAstNode getAChild(string pred) { none() } + + /** Gets any child of this node. */ + BicepAstNode getAChild() { result = this.getAChild(_) } + + /** + * Gets the primary QL class for the ast node. + */ + string getAPrimaryQlClass() { result = "???" } +} + +class Comment extends BicepAstNode, TComment { + override string getAPrimaryQlClass() { result = "Comment" } +} diff --git a/ql/lib/codeql/bicep/ast/Expr.qll b/ql/lib/codeql/bicep/ast/Expr.qll new file mode 100644 index 0000000..4392c3f --- /dev/null +++ b/ql/lib/codeql/bicep/ast/Expr.qll @@ -0,0 +1,94 @@ +import codeql.iac.ast.internal.Bicep +import codeql.bicep.ast.AstNodes + +class Expr extends BicepAstNode, TExpr { + override string getAPrimaryQlClass() { result = "Expr" } +} + +class AssignmentExpr extends Expr { + BICEP::AssignmentExpression aexpr; + + override string getAPrimaryQlClass() { result = "AssignmentExpr" } + + AssignmentExpr() { this = TAssignmentExpression(aexpr) } +} + +class BinaryExpr extends Expr { + BICEP::BinaryExpression bexpr; + + override string getAPrimaryQlClass() { result = "BinaryExpr" } + + BinaryExpr() { this = TBinaryExpression(bexpr) } +} + +class CallExpr extends Expr { + BICEP::CallExpression cexpr; + + override string getAPrimaryQlClass() { result = "CallExpr" } + + CallExpr() { this = TCallExpression(cexpr) } +} + +class LambdaExpr extends Expr { + BICEP::LambdaExpression lexpr; + + override string getAPrimaryQlClass() { result = "LambdaExpr" } + + LambdaExpr() { this = TLambdaExpression(lexpr) } +} + +class MemberExpr extends Expr { + BICEP::MemberExpression mexpr; + + override string getAPrimaryQlClass() { result = "MemberExpr" } + + MemberExpr() { this = TMemberExpression(mexpr) } +} + +class ParenthesizedExpr extends Expr { + BICEP::ParenthesizedExpression pexpr; + + override string getAPrimaryQlClass() { result = "ParenthesizedExpr" } + + ParenthesizedExpr() { this = TParenthesizedExpression(pexpr) } +} + +class PrimaryExpr extends Expr { + BICEP::PrimaryExpression pexpr; + + override string getAPrimaryQlClass() { result = "PrimaryExpr" } + + PrimaryExpr() { this = TPrimaryExpression(pexpr) } +} + +class ResourceExpr extends Expr { + BICEP::ResourceExpression rexpr; + + override string getAPrimaryQlClass() { result = "ResourceExpr" } + + ResourceExpr() { this = TResourceExpression(rexpr) } +} + +class SubscriptExpr extends Expr { + BICEP::SubscriptExpression sexpr; + + override string getAPrimaryQlClass() { result = "SubscriptExpr" } + + SubscriptExpr() { this = TSubscriptExpression(sexpr) } +} + +class TerenaryExpr extends Expr { + BICEP::TernaryExpression texpr; + + override string getAPrimaryQlClass() { result = "TerenaryExpr" } + + TerenaryExpr() { this = TTernaryExpression(texpr) } +} + +class UnaryExpr extends Expr { + BICEP::UnaryExpression uexpr; + + override string getAPrimaryQlClass() { result = "UnaryExpr" } + + UnaryExpr() { this = TUnaryExpression(uexpr) } +} diff --git a/ql/lib/codeql/bicep/ast/Literal.qll b/ql/lib/codeql/bicep/ast/Literal.qll new file mode 100644 index 0000000..368ef5d --- /dev/null +++ b/ql/lib/codeql/bicep/ast/Literal.qll @@ -0,0 +1,42 @@ +import codeql.iac.ast.internal.Bicep +import codeql.bicep.ast.AstNodes + +class Literal extends BicepAstNode, TLiteral { + override string getAPrimaryQlClass() { result = "Literal" } + + string getValue() { none() } + + override string toString() { result = this.getValue() } +} + +class NumberLiteral extends Literal, TNumber { + private BICEP::Number literal; + + override string getAPrimaryQlClass() { result = "NumberLiteral" } + + NumberLiteral() { this = TNumber(literal) } +} + +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) } +} + +class StringLiteral extends Literal, TString { + private BICEP::String literal; + + override string getAPrimaryQlClass() { result = "StringLiteral" } + + StringLiteral() { this = TString(literal) } +} diff --git a/ql/lib/codeql/iac/ast/Bicep.qll b/ql/lib/codeql/iac/ast/Bicep.qll index 5fe2ece..dc25f90 100644 --- a/ql/lib/codeql/iac/ast/Bicep.qll +++ b/ql/lib/codeql/iac/ast/Bicep.qll @@ -1,55 +1 @@ -private import codeql.Locations -private import codeql.files.FileSystem -private import codeql.iac.ast.internal.Bicep - -/** An AST node of a Bicep program */ -class BicepAstNode extends TBicepAstNode { - string toString() { result = this.getAPrimaryQlClass() } - - /** Gets the location of the AST node. */ - cached - Location getLocation() { result = this.getFullLocation() } // overridden in some subclasses - - /** Gets the file containing this AST node. */ - cached - File getFile() { result = this.getFullLocation().getFile() } - - /** Gets the location that spans the entire AST node. */ - cached - final Location getFullLocation() { result = toBicepTreeSitter(this).getLocation() } - - predicate hasLocationInfo( - string filepath, int startline, int startcolumn, int endline, int endcolumn - ) { - if exists(this.getLocation()) - then this.getLocation().hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn) - else ( - filepath = "" and - startline = 0 and - startcolumn = 0 and - endline = 0 and - endcolumn = 0 - ) - } - - /** - * Gets the parent in the AST for this node. - */ - cached - BicepAstNode getParent() { result.getAChild(_) = this } - - /** - * Gets a child of this node, which can also be retrieved using a predicate - * named `pred`. - */ - cached - BicepAstNode getAChild(string pred) { none() } - - /** Gets any child of this node. */ - BicepAstNode getAChild() { result = this.getAChild(_) } - - /** - * Gets the primary QL class for the ast node. - */ - string getAPrimaryQlClass() { result = "???" } -} +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 83a5368..5345f85 100644 --- a/ql/lib/codeql/iac/ast/internal/Bicep.qll +++ b/ql/lib/codeql/iac/ast/internal/Bicep.qll @@ -34,5 +34,20 @@ cached BICEP::AstNode toBicepTreeSitter(TBicepAstNode n) { n = TComment(result) or n = TBoolean(result) or - n = TNull(result) + n = TNull(result) or + n = TNumber(result) or + n = TString(result) or + n = TMultilineStringContent(result) or + n = TAssignmentExpression(result) or + n = TBinaryExpression(result) or + n = TCallExpression(result) or + n = TExpression(result) or + n = TLambdaExpression(result) or + n = TMemberExpression(result) or + n = TParenthesizedExpression(result) or + n = TPrimaryExpression(result) or + n = TResourceExpression(result) or + n = TSubscriptExpression(result) or + n = TTernaryExpression(result) or + n = TUnaryExpression(result) } diff --git a/ql/lib/codeql/iac/azure/Bicep.qll b/ql/lib/codeql/iac/azure/Bicep.qll index 19b1663..b292a7c 100644 --- a/ql/lib/codeql/iac/azure/Bicep.qll +++ b/ql/lib/codeql/iac/azure/Bicep.qll @@ -1,3 +1,10 @@ private import codeql.files.FileSystem -module Bicep { } +module Bicep { + /** + * All extracted Bicep files. + */ + class BicepFile extends File { + BicepFile() { this.getExtension() = "bicep" } + } +} From 32a63f354795e0df1205d985161124ea7e89ea00 Mon Sep 17 00:00:00 2001 From: Mathew Payne Date: Tue, 19 Sep 2023 17:11:29 +0100 Subject: [PATCH 08/16] Update Bicep tests --- .../library-tests/azure/bicep/AST.expected | 0 ql/test/library-tests/bicep/ast/AST.expected | 66 +++++++++++++++++++ .../{azure/bicep => bicep/ast}/AST.ql | 1 - .../{azure/bicep => bicep/ast}/sample.bicep | 0 4 files changed, 66 insertions(+), 1 deletion(-) delete mode 100644 ql/test/library-tests/azure/bicep/AST.expected create mode 100644 ql/test/library-tests/bicep/ast/AST.expected rename ql/test/library-tests/{azure/bicep => bicep/ast}/AST.ql (69%) rename ql/test/library-tests/{azure/bicep => bicep/ast}/sample.bicep (100%) diff --git a/ql/test/library-tests/azure/bicep/AST.expected b/ql/test/library-tests/azure/bicep/AST.expected deleted file mode 100644 index e69de29..0000000 diff --git a/ql/test/library-tests/bicep/ast/AST.expected b/ql/test/library-tests/bicep/ast/AST.expected new file mode 100644 index 0000000..df6af52 --- /dev/null +++ b/ql/test/library-tests/bicep/ast/AST.expected @@ -0,0 +1,66 @@ +| sample.bicep:1:7:1:14 | Expr | +| sample.bicep:1:7:1:14 | PrimaryExpr | +| sample.bicep:1:25:1:37 | Expr | +| sample.bicep:1:25:1:37 | PrimaryExpr | +| sample.bicep:1:25:1:39 | CallExpr | +| sample.bicep:1:25:1:39 | Expr | +| sample.bicep:1:25:1:39 | PrimaryExpr | +| sample.bicep:1:25:1:48 | Expr | +| sample.bicep:1:25:1:48 | MemberExpr | +| sample.bicep:1:25:1:48 | PrimaryExpr | +| sample.bicep:2:7:2:24 | Expr | +| sample.bicep:2:7:2:24 | PrimaryExpr | +| sample.bicep:2:35:2:80 | (no string representation) | +| sample.bicep:2:35:2:80 | Expr | +| sample.bicep:2:35:2:80 | PrimaryExpr | +| sample.bicep:2:47:2:58 | Expr | +| sample.bicep:2:47:2:58 | PrimaryExpr | +| sample.bicep:2:47:2:78 | CallExpr | +| sample.bicep:2:47:2:78 | Expr | +| sample.bicep:2:47:2:78 | PrimaryExpr | +| sample.bicep:2:60:2:72 | Expr | +| sample.bicep:2:60:2:72 | PrimaryExpr | +| sample.bicep:2:60:2:74 | CallExpr | +| sample.bicep:2:60:2:74 | Expr | +| sample.bicep:2:60:2:74 | PrimaryExpr | +| sample.bicep:2:60:2:77 | Expr | +| sample.bicep:2:60:2:77 | MemberExpr | +| sample.bicep:2:60:2:77 | PrimaryExpr | +| sample.bicep:4:10:4:23 | Expr | +| sample.bicep:4:10:4:23 | PrimaryExpr | +| sample.bicep:4:25:4:70 | (no string representation) | +| sample.bicep:4:25:4:70 | Expr | +| sample.bicep:4:25:4:70 | PrimaryExpr | +| sample.bicep:4:74:14:1 | Expr | +| sample.bicep:4:74:14:1 | PrimaryExpr | +| sample.bicep:5:3:5:6 | Expr | +| sample.bicep:5:3:5:6 | PrimaryExpr | +| sample.bicep:5:9:5:26 | Expr | +| sample.bicep:5:9:5:26 | PrimaryExpr | +| sample.bicep:6:3:6:10 | Expr | +| sample.bicep:6:3:6:10 | PrimaryExpr | +| sample.bicep:6:13:6:20 | Expr | +| sample.bicep:6:13:6:20 | PrimaryExpr | +| sample.bicep:7:3:7:5 | Expr | +| sample.bicep:7:3:7:5 | PrimaryExpr | +| sample.bicep:7:8:9:3 | Expr | +| sample.bicep:7:8:9:3 | PrimaryExpr | +| sample.bicep:8:5:8:8 | Expr | +| sample.bicep:8:5:8:8 | PrimaryExpr | +| sample.bicep:8:11:8:24 | (no string representation) | +| sample.bicep:8:11:8:24 | Expr | +| sample.bicep:8:11:8:24 | PrimaryExpr | +| sample.bicep:10:3:10:6 | Expr | +| sample.bicep:10:3:10:6 | PrimaryExpr | +| sample.bicep:10:9:10:19 | (no string representation) | +| sample.bicep:10:9:10:19 | Expr | +| sample.bicep:10:9:10:19 | PrimaryExpr | +| sample.bicep:11:3:11:12 | Expr | +| sample.bicep:11:3:11:12 | PrimaryExpr | +| sample.bicep:11:15:13:3 | Expr | +| sample.bicep:11:15:13:3 | PrimaryExpr | +| sample.bicep:12:5:12:14 | Expr | +| sample.bicep:12:5:12:14 | PrimaryExpr | +| sample.bicep:12:17:12:21 | (no string representation) | +| sample.bicep:12:17:12:21 | Expr | +| sample.bicep:12:17:12:21 | PrimaryExpr | diff --git a/ql/test/library-tests/azure/bicep/AST.ql b/ql/test/library-tests/bicep/ast/AST.ql similarity index 69% rename from ql/test/library-tests/azure/bicep/AST.ql rename to ql/test/library-tests/bicep/ast/AST.ql index 7342538..7c6d206 100644 --- a/ql/test/library-tests/azure/bicep/AST.ql +++ b/ql/test/library-tests/bicep/ast/AST.ql @@ -1,4 +1,3 @@ private import codeql.iac.ast.Bicep -private import codeql.iac.azure.Bicep query predicate ast(BicepAstNode ast) { any() } diff --git a/ql/test/library-tests/azure/bicep/sample.bicep b/ql/test/library-tests/bicep/ast/sample.bicep similarity index 100% rename from ql/test/library-tests/azure/bicep/sample.bicep rename to ql/test/library-tests/bicep/ast/sample.bicep From 0272be78843870b20ec4a0d538a1fb3667d7c64d Mon Sep 17 00:00:00 2001 From: Mathew Payne Date: Tue, 19 Sep 2023 18:41:10 +0100 Subject: [PATCH 09/16] Update Bicep Lib --- ql/lib/codeql/bicep/AST.qll | 1 + ql/lib/codeql/bicep/ast/Expr.qll | 48 +++++++++++++++--------- ql/lib/codeql/bicep/ast/Object.qll | 35 +++++++++++++++++ ql/lib/codeql/bicep/ast/Resources.qll | 16 ++++++++ ql/lib/codeql/iac/ast/internal/Bicep.qll | 19 ++++++++-- 5 files changed, 97 insertions(+), 22 deletions(-) create mode 100644 ql/lib/codeql/bicep/ast/Object.qll create mode 100644 ql/lib/codeql/bicep/ast/Resources.qll diff --git a/ql/lib/codeql/bicep/AST.qll b/ql/lib/codeql/bicep/AST.qll index ff9dc73..97e2427 100644 --- a/ql/lib/codeql/bicep/AST.qll +++ b/ql/lib/codeql/bicep/AST.qll @@ -1,3 +1,4 @@ import codeql.bicep.ast.AstNodes import codeql.bicep.ast.Expr import codeql.bicep.ast.Literal +import codeql.bicep.ast.Resources diff --git a/ql/lib/codeql/bicep/ast/Expr.qll b/ql/lib/codeql/bicep/ast/Expr.qll index 4392c3f..bbde216 100644 --- a/ql/lib/codeql/bicep/ast/Expr.qll +++ b/ql/lib/codeql/bicep/ast/Expr.qll @@ -5,7 +5,27 @@ class Expr extends BicepAstNode, TExpr { override string getAPrimaryQlClass() { result = "Expr" } } -class AssignmentExpr extends 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) } +} + +class AssignmentExpr extends Expr, TAssignmentExpression { BICEP::AssignmentExpression aexpr; override string getAPrimaryQlClass() { result = "AssignmentExpr" } @@ -13,7 +33,7 @@ class AssignmentExpr extends Expr { AssignmentExpr() { this = TAssignmentExpression(aexpr) } } -class BinaryExpr extends Expr { +class BinaryExpr extends Expr, TBinaryExpression { BICEP::BinaryExpression bexpr; override string getAPrimaryQlClass() { result = "BinaryExpr" } @@ -21,7 +41,7 @@ class BinaryExpr extends Expr { BinaryExpr() { this = TBinaryExpression(bexpr) } } -class CallExpr extends Expr { +class CallExpr extends Expr, TCallExpression { BICEP::CallExpression cexpr; override string getAPrimaryQlClass() { result = "CallExpr" } @@ -29,7 +49,7 @@ class CallExpr extends Expr { CallExpr() { this = TCallExpression(cexpr) } } -class LambdaExpr extends Expr { +class LambdaExpr extends Expr, TLambdaExpression { BICEP::LambdaExpression lexpr; override string getAPrimaryQlClass() { result = "LambdaExpr" } @@ -37,7 +57,7 @@ class LambdaExpr extends Expr { LambdaExpr() { this = TLambdaExpression(lexpr) } } -class MemberExpr extends Expr { +class MemberExpr extends Expr, TMemberExpression { BICEP::MemberExpression mexpr; override string getAPrimaryQlClass() { result = "MemberExpr" } @@ -45,7 +65,7 @@ class MemberExpr extends Expr { MemberExpr() { this = TMemberExpression(mexpr) } } -class ParenthesizedExpr extends Expr { +class ParenthesizedExpr extends Expr, TParenthesizedExpression { BICEP::ParenthesizedExpression pexpr; override string getAPrimaryQlClass() { result = "ParenthesizedExpr" } @@ -53,15 +73,7 @@ class ParenthesizedExpr extends Expr { ParenthesizedExpr() { this = TParenthesizedExpression(pexpr) } } -class PrimaryExpr extends Expr { - BICEP::PrimaryExpression pexpr; - - override string getAPrimaryQlClass() { result = "PrimaryExpr" } - - PrimaryExpr() { this = TPrimaryExpression(pexpr) } -} - -class ResourceExpr extends Expr { +class ResourceExpr extends Expr, TResourceExpression { BICEP::ResourceExpression rexpr; override string getAPrimaryQlClass() { result = "ResourceExpr" } @@ -69,7 +81,7 @@ class ResourceExpr extends Expr { ResourceExpr() { this = TResourceExpression(rexpr) } } -class SubscriptExpr extends Expr { +class SubscriptExpr extends Expr, TSubscriptExpression { BICEP::SubscriptExpression sexpr; override string getAPrimaryQlClass() { result = "SubscriptExpr" } @@ -77,7 +89,7 @@ class SubscriptExpr extends Expr { SubscriptExpr() { this = TSubscriptExpression(sexpr) } } -class TerenaryExpr extends Expr { +class TerenaryExpr extends Expr, TTernaryExpression { BICEP::TernaryExpression texpr; override string getAPrimaryQlClass() { result = "TerenaryExpr" } @@ -85,7 +97,7 @@ class TerenaryExpr extends Expr { TerenaryExpr() { this = TTernaryExpression(texpr) } } -class UnaryExpr extends Expr { +class UnaryExpr extends Expr, TUnaryExpression { BICEP::UnaryExpression uexpr; override string getAPrimaryQlClass() { result = "UnaryExpr" } diff --git a/ql/lib/codeql/bicep/ast/Object.qll b/ql/lib/codeql/bicep/ast/Object.qll new file mode 100644 index 0000000..a10bf5b --- /dev/null +++ b/ql/lib/codeql/bicep/ast/Object.qll @@ -0,0 +1,35 @@ +import codeql.iac.ast.internal.Bicep +import codeql.bicep.ast.AstNodes +import codeql.bicep.ast.Literal +import codeql.bicep.ast.Expr + +class Object extends BicepAstNode, TObject { + private BICEP::Object object; + + 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() + ) + } +} + +class ObjectProperty extends BicepAstNode, TObjectProperty { + private BICEP::ObjectProperty property; + + override string getAPrimaryQlClass() { result = "ObjectProperty" } + + ObjectProperty() { this = TObjectProperty(property) } + + override string toString() { result = this.getKey().getName() + " = " + this.getValue() } + + Identifier getKey() { toBicepTreeSitter(result) = property.getChild(0) } + + Expr getValue() { toBicepTreeSitter(result) = property.getChild(1) } +} diff --git a/ql/lib/codeql/bicep/ast/Resources.qll b/ql/lib/codeql/bicep/ast/Resources.qll new file mode 100644 index 0000000..997ca0e --- /dev/null +++ b/ql/lib/codeql/bicep/ast/Resources.qll @@ -0,0 +1,16 @@ +import codeql.iac.ast.internal.Bicep +import codeql.bicep.ast.AstNodes +import codeql.bicep.ast.Literal +import codeql.bicep.ast.Object + +class Resource extends BicepAstNode, TResourceDeclaration { + private BICEP::ResourceDeclaration resource; + + override string getAPrimaryQlClass() { result = "ResourceDeclaration" } + + Resource() { this = TResourceDeclaration(resource) } + + Object getBody() { toBicepTreeSitter(result) = resource.getAFieldOrChild() } + + Expr getProperty(string name) { result = this.getBody().getProperty(name) } +} diff --git a/ql/lib/codeql/iac/ast/internal/Bicep.qll b/ql/lib/codeql/iac/ast/internal/Bicep.qll index 5345f85..553c6d3 100644 --- a/ql/lib/codeql/iac/ast/internal/Bicep.qll +++ b/ql/lib/codeql/iac/ast/internal/Bicep.qll @@ -21,14 +21,21 @@ newtype TBicepAstNode = TResourceExpression(BICEP::ResourceExpression r) or TSubscriptExpression(BICEP::SubscriptExpression s) or TTernaryExpression(BICEP::TernaryExpression t) or - TUnaryExpression(BICEP::UnaryExpression u) + TUnaryExpression(BICEP::UnaryExpression u) or + // Declarations + TResourceDeclaration(BICEP::ResourceDeclaration r) or + TObject(BICEP::Object o) or + TObjectProperty(BICEP::ObjectProperty p) or + TIdentifier(BICEP::Identifier i) class TLiteral = TBoolean or TNull or TNumber or TString or TMultilineStringContent; +class TDeclaration = TResourceDeclaration or TObject or TObjectProperty or TIdentifier; + class TExpr = TAssignmentExpression or TBinaryExpression or TCallExpression or TExpression or - TLambdaExpression or TMemberExpression or TParenthesizedExpression or TPrimaryExpression or - TResourceExpression or TSubscriptExpression or TTernaryExpression or TUnaryExpression; + TLambdaExpression or TMemberExpression or TParenthesizedExpression or TResourceExpression or + TSubscriptExpression or TTernaryExpression or TUnaryExpression or TIdentifier; cached BICEP::AstNode toBicepTreeSitter(TBicepAstNode n) { @@ -49,5 +56,9 @@ BICEP::AstNode toBicepTreeSitter(TBicepAstNode n) { n = TResourceExpression(result) or n = TSubscriptExpression(result) or n = TTernaryExpression(result) or - n = TUnaryExpression(result) + n = TUnaryExpression(result) or + n = TResourceDeclaration(result) or + n = TObject(result) or + n = TObjectProperty(result) or + n = TIdentifier(result) } From c9d5a35a12692b291098e52eea30fc4d3b602816 Mon Sep 17 00:00:00 2001 From: Mathew Payne Date: Tue, 19 Sep 2023 18:41:18 +0100 Subject: [PATCH 10/16] Update docs --- docs/languages-and-frameworks.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/docs/languages-and-frameworks.md b/docs/languages-and-frameworks.md index 21883d6..83bc208 100644 --- a/docs/languages-and-frameworks.md +++ b/docs/languages-and-frameworks.md @@ -10,6 +10,7 @@ The `codeql-extractor-iac` extractor supports the following languages: | JSON | `.json`, `.jsonl`, `.jsonc` | | YAML | `.yaml`, `.yml` | | Container files | `*Dockerfile`, `*Containerfile` | +| Bicep | `.bicep` | All of these files will be extracted and stored inside the IaC CodeQL Database. @@ -30,7 +31,7 @@ The following table lists the supported frameworks and technologies: | Docker / Container file(s) | 2 | extractor and library | | GitHub Actions | 2 | extractor and library | | OpenAPI / Swagger | 2 | extractor and library | -| Azure Bicep | 0 | currently unsupported | +| Azure Bicep | 2 | extractor and library | _levels grades are based on completeness, higher the grade the better its supported._ From b4b2b5691a377f3ce26b2bbf9ba03777b835e27c Mon Sep 17 00:00:00 2001 From: Mathew Payne Date: Tue, 19 Sep 2023 18:44:32 +0100 Subject: [PATCH 11/16] Add Infrastructure to lib --- ql/lib/codeql/bicep/ast/AstNodes.qll | 8 ++++++++ ql/lib/codeql/iac/ast/internal/Bicep.qll | 4 +++- 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/ql/lib/codeql/bicep/ast/AstNodes.qll b/ql/lib/codeql/bicep/ast/AstNodes.qll index ba2593a..16847a2 100644 --- a/ql/lib/codeql/bicep/ast/AstNodes.qll +++ b/ql/lib/codeql/bicep/ast/AstNodes.qll @@ -57,3 +57,11 @@ class BicepAstNode extends TBicepAstNode { 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/iac/ast/internal/Bicep.qll b/ql/lib/codeql/iac/ast/internal/Bicep.qll index 553c6d3..c1a743f 100644 --- a/ql/lib/codeql/iac/ast/internal/Bicep.qll +++ b/ql/lib/codeql/iac/ast/internal/Bicep.qll @@ -3,6 +3,7 @@ import TreeSitter cached newtype TBicepAstNode = TComment(BICEP::Comment c) or + TInfrastructure(BICEP::Infrastructure i) or // Literals TBoolean(BICEP::Boolean b) or TNull(BICEP::Null n) or @@ -33,13 +34,14 @@ class TLiteral = TBoolean or TNull or TNumber or TString or TMultilineStringCont class TDeclaration = TResourceDeclaration or TObject or TObjectProperty or TIdentifier; class TExpr = - TAssignmentExpression or TBinaryExpression or TCallExpression or TExpression or + TLiteral or TAssignmentExpression or TBinaryExpression or TCallExpression or TExpression or TLambdaExpression or TMemberExpression or TParenthesizedExpression or TResourceExpression or TSubscriptExpression or TTernaryExpression or TUnaryExpression or TIdentifier; cached BICEP::AstNode toBicepTreeSitter(TBicepAstNode n) { n = TComment(result) or + n = TInfrastructure(result) or n = TBoolean(result) or n = TNull(result) or n = TNumber(result) or From a5ca36e97b475ce2fd868da43c34bbcf2d986e95 Mon Sep 17 00:00:00 2001 From: Mathew Payne Date: Tue, 19 Sep 2023 18:51:49 +0100 Subject: [PATCH 12/16] Update imports --- ql/lib/codeql/bicep/ast/Expr.qll | 4 ++-- ql/lib/codeql/bicep/ast/Literal.qll | 4 ++-- ql/lib/codeql/bicep/ast/Object.qll | 8 ++++---- ql/lib/codeql/bicep/ast/Resources.qll | 8 ++++---- ql/lib/codeql/iac/ast/internal/AstNodes.qll | 1 + 5 files changed, 13 insertions(+), 12 deletions(-) diff --git a/ql/lib/codeql/bicep/ast/Expr.qll b/ql/lib/codeql/bicep/ast/Expr.qll index bbde216..c0f4692 100644 --- a/ql/lib/codeql/bicep/ast/Expr.qll +++ b/ql/lib/codeql/bicep/ast/Expr.qll @@ -1,5 +1,5 @@ -import codeql.iac.ast.internal.Bicep -import codeql.bicep.ast.AstNodes +private import codeql.iac.ast.internal.Bicep +private import codeql.bicep.ast.AstNodes class Expr extends BicepAstNode, TExpr { override string getAPrimaryQlClass() { result = "Expr" } diff --git a/ql/lib/codeql/bicep/ast/Literal.qll b/ql/lib/codeql/bicep/ast/Literal.qll index 368ef5d..c8f8af6 100644 --- a/ql/lib/codeql/bicep/ast/Literal.qll +++ b/ql/lib/codeql/bicep/ast/Literal.qll @@ -1,5 +1,5 @@ -import codeql.iac.ast.internal.Bicep -import codeql.bicep.ast.AstNodes +private import codeql.iac.ast.internal.Bicep +private import codeql.bicep.ast.AstNodes class Literal extends BicepAstNode, TLiteral { override string getAPrimaryQlClass() { result = "Literal" } diff --git a/ql/lib/codeql/bicep/ast/Object.qll b/ql/lib/codeql/bicep/ast/Object.qll index a10bf5b..33bb7e5 100644 --- a/ql/lib/codeql/bicep/ast/Object.qll +++ b/ql/lib/codeql/bicep/ast/Object.qll @@ -1,7 +1,7 @@ -import codeql.iac.ast.internal.Bicep -import codeql.bicep.ast.AstNodes -import codeql.bicep.ast.Literal -import codeql.bicep.ast.Expr +private import codeql.iac.ast.internal.Bicep +private import codeql.bicep.ast.AstNodes +private import codeql.bicep.ast.Literal +private import codeql.bicep.ast.Expr class Object extends BicepAstNode, TObject { private BICEP::Object object; diff --git a/ql/lib/codeql/bicep/ast/Resources.qll b/ql/lib/codeql/bicep/ast/Resources.qll index 997ca0e..2e0ead1 100644 --- a/ql/lib/codeql/bicep/ast/Resources.qll +++ b/ql/lib/codeql/bicep/ast/Resources.qll @@ -1,7 +1,7 @@ -import codeql.iac.ast.internal.Bicep -import codeql.bicep.ast.AstNodes -import codeql.bicep.ast.Literal -import codeql.bicep.ast.Object +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 class Resource extends BicepAstNode, TResourceDeclaration { private BICEP::ResourceDeclaration resource; diff --git a/ql/lib/codeql/iac/ast/internal/AstNodes.qll b/ql/lib/codeql/iac/ast/internal/AstNodes.qll index c6c2e1b..f368acf 100644 --- a/ql/lib/codeql/iac/ast/internal/AstNodes.qll +++ b/ql/lib/codeql/iac/ast/internal/AstNodes.qll @@ -8,4 +8,5 @@ import Container cached newtype TAstNode = THclAstNode(HCL::AstNode node) or + TBicepAstNode(BICEP::AstNode node) or TContainerAstNode(DOCKERFILE::AstNode node) From 9b740245fcdfd4c10f9f26e9a0cf97e9d241b621 Mon Sep 17 00:00:00 2001 From: Mathew Payne Date: Tue, 19 Sep 2023 20:04:20 +0100 Subject: [PATCH 13/16] Add better AST lib support --- ql/lib/codeql/bicep/ast/Literal.qll | 18 ++++++++++++++++++ ql/lib/codeql/bicep/ast/Resources.qll | 7 +++++++ ql/lib/codeql/iac/ast/internal/Bicep.qll | 8 ++++++-- 3 files changed, 31 insertions(+), 2 deletions(-) diff --git a/ql/lib/codeql/bicep/ast/Literal.qll b/ql/lib/codeql/bicep/ast/Literal.qll index c8f8af6..d50c6eb 100644 --- a/ql/lib/codeql/bicep/ast/Literal.qll +++ b/ql/lib/codeql/bicep/ast/Literal.qll @@ -31,6 +31,8 @@ class BooleanLiteral extends Literal, TBoolean { override string getAPrimaryQlClass() { result = "BooleanLiteral" } BooleanLiteral() { this = TBoolean(literal) } + + boolean getBool() { result.toString() = literal.getValue() } } class StringLiteral extends Literal, TString { @@ -39,4 +41,20 @@ class StringLiteral extends Literal, TString { 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() } } diff --git a/ql/lib/codeql/bicep/ast/Resources.qll b/ql/lib/codeql/bicep/ast/Resources.qll index 2e0ead1..896b2b4 100644 --- a/ql/lib/codeql/bicep/ast/Resources.qll +++ b/ql/lib/codeql/bicep/ast/Resources.qll @@ -2,6 +2,7 @@ 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.Expr class Resource extends BicepAstNode, TResourceDeclaration { private BICEP::ResourceDeclaration resource; @@ -10,6 +11,12 @@ class Resource extends BicepAstNode, TResourceDeclaration { Resource() { this = TResourceDeclaration(resource) } + string getResourceType() { + exists(StringLiteral s | toBicepTreeSitter(s) = resource.getAFieldOrChild() | + result = s.getValue() + ) + } + Object getBody() { toBicepTreeSitter(result) = resource.getAFieldOrChild() } Expr getProperty(string name) { result = this.getBody().getProperty(name) } diff --git a/ql/lib/codeql/iac/ast/internal/Bicep.qll b/ql/lib/codeql/iac/ast/internal/Bicep.qll index c1a743f..c0b54dc 100644 --- a/ql/lib/codeql/iac/ast/internal/Bicep.qll +++ b/ql/lib/codeql/iac/ast/internal/Bicep.qll @@ -9,6 +9,7 @@ newtype TBicepAstNode = TNull(BICEP::Null n) or TNumber(BICEP::Number n) or TString(BICEP::String s) or + TStringContent(BICEP::StringContent s) or TMultilineStringContent(BICEP::MultilineStringContent m) or // Expressions TAssignmentExpression(BICEP::AssignmentExpression a) or @@ -29,14 +30,16 @@ newtype TBicepAstNode = TObjectProperty(BICEP::ObjectProperty p) or TIdentifier(BICEP::Identifier i) -class TLiteral = TBoolean or TNull or TNumber or TString or TMultilineStringContent; +class TLiteral = + TBoolean or TNull or TNumber or TString or TStringContent or TMultilineStringContent; class TDeclaration = TResourceDeclaration or TObject or TObjectProperty or TIdentifier; class TExpr = TLiteral or TAssignmentExpression or TBinaryExpression or TCallExpression or TExpression or TLambdaExpression or TMemberExpression or TParenthesizedExpression or TResourceExpression or - TSubscriptExpression or TTernaryExpression or TUnaryExpression or TIdentifier; + TSubscriptExpression or TTernaryExpression or TUnaryExpression or TIdentifier or TObject or + TObjectProperty; cached BICEP::AstNode toBicepTreeSitter(TBicepAstNode n) { @@ -46,6 +49,7 @@ BICEP::AstNode toBicepTreeSitter(TBicepAstNode n) { n = TNull(result) or n = TNumber(result) or n = TString(result) or + n = TStringContent(result) or n = TMultilineStringContent(result) or n = TAssignmentExpression(result) or n = TBinaryExpression(result) or From 6cb7475b25657432411673d3a2420529845fca3d Mon Sep 17 00:00:00 2001 From: Mathew Payne Date: Tue, 19 Sep 2023 20:04:34 +0100 Subject: [PATCH 14/16] Add Microsoft resources --- ql/lib/bicep.qll | 3 +++ ql/lib/codeql/bicep/microsoft/Compute.qll | 23 +++++++++++++++++++++ ql/lib/codeql/bicep/microsoft/Storage.qll | 25 +++++++++++++++++++++++ 3 files changed, 51 insertions(+) create mode 100644 ql/lib/codeql/bicep/microsoft/Compute.qll create mode 100644 ql/lib/codeql/bicep/microsoft/Storage.qll diff --git a/ql/lib/bicep.qll b/ql/lib/bicep.qll index 2545b4d..5686f16 100644 --- a/ql/lib/bicep.qll +++ b/ql/lib/bicep.qll @@ -1,3 +1,6 @@ import codeql.Locations import codeql.files.FileSystem import codeql.bicep.AST +// Resources +import codeql.bicep.microsoft.Compute +import codeql.bicep.microsoft.Storage diff --git a/ql/lib/codeql/bicep/microsoft/Compute.qll b/ql/lib/codeql/bicep/microsoft/Compute.qll new file mode 100644 index 0000000..1008a77 --- /dev/null +++ b/ql/lib/codeql/bicep/microsoft/Compute.qll @@ -0,0 +1,23 @@ +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 + +module Compute { + class Disks extends Resource { + Disks() { this.getResourceType().regexpMatch("^Microsoft.Compute/disks@.*") } + } + + class DisksProperties extends Object { + private Disks disks; + + DisksProperties() { this = disks.getProperty("properties") } + + Object getEncryptionSettings() { result = this.getProperty("encryptionSettingsCollection") } + + boolean getEncryptionEnabled() { + result = this.getEncryptionSettings().getProperty("enabled").(BooleanLiteral).getBool() + } + } +} diff --git a/ql/lib/codeql/bicep/microsoft/Storage.qll b/ql/lib/codeql/bicep/microsoft/Storage.qll new file mode 100644 index 0000000..fa486b8 --- /dev/null +++ b/ql/lib/codeql/bicep/microsoft/Storage.qll @@ -0,0 +1,25 @@ +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 + +module Storage { + class StorageAccounts extends Resource { + StorageAccounts() { + this.getResourceType().regexpMatch("^Microsoft.Storage/storageAccounts@.*") + } + + Expr getKind() { result = this.getProperty("kind") } + } + + class StorageAccountsProperties extends Object { + private StorageAccounts storageAccounts; + + StorageAccountsProperties() { this = storageAccounts.getProperty("properties") } + + boolean getSupportsHttpsTrafficOnly() { + result = this.getProperty("supportsHttpsTrafficOnly").(BooleanLiteral).getBool() + } + } +} From 7fb49cb1e9d47b888ca99917a7852debdb2be21f Mon Sep 17 00:00:00 2001 From: Mathew Payne Date: Tue, 19 Sep 2023 20:04:51 +0100 Subject: [PATCH 15/16] Add Bicep queries --- .../security/Bicep/Compute/UnencryptedDisks.ql | 16 ++++++++++++++++ .../Bicep/Storage/SupportHttpTraffic.ql | 17 +++++++++++++++++ 2 files changed, 33 insertions(+) create mode 100644 ql/src/security/Bicep/Compute/UnencryptedDisks.ql create mode 100644 ql/src/security/Bicep/Storage/SupportHttpTraffic.ql diff --git a/ql/src/security/Bicep/Compute/UnencryptedDisks.ql b/ql/src/security/Bicep/Compute/UnencryptedDisks.ql new file mode 100644 index 0000000..5d0ee09 --- /dev/null +++ b/ql/src/security/Bicep/Compute/UnencryptedDisks.ql @@ -0,0 +1,16 @@ +/** + * @name Unencrypted compute disks + * @description Unencrypted compute disks + * @kind problem + * @severity warning + * @security-severity 4.0 + * @precision very-high + * @id bicep/compute/unencrypted-disks + * @tags security + */ + +import bicep + +from Compute::DisksProperties properties +where properties.getEncryptionEnabled() = false +select properties.getProperty("encryptionSettingsCollection"), "Unencrypted compute disks." diff --git a/ql/src/security/Bicep/Storage/SupportHttpTraffic.ql b/ql/src/security/Bicep/Storage/SupportHttpTraffic.ql new file mode 100644 index 0000000..fb709dc --- /dev/null +++ b/ql/src/security/Bicep/Storage/SupportHttpTraffic.ql @@ -0,0 +1,17 @@ +/** + * @name Supports non-HTTPS traffic for storage accounts + * @description Supports non-HTTPS traffic for storage accounts + * @kind problem + * @severity warning + * @security-severity 4.0 + * @precision very-high + * @id bicep/storage/support-http-traffic + * @tags security + */ + +import bicep + +from Storage::StorageAccountsProperties properties +where properties.getSupportsHttpsTrafficOnly() = false +select properties.getProperty("supportsHttpsTrafficOnly"), + "Supports non-HTTPS traffic for storage accounts." From db06a22b898f96fe2833483d02c0ac70388e60a0 Mon Sep 17 00:00:00 2001 From: Mathew Payne Date: Tue, 19 Sep 2023 20:05:35 +0100 Subject: [PATCH 16/16] Updated AST test --- ql/test/library-tests/bicep/ast/AST.expected | 160 ++++++++++++------- 1 file changed, 102 insertions(+), 58 deletions(-) diff --git a/ql/test/library-tests/bicep/ast/AST.expected b/ql/test/library-tests/bicep/ast/AST.expected index df6af52..484b88b 100644 --- a/ql/test/library-tests/bicep/ast/AST.expected +++ b/ql/test/library-tests/bicep/ast/AST.expected @@ -1,66 +1,110 @@ -| sample.bicep:1:7:1:14 | Expr | -| sample.bicep:1:7:1:14 | PrimaryExpr | -| sample.bicep:1:25:1:37 | Expr | -| sample.bicep:1:25:1:37 | PrimaryExpr | +| sample.bicep:1:1:14:2 | Infrastructure | +| sample.bicep:1:7:1:14 | ??? | +| sample.bicep:1:7:1:14 | Expression | +| sample.bicep:1:7:1:14 | location | +| sample.bicep:1:25:1:37 | ??? | +| sample.bicep:1:25:1:37 | Expression | +| sample.bicep:1:25:1:37 | resourceGroup | +| sample.bicep:1:25:1:39 | ??? | | sample.bicep:1:25:1:39 | CallExpr | -| sample.bicep:1:25:1:39 | Expr | -| sample.bicep:1:25:1:39 | PrimaryExpr | -| sample.bicep:1:25:1:48 | Expr | +| sample.bicep:1:25:1:39 | Expression | +| sample.bicep:1:25:1:48 | ??? | +| sample.bicep:1:25:1:48 | Expression | | sample.bicep:1:25:1:48 | MemberExpr | -| sample.bicep:1:25:1:48 | PrimaryExpr | -| sample.bicep:2:7:2:24 | Expr | -| sample.bicep:2:7:2:24 | PrimaryExpr | -| sample.bicep:2:35:2:80 | (no string representation) | -| sample.bicep:2:35:2:80 | Expr | -| sample.bicep:2:35:2:80 | PrimaryExpr | -| sample.bicep:2:47:2:58 | Expr | -| sample.bicep:2:47:2:58 | PrimaryExpr | +| sample.bicep:2:7:2:24 | ??? | +| sample.bicep:2:7:2:24 | Expression | +| sample.bicep:2:7:2:24 | storageAccountName | +| sample.bicep:2:35:2:80 | ??? | +| sample.bicep:2:35:2:80 | Expression | +| sample.bicep:2:35:2:80 | toylaunch | +| sample.bicep:2:36:2:44 | toylaunch | +| sample.bicep:2:47:2:58 | ??? | +| sample.bicep:2:47:2:58 | Expression | +| sample.bicep:2:47:2:58 | uniqueString | +| sample.bicep:2:47:2:78 | ??? | | sample.bicep:2:47:2:78 | CallExpr | -| sample.bicep:2:47:2:78 | Expr | -| sample.bicep:2:47:2:78 | PrimaryExpr | -| sample.bicep:2:60:2:72 | Expr | -| sample.bicep:2:60:2:72 | PrimaryExpr | +| sample.bicep:2:47:2:78 | Expression | +| sample.bicep:2:60:2:72 | ??? | +| sample.bicep:2:60:2:72 | Expression | +| sample.bicep:2:60:2:72 | resourceGroup | +| sample.bicep:2:60:2:74 | ??? | | sample.bicep:2:60:2:74 | CallExpr | -| sample.bicep:2:60:2:74 | Expr | -| sample.bicep:2:60:2:74 | PrimaryExpr | -| sample.bicep:2:60:2:77 | Expr | +| sample.bicep:2:60:2:74 | Expression | +| sample.bicep:2:60:2:77 | ??? | +| sample.bicep:2:60:2:77 | Expression | | sample.bicep:2:60:2:77 | MemberExpr | -| sample.bicep:2:60:2:77 | PrimaryExpr | -| sample.bicep:4:10:4:23 | Expr | -| sample.bicep:4:10:4:23 | PrimaryExpr | -| sample.bicep:4:25:4:70 | (no string representation) | -| sample.bicep:4:25:4:70 | Expr | -| sample.bicep:4:25:4:70 | PrimaryExpr | +| sample.bicep:4:1:14:1 | ResourceDeclaration | +| sample.bicep:4:10:4:23 | ??? | +| sample.bicep:4:10:4:23 | Expression | +| sample.bicep:4:10:4:23 | storageAccount | +| sample.bicep:4:25:4:70 | ??? | +| sample.bicep:4:25:4:70 | Expression | +| sample.bicep:4:25:4:70 | Microsoft.Storage/storageAccounts@2021-06-01 | +| sample.bicep:4:26:4:69 | Microsoft.Storage/storageAccounts@2021-06-01 | +| sample.bicep:4:74:14:1 | ??? | | sample.bicep:4:74:14:1 | Expr | -| sample.bicep:4:74:14:1 | PrimaryExpr | -| sample.bicep:5:3:5:6 | Expr | -| sample.bicep:5:3:5:6 | PrimaryExpr | -| sample.bicep:5:9:5:26 | Expr | -| sample.bicep:5:9:5:26 | PrimaryExpr | -| sample.bicep:6:3:6:10 | Expr | -| sample.bicep:6:3:6:10 | PrimaryExpr | -| sample.bicep:6:13:6:20 | Expr | -| sample.bicep:6:13:6:20 | PrimaryExpr | -| sample.bicep:7:3:7:5 | Expr | -| sample.bicep:7:3:7:5 | PrimaryExpr | +| sample.bicep:4:74:14:1 | Expression | +| sample.bicep:4:74:14:1 | Object | +| sample.bicep:5:3:5:6 | ??? | +| sample.bicep:5:3:5:6 | Expression | +| sample.bicep:5:3:5:6 | name | +| sample.bicep:5:3:5:26 | name = Expression | +| sample.bicep:5:3:5:26 | name = storageAccountName | +| sample.bicep:5:9:5:26 | ??? | +| sample.bicep:5:9:5:26 | Expression | +| sample.bicep:5:9:5:26 | storageAccountName | +| sample.bicep:6:3:6:10 | ??? | +| sample.bicep:6:3:6:10 | Expression | +| sample.bicep:6:3:6:10 | location | +| sample.bicep:6:3:6:20 | location = Expression | +| sample.bicep:6:3:6:20 | location = location | +| sample.bicep:6:13:6:20 | ??? | +| sample.bicep:6:13:6:20 | Expression | +| sample.bicep:6:13:6:20 | location | +| sample.bicep:7:3:7:5 | ??? | +| sample.bicep:7:3:7:5 | Expression | +| sample.bicep:7:3:7:5 | sku | +| sample.bicep:7:3:9:3 | sku = Expr | +| sample.bicep:7:3:9:3 | sku = Expression | +| sample.bicep:7:3:9:3 | sku = Object | +| sample.bicep:7:8:9:3 | ??? | | sample.bicep:7:8:9:3 | Expr | -| sample.bicep:7:8:9:3 | PrimaryExpr | -| sample.bicep:8:5:8:8 | Expr | -| sample.bicep:8:5:8:8 | PrimaryExpr | -| sample.bicep:8:11:8:24 | (no string representation) | -| sample.bicep:8:11:8:24 | Expr | -| sample.bicep:8:11:8:24 | PrimaryExpr | -| sample.bicep:10:3:10:6 | Expr | -| sample.bicep:10:3:10:6 | PrimaryExpr | -| sample.bicep:10:9:10:19 | (no string representation) | -| sample.bicep:10:9:10:19 | Expr | -| sample.bicep:10:9:10:19 | PrimaryExpr | -| sample.bicep:11:3:11:12 | Expr | -| sample.bicep:11:3:11:12 | PrimaryExpr | +| sample.bicep:7:8:9:3 | Expression | +| sample.bicep:7:8:9:3 | Object | +| sample.bicep:8:5:8:8 | ??? | +| sample.bicep:8:5:8:8 | Expression | +| sample.bicep:8:5:8:8 | name | +| sample.bicep:8:5:8:24 | name = Expression | +| sample.bicep:8:5:8:24 | name = Standard_LRS | +| sample.bicep:8:11:8:24 | ??? | +| sample.bicep:8:11:8:24 | Expression | +| sample.bicep:8:11:8:24 | Standard_LRS | +| sample.bicep:8:12:8:23 | Standard_LRS | +| sample.bicep:10:3:10:6 | ??? | +| sample.bicep:10:3:10:6 | Expression | +| sample.bicep:10:3:10:6 | kind | +| sample.bicep:10:3:10:19 | kind = Expression | +| sample.bicep:10:3:10:19 | kind = StorageV2 | +| sample.bicep:10:9:10:19 | ??? | +| sample.bicep:10:9:10:19 | Expression | +| sample.bicep:10:9:10:19 | StorageV2 | +| sample.bicep:10:10:10:18 | StorageV2 | +| sample.bicep:11:3:11:12 | ??? | +| sample.bicep:11:3:11:12 | Expression | +| sample.bicep:11:3:11:12 | properties | +| sample.bicep:11:3:13:3 | properties = Expr | +| sample.bicep:11:3:13:3 | properties = Expression | +| sample.bicep:11:3:13:3 | properties = Object | +| sample.bicep:11:15:13:3 | ??? | | sample.bicep:11:15:13:3 | Expr | -| sample.bicep:11:15:13:3 | PrimaryExpr | -| sample.bicep:12:5:12:14 | Expr | -| sample.bicep:12:5:12:14 | PrimaryExpr | -| sample.bicep:12:17:12:21 | (no string representation) | -| sample.bicep:12:17:12:21 | Expr | -| sample.bicep:12:17:12:21 | PrimaryExpr | +| sample.bicep:11:15:13:3 | Expression | +| sample.bicep:11:15:13:3 | Object | +| sample.bicep:12:5:12:14 | ??? | +| sample.bicep:12:5:12:14 | Expression | +| sample.bicep:12:5:12:14 | accessTier | +| sample.bicep:12:5:12:21 | accessTier = Expression | +| sample.bicep:12:5:12:21 | accessTier = Hot | +| sample.bicep:12:17:12:21 | ??? | +| sample.bicep:12:17:12:21 | Expression | +| sample.bicep:12:17:12:21 | Hot | +| sample.bicep:12:18:12:20 | Hot |