Skip to content

Commit bbc3f48

Browse files
owen-mcCopilot
andcommitted
Go: migrate remaining guard consumers
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
1 parent 77efb6c commit bbc3f48

7 files changed

Lines changed: 60 additions & 65 deletions

File tree

‎go/ql/src/InconsistentCode/ConstantLengthComparison.ql‎

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -13,10 +13,11 @@
1313
*/
1414

1515
import go
16+
private import semmle.go.controlflow.Guards
1617

1718
from
18-
ForStmt fs, Variable i, DataFlow::ElementReadNode idx, GVN a,
19-
ControlFlow::ConditionGuardNode cond, DataFlow::CallNode lenA
19+
ForStmt fs, Variable i, DataFlow::ElementReadNode idx, GVN a, Guard cond, boolean branch,
20+
DataFlow::CallNode lenA
2021
where
2122
// `i` is incremented in `fs`
2223
fs.getPost().(IncStmt).getOperand() = i.getAReference() and
@@ -27,11 +28,11 @@ where
2728
lenA.getArgument(0) = a.getANode() and
2829
// and is checked against a constant
2930
exists(DataFlow::Node const | exists(const.getIntValue()) |
30-
cond.ensuresNeq(lenA, const) or
31-
cond.ensuresLeq(const, lenA, _)
31+
guardEnsuresNeq(cond, branch, lenA, const) or
32+
guardEnsuresLeq(cond, branch, const, lenA, _)
3233
) and
33-
cond.dominates(idx.getBasicBlock()) and
34+
cond.controls(idx.getBasicBlock(), branch) and
3435
// and that check happens inside the loop body
35-
cond.getCondition().getParent+() = fs
36-
select cond.getCondition(), "This checks the length against a constant, but it $@.", idx,
36+
cond.(Expr).getParent+() = fs
37+
select cond, "This checks the length against a constant, but it $@.", idx,
3738
"is indexed using a variable"

‎go/ql/src/InconsistentCode/LengthComparisonOffByOne.ql‎

Lines changed: 24 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
*/
1414

1515
import go
16+
private import semmle.go.controlflow.Guards
1617

1718
newtype TIndex =
1819
VariableIndex(DataFlow::SsaNode v) { v.getAUse() = any(DataFlow::ElementReadNode e).getIndex() } or
@@ -41,28 +42,29 @@ DataFlow::CallNode arrayLen(DataFlow::SsaNode array) {
4142
}
4243

