Skip to content

Commit c89ce06

Browse files
Merge branch 'main' into systemwebhttprequest-test-stubs
2 parents b267bd1 + 39ceada commit c89ce06

File tree

15 files changed

+925
-29
lines changed

15 files changed

+925
-29
lines changed

.gitignore

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,3 +76,6 @@ node_modules/
7676
# some upgrade/downgrade checks create these files
7777
**/upgrades/*/*.dbscheme.stats
7878
**/downgrades/*/*.dbscheme.stats
79+
80+
# Mergetool files
81+
*.orig

python/ql/lib/semmle/python/regexp/RegexTreeView.qll

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -964,7 +964,7 @@ module Impl implements RegexTreeViewSig {
964964
* ```
965965
*/
966966
class RegExpPositiveLookahead extends RegExpLookahead {
967-
RegExpPositiveLookahead() { re.positiveLookaheadAssertionGroup(start, end) }
967+
RegExpPositiveLookahead() { re.positiveLookaheadAssertionGroup(start, end, _, _) }
968968

969969
override string getPrimaryQLClass() { result = "RegExpPositiveLookahead" }
970970
}
@@ -979,7 +979,7 @@ module Impl implements RegexTreeViewSig {
979979
* ```
980980
*/
981981
additional class RegExpNegativeLookahead extends RegExpLookahead {
982-
RegExpNegativeLookahead() { re.negativeLookaheadAssertionGroup(start, end) }
982+
RegExpNegativeLookahead() { re.negativeLookaheadAssertionGroup(start, end, _, _) }
983983

984984
override string getPrimaryQLClass() { result = "RegExpNegativeLookahead" }
985985
}
@@ -1006,7 +1006,7 @@ module Impl implements RegexTreeViewSig {
10061006
* ```
10071007
*/
10081008
class RegExpPositiveLookbehind extends RegExpLookbehind {
1009-
RegExpPositiveLookbehind() { re.positiveLookbehindAssertionGroup(start, end) }
1009+
RegExpPositiveLookbehind() { re.positiveLookbehindAssertionGroup(start, end, _, _) }
10101010

10111011
override string getPrimaryQLClass() { result = "RegExpPositiveLookbehind" }
10121012
}
@@ -1021,7 +1021,7 @@ module Impl implements RegexTreeViewSig {
10211021
* ```
10221022
*/
10231023
additional class RegExpNegativeLookbehind extends RegExpLookbehind {
1024-
RegExpNegativeLookbehind() { re.negativeLookbehindAssertionGroup(start, end) }
1024+
RegExpNegativeLookbehind() { re.negativeLookbehindAssertionGroup(start, end, _, _) }
10251025

10261026
override string getPrimaryQLClass() { result = "RegExpNegativeLookbehind" }
10271027
}

python/ql/lib/semmle/python/regexp/internal/ParseRegExp.qll

Lines changed: 46 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -554,9 +554,9 @@ class RegExp extends Expr instanceof StringLiteral {
554554
or
555555
this.negativeAssertionGroup(start, end)
556556
or
557-
this.positiveLookaheadAssertionGroup(start, end)
557+
this.positiveLookaheadAssertionGroup(start, end, _, _)
558558
or
559-
this.positiveLookbehindAssertionGroup(start, end)
559+
this.positiveLookbehindAssertionGroup(start, end, _, _)
560560
}
561561

562562
/** Holds if an empty group is found between `start` and `end`. */
@@ -572,15 +572,15 @@ class RegExp extends Expr instanceof StringLiteral {
572572
or
573573
this.negativeAssertionGroup(start, end)
574574
or
575-
this.positiveLookaheadAssertionGroup(start, end)
575+
this.positiveLookaheadAssertionGroup(start, end, _, _)
576576
}
577577

578578
private predicate emptyMatchAtEndGroup(int start, int end) {
579579
this.emptyGroup(start, end)
580580
or
581581
this.negativeAssertionGroup(start, end)
582582
or
583-
this.positiveLookbehindAssertionGroup(start, end)
583+
this.positiveLookbehindAssertionGroup(start, end, _, _)
584584
}
585585

586586
private predicate negativeAssertionGroup(int start, int end) {
@@ -593,32 +593,40 @@ class RegExp extends Expr instanceof StringLiteral {
593593
)
594594
}
595595

596-
/** Holds if a negative lookahead is found between `start` and `end` */
597-
predicate negativeLookaheadAssertionGroup(int start, int end) {
598-
exists(int in_start | this.negative_lookahead_assertion_start(start, in_start) |
599-
this.groupContents(start, end, in_start, _)
600-
)
596+
/**
597+
* Holds if a negative lookahead is found between `start` and `end`, with contents
598+
* between `in_start` and `in_end`.
599+
*/
600+
predicate negativeLookaheadAssertionGroup(int start, int end, int in_start, int in_end) {
601+
this.negative_lookahead_assertion_start(start, in_start) and
602+
this.groupContents(start, end, in_start, in_end)
601603
}
602604

603-
/** Holds if a negative lookbehind is found between `start` and `end` */
604-
predicate negativeLookbehindAssertionGroup(int start, int end) {
605-
exists(int in_start | this.negative_lookbehind_assertion_start(start, in_start) |
606-
this.groupContents(start, end, in_start, _)
607-
)
605+
/**
606+
* Holds if a negative lookbehind is found between `start` and `end`, with contents
607+
* between `in_start` and `in_end`.
608+
*/
609+
predicate negativeLookbehindAssertionGroup(int start, int end, int in_start, int in_end) {
610+
this.negative_lookbehind_assertion_start(start, in_start) and
611+
this.groupContents(start, end, in_start, in_end)
608612
}
609613

610-
/** Holds if a positive lookahead is found between `start` and `end` */
611-
predicate positiveLookaheadAssertionGroup(int start, int end) {
612-
exists(int in_start | this.lookahead_assertion_start(start, in_start) |
613-
this.groupContents(start, end, in_start, _)
614-
)
614+
/**
615+
* Holds if a positive lookahead is found between `start` and `end`, with contents
616+
* between `in_start` and `in_end`.
617+
*/
618+
predicate positiveLookaheadAssertionGroup(int start, int end, int in_start, int in_end) {
619+
this.lookahead_assertion_start(start, in_start) and
620+
this.groupContents(start, end, in_start, in_end)
615621
}
616622

617-
/** Holds if a positive lookbehind is found between `start` and `end` */
618-
predicate positiveLookbehindAssertionGroup(int start, int end) {
619-
exists(int in_start | this.lookbehind_assertion_start(start, in_start) |
620-
this.groupContents(start, end, in_start, _)
621-
)
623+
/**
624+
* Holds if a positive lookbehind is found between `start` and `end`, with contents
625+
* between `in_start` and `in_end`.
626+
*/
627+
predicate positiveLookbehindAssertionGroup(int start, int end, int in_start, int in_end) {
628+
this.lookbehind_assertion_start(start, in_start) and
629+
this.groupContents(start, end, in_start, in_end)
622630
}
623631

624632
private predicate group_start(int start, int end) {
@@ -1049,6 +1057,13 @@ class RegExp extends Expr instanceof StringLiteral {
10491057
or
10501058
this.alternationOption(x, y, start, end)
10511059
)
1060+
or
1061+
// Lookbehind assertions can potentially match the start of the string
1062+
(
1063+
this.positiveLookbehindAssertionGroup(_, _, start, _) or
1064+
this.negativeLookbehindAssertionGroup(_, _, start, _)
1065+
) and
1066+
this.item(start, end)
10521067
}
10531068

10541069
/** A part of the regex that may match the end of the string. */
@@ -1074,6 +1089,13 @@ class RegExp extends Expr instanceof StringLiteral {
10741089
or
10751090
this.alternationOption(x, y, start, end)
10761091
)
1092+
or
1093+
// Lookahead assertions can potentially match the end of the string
1094+
(
1095+
this.positiveLookaheadAssertionGroup(_, _, _, end) or
1096+
this.negativeLookaheadAssertionGroup(_, _, _, end)
1097+
) and
1098+
this.item(start, end)
10771099
}
10781100

10791101
/**
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
category: minorAnalysis
3+
---
4+
5+
- The queries that check for unmatchable `$` and `^` in regular expressions did not account correctly for occurrences inside lookahead and lookbehind assertions. These occurrences are now handled correctly, eliminating this source of false positives.

python/ql/test/library-tests/regex/FirstLast.expected

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
| (?!not-this)^[A-Z_]+$ | first | 12 | 13 |
55
| (?!not-this)^[A-Z_]+$ | first | 13 | 19 |
66
| (?!not-this)^[A-Z_]+$ | first | 13 | 20 |
7+
| (?!not-this)^[A-Z_]+$ | last | 3 | 11 |
78
| (?!not-this)^[A-Z_]+$ | last | 13 | 19 |
89
| (?!not-this)^[A-Z_]+$ | last | 13 | 20 |
910
| (?!not-this)^[A-Z_]+$ | last | 20 | 21 |
@@ -101,6 +102,7 @@
101102
| ^[A-Z_]+$(?<!not-this) | first | 0 | 1 |
102103
| ^[A-Z_]+$(?<!not-this) | first | 1 | 7 |
103104
| ^[A-Z_]+$(?<!not-this) | first | 1 | 8 |
105+
| ^[A-Z_]+$(?<!not-this) | first | 13 | 21 |
104106
| ^[A-Z_]+$(?<!not-this) | last | 1 | 7 |
105107
| ^[A-Z_]+$(?<!not-this) | last | 1 | 8 |
106108
| ^[A-Z_]+$(?<!not-this) | last | 8 | 9 |

python/ql/test/query-tests/Expressions/Regex/test.py

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -150,4 +150,12 @@
150150
re.compile(r"[\u0000-\uFFFF]")
151151

152152
#Allow unicode names
153-
re.compile(r"[\N{degree sign}\N{EM DASH}]")
153+
re.compile(r"[\N{degree sign}\N{EM DASH}]")
154+
155+
#Lookahead assertions. None of these are unmatchable dollars:
156+
re.compile(r"^(?=a$)[ab]")
157+
re.compile(r"^(?!a$)[ab]")
158+
159+
#Lookbehind assertions. None of these are unmatchable carets:
160+
re.compile(r"(?<=^a)a")
161+
re.compile(r"(?<!^a)a")
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
---
2+
category: feature
3+
---
4+
* Initial modeling for the Ruby Grape framework in `Grape.qll` has been added to detect API endpoints, parameters, and headers within Grape API classes.

ruby/ql/lib/codeql/ruby/Frameworks.qll

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ private import codeql.ruby.frameworks.Rails
2121
private import codeql.ruby.frameworks.Railties
2222
private import codeql.ruby.frameworks.Stdlib
2323
private import codeql.ruby.frameworks.Files
24+
private import codeql.ruby.frameworks.Grape
2425
private import codeql.ruby.frameworks.HttpClients
2526
private import codeql.ruby.frameworks.XmlParsing
2627
private import codeql.ruby.frameworks.ActionDispatch

0 commit comments

Comments
 (0)