Skip to content

Commit e94d128

Browse files
committed
feat(cfg): Add control flow graph nodes and trees for various Bicep statements
1 parent 186f0cd commit e94d128

File tree

2 files changed

+347
-6
lines changed

2 files changed

+347
-6
lines changed

ql/lib/codeql/bicep/controlflow/CfgNodes.qll

Lines changed: 275 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,51 @@ private import BasicBlocks
88
private import ControlFlowGraph
99
import internal.ControlFlowGraphImpl
1010

11+
/**
12+
* A class for mapping parent-child AST nodes to parent-child CFG nodes.
13+
*/
14+
abstract private class ChildMapping extends AstNode {
15+
/**
16+
* Holds if `child` is a (possibly nested) child of this expression
17+
* for which we would like to find a matching CFG child.
18+
*/
19+
abstract predicate relevantChild(AstNode child);
20+
21+
pragma[nomagic]
22+
abstract predicate reachesBasicBlock(AstNode child, CfgNode cfn, BasicBlock bb);
23+
24+
/**
25+
* Holds if there is a control-flow path from `cfn` to `cfnChild`, where `cfn`
26+
* is a control-flow node for this expression, and `cfnChild` is a control-flow
27+
* node for `child`.
28+
*
29+
* The path never escapes the syntactic scope of this expression.
30+
*/
31+
cached
32+
predicate hasCfgChild(AstNode child, CfgNode cfn, CfgNode cfnChild) {
33+
this.reachesBasicBlock(child, cfn, cfnChild.getBasicBlock()) and
34+
cfnChild.getAstNode() = child
35+
}
36+
}
37+
38+
/**
39+
* A class for mapping parent-child AST nodes to parent-child CFG nodes.
40+
*/
41+
abstract private class ExprChildMapping extends Expr, ChildMapping {
42+
pragma[nomagic]
43+
override predicate reachesBasicBlock(AstNode child, CfgNode cfn, BasicBlock bb) {
44+
this.relevantChild(child) and
45+
cfn = this.getAControlFlowNode() and
46+
bb.getANode() = cfn
47+
or
48+
exists(BasicBlock mid |
49+
this.reachesBasicBlock(child, cfn, mid) and
50+
bb = mid.getAPredecessor() and
51+
not mid.getANode().getAstNode() = child
52+
)
53+
}
54+
}
55+
1156
/** A control-flow node that wraps an AST expression. */
1257
class ExprCfgNode extends AstCfgNode {
1358
string getAPrimaryQlClass() { result = "ExprCfgNode" }
@@ -44,6 +89,62 @@ class LiteralsCfgNode extends AstCfgNode {
4489
Literals getLiteral() { result = l }
4590
}
4691

92+
class VariableCfgNode extends AstCfgNode {
93+
string getAPrimaryQlClass() { result = "VariableCfgNode" }
94+
95+
Variable v;
96+
97+
VariableCfgNode() { v.getAstNode() = this.getAstNode() }
98+
99+
/**
100+
* Gets the underlying `Variable` expression.
101+
*/
102+
Variable getExpr() { result = v }
103+
104+
/** Gets the name of the variable. */
105+
string getName() { result = v.getName() }
106+
}
107+
108+
/** A control-flow node that wraps a `VariableAccess` AST expression. */
109+
class VariableAccessCfgNode extends AstCfgNode {
110+
string getAPrimaryQlClass() { result = "VariableAccessCfgNode" }
111+
112+
VariableAccess v;
113+
114+
VariableAccessCfgNode() { v.getAstNode() = this.getAstNode() }
115+
116+
/**
117+
* Gets the underlying `VariableAccess` expression.
118+
*/
119+
VariableAccess getExpr() { result = v }
120+
121+
/** Gets the variable */
122+
VariableCfgNode getTarget() { result.getExpr() = v.getVariable() }
123+
}
124+
125+
/** A control-flow node that wraps a `VariableReadAccess` AST expression. */
126+
class VariableReadAccessCfgNode extends VariableAccessCfgNode {
127+
override string getAPrimaryQlClass() { result = "VariableReadAccessCfgNode" }
128+
129+
override VariableReadAccess v;
130+
131+
override VariableReadAccess getExpr() { result = super.getExpr() }
132+
133+
Variable getVariable() { result = v.getVariable() }
134+
}
135+
136+
class VariableWriteAccessCfgNode extends VariableAccessCfgNode {
137+
override string getAPrimaryQlClass() { result = "VariableWriteAccessCfgNode" }
138+
139+
override VariableWriteAccess v;
140+
141+
override VariableWriteAccess getExpr() { result.asExpr() = v.asExpr() }
142+
143+
Variable getVariable() { result = v.getVariable() }
144+
145+
final ExprCfgNode getReceiver() { result.getExpr() = v.asExpr() }
146+
}
147+
47148
/** Provides classes for control-flow nodes that wrap AST expressions. */
48149
module ExprNodes {
49150
/** A mapping from a child of an expression to an expression. */
@@ -140,7 +241,7 @@ module ExprNodes {
140241
}
141242

142243
/** A mapping from a child of an Arguments expression to the expression. */
143-
abstract class ArgumentsChildMapping extends ExprChildMapping, Arguments {
244+
abstract class ArgumentsChildMapping extends ExprNodes::ExprChildMapping, Arguments {
144245
override predicate relevantChild(AstNode n) { n = this.getArgument(_) }
145246
}
146247

@@ -154,7 +255,9 @@ module ExprNodes {
154255
}
155256

156257
/** A mapping from a child of an AssignmentExpression to the expression. */
157-
abstract class AssignmentExpressionChildMapping extends ExprChildMapping, AssignmentExpression {
258+
abstract class AssignmentExpressionChildMapping extends ExprNodes::ExprChildMapping,
259+
AssignmentExpression
260+
{
158261
override predicate relevantChild(AstNode n) { n = this.getLeft() or n = this.getRight() }
159262
}
160263

@@ -168,7 +271,7 @@ module ExprNodes {
168271
}
169272

170273
/** A mapping from a child of a BinaryExpression to the expression. */
171-
abstract class BinaryExpressionChildMapping extends ExprChildMapping, BinaryExpression {
274+
abstract class BinaryExpressionChildMapping extends ExprNodes::ExprChildMapping, BinaryExpression {
172275
override predicate relevantChild(AstNode n) { n = this.getLeft() or n = this.getRight() }
173276
}
174277

@@ -194,7 +297,7 @@ module ExprNodes {
194297
}
195298

196299
/** A mapping from a child of an Interpolation to the expression. */
197-
abstract class InterpolationChildMapping extends ExprChildMapping, Interpolation {
300+
abstract class InterpolationChildMapping extends ExprNodes::ExprChildMapping, Interpolation {
198301
override predicate relevantChild(AstNode n) { n = this.getExpression() }
199302
}
200303

@@ -306,10 +409,177 @@ module ExprNodes {
306409

307410
override UnaryExpression getExpr() { result = super.getExpr() }
308411
}
412+
413+
/** A mapping from a child of a Parameter to the expression. */
414+
abstract class ParameterChildMapping extends ExprNodes::ExprChildMapping, Parameter {
415+
override predicate relevantChild(AstNode n) { n = this.getIdentifier() or n = this.getType() }
416+
}
417+
418+
/** A control-flow node that wraps a Parameter AST node. */
419+
class ParameterCfgNode extends ExprCfgNode {
420+
override string getAPrimaryQlClass() { result = "ParameterCfgNode" }
421+
422+
override Parameter e;
423+
424+
override Parameter getExpr() { result = super.getExpr() }
425+
}
426+
427+
/** A mapping from a child of Parameters to the expression. */
428+
abstract class ParametersChildMaping extends ExprNodes::ExprChildMapping, Parameters {
429+
override predicate relevantChild(AstNode n) { n = this.getParameter(_) }
430+
}
431+
432+
/** A control-flow node that wraps a Parameters AST node. */
433+
class ParametersCfgNode extends ExprCfgNode {
434+
override string getAPrimaryQlClass() { result = "ParametersCfgNode" }
435+
436+
override Parameters e;
437+
438+
override Parameters getExpr() { result = super.getExpr() }
439+
}
309440
}
310441

442+
/** Provides classes for control-flow nodes that wrap AST statements. */
311443
module StmtNodes {
312-
/** A control-flow node that wraps a `Cmd` AST expression. */
444+
/** A mapping from a child of a statement to a statement. */
445+
abstract class StmtChildMapping extends Stmts {
446+
/** Holds if `n` is a relevant child of this statement. */
447+
abstract predicate relevantChild(AstNode n);
448+
}
449+
450+
/** A control-flow node that wraps an AssertStatement AST node. */
451+
class AssertStatementCfgNode extends StmtsCfgNode {
452+
override string getAPrimaryQlClass() { result = "AssertStatementCfgNode" }
453+
454+
override AssertStatementStmt s;
455+
456+
override AssertStatementStmt getStmt() { result = super.getStmt() }
457+
}
458+
459+
/** A control-flow node that wraps a ForStatement AST node. */
460+
class ForStatementCfgNode extends StmtsCfgNode {
461+
override string getAPrimaryQlClass() { result = "ForStatementCfgNode" }
462+
463+
override ForStatementStmt s;
464+
465+
override ForStatementStmt getStmt() { result = super.getStmt() }
466+
}
467+
468+
/** A mapping from a child of an IfStatement to the statement. */
469+
abstract class IfStatementChildMapping extends StmtChildMapping, IfStatement {
470+
override predicate relevantChild(AstNode n) { n = this.getCondition() or n = this.getBody() }
471+
}
472+
473+
/** A control-flow node that wraps an IfStatement AST node. */
474+
class IfStatementCfgNode extends StmtsCfgNode {
475+
override string getAPrimaryQlClass() { result = "IfStatementCfgNode" }
476+
477+
override IfStatement s;
478+
479+
override IfStatement getStmt() { result = super.getStmt() }
480+
481+
/** Gets the condition CFG node */
482+
final ExprCfgNode getCondition() {
483+
result = this.getAPredecessor().(ExprCfgNode) and
484+
result.getExpr() = this.getStmt().getCondition()
485+
}
486+
487+
/** Gets the body CFG node */
488+
final ExprCfgNode getBody() {
489+
result = this.getAPredecessor().(ExprCfgNode) and
490+
result.getExpr() = this.getStmt().getBody()
491+
}
492+
}
493+
494+
/** A control-flow node that wraps an ImportStatement AST node. */
495+
class ImportStatementCfgNode extends StmtsCfgNode {
496+
override string getAPrimaryQlClass() { result = "ImportStatementCfgNode" }
497+
498+
override ImportStatementStmt s;
499+
500+
override ImportStatementStmt getStmt() { result = super.getStmt() }
501+
}
502+
503+
/** A control-flow node that wraps an Infrastructure AST node. */
504+
class InfrastructureCfgNode extends AstCfgNode {
505+
string getAPrimaryQlClass() { result = "InfrastructureCfgNode" }
506+
507+
Infrastructure i;
508+
509+
InfrastructureCfgNode() { i = this.getAstNode() }
510+
511+
/** Gets the underlying Infrastructure. */
512+
Infrastructure getInfrastructure() { result = i }
513+
}
514+
515+
/** A mapping from a child of a ParameterDeclaration to the statement. */
516+
abstract class ParameterDeclarationChildMapping extends StmtChildMapping, ParameterDeclaration {
517+
override predicate relevantChild(AstNode n) {
518+
n = this.getIdentifier() or n = this.getDefaultValue()
519+
}
520+
}
521+
522+
/** A control-flow node that wraps a ParameterDeclaration AST node. */
523+
class ParameterDeclarationCfgNode extends StmtsCfgNode {
524+
override string getAPrimaryQlClass() { result = "ParameterDeclarationCfgNode" }
525+
526+
override ParameterDeclaration s;
527+
528+
override ParameterDeclaration getStmt() { result = super.getStmt() }
529+
}
530+
531+
/** A mapping from a child of an OutputDeclaration to the statement. */
532+
abstract class OutputDeclarationChildMapping extends StmtChildMapping, OutputDeclaration {
533+
override predicate relevantChild(AstNode n) { n = this.getIdentifier() or n = this.getValue() }
534+
}
535+
536+
/** A control-flow node that wraps an OutputDeclaration AST node. */
537+
class OutputDeclarationCfgNode extends StmtsCfgNode {
538+
override string getAPrimaryQlClass() { result = "OutputDeclarationCfgNode" }
539+
540+
override OutputDeclaration s;
541+
542+
override OutputDeclaration getStmt() { result = super.getStmt() }
543+
}
544+
545+
/** A mapping from a child of a UserDefinedFunction to the statement. */
546+
abstract class UserDefinedFunctionChildMapping extends StmtChildMapping, UserDefinedFunction {
547+
override predicate relevantChild(AstNode n) {
548+
n = this.getIdentifier() or
549+
n = this.getDeclaredParameters() or
550+
n = this.getReturnType() or
551+
n = this.getBody()
552+
}
553+
}
554+
555+
/** A control-flow node that wraps a UserDefinedFunction AST node. */
556+
class UserDefinedFunctionCfgNode extends StmtsCfgNode {
557+
override string getAPrimaryQlClass() { result = "UserDefinedFunctionCfgNode" }
558+
559+
override UserDefinedFunction s;
560+
561+
override UserDefinedFunction getStmt() { result = super.getStmt() }
562+
}
563+
564+
/** A control-flow node that wraps an ImportWithStatement AST node. */
565+
class ImportWithStatementCfgNode extends StmtsCfgNode {
566+
override string getAPrimaryQlClass() { result = "ImportWithStatementCfgNode" }
567+
568+
override ImportWithStatementStmt s;
569+
570+
override ImportWithStatementStmt getStmt() { result = super.getStmt() }
571+
}
572+
573+
/** A control-flow node that wraps a UsingStatement AST node. */
574+
class UsingStatementCfgNode extends StmtsCfgNode {
575+
override string getAPrimaryQlClass() { result = "UsingStatementCfgNode" }
576+
577+
override UsingStatementStmt s;
578+
579+
override UsingStatementStmt getStmt() { result = super.getStmt() }
580+
}
581+
582+
/** A control-flow node that wraps a UserDefinedFunction AST node. */
313583
class CallCfgNode extends StmtsCfgNode {
314584
override string getAPrimaryQlClass() { result = "CallCfgNode" }
315585

0 commit comments

Comments
 (0)