4344
/**
44-
* Gets a condition that checks that `index` is less than or equal to `array.length`.
45+
* Holds if `guard` evaluating to `branch` checks that `index` is less than or equal to
46+
* `array.length`.
4547
*/
46-
ControlFlow::ConditionGuardNode getLengthLEGuard(Index index, DataFlow::SsaNode array) {
47-
result.ensuresLeq(getAUse(index), arrayLen(array), 0)
48+
predicate lengthLeGuard(Guard guard, boolean branch, Index index, DataFlow::SsaNode array) {
49+
guardEnsuresLeq(guard, branch, getAUse(index), arrayLen(array), 0)
4850
or
4951
exists(int i, int bias | index = ConstantIndex(i) |
50-
result.ensuresLeq(getAUse(ConstantIndex(i + bias)), arrayLen(array), bias)
52+
guardEnsuresLeq(guard, branch, getAUse(ConstantIndex(i + bias)), arrayLen(array), bias)
5153
)
5254
}
5355

54-
predicate isDominatingLengthLEGuard(
55-
ControlFlow::ConditionGuardNode guard, Index index, DataFlow::SsaNode array, BasicBlock bb
56+
predicate isControllingLengthLeGuard(
57+
Guard guard, boolean branch, Index index, DataFlow::SsaNode array, BasicBlock bb
5658
) {
57-
guard = getLengthLEGuard(index, array) and
58-
guard.dominates(bb)
59+
lengthLeGuard(guard, branch, index, array) and
60+
guard.controls(bb, branch)
5961
}
6062

6163
/**
62-
* Gets a condition that checks that `index` is not equal to `array.length`.
64+
* Holds if `guard` evaluating to `branch` checks that `index` is not equal to `array.length`.
6365
*/
64-
ControlFlow::ConditionGuardNode getLengthNEGuard(Index index, DataFlow::SsaNode array) {
65-
result.ensuresNeq(getAUse(index), arrayLen(array))
66+
predicate lengthNeGuard(Guard guard, boolean branch, Index index, DataFlow::SsaNode array) {
67+
guardEnsuresNeq(guard, branch, getAUse(index), arrayLen(array))
6668
}
6769

6870
/**
@@ -85,26 +87,27 @@ predicate isRegexpMethodCall(DataFlow::MethodCallNode c) {
8587
}
8688

8789
from
88-
ControlFlow::ConditionGuardNode cond, DataFlow::SsaNode array, Index index,
89-
DataFlow::ElementReadNode ea, BasicBlock bb
90+
Guard cond, boolean branch, DataFlow::SsaNode array, Index index, DataFlow::ElementReadNode ea,
91+
BasicBlock bb
9092
where
9193
// there is a read from `array[index]`
9294
elementRead(ea, array, index, bb) and
9395
// and it is guarded by a comparison `index <= len(array)`
94-
isDominatingLengthLEGuard(cond, index, array, bb) and
96+
isControllingLengthLeGuard(cond, branch, index, array, bb) and
9597
// and report the innermost guard that establishes the comparison
96-
not exists(ControlFlow::ConditionGuardNode innerCond |
97-
isDominatingLengthLEGuard(innerCond, index, array, bb) and
98-
innerCond.getCondition().getParent+() = cond.getCondition()
98+
not exists(Guard innerCond |
99+
isControllingLengthLeGuard(innerCond, _, index, array, bb) and
100+
innerCond.(Expr).getParent+() = cond.(Expr)
99101
) and
100102
// but the read is not guarded by another check that `index != len(array)`
101-
not getLengthNEGuard(index, array).dominates(bb) and
103+
not exists(Guard ne, boolean neBranch |
104+
lengthNeGuard(ne, neBranch, index, array) and ne.controls(bb, neBranch)
105+
) and
102106
// and it is not additionally guarded by a stronger index check
103107
not exists(Index index2, int i, int i2 |
104108
index = ConstantIndex(i) and index2 = ConstantIndex(i2) and i < i2
105109
|
106-
isDominatingLengthLEGuard(_, index2, array, bb)
110+
isControllingLengthLeGuard(_, _, index2, array, bb)
107111
) and
108112
not isRegexpMethodCall(array.getInit())
109-
select cond.getCondition(),
110-
"Off-by-one index comparison against length may lead to out-of-bounds $@.", ea, "read"
113+
select cond, "Off-by-one index comparison against length may lead to out-of-bounds $@.", ea, "read"

‎go/ql/src/experimental/CWE-807/SensitiveConditionBypass.ql‎

Lines changed: 5 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -14,20 +14,18 @@
1414

1515
import go
1616
import SensitiveConditionBypass
17+
private import semmle.go.controlflow.Guards
1718

1819
from
19-
ControlFlow::ConditionGuardNode guard, DataFlow::Node sensitiveSink,
20-
SensitiveExpr::Classification classification, DataFlow::Node source, DataFlow::Node operand,
21-
ComparisonExpr comp
20+
DataFlow::Node sensitiveSink, SensitiveExpr::Classification classification, DataFlow::Node source,
21+
DataFlow::Node operand, ComparisonExpr comp
2222
where
2323
// there should be a flow between source and the operand sink
2424
Flow::flow(source, operand) and
2525
// both the operand should belong to the same comparison expression
2626
operand.asExpr() = comp.getAnOperand() and
27-
// get the ConditionGuardNode corresponding to the comparison expr.
28-
guard.getCondition() = comp and
2927
// the sink `sensitiveSink` should be sensitive,
3028
isSensitive(sensitiveSink, classification) and
31-
// the guard should control the sink
32-
guard.dominates(sensitiveSink.getBasicBlock())
29+
// the comparison should control the sink
30+
comp.(Guard).controls(sensitiveSink.getBasicBlock(), _)
3331
select comp, "This sensitive comparision check can potentially be bypassed."

‎go/ql/src/experimental/CWE-942/CorsMisconfiguration.ql‎

Lines changed: 9 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414

1515
import go
1616
import semmle.go.security.InsecureFeatureFlag::InsecureFeatureFlag
17+
private import semmle.go.controlflow.Guards
1718

1819
/**
1920
* A flag indicating a check for satisfied permissions or test configuration.
@@ -59,11 +60,7 @@ module UntrustedToAllowOriginHeaderConfig implements DataFlow::ConfigSig {
5960
}
6061

6162
predicate isBarrier(DataFlow::Node node) {
62-
exists(ControlFlow::ConditionGuardNode cgn |
63-
cgn.ensures(any(AllowedFlag f).getAFlag().getANode(), _)
64-
|
65-
cgn.dominates(node.getBasicBlock())
66-
)
63+
flagControls(any(AllowedFlag f), node.getBasicBlock())
6764
}
6865

6966
predicate isSink(DataFlow::Node sink) { isSinkHW(sink, _) }
@@ -171,9 +168,9 @@ class MapRead extends DataFlow::ElementReadNode {
171168
module FromUntrustedConfig implements DataFlow::ConfigSig {
172169
predicate isSource(DataFlow::Node source) { source instanceof ActiveThreatModelSource }
173170

174-
predicate isSink(DataFlow::Node sink) { isSinkCgn(sink, _) }
171+
predicate isSink(DataFlow::Node sink) { isSinkGuard(sink, _) }
175172

176-
additional predicate isSinkCgn(DataFlow::Node sink, ControlFlow::ConditionGuardNode cgn) {
173+
additional predicate isSinkGuard(DataFlow::Node sink, Guard guard) {
177174
exists(IfStmt ifs |
178175
exists(Expr operand |
179176
operand = ifs.getCondition().getAChildExpr*() and
@@ -202,7 +199,7 @@ module FromUntrustedConfig implements DataFlow::ConfigSig {
202199
)
203200
)
204201
|
205-
cgn.getCondition() = ifs.getCondition()
202+
guard = ifs.getCondition()
206203
)
207204
}
208205
}
@@ -217,10 +214,10 @@ module FromUntrustedFlow = TaintTracking::Global<FromUntrustedConfig>;
217214
* Holds if the provided `allowOriginHW` is also destination of a `ActiveThreatModelSource`.
218215
*/
219216
predicate flowsToGuardedByCheckOnUntrusted(DataFlow::ExprNode allowOriginHW) {
220-
exists(DataFlow::Node sink, ControlFlow::ConditionGuardNode cgn |
221-
FromUntrustedFlow::flowTo(sink) and FromUntrustedConfig::isSinkCgn(sink, cgn)
217+
exists(DataFlow::Node sink, Guard guard |
218+
FromUntrustedFlow::flowTo(sink) and FromUntrustedConfig::isSinkGuard(sink, guard)
222219
|
223-
cgn.dominates(allowOriginHW.getBasicBlock())
220+
guard.controls(allowOriginHW.getBasicBlock(), _)
224221
)
225222
}
226223

@@ -233,9 +230,5 @@ where
233230
allowOriginIsNull(allowOriginHW, message)
234231
) and
235232
not flowsToGuardedByCheckOnUntrusted(allowOriginHW) and
236-
not exists(ControlFlow::ConditionGuardNode cgn |
237-
cgn.ensures(any(AllowedFlag f).getAFlag().getANode(), _)
238-
|
239-
cgn.dominates(allowOriginHW.getBasicBlock())
240-
)
233+
not flagControls(any(AllowedFlag f), allowOriginHW.getBasicBlock())
241234
select allowOriginHW, message

‎go/ql/src/experimental/IntegerOverflow/RangeAnalysis.qll‎

Lines changed: 13 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import go
2+
private import semmle.go.controlflow.Guards
23

34
Expr getAUse(SsaDefinition def) {
45
result = def.getVariable().getAUse().(IR::EvalInstruction).getExpr()
@@ -40,12 +41,12 @@ float getAnUpperBound(Expr expr) {
4041
if
4142
//if a condition expression exists before and one of the operand happens to be the identifier, we use this condition expression to narrow down the range.
4243
exists(
43-
ControlFlow::ConditionGuardNode n, DataFlow::Node lesser, DataFlow::Node greater,
44+
Guard n, boolean branch, DataFlow::Node lesser, DataFlow::Node greater,
4445
ReachableBasicBlock bb
4546
|
46-
n.ensuresLeq(lesser, greater, _) and
47+
guardEnsuresLeq(n, branch, lesser, greater, _) and
4748
IR::evalExprInstruction(lesser.asExpr()) = v.getAUse() and
48-
n.dominates(bb) and
49+
n.controls(bb, branch) and
4950
bb.getANode() = IR::evalExprInstruction(identifier) and
5051
not exists(Expr e |
5152
e = v.getAUse().(IR::EvalInstruction).getExpr() and
@@ -54,12 +55,12 @@ float getAnUpperBound(Expr expr) {
5455
)
5556
then
5657
exists(
57-
ControlFlow::ConditionGuardNode n, ReachableBasicBlock bb, DataFlow::Node lesser,
58+
Guard n, boolean branch, ReachableBasicBlock bb, DataFlow::Node lesser,
5859
DataFlow::Node greater, int bias
5960
|
60-
n.dominates(bb) and
61+
n.controls(bb, branch) and
6162
bb.getANode() = IR::evalExprInstruction(identifier) and
62-
n.ensuresLeq(lesser, greater, bias) and
63+
guardEnsuresLeq(n, branch, lesser, greater, bias) and
6364
v.getAUse() = IR::evalExprInstruction(lesser.asExpr()) and
6465
not exists(Expr e |
6566
e = v.getAUse().(IR::EvalInstruction).getExpr() and
@@ -191,12 +192,12 @@ float getALowerBound(Expr expr) {
191192
//if exists a condition expression before this identifier
192193
if
193194
exists(
194-
ControlFlow::ConditionGuardNode n, DataFlow::Node greater, DataFlow::Node lesser,
195+
Guard n, boolean branch, DataFlow::Node greater, DataFlow::Node lesser,
195196
ReachableBasicBlock bb
196197
|
197-
n.ensuresLeq(lesser, greater, _) and
198+
guardEnsuresLeq(n, branch, lesser, greater, _) and
198199
IR::evalExprInstruction(greater.asExpr()) = v.getAUse() and
199-
n.dominates(bb) and
200+
n.controls(bb, branch) and
200201
bb.getANode() = IR::evalExprInstruction(identifier) and
201202
not exists(Expr e |
202203
e = v.getAUse().(IR::EvalInstruction).getExpr() and
@@ -205,12 +206,12 @@ float getALowerBound(Expr expr) {
205206
)
206207
then
207208
exists(
208-
ControlFlow::ConditionGuardNode n, ReachableBasicBlock bb, DataFlow::Node lesser,
209+
Guard n, boolean branch, ReachableBasicBlock bb, DataFlow::Node lesser,
209210
DataFlow::Node greater, int bias, float lbs
210211
|
211-
n.dominates(bb) and
212+
n.controls(bb, branch) and
212213
bb.getANode() = IR::evalExprInstruction(identifier) and
213-
n.ensuresLeq(lesser, greater, bias) and
214+
guardEnsuresLeq(n, branch, lesser, greater, bias) and
214215
v.getAUse() = IR::evalExprInstruction(greater.asExpr()) and
215216
not exists(Expr e |
216217
e = v.getAUse().(IR::EvalInstruction).getExpr() and

‎go/ql/test/experimental/CWE-942/CorsMisconfiguration.expected‎

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,6 @@
77
| CorsMisconfiguration.go:53:4:53:44 | call to Set | access-control-allow-origin header is set to a user-defined value, and access-control-allow-credentials is set to `true` |
88
| CorsMisconfiguration.go:60:4:60:56 | call to Set | access-control-allow-origin header is set to a user-defined value, and access-control-allow-credentials is set to `true` |
99
| CorsMisconfiguration.go:67:5:67:57 | call to Set | access-control-allow-origin header is set to a user-defined value, and access-control-allow-credentials is set to `true` |
10-
| CorsMisconfiguration.go:222:5:222:57 | call to Set | access-control-allow-origin header is set to a user-defined value, and access-control-allow-credentials is set to `true` |
1110
| CorsMisconfiguration.go:224:5:224:57 | call to Set | access-control-allow-origin header is set to a user-defined value, and access-control-allow-credentials is set to `true` |
1211
| RsCors.go:11:21:11:59 | slice literal | access-control-allow-origin header is set to `null`, and access-control-allow-credentials is set to `true` |
1312
| RsCors.go:31:23:31:61 | slice literal | access-control-allow-origin header is set to `null`, and access-control-allow-credentials is set to `true` |

‎go/ql/test/experimental/CWE-942/CorsMisconfiguration.go‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -219,7 +219,7 @@ func main() {
219219
origin := req.Header.Get("origin")
220220
switch req.Method {
221221
case "allowed":
222-
w.Header().Set("Access-Control-Allow-Origin", origin) // $ SPURIOUS: Alert
222+
w.Header().Set("Access-Control-Allow-Origin", origin)
223223
default:
224224
w.Header().Set("Access-Control-Allow-Origin", origin) // $ Alert
225225
}

0 commit comments

Comments
 (0)