diff --git a/cpp/ql/lib/semmle/code/cpp/regex/internal/ParseRegExp.qll b/cpp/ql/lib/semmle/code/cpp/regex/internal/ParseRegExp.qll index 968e19d10b0..326b3e7ae15 100644 --- a/cpp/ql/lib/semmle/code/cpp/regex/internal/ParseRegExp.qll +++ b/cpp/ql/lib/semmle/code/cpp/regex/internal/ParseRegExp.qll @@ -162,7 +162,9 @@ class RegExp extends StringLiteral { or this.namedCharacterProperty(start, end, _) or - exists(this.nonEscapedCharAt(start)) and end = start + 1 + exists(this.nonEscapedCharAt(start)) and + not this.inAnyPosixBracket(start) and + end = start + 1 ) or this.charSetToken(charsetStart, _, start) and @@ -172,6 +174,7 @@ class RegExp extends StringLiteral { this.namedCharacterProperty(start, end, _) or exists(this.nonEscapedCharAt(start)) and + not this.inAnyPosixBracket(start) and end = start + 1 and not this.getChar(start) = "]" ) @@ -313,6 +316,13 @@ class RegExp extends StringLiteral { private predicate posixStyleNamedCharacterProperty(int start, int end, string name) { this.getChar(start) = "[" and this.getChar(start + 1) = ":" and + // POSIX bracket expressions are only valid nested inside another character + // class. Approximate this by requiring at least one more (non-escaped) + // opening `[` than closing `]` before `start`. A well-formed POSIX bracket + // contributes one `[` and one `]` at/after `start`, so this check is + // unaffected by other POSIX brackets earlier in the text. + count(int p | p < start and this.nonEscapedCharAt(p) = "[") > + count(int p | p < start and this.nonEscapedCharAt(p) = "]") and end = min(int e | e > start and @@ -337,6 +347,8 @@ class RegExp extends StringLiteral { private predicate posixCollatingSymbol(int start, int end, string name) { this.getChar(start) = "[" and this.getChar(start + 1) = "." and + count(int p | p < start and this.nonEscapedCharAt(p) = "[") > + count(int p | p < start and this.nonEscapedCharAt(p) = "]") and end = min(int e | e > start and @@ -355,6 +367,8 @@ class RegExp extends StringLiteral { private predicate posixEquivalenceClass(int start, int end, string name) { this.getChar(start) = "[" and this.getChar(start + 1) = "=" and + count(int p | p < start and this.nonEscapedCharAt(p) = "[") > + count(int p | p < start and this.nonEscapedCharAt(p) = "]") and end = min(int e | e > start and diff --git a/cpp/ql/test/library-tests/regex/locations.expected b/cpp/ql/test/library-tests/regex/locations.expected index f9fba8ee68c..a70822fa3a6 100644 --- a/cpp/ql/test/library-tests/regex/locations.expected +++ b/cpp/ql/test/library-tests/regex/locations.expected @@ -6,10 +6,19 @@ | test.cpp:143:29:143:29 | p | 23 | 5 | 6 | 29 | 29 | | test.cpp:143:30:143:30 | h | 23 | 6 | 7 | 30 | 30 | | test.cpp:143:31:143:31 | a | 23 | 7 | 8 | 31 | 31 | +| test.cpp:146:24:146:35 | []a[:alpha:] | 23 | 0 | 12 | 24 | 35 | | test.cpp:146:24:146:36 | []a[:alpha:]] | 23 | 0 | 13 | 24 | 36 | | test.cpp:146:25:146:25 | ] | 23 | 1 | 2 | 25 | 25 | | test.cpp:146:26:146:26 | a | 23 | 2 | 3 | 26 | 26 | -| test.cpp:146:27:146:35 | [:alpha:] | 23 | 3 | 12 | 27 | 35 | +| test.cpp:146:27:146:27 | [ | 23 | 3 | 4 | 27 | 27 | +| test.cpp:146:28:146:28 | : | 23 | 4 | 5 | 28 | 28 | +| test.cpp:146:29:146:29 | a | 23 | 5 | 6 | 29 | 29 | +| test.cpp:146:30:146:30 | l | 23 | 6 | 7 | 30 | 30 | +| test.cpp:146:31:146:31 | p | 23 | 7 | 8 | 31 | 31 | +| test.cpp:146:32:146:32 | h | 23 | 8 | 9 | 32 | 32 | +| test.cpp:146:33:146:33 | a | 23 | 9 | 10 | 33 | 33 | +| test.cpp:146:34:146:34 | : | 23 | 10 | 11 | 34 | 34 | +| test.cpp:146:36:146:36 | ] | 23 | 12 | 13 | 36 | 36 | | test.cpp:149:25:149:53 | [[:alpha:][:digit:][:space:]] | 24 | 0 | 29 | 25 | 53 | | test.cpp:149:26:149:34 | [:alpha:] | 24 | 1 | 10 | 26 | 34 | | test.cpp:149:35:149:43 | [:digit:] | 24 | 10 | 19 | 35 | 43 | diff --git a/cpp/ql/test/library-tests/regex/parse.expected b/cpp/ql/test/library-tests/regex/parse.expected index 47307602ac8..b1ee43d1748 100644 --- a/cpp/ql/test/library-tests/regex/parse.expected +++ b/cpp/ql/test/library-tests/regex/parse.expected @@ -515,26 +515,81 @@ test.cpp: # 128| [RegExpConstant, RegExpNormalChar] f -# 131| [RegExpNamedCharacterProperty] [:digit:] +# 131| [RegExpCharacterClass] [:digit:] +#-----| 0 -> [RegExpConstant, RegExpNormalChar] : +#-----| 1 -> [RegExpConstant, RegExpNormalChar] d +#-----| 2 -> [RegExpConstant, RegExpNormalChar] i +#-----| 3 -> [RegExpConstant, RegExpNormalChar] g +#-----| 4 -> [RegExpConstant, RegExpNormalChar] i +#-----| 5 -> [RegExpConstant, RegExpNormalChar] t +#-----| 6 -> [RegExpConstant, RegExpNormalChar] : -# 134| [RegExpNamedCharacterProperty] [:alpha:] +# 131| [RegExpConstant, RegExpNormalChar] : + +# 131| [RegExpConstant, RegExpNormalChar] d + +# 131| [RegExpConstant, RegExpNormalChar] i + +# 131| [RegExpConstant, RegExpNormalChar] g + +# 131| [RegExpConstant, RegExpNormalChar] i + +# 131| [RegExpConstant, RegExpNormalChar] t + +# 131| [RegExpConstant, RegExpNormalChar] : + +# 134| [RegExpCharacterClass] [:alpha:] +#-----| 0 -> [RegExpConstant, RegExpNormalChar] : +#-----| 1 -> [RegExpConstant, RegExpNormalChar] a +#-----| 2 -> [RegExpConstant, RegExpNormalChar] l +#-----| 3 -> [RegExpConstant, RegExpNormalChar] p +#-----| 4 -> [RegExpConstant, RegExpNormalChar] h +#-----| 5 -> [RegExpConstant, RegExpNormalChar] a +#-----| 6 -> [RegExpConstant, RegExpNormalChar] : + +# 134| [RegExpConstant, RegExpNormalChar] : + +# 134| [RegExpConstant, RegExpNormalChar] a + +# 134| [RegExpConstant, RegExpNormalChar] l + +# 134| [RegExpConstant, RegExpNormalChar] p + +# 134| [RegExpConstant, RegExpNormalChar] h + +# 134| [RegExpConstant, RegExpNormalChar] a + +# 134| [RegExpConstant, RegExpNormalChar] : # 137| [RegExpConstant, RegExpNormalChar] a -# 137| [RegExpNamedCharacterProperty] [:b:] +# 137| [RegExpSequence] a[:b:]c +#-----| 0 -> [RegExpConstant, RegExpNormalChar] a +#-----| 1 -> [RegExpCharacterClass] [:b:] +#-----| 2 -> [RegExpConstant, RegExpNormalChar] c + +# 137| [RegExpCharacterClass] [:b:] +#-----| 0 -> [RegExpConstant, RegExpNormalChar] : +#-----| 1 -> [RegExpConstant, RegExpNormalChar] b +#-----| 2 -> [RegExpConstant, RegExpNormalChar] : + +# 137| [RegExpConstant, RegExpNormalChar] : + +# 137| [RegExpConstant, RegExpNormalChar] b + +# 137| [RegExpConstant, RegExpNormalChar] : # 137| [RegExpConstant, RegExpNormalChar] c # 140| [RegExpCharacterClass] [[:alpha:]-z] -#-----| 0 -> [RegExpNamedCharacterProperty] [:alpha:] +#-----| 0 -> [RegExpCharacterRange] [:alpha:]-z # 140| [RegExpNamedCharacterProperty] [:alpha:] -# 140| [RegExpCharacterRange] :]-z +# 140| [RegExpCharacterRange] [:alpha:]-z +#-----| 0 -> [RegExpNamedCharacterProperty] [:alpha:] #-----| 1 -> [RegExpConstant, RegExpNormalChar] z -# 140| [RegExpConstant, RegExpNormalChar] - - # 140| [RegExpConstant, RegExpNormalChar] z # 143| [RegExpCharacterClass] [[:alpha] @@ -560,16 +615,43 @@ test.cpp: # 143| [RegExpConstant, RegExpNormalChar] a -# 146| [RegExpCharacterClass] []a[:alpha:]] +# 146| [RegExpCharacterClass] []a[:alpha:] #-----| 0 -> [RegExpConstant, RegExpNormalChar] ] #-----| 1 -> [RegExpConstant, RegExpNormalChar] a -#-----| 2 -> [RegExpNamedCharacterProperty] [:alpha:] +#-----| 2 -> [RegExpConstant, RegExpNormalChar] [ +#-----| 3 -> [RegExpConstant, RegExpNormalChar] : +#-----| 4 -> [RegExpConstant, RegExpNormalChar] a +#-----| 5 -> [RegExpConstant, RegExpNormalChar] l +#-----| 6 -> [RegExpConstant, RegExpNormalChar] p +#-----| 7 -> [RegExpConstant, RegExpNormalChar] h +#-----| 8 -> [RegExpConstant, RegExpNormalChar] a +#-----| 9 -> [RegExpConstant, RegExpNormalChar] : + +# 146| [RegExpSequence] []a[:alpha:]] +#-----| 0 -> [RegExpCharacterClass] []a[:alpha:] +#-----| 1 -> [RegExpConstant, RegExpNormalChar] ] # 146| [RegExpConstant, RegExpNormalChar] ] # 146| [RegExpConstant, RegExpNormalChar] a -# 146| [RegExpNamedCharacterProperty] [:alpha:] +# 146| [RegExpConstant, RegExpNormalChar] [ + +# 146| [RegExpConstant, RegExpNormalChar] : + +# 146| [RegExpConstant, RegExpNormalChar] a + +# 146| [RegExpConstant, RegExpNormalChar] l + +# 146| [RegExpConstant, RegExpNormalChar] p + +# 146| [RegExpConstant, RegExpNormalChar] h + +# 146| [RegExpConstant, RegExpNormalChar] a + +# 146| [RegExpConstant, RegExpNormalChar] : + +# 146| [RegExpConstant, RegExpNormalChar] ] # 149| [RegExpCharacterClass] [[:alpha:][:digit:][:space:]] #-----| 0 -> [RegExpNamedCharacterProperty] [:alpha:] diff --git a/cpp/ql/test/library-tests/regex/regexp.expected b/cpp/ql/test/library-tests/regex/regexp.expected index e9a8bc0511d..cdb750c90fb 100644 --- a/cpp/ql/test/library-tests/regex/regexp.expected +++ b/cpp/ql/test/library-tests/regex/regexp.expected @@ -200,15 +200,32 @@ term | test.cpp:128:37:128:37 | a | RegExpConstant,RegExpNormalChar | | test.cpp:128:37:128:39 | a-f | RegExpCharacterRange | | test.cpp:128:39:128:39 | f | RegExpConstant,RegExpNormalChar | -| test.cpp:131:24:131:32 | [:digit:] | RegExpNamedCharacterProperty | -| test.cpp:134:24:134:32 | [:alpha:] | RegExpNamedCharacterProperty | +| test.cpp:131:24:131:32 | [:digit:] | RegExpCharacterClass | +| test.cpp:131:25:131:25 | : | RegExpConstant,RegExpNormalChar | +| test.cpp:131:26:131:26 | d | RegExpConstant,RegExpNormalChar | +| test.cpp:131:27:131:27 | i | RegExpConstant,RegExpNormalChar | +| test.cpp:131:28:131:28 | g | RegExpConstant,RegExpNormalChar | +| test.cpp:131:29:131:29 | i | RegExpConstant,RegExpNormalChar | +| test.cpp:131:30:131:30 | t | RegExpConstant,RegExpNormalChar | +| test.cpp:131:31:131:31 | : | RegExpConstant,RegExpNormalChar | +| test.cpp:134:24:134:32 | [:alpha:] | RegExpCharacterClass | +| test.cpp:134:25:134:25 | : | RegExpConstant,RegExpNormalChar | +| test.cpp:134:26:134:26 | a | RegExpConstant,RegExpNormalChar | +| test.cpp:134:27:134:27 | l | RegExpConstant,RegExpNormalChar | +| test.cpp:134:28:134:28 | p | RegExpConstant,RegExpNormalChar | +| test.cpp:134:29:134:29 | h | RegExpConstant,RegExpNormalChar | +| test.cpp:134:30:134:30 | a | RegExpConstant,RegExpNormalChar | +| test.cpp:134:31:134:31 | : | RegExpConstant,RegExpNormalChar | | test.cpp:137:24:137:24 | a | RegExpConstant,RegExpNormalChar | -| test.cpp:137:25:137:29 | [:b:] | RegExpNamedCharacterProperty | +| test.cpp:137:24:137:30 | a[:b:]c | RegExpSequence | +| test.cpp:137:25:137:29 | [:b:] | RegExpCharacterClass | +| test.cpp:137:26:137:26 | : | RegExpConstant,RegExpNormalChar | +| test.cpp:137:27:137:27 | b | RegExpConstant,RegExpNormalChar | +| test.cpp:137:28:137:28 | : | RegExpConstant,RegExpNormalChar | | test.cpp:137:30:137:30 | c | RegExpConstant,RegExpNormalChar | | test.cpp:140:24:140:36 | [[:alpha:]-z] | RegExpCharacterClass | | test.cpp:140:25:140:33 | [:alpha:] | RegExpNamedCharacterProperty | -| test.cpp:140:32:140:35 | :]-z | RegExpCharacterRange | -| test.cpp:140:34:140:34 | - | RegExpConstant,RegExpNormalChar | +| test.cpp:140:25:140:35 | [:alpha:]-z | RegExpCharacterRange | | test.cpp:140:35:140:35 | z | RegExpConstant,RegExpNormalChar | | test.cpp:143:24:143:32 | [[:alpha] | RegExpCharacterClass | | test.cpp:143:25:143:25 | [ | RegExpConstant,RegExpNormalChar | @@ -218,10 +235,19 @@ term | test.cpp:143:29:143:29 | p | RegExpConstant,RegExpNormalChar | | test.cpp:143:30:143:30 | h | RegExpConstant,RegExpNormalChar | | test.cpp:143:31:143:31 | a | RegExpConstant,RegExpNormalChar | -| test.cpp:146:24:146:36 | []a[:alpha:]] | RegExpCharacterClass | +| test.cpp:146:24:146:35 | []a[:alpha:] | RegExpCharacterClass | +| test.cpp:146:24:146:36 | []a[:alpha:]] | RegExpSequence | | test.cpp:146:25:146:25 | ] | RegExpConstant,RegExpNormalChar | | test.cpp:146:26:146:26 | a | RegExpConstant,RegExpNormalChar | -| test.cpp:146:27:146:35 | [:alpha:] | RegExpNamedCharacterProperty | +| test.cpp:146:27:146:27 | [ | RegExpConstant,RegExpNormalChar | +| test.cpp:146:28:146:28 | : | RegExpConstant,RegExpNormalChar | +| test.cpp:146:29:146:29 | a | RegExpConstant,RegExpNormalChar | +| test.cpp:146:30:146:30 | l | RegExpConstant,RegExpNormalChar | +| test.cpp:146:31:146:31 | p | RegExpConstant,RegExpNormalChar | +| test.cpp:146:32:146:32 | h | RegExpConstant,RegExpNormalChar | +| test.cpp:146:33:146:33 | a | RegExpConstant,RegExpNormalChar | +| test.cpp:146:34:146:34 | : | RegExpConstant,RegExpNormalChar | +| test.cpp:146:36:146:36 | ] | RegExpConstant,RegExpNormalChar | | test.cpp:149:25:149:53 | [[:alpha:][:digit:][:space:]] | RegExpCharacterClass | | test.cpp:149:26:149:34 | [:alpha:] | RegExpNamedCharacterProperty | | test.cpp:149:35:149:43 | [:digit:] | RegExpNamedCharacterProperty | @@ -377,9 +403,25 @@ regExpNormalCharValue | test.cpp:128:27:128:27 | F | F | | test.cpp:128:37:128:37 | a | a | | test.cpp:128:39:128:39 | f | f | +| test.cpp:131:25:131:25 | : | : | +| test.cpp:131:26:131:26 | d | d | +| test.cpp:131:27:131:27 | i | i | +| test.cpp:131:28:131:28 | g | g | +| test.cpp:131:29:131:29 | i | i | +| test.cpp:131:30:131:30 | t | t | +| test.cpp:131:31:131:31 | : | : | +| test.cpp:134:25:134:25 | : | : | +| test.cpp:134:26:134:26 | a | a | +| test.cpp:134:27:134:27 | l | l | +| test.cpp:134:28:134:28 | p | p | +| test.cpp:134:29:134:29 | h | h | +| test.cpp:134:30:134:30 | a | a | +| test.cpp:134:31:134:31 | : | : | | test.cpp:137:24:137:24 | a | a | +| test.cpp:137:26:137:26 | : | : | +| test.cpp:137:27:137:27 | b | b | +| test.cpp:137:28:137:28 | : | : | | test.cpp:137:30:137:30 | c | c | -| test.cpp:140:34:140:34 | - | - | | test.cpp:140:35:140:35 | z | z | | test.cpp:143:25:143:25 | [ | [ | | test.cpp:143:26:143:26 | : | : | @@ -390,6 +432,15 @@ regExpNormalCharValue | test.cpp:143:31:143:31 | a | a | | test.cpp:146:25:146:25 | ] | ] | | test.cpp:146:26:146:26 | a | a | +| test.cpp:146:27:146:27 | [ | [ | +| test.cpp:146:28:146:28 | : | : | +| test.cpp:146:29:146:29 | a | a | +| test.cpp:146:30:146:30 | l | l | +| test.cpp:146:31:146:31 | p | p | +| test.cpp:146:32:146:32 | h | h | +| test.cpp:146:33:146:33 | a | a | +| test.cpp:146:34:146:34 | : | : | +| test.cpp:146:36:146:36 | ] | ] | | test.cpp:163:21:163:26 | \\u9879 | \u9879 | | test.cpp:171:23:171:23 | a | a | | test.cpp:171:25:171:25 | b | b |