Skip to content

Commit 3c1f755

Browse files
committed
Swift: Support other parse modes.
1 parent 8273fa1 commit 3c1f755

File tree

4 files changed

+55
-18
lines changed

4 files changed

+55
-18
lines changed

swift/ql/lib/codeql/swift/regex/Regex.qll

Lines changed: 29 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -72,8 +72,8 @@ class RegexParseMode extends TRegexParseMode {
7272
(this = MkIgnoreCase() and result = "IGNORECASE") or
7373
(this = MkVerbose() and result = "VERBOSE") or
7474
(this = MkDotAll() and result = "DOTALL") or
75-
(this = MkUnicode() and result = "MULTILINE") or
76-
(this = MkIgnoreCase() and result = "UNICODE")
75+
(this = MkMultiLine() and result = "MULTILINE") or
76+
(this = MkUnicode() and result = "UNICODE")
7777
}
7878
}
7979

@@ -105,11 +105,21 @@ class StandardRegexAdditionalFlowStep extends RegexAdditionalFlowStep {
105105
override predicate modifiesParseMode(DataFlow::Node nodeFrom, DataFlow::Node nodeTo, RegexParseMode mode, boolean isSet)
106106
{
107107
exists(CallExpr ce |
108-
ce.getStaticTarget().(Method).hasQualifiedName("Regex", "dotMatchesNewlines(_:)") and
109108
nodeFrom.asExpr() = ce.getQualifier() and
110109
nodeTo.asExpr() = ce and
111-
mode = MkDotAll() and
112-
// TODO: other methods
110+
// decode the parse mode being set
111+
(
112+
(
113+
ce.getStaticTarget().(Method).hasQualifiedName("Regex", "ignoresCase(_:)") and
114+
mode = MkIgnoreCase()
115+
) or (
116+
ce.getStaticTarget().(Method).hasQualifiedName("Regex", "dotMatchesNewlines(_:)") and
117+
mode = MkDotAll()
118+
) or (
119+
ce.getStaticTarget().(Method).hasQualifiedName("Regex", "anchorsMatchLineEndings(_:)") and
120+
mode = MkMultiLine()
121+
)
122+
) and
113123
// decode the value being set
114124
if ce.getArgument(0).getExpr().(BooleanLiteralExpr).getValue() = false then
115125
isSet = false // mode is set to false
@@ -150,6 +160,20 @@ abstract class RegexEval extends CallExpr {
150160
RegexUseFlow::flow(regexCreation, DataFlow::exprNode(this.getRegexInput()))
151161
)
152162
}
163+
164+
/**
165+
* Gets a parse mode that is set at this evaluation (in at least one path
166+
* from the creation of the regular expression object).
167+
*/
168+
RegexParseMode getAParseMode() {
169+
exists(DataFlow::Node setNode |
170+
// parse mode flag is set
171+
any(RegexAdditionalFlowStep s).modifiesParseMode(_, setNode, result, true)
172+
and
173+
// reaches here
174+
RegexParseModeFlow::flow(setNode, DataFlow::exprNode(this.getRegexInput()))
175+
)
176+
}
153177
}
154178

155179
/**

swift/ql/lib/codeql/swift/regex/internal/ParseRegex.qll

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -333,8 +333,7 @@ abstract class RegExp extends Expr {
333333
// mode flags applied to the regex object before evaluation
334334
exists(RegexEval e |
335335
e.getARegex() = this and
336-
RegexParseModeFlow::flow(_, DataFlow::exprNode(e.getRegexInput())) and
337-
result = "DOTALL" // TODO
336+
result = e.getAParseMode().toString() // TODO: temp toString()
338337
)
339338
}
340339

swift/ql/lib/codeql/swift/regex/internal/RegexTracking.qll

Lines changed: 24 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -56,25 +56,39 @@ module RegexUseFlow = DataFlow::Global<RegexUseConfig>;
5656
* flags from the point they are set to the point of use. The flow state
5757
* encodes which parse mode flag was set.
5858
*/
59-
private module RegexParseModeConfig implements DataFlow::ConfigSig {
60-
predicate isSource(DataFlow::Node node) {
61-
// parse mode flag is set
62-
any(RegexAdditionalFlowStep s).modifiesParseMode(_, node, MkDotAll(), true)
63-
}
59+
private module RegexParseModeConfig implements DataFlow::StateConfigSig {
60+
class FlowState = RegexParseMode;
6461

65-
predicate isBarrierIn(DataFlow::Node node) {
66-
// parse mode flag is set or unset
67-
any(RegexAdditionalFlowStep s).modifiesParseMode(_, node, MkDotAll(), _)
62+
predicate isSource(DataFlow::Node node, FlowState flowstate) {
63+
// parse mode flag is set
64+
any(RegexAdditionalFlowStep s).modifiesParseMode(_, node, flowstate, true)
6865
}
6966

70-
predicate isSink(DataFlow::Node node) {
67+
predicate isSink(DataFlow::Node node, FlowState flowstate) {
7168
// evaluation of the regex
7269
node.asExpr() = any(RegexEval eval).getRegexInput()
70+
and
71+
flowstate = any(FlowState fs)
72+
}
73+
74+
predicate isBarrier(DataFlow::Node node) {
75+
none()
76+
}
77+
78+
predicate isBarrier(DataFlow::Node node, FlowState flowstate) {
79+
// parse mode flag is set or unset
80+
any(RegexAdditionalFlowStep s).modifiesParseMode(node, _, flowstate, _)
7381
}
7482

7583
predicate isAdditionalFlowStep(DataFlow::Node nodeFrom, DataFlow::Node nodeTo) {
7684
any(RegexAdditionalFlowStep s).step(nodeFrom, nodeTo)
7785
}
86+
87+
predicate isAdditionalFlowStep(
88+
DataFlow::Node nodeFrom, FlowState flowstateFrom, DataFlow::Node nodeTo, FlowState flowStateTo
89+
) {
90+
none()
91+
}
7892
}
7993

80-
module RegexParseModeFlow = DataFlow::Global<RegexParseModeConfig>;
94+
module RegexParseModeFlow = DataFlow::GlobalWithState<RegexParseModeConfig>;

swift/ql/test/library-tests/regex/regex.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -214,7 +214,7 @@ func myRegexpMethodsTests(b: Bool, str_unknown: String) throws {
214214
_ = try Regex("abc").dotMatchesNewlines(false).firstMatch(in: input) // $ input=input regex=abc
215215
_ = try Regex("abc").dotMatchesNewlines(true).dotMatchesNewlines(false).firstMatch(in: input) // $ input=input regex=abc
216216
_ = try Regex("abc").dotMatchesNewlines(false).dotMatchesNewlines(true).firstMatch(in: input) // $ input=input regex=abc modes=DOTALL
217-
_ = try Regex("abc").dotMatchesNewlines().ignoresCase().firstMatch(in: input) // $ input=input regex=abc SPURIOUS: modes=DOTALL MISSING: modes="DOTALL | IGNORECASE"
217+
_ = try Regex("abc").dotMatchesNewlines().ignoresCase().firstMatch(in: input) // $ input=input regex=abc modes="DOTALL | IGNORECASE"
218218

219219
_ = try NSRegularExpression(pattern: ".*", options: .caseInsensitive).firstMatch(in: input, range: NSMakeRange(0, input.utf16.count)) // $ regex=.* input=input MISSING: modes=IGNORECASE
220220
_ = try NSRegularExpression(pattern: ".*", options: .dotMatchesLineSeparators).firstMatch(in: input, range: NSMakeRange(0, input.utf16.count)) // $ regex=.* input=input MISSING: modes=DOTALL

0 commit comments

Comments
 (0)