diff --git a/config/identical-files.json b/config/identical-files.json index 582e4c7b6dc3..b3be90b75b22 100644 --- a/config/identical-files.json +++ b/config/identical-files.json @@ -448,5 +448,17 @@ "SensitiveDataHeuristics Python/JS": [ "javascript/ql/src/semmle/javascript/security/internal/SensitiveDataHeuristics.qll", "python/ql/src/semmle/python/security/internal/SensitiveDataHeuristics.qll" + ], + "ReDoS Util Python/JS": [ + "javascript/ql/src/semmle/javascript/security/performance/ReDoSUtil.qll", + "python/ql/src/semmle/python/regex/ReDoSUtil.qll" + ], + "ReDoS Exponential Python/JS": [ + "javascript/ql/src/semmle/javascript/security/performance/ExponentialBackTracking.qll", + "python/ql/src/semmle/python/regex/ExponentialBackTracking.qll" + ], + "ReDoS Polynomial Python/JS": [ + "javascript/ql/src/semmle/javascript/security/performance/SuperlinearBackTracking.qll", + "python/ql/src/semmle/python/regex/SuperlinearBackTracking.qll" ] } diff --git a/javascript/ql/src/Performance/ReDoS.ql b/javascript/ql/src/Performance/ReDoS.ql index 8d33e7bc5076..76e0b23dee93 100644 --- a/javascript/ql/src/Performance/ReDoS.ql +++ b/javascript/ql/src/Performance/ReDoS.ql @@ -14,328 +14,7 @@ import javascript import semmle.javascript.security.performance.ReDoSUtil - -/* - * This query implements the analysis described in the following two papers: - * - * James Kirrage, Asiri Rathnayake, Hayo Thielecke: Static Analysis for - * Regular Expression Denial-of-Service Attacks. NSS 2013. - * (http://www.cs.bham.ac.uk/~hxt/research/reg-exp-sec.pdf) - * Asiri Rathnayake, Hayo Thielecke: Static Analysis for Regular Expression - * Exponential Runtime via Substructural Logics. 2014. - * (https://www.cs.bham.ac.uk/~hxt/research/redos_full.pdf) - * - * The basic idea is to search for overlapping cycles in the NFA, that is, - * states `q` such that there are two distinct paths from `q` to itself - * that consume the same word `w`. - * - * For any such state `q`, an attack string can be constructed as follows: - * concatenate a prefix `v` that takes the NFA to `q` with `n` copies of - * the word `w` that leads back to `q` along two different paths, followed - * by a suffix `x` that is _not_ accepted in state `q`. A backtracking - * implementation will need to explore at least 2^n different ways of going - * from `q` back to itself while trying to match the `n` copies of `w` - * before finally giving up. - * - * Now in order to identify overlapping cycles, all we have to do is find - * pumpable forks, that is, states `q` that can transition to two different - * states `r1` and `r2` on the same input symbol `c`, such that there are - * paths from both `r1` and `r2` to `q` that consume the same word. The latter - * condition is equivalent to saying that `(q, q)` is reachable from `(r1, r2)` - * in the product NFA. - * - * This is what the query does. It makes a simple attempt to construct a - * prefix `v` leading into `q`, but only to improve the alert message. - * And the query tries to prove the existence of a suffix that ensures - * rejection. This check might fail, which can cause false positives. - * - * Finally, sometimes it depends on the translation whether the NFA generated - * for a regular expression has a pumpable fork or not. We implement one - * particular translation, which may result in false positives or negatives - * relative to some particular JavaScript engine. - * - * More precisely, the query constructs an NFA from a regular expression `r` - * as follows: - * - * * Every sub-term `t` gives rise to an NFA state `Match(t,i)`, representing - * the state of the automaton before attempting to match the `i`th character in `t`. - * * There is one accepting state `Accept(r)`. - * * There is a special `AcceptAnySuffix(r)` state, which accepts any suffix string - * by using an epsilon transition to `Accept(r)` and an any transition to itself. - * * Transitions between states may be labelled with epsilon, or an abstract - * input symbol. - * * Each abstract input symbol represents a set of concrete input characters: - * either a single character, a set of characters represented by a - * character class, or the set of all characters. - * * The product automaton is constructed lazily, starting with pair states - * `(q, q)` where `q` is a fork, and proceding along an over-approximate - * step relation. - * * The over-approximate step relation allows transitions along pairs of - * abstract input symbols where the symbols have overlap in the characters they accept. - * * Once a trace of pairs of abstract input symbols that leads from a fork - * back to itself has been identified, we attempt to construct a concrete - * string corresponding to it, which may fail. - * * Lastly we ensure that any state reached by repeating `n` copies of `w` has - * a suffix `x` (possible empty) that is most likely __not__ accepted. - */ - -/** - * Holds if state `s` might be inside a backtracking repetition. - */ -pragma[noinline] -predicate stateInsideBacktracking(State s) { - s.getRepr().getParent*() instanceof MaybeBacktrackingRepetition -} - -/** - * A infinitely repeating quantifier that might backtrack. - */ -class MaybeBacktrackingRepetition extends InfiniteRepetitionQuantifier { - MaybeBacktrackingRepetition() { - exists(RegExpTerm child | - child instanceof RegExpAlt or - child instanceof RegExpQuantifier - | - child.getParent+() = this - ) - } -} - -/** - * A state in the product automaton. - * - * We lazily only construct those states that we are actually - * going to need: `(q, q)` for every fork state `q`, and any - * pair of states that can be reached from a pair that we have - * already constructed. To cut down on the number of states, - * we only represent states `(q1, q2)` where `q1` is lexicographically - * no bigger than `q2`. - * - * States are only constructed if both states in the pair are - * inside a repetition that might backtrack. - */ -newtype TStatePair = - MkStatePair(State q1, State q2) { - isFork(q1, _, _, _, _) and q2 = q1 - or - (step(_, _, _, q1, q2) or step(_, _, _, q2, q1)) and - rankState(q1) <= rankState(q2) - } - -/** - * Gets a unique number for a `state`. - * Is used to create an ordering of states, where states with the same `toString()` will be ordered differently. - */ -int rankState(State state) { - state = - rank[result](State s, Location l | - l = s.getRepr().getLocation() - | - s order by l.getStartLine(), l.getStartColumn(), s.toString() - ) -} - -class StatePair extends TStatePair { - State q1; - State q2; - - StatePair() { this = MkStatePair(q1, q2) } - - string toString() { result = "(" + q1 + ", " + q2 + ")" } - - State getLeft() { result = q1 } - - State getRight() { result = q2 } -} - -predicate isStatePair(StatePair p) { any() } - -predicate delta2(StatePair q, StatePair r) { step(q, _, _, r) } - -/** - * Gets the minimum length of a path from `q` to `r` in the - * product automaton. - */ -int statePairDist(StatePair q, StatePair r) = - shortestDistances(isStatePair/1, delta2/2)(q, r, result) - -/** - * Holds if there are transitions from `q` to `r1` and from `q` to `r2` - * labelled with `s1` and `s2`, respectively, where `s1` and `s2` do not - * trivially have an empty intersection. - * - * This predicate only holds for states associated with regular expressions - * that have at least one repetition quantifier in them (otherwise the - * expression cannot be vulnerable to ReDoS attacks anyway). - */ -pragma[noopt] -predicate isFork(State q, InputSymbol s1, InputSymbol s2, State r1, State r2) { - stateInsideBacktracking(q) and - exists(State q1, State q2 | - q1 = epsilonSucc*(q) and - delta(q1, s1, r1) and - q2 = epsilonSucc*(q) and - delta(q2, s2, r2) and - // Use pragma[noopt] to prevent intersect(s1,s2) from being the starting point of the join. - // From (s1,s2) it would find a huge number of intermediate state pairs (q1,q2) originating from different literals, - // and discover at the end that no `q` can reach both `q1` and `q2` by epsilon transitions. - exists(intersect(s1, s2)) - | - s1 != s2 - or - r1 != r2 - or - r1 = r2 and q1 != q2 - or - // If q can reach itself by epsilon transitions, then there are two distinct paths to the q1/q2 state: - // one that uses the loop and one that doesn't. The engine will separately attempt to match with each path, - // despite ending in the same state. The "fork" thus arises from the choice of whether to use the loop or not. - // To avoid every state in the loop becoming a fork state, - // we arbitrarily pick the InfiniteRepetitionQuantifier state as the canonical fork state for the loop - // (every epsilon-loop must contain such a state). - // - // We additionally require that the there exists another InfiniteRepetitionQuantifier `mid` on the path from `q` to itself. - // This is done to avoid flagging regular expressions such as `/(a?)*b/` - that only has polynomial runtime, and is detected by `js/polynomial-redos`. - // The below code is therefore a heuritic, that only flags regular expressions such as `/(a*)*b/`, - // and does not flag regular expressions such as `/(a?b?)c/`, but the latter pattern is not used frequently. - r1 = r2 and - q1 = q2 and - epsilonSucc+(q) = q and - exists(RegExpTerm term | term = q.getRepr() | term instanceof InfiniteRepetitionQuantifier) and - // One of the mid states is an infinite quantifier itself - exists(State mid, RegExpTerm term | - mid = epsilonSucc+(q) and - term = mid.getRepr() and - term instanceof InfiniteRepetitionQuantifier and - q = epsilonSucc+(mid) and - not mid = q - ) - ) and - stateInsideBacktracking(r1) and - stateInsideBacktracking(r2) -} - -/** - * Gets the state pair `(q1, q2)` or `(q2, q1)`; note that only - * one or the other is defined. - */ -StatePair mkStatePair(State q1, State q2) { - result = MkStatePair(q1, q2) or result = MkStatePair(q2, q1) -} - -/** - * Holds if there are transitions from the components of `q` to the corresponding - * components of `r` labelled with `s1` and `s2`, respectively. - */ -predicate step(StatePair q, InputSymbol s1, InputSymbol s2, StatePair r) { - exists(State r1, State r2 | step(q, s1, s2, r1, r2) and r = mkStatePair(r1, r2)) -} - -/** - * Holds if there are transitions from the components of `q` to `r1` and `r2` - * labelled with `s1` and `s2`, respectively. - * - * We only consider transitions where the resulting states `(r1, r2)` are both - * inside a repetition that might backtrack. - */ -pragma[noopt] -predicate step(StatePair q, InputSymbol s1, InputSymbol s2, State r1, State r2) { - exists(State q1, State q2 | q.getLeft() = q1 and q.getRight() = q2 | - deltaClosed(q1, s1, r1) and - deltaClosed(q2, s2, r2) and - // use noopt to force the join on `intersect` to happen last. - exists(intersect(s1, s2)) - ) and - stateInsideBacktracking(r1) and - stateInsideBacktracking(r2) -} - -private newtype TTrace = - Nil() or - Step(InputSymbol s1, InputSymbol s2, TTrace t) { - exists(StatePair p | - isReachableFromFork(_, p, t, _) and - step(p, s1, s2, _) - ) - or - t = Nil() and isFork(_, s1, s2, _, _) - } - -/** - * A list of pairs of input symbols that describe a path in the product automaton - * starting from some fork state. - */ -class Trace extends TTrace { - string toString() { - this = Nil() and result = "Nil()" - or - exists(InputSymbol s1, InputSymbol s2, Trace t | this = Step(s1, s2, t) | - result = "Step(" + s1 + ", " + s2 + ", " + t + ")" - ) - } -} - -/** - * Gets a string corresponding to the trace `t`. - */ -string concretise(Trace t) { - t = Nil() and result = "" - or - exists(InputSymbol s1, InputSymbol s2, Trace rest | t = Step(s1, s2, rest) | - result = concretise(rest) + intersect(s1, s2) - ) -} - -/** - * Holds if `r` is reachable from `(fork, fork)` under input `w`, and there is - * a path from `r` back to `(fork, fork)` with `rem` steps. - */ -predicate isReachableFromFork(State fork, StatePair r, Trace w, int rem) { - // base case - exists(InputSymbol s1, InputSymbol s2, State q1, State q2 | - isFork(fork, s1, s2, q1, q2) and - r = MkStatePair(q1, q2) and - w = Step(s1, s2, Nil()) and - rem = statePairDist(r, MkStatePair(fork, fork)) - ) - or - // recursive case - exists(StatePair p, Trace v, InputSymbol s1, InputSymbol s2 | - isReachableFromFork(fork, p, v, rem + 1) and - step(p, s1, s2, r) and - w = Step(s1, s2, v) and - rem >= statePairDist(r, MkStatePair(fork, fork)) - ) -} - -/** - * Gets a state in the product automaton from which `(fork, fork)` is - * reachable in zero or more epsilon transitions. - */ -StatePair getAForkPair(State fork) { - isFork(fork, _, _, _, _) and - result = MkStatePair(epsilonPred*(fork), epsilonPred*(fork)) -} - -/** - * Holds if `fork` is a pumpable fork with word `w`. - */ -predicate isPumpable(State fork, string w) { - exists(StatePair q, Trace t | - isReachableFromFork(fork, q, t, _) and - q = getAForkPair(fork) and - w = concretise(t) - ) -} - -/** - * An instantiation of `ReDoSConfiguration` for exponential backtracking. - */ -class ExponentialReDoSConfiguration extends ReDoSConfiguration { - ExponentialReDoSConfiguration() { this = "ExponentialReDoSConfiguration" } - - override predicate isReDoSCandidate(State state, string pump) { isPumpable(state, pump) } -} +import semmle.javascript.security.performance.ExponentialBackTracking from RegExpTerm t, string pump, State s, string prefixMsg where hasReDoSResult(t, pump, s, prefixMsg) diff --git a/javascript/ql/src/semmle/javascript/security/performance/ExponentialBackTracking.qll b/javascript/ql/src/semmle/javascript/security/performance/ExponentialBackTracking.qll new file mode 100644 index 000000000000..61707b1f392f --- /dev/null +++ b/javascript/ql/src/semmle/javascript/security/performance/ExponentialBackTracking.qll @@ -0,0 +1,342 @@ +/** + * This library implements the analysis described in the following two papers: + * + * James Kirrage, Asiri Rathnayake, Hayo Thielecke: Static Analysis for + * Regular Expression Denial-of-Service Attacks. NSS 2013. + * (http://www.cs.bham.ac.uk/~hxt/research/reg-exp-sec.pdf) + * Asiri Rathnayake, Hayo Thielecke: Static Analysis for Regular Expression + * Exponential Runtime via Substructural Logics. 2014. + * (https://www.cs.bham.ac.uk/~hxt/research/redos_full.pdf) + * + * The basic idea is to search for overlapping cycles in the NFA, that is, + * states `q` such that there are two distinct paths from `q` to itself + * that consume the same word `w`. + * + * For any such state `q`, an attack string can be constructed as follows: + * concatenate a prefix `v` that takes the NFA to `q` with `n` copies of + * the word `w` that leads back to `q` along two different paths, followed + * by a suffix `x` that is _not_ accepted in state `q`. A backtracking + * implementation will need to explore at least 2^n different ways of going + * from `q` back to itself while trying to match the `n` copies of `w` + * before finally giving up. + * + * Now in order to identify overlapping cycles, all we have to do is find + * pumpable forks, that is, states `q` that can transition to two different + * states `r1` and `r2` on the same input symbol `c`, such that there are + * paths from both `r1` and `r2` to `q` that consume the same word. The latter + * condition is equivalent to saying that `(q, q)` is reachable from `(r1, r2)` + * in the product NFA. + * + * This is what the library does. It makes a simple attempt to construct a + * prefix `v` leading into `q`, but only to improve the alert message. + * And the library tries to prove the existence of a suffix that ensures + * rejection. This check might fail, which can cause false positives. + * + * Finally, sometimes it depends on the translation whether the NFA generated + * for a regular expression has a pumpable fork or not. We implement one + * particular translation, which may result in false positives or negatives + * relative to some particular JavaScript engine. + * + * More precisely, the library constructs an NFA from a regular expression `r` + * as follows: + * + * * Every sub-term `t` gives rise to an NFA state `Match(t,i)`, representing + * the state of the automaton before attempting to match the `i`th character in `t`. + * * There is one accepting state `Accept(r)`. + * * There is a special `AcceptAnySuffix(r)` state, which accepts any suffix string + * by using an epsilon transition to `Accept(r)` and an any transition to itself. + * * Transitions between states may be labelled with epsilon, or an abstract + * input symbol. + * * Each abstract input symbol represents a set of concrete input characters: + * either a single character, a set of characters represented by a + * character class, or the set of all characters. + * * The product automaton is constructed lazily, starting with pair states + * `(q, q)` where `q` is a fork, and proceding along an over-approximate + * step relation. + * * The over-approximate step relation allows transitions along pairs of + * abstract input symbols where the symbols have overlap in the characters they accept. + * * Once a trace of pairs of abstract input symbols that leads from a fork + * back to itself has been identified, we attempt to construct a concrete + * string corresponding to it, which may fail. + * * Lastly we ensure that any state reached by repeating `n` copies of `w` has + * a suffix `x` (possible empty) that is most likely __not__ accepted. + */ + +import ReDoSUtil + +/** + * Holds if state `s` might be inside a backtracking repetition. + */ +pragma[noinline] +predicate stateInsideBacktracking(State s) { + s.getRepr().getParent*() instanceof MaybeBacktrackingRepetition +} + +/** + * A infinitely repeating quantifier that might backtrack. + */ +class MaybeBacktrackingRepetition extends InfiniteRepetitionQuantifier { + MaybeBacktrackingRepetition() { + exists(RegExpTerm child | + child instanceof RegExpAlt or + child instanceof RegExpQuantifier + | + child.getParent+() = this + ) + } +} + +/** + * A state in the product automaton. + */ +newtype TStatePair = + /** + * We lazily only construct those states that we are actually + * going to need: `(q, q)` for every fork state `q`, and any + * pair of states that can be reached from a pair that we have + * already constructed. To cut down on the number of states, + * we only represent states `(q1, q2)` where `q1` is lexicographically + * no bigger than `q2`. + * + * States are only constructed if both states in the pair are + * inside a repetition that might backtrack. + */ + MkStatePair(State q1, State q2) { + isFork(q1, _, _, _, _) and q2 = q1 + or + (step(_, _, _, q1, q2) or step(_, _, _, q2, q1)) and + rankState(q1) <= rankState(q2) + } + +/** + * Gets a unique number for a `state`. + * Is used to create an ordering of states, where states with the same `toString()` will be ordered differently. + */ +int rankState(State state) { + state = + rank[result](State s, Location l | + l = s.getRepr().getLocation() + | + s order by l.getStartLine(), l.getStartColumn(), s.toString() + ) +} + +/** + * A state in the product automaton. + */ +class StatePair extends TStatePair { + State q1; + State q2; + + StatePair() { this = MkStatePair(q1, q2) } + + /** Gets a textual representation of this element. */ + string toString() { result = "(" + q1 + ", " + q2 + ")" } + + /** Gets the first component of the state pair. */ + State getLeft() { result = q1 } + + /** Gets the second component of the state pair. */ + State getRight() { result = q2 } +} + +/** + * Holds for all constructed state pairs. + * + * Used in `statePairDist` + */ +predicate isStatePair(StatePair p) { any() } + +/** + * Holds if there are transitions from the components of `q` to the corresponding + * components of `r`. + * + * Used in `statePairDist` + */ +predicate delta2(StatePair q, StatePair r) { step(q, _, _, r) } + +/** + * Gets the minimum length of a path from `q` to `r` in the + * product automaton. + */ +int statePairDist(StatePair q, StatePair r) = + shortestDistances(isStatePair/1, delta2/2)(q, r, result) + +/** + * Holds if there are transitions from `q` to `r1` and from `q` to `r2` + * labelled with `s1` and `s2`, respectively, where `s1` and `s2` do not + * trivially have an empty intersection. + * + * This predicate only holds for states associated with regular expressions + * that have at least one repetition quantifier in them (otherwise the + * expression cannot be vulnerable to ReDoS attacks anyway). + */ +pragma[noopt] +predicate isFork(State q, InputSymbol s1, InputSymbol s2, State r1, State r2) { + stateInsideBacktracking(q) and + exists(State q1, State q2 | + q1 = epsilonSucc*(q) and + delta(q1, s1, r1) and + q2 = epsilonSucc*(q) and + delta(q2, s2, r2) and + // Use pragma[noopt] to prevent intersect(s1,s2) from being the starting point of the join. + // From (s1,s2) it would find a huge number of intermediate state pairs (q1,q2) originating from different literals, + // and discover at the end that no `q` can reach both `q1` and `q2` by epsilon transitions. + exists(intersect(s1, s2)) + | + s1 != s2 + or + r1 != r2 + or + r1 = r2 and q1 != q2 + or + // If q can reach itself by epsilon transitions, then there are two distinct paths to the q1/q2 state: + // one that uses the loop and one that doesn't. The engine will separately attempt to match with each path, + // despite ending in the same state. The "fork" thus arises from the choice of whether to use the loop or not. + // To avoid every state in the loop becoming a fork state, + // we arbitrarily pick the InfiniteRepetitionQuantifier state as the canonical fork state for the loop + // (every epsilon-loop must contain such a state). + // + // We additionally require that the there exists another InfiniteRepetitionQuantifier `mid` on the path from `q` to itself. + // This is done to avoid flagging regular expressions such as `/(a?)*b/` - that only has polynomial runtime, and is detected by `js/polynomial-redos`. + // The below code is therefore a heuritic, that only flags regular expressions such as `/(a*)*b/`, + // and does not flag regular expressions such as `/(a?b?)c/`, but the latter pattern is not used frequently. + r1 = r2 and + q1 = q2 and + epsilonSucc+(q) = q and + exists(RegExpTerm term | term = q.getRepr() | term instanceof InfiniteRepetitionQuantifier) and + // One of the mid states is an infinite quantifier itself + exists(State mid, RegExpTerm term | + mid = epsilonSucc+(q) and + term = mid.getRepr() and + term instanceof InfiniteRepetitionQuantifier and + q = epsilonSucc+(mid) and + not mid = q + ) + ) and + stateInsideBacktracking(r1) and + stateInsideBacktracking(r2) +} + +/** + * Gets the state pair `(q1, q2)` or `(q2, q1)`; note that only + * one or the other is defined. + */ +StatePair mkStatePair(State q1, State q2) { + result = MkStatePair(q1, q2) or result = MkStatePair(q2, q1) +} + +/** + * Holds if there are transitions from the components of `q` to the corresponding + * components of `r` labelled with `s1` and `s2`, respectively. + */ +predicate step(StatePair q, InputSymbol s1, InputSymbol s2, StatePair r) { + exists(State r1, State r2 | step(q, s1, s2, r1, r2) and r = mkStatePair(r1, r2)) +} + +/** + * Holds if there are transitions from the components of `q` to `r1` and `r2` + * labelled with `s1` and `s2`, respectively. + * + * We only consider transitions where the resulting states `(r1, r2)` are both + * inside a repetition that might backtrack. + */ +pragma[noopt] +predicate step(StatePair q, InputSymbol s1, InputSymbol s2, State r1, State r2) { + exists(State q1, State q2 | q.getLeft() = q1 and q.getRight() = q2 | + deltaClosed(q1, s1, r1) and + deltaClosed(q2, s2, r2) and + // use noopt to force the join on `intersect` to happen last. + exists(intersect(s1, s2)) + ) and + stateInsideBacktracking(r1) and + stateInsideBacktracking(r2) +} + +private newtype TTrace = + Nil() or + Step(InputSymbol s1, InputSymbol s2, TTrace t) { + exists(StatePair p | + isReachableFromFork(_, p, t, _) and + step(p, s1, s2, _) + ) + or + t = Nil() and isFork(_, s1, s2, _, _) + } + +/** + * A list of pairs of input symbols that describe a path in the product automaton + * starting from some fork state. + */ +class Trace extends TTrace { + /** Gets a textual representation of this element. */ + string toString() { + this = Nil() and result = "Nil()" + or + exists(InputSymbol s1, InputSymbol s2, Trace t | this = Step(s1, s2, t) | + result = "Step(" + s1 + ", " + s2 + ", " + t + ")" + ) + } +} + +/** + * Gets a string corresponding to the trace `t`. + */ +string concretise(Trace t) { + t = Nil() and result = "" + or + exists(InputSymbol s1, InputSymbol s2, Trace rest | t = Step(s1, s2, rest) | + result = concretise(rest) + intersect(s1, s2) + ) +} + +/** + * Holds if `r` is reachable from `(fork, fork)` under input `w`, and there is + * a path from `r` back to `(fork, fork)` with `rem` steps. + */ +predicate isReachableFromFork(State fork, StatePair r, Trace w, int rem) { + // base case + exists(InputSymbol s1, InputSymbol s2, State q1, State q2 | + isFork(fork, s1, s2, q1, q2) and + r = MkStatePair(q1, q2) and + w = Step(s1, s2, Nil()) and + rem = statePairDist(r, MkStatePair(fork, fork)) + ) + or + // recursive case + exists(StatePair p, Trace v, InputSymbol s1, InputSymbol s2 | + isReachableFromFork(fork, p, v, rem + 1) and + step(p, s1, s2, r) and + w = Step(s1, s2, v) and + rem >= statePairDist(r, MkStatePair(fork, fork)) + ) +} + +/** + * Gets a state in the product automaton from which `(fork, fork)` is + * reachable in zero or more epsilon transitions. + */ +StatePair getAForkPair(State fork) { + isFork(fork, _, _, _, _) and + result = MkStatePair(epsilonPred*(fork), epsilonPred*(fork)) +} + +/** + * Holds if `fork` is a pumpable fork with word `w`. + */ +predicate isPumpable(State fork, string w) { + exists(StatePair q, Trace t | + isReachableFromFork(fork, q, t, _) and + q = getAForkPair(fork) and + w = concretise(t) + ) +} + +/** + * An instantiation of `ReDoSConfiguration` for exponential backtracking. + */ +class ExponentialReDoSConfiguration extends ReDoSConfiguration { + ExponentialReDoSConfiguration() { this = "ExponentialReDoSConfiguration" } + + override predicate isReDoSCandidate(State state, string pump) { isPumpable(state, pump) } +} diff --git a/javascript/ql/src/semmle/javascript/security/performance/ReDoSUtil.qll b/javascript/ql/src/semmle/javascript/security/performance/ReDoSUtil.qll index a6f91c595dd0..40b05825bc6d 100644 --- a/javascript/ql/src/semmle/javascript/security/performance/ReDoSUtil.qll +++ b/javascript/ql/src/semmle/javascript/security/performance/ReDoSUtil.qll @@ -12,7 +12,7 @@ * states that will cause backtracking (a rejecting suffix exists). */ -import javascript +import RegExpTreeView /** * A configuration for which parts of a regular expression should be considered relevant for @@ -100,8 +100,8 @@ class RegExpRoot extends RegExpTerm { not exists(RegExpLookbehind lbh | getRoot(lbh) = this) and // is actually used as a RegExp isUsedAsRegExp() and - // pragmatic performance optimization: ignore minified files. - not getRootTerm().getParent().(Expr).getTopLevel().isMinified() + // not excluded for library specific reasons + not isExcluded(getRootTerm().getParent()) } } @@ -725,7 +725,10 @@ private module PrefixConstruction { max(State s, Location l | isStartState(s) and getRoot(s.getRepr()) = root and l = s.getRepr().getLocation() | - s order by l.getStartLine(), l.getStartColumn() + s + order by + l.getStartLine(), l.getStartColumn(), s.getRepr().toString(), l.getEndColumn(), + l.getEndLine() ) ) } @@ -767,7 +770,10 @@ private module PrefixConstruction { loc = s.getRepr().getLocation() and delta(s, _, state) | - s order by loc.getStartLine(), loc.getStartColumn(), loc.getEndLine(), loc.getEndColumn() + s + order by + loc.getStartLine(), loc.getStartColumn(), loc.getEndLine(), loc.getEndColumn(), + s.getRepr().toString() ) | // greedy search for the shortest prefix diff --git a/javascript/ql/src/semmle/javascript/security/performance/RegExpTreeView.qll b/javascript/ql/src/semmle/javascript/security/performance/RegExpTreeView.qll new file mode 100644 index 000000000000..f730f62f5b8a --- /dev/null +++ b/javascript/ql/src/semmle/javascript/security/performance/RegExpTreeView.qll @@ -0,0 +1,14 @@ +/** + * This module should provide a class hierarchy corresponding to a parse tree of regular expressions. + * + * Since the javascript extractor already provides such a hierarchy, we simply import that. + */ + +import javascript + +/** + * Holds if the regular expression should not be considered. + * + * For javascript we make the pragmatic performance optimization to ignore minified files. + */ +predicate isExcluded(RegExpParent parent) { parent.(Expr).getTopLevel().isMinified() } diff --git a/javascript/ql/src/semmle/javascript/security/performance/SuperlinearBackTracking.qll b/javascript/ql/src/semmle/javascript/security/performance/SuperlinearBackTracking.qll index 153093e96645..0bbff12b49d7 100644 --- a/javascript/ql/src/semmle/javascript/security/performance/SuperlinearBackTracking.qll +++ b/javascript/ql/src/semmle/javascript/security/performance/SuperlinearBackTracking.qll @@ -3,7 +3,6 @@ * perform backtracking in superlinear time. */ -import javascript import ReDoSUtil /* diff --git a/python/ql/src/Security/CWE-730/PolynomialBackTracking.ql b/python/ql/src/Security/CWE-730/PolynomialBackTracking.ql new file mode 100644 index 000000000000..a98d4eefa7ec --- /dev/null +++ b/python/ql/src/Security/CWE-730/PolynomialBackTracking.ql @@ -0,0 +1,6 @@ +import python +import semmle.python.regex.SuperlinearBackTracking + +from PolynomialBackTrackingTerm t +where t.getLocation().getFile().getBaseName() = "KnownCVEs.py" +select t.getRegex(), t, t.getReason() diff --git a/python/ql/src/Security/CWE-730/PolynomialReDoS.ql b/python/ql/src/Security/CWE-730/PolynomialReDoS.ql new file mode 100644 index 000000000000..b948c1601a85 --- /dev/null +++ b/python/ql/src/Security/CWE-730/PolynomialReDoS.ql @@ -0,0 +1,33 @@ +/** + * @name Polynomial regular expression used on uncontrolled data + * @description A regular expression that can require polynomial time + * to match may be vulnerable to denial-of-service attacks. + * @kind path-problem + * @problem.severity warning + * @precision high + * @id py/polynomial-redos + * @tags security + * external/cwe/cwe-730 + * external/cwe/cwe-400 + */ + +import python +import semmle.python.regex.SuperlinearBackTracking +import semmle.python.security.dataflow.PolynomialReDoS +import DataFlow::PathGraph + +from + PolynomialReDoSConfiguration config, DataFlow::PathNode source, DataFlow::PathNode sink, + PolynomialReDoSSink sinkNode, PolynomialBackTrackingTerm regexp +where + config.hasFlowPath(source, sink) and + sinkNode = sink.getNode() and + regexp.getRootTerm() = sinkNode.getRegExp() +// not ( +// source.getNode().(Source).getKind() = "url" and +// regexp.isAtEndLine() +// ) +select sinkNode.getHighlight(), source, sink, + "This $@ that depends on $@ may run slow on strings " + regexp.getPrefixMessage() + + "with many repetitions of '" + regexp.getPumpString() + "'.", regexp, "regular expression", + source.getNode(), "a user-provided value" diff --git a/python/ql/src/Security/CWE-730/ReDoS.ql b/python/ql/src/Security/CWE-730/ReDoS.ql new file mode 100644 index 000000000000..aebc2f81cffa --- /dev/null +++ b/python/ql/src/Security/CWE-730/ReDoS.ql @@ -0,0 +1,25 @@ +/** + * @name Inefficient regular expression + * @description A regular expression that requires exponential time to match certain inputs + * can be a performance bottleneck, and may be vulnerable to denial-of-service + * attacks. + * @kind problem + * @problem.severity error + * @precision high + * @id py/redos + * @tags security + * external/cwe/cwe-730 + * external/cwe/cwe-400 + */ + +import python +import semmle.python.regex.ExponentialBackTracking + +from RegExpTerm t, string pump, State s, string prefixMsg +where + hasReDoSResult(t, pump, s, prefixMsg) and + // exclude verbose mode regexes for now + not t.getRegex().getAMode() = "VERBOSE" +select t, + "This part of the regular expression may cause exponential backtracking on strings " + prefixMsg + + "containing many repetitions of '" + pump + "'." diff --git a/python/ql/src/semmle/python/PrintAst.qll b/python/ql/src/semmle/python/PrintAst.qll index 63ec5b53d0a2..e06e8bc9ffb4 100644 --- a/python/ql/src/semmle/python/PrintAst.qll +++ b/python/ql/src/semmle/python/PrintAst.qll @@ -7,6 +7,7 @@ */ import python +import semmle.python.RegexTreeView private newtype TPrintAstConfiguration = MkPrintAstConfiguration() @@ -53,6 +54,9 @@ private newtype TPrintAstNode = not list = any(Module mod).getBody() and not forall(AstNode child | child = list.getAnItem() | isNotNeeded(child)) and exists(list.getAnItem()) + } or + TRegExpTermNode(RegExpTerm term) { + exists(StrConst str | term.getRootTerm() = getParsedRegExp(str) and shouldPrint(str, _)) } /** @@ -419,6 +423,42 @@ class ParameterNode extends AstElementNode { } } +/** + * A print node for a `StrConst`. + * + * The string has a child, if the child is used as a regular expression, + * which is the root of the regular expression. + */ +class StrConstNode extends AstElementNode { + override StrConst element; + + override PrintAstNode getChild(int childIndex) { + childIndex = 0 and result.(RegExpTermNode).getTerm() = getParsedRegExp(element) + } +} + +/** + * A print node for a regular expression term. + */ +class RegExpTermNode extends TRegExpTermNode, PrintAstNode { + RegExpTerm term; + + RegExpTermNode() { this = TRegExpTermNode(term) } + + /** Gets the `RegExpTerm` for this node. */ + RegExpTerm getTerm() { result = term } + + override PrintAstNode getChild(int childIndex) { + result.(RegExpTermNode).getTerm() = term.getChild(childIndex) + } + + override string toString() { + result = "[" + strictconcat(term.getPrimaryQLClass(), " | ") + "] " + term.toString() + } + + override Location getLocation() { result = term.getLocation() } +} + /** * Gets the `i`th child from `node` ordered by location. */ @@ -447,7 +487,7 @@ private module PrettyPrinting { string getQlClass(AstNode a) { shouldPrint(a, _) and ( - not exists(getQlCustomClass(a)) and result = a.toString() + not exists(getQlCustomClass(a)) and result = strictconcat(a.toString(), " | ") or result = strictconcat(getQlCustomClass(a), " | ") ) diff --git a/python/ql/src/semmle/python/RegexTreeView.qll b/python/ql/src/semmle/python/RegexTreeView.qll new file mode 100644 index 000000000000..30c6dc70f0d8 --- /dev/null +++ b/python/ql/src/semmle/python/RegexTreeView.qll @@ -0,0 +1,958 @@ +/** Provides a class hierarchy corresponding to a parse tree of regular expressions. */ + +import python +private import semmle.python.regex + +/** + * An element containing a regular expression term, that is, either + * a string literal (parsed as a regular expression) + * or another regular expression term. + */ +newtype TRegExpParent = + /** A string literal used as a regular expression */ + TRegExpLiteral(Regex re) or + /** A quantified term */ + TRegExpQuantifier(Regex re, int start, int end) { re.qualifiedItem(start, end, _, _) } or + /** A sequence term */ + TRegExpSequence(Regex re, int start, int end) { re.sequence(start, end) } or + /** An alternatio term */ + TRegExpAlt(Regex re, int start, int end) { re.alternation(start, end) } or + /** A character class term */ + TRegExpCharacterClass(Regex re, int start, int end) { re.charSet(start, end) } or + /** A character range term */ + TRegExpCharacterRange(Regex re, int start, int end) { re.charRange(_, start, _, _, end) } or + /** A group term */ + TRegExpGroup(Regex re, int start, int end) { re.group(start, end) } or + /** A special character */ + TRegExpSpecialChar(Regex re, int start, int end) { re.specialCharacter(start, end, _) } or + /** A normal character */ + TRegExpNormalChar(Regex re, int start, int end) { re.normalCharacter(start, end) } or + /** A back reference */ + TRegExpBackRef(Regex re, int start, int end) { re.backreference(start, end) } + +/** + * An element containing a regular expression term, that is, either + * a string literal (parsed as a regular expression) + * or another regular expression term. + */ +class RegExpParent extends TRegExpParent { + string toString() { result = "RegExpParent" } + + /** Gets the `i`th child term. */ + abstract RegExpTerm getChild(int i); + + /** Gets a child term . */ + RegExpTerm getAChild() { result = getChild(_) } + + /** Gets the number of child terms. */ + int getNumChild() { result = count(getAChild()) } + + /** Gets the associated regex. */ + abstract Regex getRegex(); +} + +/** A string literal used as a regular expression */ +class RegExpLiteral extends TRegExpLiteral, RegExpParent { + Regex re; + + RegExpLiteral() { this = TRegExpLiteral(re) } + + override RegExpTerm getChild(int i) { i = 0 and result.getRegex() = re and result.isRootTerm() } + + predicate isDotAll() { re.getAMode() = "DOTALL" } + + override Regex getRegex() { result = re } + + string getPrimaryQLClass() { result = "RegExpLiteral" } +} + +/** + * A regular expression term, that is, a syntactic part of a regular expression. + */ +class RegExpTerm extends RegExpParent { + Regex re; + int start; + int end; + + RegExpTerm() { + this = TRegExpAlt(re, start, end) + or + this = TRegExpBackRef(re, start, end) + or + this = TRegExpCharacterClass(re, start, end) + or + this = TRegExpCharacterRange(re, start, end) + or + this = TRegExpNormalChar(re, start, end) + or + this = TRegExpGroup(re, start, end) + or + this = TRegExpQuantifier(re, start, end) + or + this = TRegExpSequence(re, start, end) and + exists(seqChild(re, start, end, 1)) // if a sequence does not have more than one element, it should be treated as that element instead. + or + this = TRegExpSpecialChar(re, start, end) + } + + /** + * Gets the outermost term of this regular expression. + */ + RegExpTerm getRootTerm() { + this.isRootTerm() and result = this + or + result = getParent().(RegExpTerm).getRootTerm() + } + + /** + * Holds if this term is part of a string literal + * that is interpreted as a regular expression. + */ + predicate isUsedAsRegExp() { any() } + + /** + * Holds if this is the root term of a regular expression. + */ + predicate isRootTerm() { start = 0 and end = re.getText().length() } + + override RegExpTerm getChild(int i) { + result = this.(RegExpAlt).getChild(i) + or + result = this.(RegExpBackRef).getChild(i) + or + result = this.(RegExpCharacterClass).getChild(i) + or + result = this.(RegExpCharacterRange).getChild(i) + or + result = this.(RegExpNormalChar).getChild(i) + or + result = this.(RegExpGroup).getChild(i) + or + result = this.(RegExpQuantifier).getChild(i) + or + result = this.(RegExpSequence).getChild(i) + or + result = this.(RegExpSpecialChar).getChild(i) + } + + /** + * Gets the parent term of this regular expression term, or the + * regular expression literal if this is the root term. + */ + RegExpParent getParent() { result.getAChild() = this } + + override Regex getRegex() { result = re } + + /** Gets the offset at which this term starts. */ + int getStart() { result = start } + + /** Gets the offset at which this term ends. */ + int getEnd() { result = end } + + override string toString() { result = re.getText().substring(start, end) } + + /** + * Gets the location of the surrounding regex, as locations inside the regex do not exist. + * To get location information corresponding to the term inside the regex, + * use `hasLocationInfo`. + */ + Location getLocation() { result = re.getLocation() } + + /** Holds if this term is found at the specified location offsets. */ + predicate hasLocationInfo( + string filepath, int startline, int startcolumn, int endline, int endcolumn + ) { + exists(int re_start, int re_end | + re.getLocation().hasLocationInfo(filepath, startline, re_start, endline, re_end) and + startcolumn = re_start + start + 4 and + endcolumn = re_start + end + 3 + ) + } + + /** Gets the file in which this term is found. */ + File getFile() { result = this.getLocation().getFile() } + + /** Gets the raw source text of this term. */ + string getRawValue() { result = this.toString() } + + /** Gets the string literal in which this term is found. */ + RegExpLiteral getLiteral() { result = TRegExpLiteral(re) } + + /** Gets the regular expression term that is matched (textually) before this one, if any. */ + RegExpTerm getPredecessor() { + exists(RegExpTerm parent | parent = getParent() | + result = parent.(RegExpSequence).previousElement(this) + or + not exists(parent.(RegExpSequence).previousElement(this)) and + not parent instanceof RegExpSubPattern and + result = parent.getPredecessor() + ) + } + + /** Gets the regular expression term that is matched (textually) after this one, if any. */ + RegExpTerm getSuccessor() { + exists(RegExpTerm parent | parent = getParent() | + result = parent.(RegExpSequence).nextElement(this) + or + not exists(parent.(RegExpSequence).nextElement(this)) and + not parent instanceof RegExpSubPattern and + result = parent.getSuccessor() + ) + } + + /** Gets the primary QL class for this term. */ + string getPrimaryQLClass() { result = "RegExpTerm" } +} + +/** + * A quantified regular expression term. + * + * Example: + * + * ``` + * ((ECMA|Java)[sS]cript)* + * ``` + */ +class RegExpQuantifier extends RegExpTerm, TRegExpQuantifier { + int part_end; + boolean maybe_empty; + boolean may_repeat_forever; + + RegExpQuantifier() { + this = TRegExpQuantifier(re, start, end) and + re.qualifiedPart(start, part_end, end, maybe_empty, may_repeat_forever) + } + + override RegExpTerm getChild(int i) { + i = 0 and + result.getRegex() = re and + result.getStart() = start and + result.getEnd() = part_end + } + + predicate mayRepeatForever() { may_repeat_forever = true } + + string getQualifier() { result = re.getText().substring(part_end, end) } + + override string getPrimaryQLClass() { result = "RegExpQuantifier" } +} + +/** + * A regular expression term that permits unlimited repetitions. + */ +class InfiniteRepetitionQuantifier extends RegExpQuantifier { + InfiniteRepetitionQuantifier() { this.mayRepeatForever() } +} + +/** + * A star-quantified term. + * + * Example: + * + * ``` + * \w* + * ``` + */ +class RegExpStar extends InfiniteRepetitionQuantifier { + RegExpStar() { this.getQualifier().charAt(0) = "*" } + + override string getPrimaryQLClass() { result = "RegExpStar" } +} + +/** + * A plus-quantified term. + * + * Example: + * + * ``` + * \w+ + * ``` + */ +class RegExpPlus extends InfiniteRepetitionQuantifier { + RegExpPlus() { this.getQualifier().charAt(0) = "+" } + + override string getPrimaryQLClass() { result = "RegExpPlus" } +} + +/** + * An optional term. + * + * Example: + * + * ``` + * ;? + * ``` + */ +class RegExpOpt extends RegExpQuantifier { + RegExpOpt() { this.getQualifier().charAt(0) = "?" } + + override string getPrimaryQLClass() { result = "RegExpOpt" } +} + +/** + * A range-quantified term + * + * Examples: + * + * ``` + * \w{2,4} + * \w{2,} + * \w{2} + * ``` + */ +class RegExpRange extends RegExpQuantifier { + string upper; + string lower; + + RegExpRange() { re.multiples(part_end, end, lower, upper) } + + string getUpper() { result = upper } + + string getLower() { result = lower } + + /** + * Gets the upper bound of the range, if any. + * + * If there is no upper bound, any number of repetitions is allowed. + * For a term of the form `r{lo}`, both the lower and the upper bound + * are `lo`. + */ + int getUpperBound() { result = this.getUpper().toInt() } + + /** Gets the lower bound of the range. */ + int getLowerBound() { result = this.getLower().toInt() } + + override string getPrimaryQLClass() { result = "RegExpRange" } +} + +/** + * A sequence term. + * + * Example: + * + * ``` + * (ECMA|Java)Script + * ``` + * + * This is a sequence with the elements `(ECMA|Java)` and `Script`. + */ +class RegExpSequence extends RegExpTerm, TRegExpSequence { + RegExpSequence() { + this = TRegExpSequence(re, start, end) and + exists(seqChild(re, start, end, 1)) // if a sequence does not have more than one element, it should be treated as that element instead. + } + + override RegExpTerm getChild(int i) { result = seqChild(re, start, end, i) } + + /** Gets the element preceding `element` in this sequence. */ + RegExpTerm previousElement(RegExpTerm element) { element = nextElement(result) } + + /** Gets the element following `element` in this sequence. */ + RegExpTerm nextElement(RegExpTerm element) { + exists(int i | + element = this.getChild(i) and + result = this.getChild(i + 1) + ) + } + + override string getPrimaryQLClass() { result = "RegExpSequence" } +} + +// moved out so we can use it in the charpred +private RegExpTerm seqChild(Regex re, int start, int end, int i) { + re.sequence(start, end) and + ( + i = 0 and + result.getRegex() = re and + result.getStart() = start and + exists(int itemEnd | + re.item(start, itemEnd) and + result.getEnd() = itemEnd + ) + or + i > 0 and + result.getRegex() = re and + exists(int itemStart | itemStart = seqChild(re, start, end, i - 1).getEnd() | + result.getStart() = itemStart and + re.item(itemStart, result.getEnd()) + ) + ) +} + +/** + * An alternative term, that is, a term of the form `a|b`. + * + * Example: + * + * ``` + * ECMA|Java + * ``` + */ +class RegExpAlt extends RegExpTerm, TRegExpAlt { + RegExpAlt() { this = TRegExpAlt(re, start, end) } + + override RegExpTerm getChild(int i) { + i = 0 and + result.getRegex() = re and + result.getStart() = start and + exists(int part_end | + re.alternationOption(start, end, start, part_end) and + result.getEnd() = part_end + ) + or + i > 0 and + result.getRegex() = re and + exists(int part_start | + part_start = this.getChild(i - 1).getEnd() + 1 // allow for the | + | + result.getStart() = part_start and + re.alternationOption(start, end, part_start, result.getEnd()) + ) + } + + override string getPrimaryQLClass() { result = "RegExpAlt" } +} + +/** + * An escaped regular expression term, that is, a regular expression + * term starting with a backslash, which is not a backreference. + * + * Example: + * + * ``` + * \. + * \w + * ``` + */ +class RegExpEscape extends RegExpNormalChar { + RegExpEscape() { re.escapedCharacter(start, end) } + + /** + * Gets the name of the escaped; for example, `w` for `\w`. + * TODO: Handle named escapes. + */ + override string getValue() { + this.isIdentityEscape() and result = this.getUnescaped() + or + this.getUnescaped() = "n" and result = "\n" + or + this.getUnescaped() = "r" and result = "\r" + or + this.getUnescaped() = "t" and result = "\t" + or + this.getUnescaped() = "f" and result = " " + or + isUnicode() and + result = getUnicode() + } + + predicate isIdentityEscape() { not this.getUnescaped() in ["n", "r", "t", "f"] } + + override string getPrimaryQLClass() { result = "RegExpEscape" } + + string getUnescaped() { result = this.getText().suffix(1) } + + /** + * Gets the text for this escape. That is e.g. "\w". + */ + private string getText() { result = re.getText().substring(start, end) } + + /** + * Holds if this is a unicode escape. + */ + private predicate isUnicode() { getText().prefix(2) = ["\\u", "\\U"] } + + /** + * Gets the unicode char for this escape. + * E.g. for `\u0061` this returns "a". + */ + private string getUnicode() { + exists(int codepoint | codepoint = sum(getHexValueFromUnicode(_)) | + result = codepoint.toUnicode() + ) + } + + /** + * Gets int value for the `index`th char in the hex number of the unicode escape. + * E.g. for `\u0061` and `index = 2` this returns 96 (the number `6` interpreted as hex). + */ + private int getHexValueFromUnicode(int index) { + isUnicode() and + exists(string hex, string char | hex = getText().suffix(2) | + char = hex.charAt(index) and + result = 16.pow(hex.length() - index - 1) * toHex(char) + ) + } +} + +/** + * Gets the hex number for the `hex` char. + */ +private int toHex(string hex) { + hex = [0 .. 9].toString() and + result = hex.toInt() + or + result = 10 and hex = ["a", "A"] + or + result = 11 and hex = ["b", "B"] + or + result = 12 and hex = ["c", "C"] + or + result = 13 and hex = ["d", "D"] + or + result = 14 and hex = ["e", "E"] + or + result = 15 and hex = ["f", "F"] +} + +/** + * A character class escape in a regular expression. + * That is, an escaped charachter that denotes multiple characters. + * + * Examples: + * + * ``` + * \w + * \S + * ``` + */ +class RegExpCharacterClassEscape extends RegExpEscape { + // string value; + RegExpCharacterClassEscape() { + // value = re.getText().substring(start + 1, end) and + // value in ["d", "D", "s", "S", "w", "W"] + this.getValue() in ["d", "D", "s", "S", "w", "W"] + } + + /** Gets the name of the character class; for example, `w` for `\w`. */ + // override string getValue() { result = value } + override RegExpTerm getChild(int i) { none() } + + override string getPrimaryQLClass() { result = "RegExpCharacterClassEscape" } +} + +/** + * A character class in a regular expression. + * + * Examples: + * + * ``` + * [a-z_] + * [^<>&] + * ``` + */ +class RegExpCharacterClass extends RegExpTerm, TRegExpCharacterClass { + RegExpCharacterClass() { this = TRegExpCharacterClass(re, start, end) } + + predicate isInverted() { re.getChar(start + 1) = "^" } + + string getCharThing(int i) { result = re.getChar(i + start) } + + predicate isUniversalClass() { + // [^] + isInverted() and not exists(getAChild()) + or + // [\w\W] and similar + not isInverted() and + exists(string cce1, string cce2 | + cce1 = getAChild().(RegExpCharacterClassEscape).getValue() and + cce2 = getAChild().(RegExpCharacterClassEscape).getValue() + | + cce1 != cce2 and cce1.toLowerCase() = cce2.toLowerCase() + ) + } + + override RegExpTerm getChild(int i) { + i = 0 and + result.getRegex() = re and + exists(int itemStart, int itemEnd | + result.getStart() = itemStart and + re.char_set_start(start, itemStart) and + re.char_set_child(start, itemStart, itemEnd) and + result.getEnd() = itemEnd + ) + or + i > 0 and + result.getRegex() = re and + exists(int itemStart | itemStart = this.getChild(i - 1).getEnd() | + result.getStart() = itemStart and + re.char_set_child(start, itemStart, result.getEnd()) + ) + } + + override string getPrimaryQLClass() { result = "RegExpCharacterClass" } +} + +/** + * A character range in a character class in a regular expression. + * + * Example: + * + * ``` + * a-z + * ``` + */ +class RegExpCharacterRange extends RegExpTerm, TRegExpCharacterRange { + int lower_end; + int upper_start; + + RegExpCharacterRange() { + this = TRegExpCharacterRange(re, start, end) and + re.charRange(_, start, lower_end, upper_start, end) + } + + predicate isRange(string lo, string hi) { + lo = re.getText().substring(start, lower_end) and + hi = re.getText().substring(upper_start, end) + } + + override RegExpTerm getChild(int i) { + i = 0 and + result.getRegex() = re and + result.getStart() = start and + result.getEnd() = lower_end + or + i = 1 and + result.getRegex() = re and + result.getStart() = upper_start and + result.getEnd() = end + } + + override string getPrimaryQLClass() { result = "RegExpCharacterRange" } +} + +/** + * A normal character in a regular expression, that is, a character + * without special meaning. This includes escaped characters. + * + * Examples: + * ``` + * t + * \t + * ``` + */ +class RegExpNormalChar extends RegExpTerm, TRegExpNormalChar { + RegExpNormalChar() { this = TRegExpNormalChar(re, start, end) } + + predicate isCharacter() { any() } + + string getValue() { result = re.getText().substring(start, end) } + + override RegExpTerm getChild(int i) { none() } + + override string getPrimaryQLClass() { result = "RegExpNormalChar" } +} + +/** + * A constant regular expression term, that is, a regular expression + * term matching a single string. Currently, this will always be a single character. + * + * Example: + * + * ``` + * a + * ``` + */ +class RegExpConstant extends RegExpTerm { + string value; + + RegExpConstant() { + this = TRegExpNormalChar(re, start, end) and + not this instanceof RegExpCharacterClassEscape and + // exclude chars in qualifiers + // TODO: push this into regex library + not exists(int qstart, int qend | re.qualifiedPart(_, qstart, qend, _, _) | + qstart <= start and end <= qend + ) and + value = this.(RegExpNormalChar).getValue() + // This will never hold + // or + // this = TRegExpSpecialChar(re, start, end) and + // re.inCharSet(start) and + // value = this.(RegExpSpecialChar).getChar() + } + + predicate isCharacter() { any() } + + string getValue() { result = value } + + override RegExpTerm getChild(int i) { none() } + + override string getPrimaryQLClass() { result = "RegExpConstant" } +} + +/** + * A grouped regular expression. + * + * Examples: + * + * ``` + * (ECMA|Java) + * (?:ECMA|Java) + * (?['"]) + * ``` + */ +class RegExpGroup extends RegExpTerm, TRegExpGroup { + RegExpGroup() { this = TRegExpGroup(re, start, end) } + + /** + * Gets the index of this capture group within the enclosing regular + * expression literal. + * + * For example, in the regular expression `/((a?).)(?:b)/`, the + * group `((a?).)` has index 1, the group `(a?)` nested inside it + * has index 2, and the group `(?:b)` has no index, since it is + * not a capture group. + */ + int getNumber() { result = re.getGroupNumber(start, end) } + + /** Holds if this is a named capture group. */ + predicate isNamed() { exists(this.getName()) } + + /** Gets the name of this capture group, if any. */ + string getName() { result = re.getGroupName(start, end) } + + predicate isCharacter() { any() } + + string getValue() { result = re.getText().substring(start, end) } + + override RegExpTerm getChild(int i) { + result.getRegex() = re and + i = 0 and + re.groupContents(start, end, result.getStart(), result.getEnd()) + } + + override string getPrimaryQLClass() { result = "RegExpGroup" } +} + +/** + * A special character in a regular expression. + * + * Examples: + * ``` + * ^ + * $ + * . + * ``` + */ +class RegExpSpecialChar extends RegExpTerm, TRegExpSpecialChar { + string char; + + RegExpSpecialChar() { + this = TRegExpSpecialChar(re, start, end) and + re.specialCharacter(start, end, char) + } + + predicate isCharacter() { any() } + + string getChar() { result = char } + + override RegExpTerm getChild(int i) { none() } + + override string getPrimaryQLClass() { result = "RegExpSpecialChar" } +} + +/** + * A dot regular expression. + * + * Example: + * + * ``` + * . + * ``` + */ +class RegExpDot extends RegExpSpecialChar { + RegExpDot() { this.getChar() = "." } + + override string getPrimaryQLClass() { result = "RegExpDot" } +} + +/** + * A dollar assertion `$` matching the end of a line. + * + * Example: + * + * ``` + * $ + * ``` + */ +class RegExpDollar extends RegExpSpecialChar { + RegExpDollar() { this.getChar() = "$" } + + override string getPrimaryQLClass() { result = "RegExpDollar" } +} + +/** + * A caret assertion `^` matching the beginning of a line. + * + * Example: + * + * ``` + * ^ + * ``` + */ +class RegExpCaret extends RegExpSpecialChar { + RegExpCaret() { this.getChar() = "^" } + + override string getPrimaryQLClass() { result = "RegExpCaret" } +} + +/** + * A zero-width match, that is, either an empty group or an assertion. + * + * Examples: + * ``` + * () + * (?=\w) + * ``` + */ +class RegExpZeroWidthMatch extends RegExpGroup { + RegExpZeroWidthMatch() { re.zeroWidthMatch(start, end) } + + override predicate isCharacter() { any() } + + override RegExpTerm getChild(int i) { none() } + + override string getPrimaryQLClass() { result = "RegExpZeroWidthMatch" } +} + +/** + * A zero-width lookahead or lookbehind assertion. + * + * Examples: + * + * ``` + * (?=\w) + * (?!\n) + * (?<=\.) + * (?` + * in a regular expression. + * + * Examples: + * + * ``` + * \1 + * (?P=quote) + * ``` + */ +class RegExpBackRef extends RegExpTerm, TRegExpBackRef { + RegExpBackRef() { this = TRegExpBackRef(re, start, end) } + + /** + * Gets the number of the capture group this back reference refers to, if any. + */ + int getNumber() { result = re.getBackrefNumber(start, end) } + + /** + * Gets the name of the capture group this back reference refers to, if any. + */ + string getName() { result = re.getBackrefName(start, end) } + + /** Gets the capture group this back reference refers to. */ + RegExpGroup getGroup() { + result.getLiteral() = this.getLiteral() and + ( + result.getNumber() = this.getNumber() or + result.getName() = this.getName() + ) + } + + override RegExpTerm getChild(int i) { none() } + + override string getPrimaryQLClass() { result = "RegExpBackRef" } +} + +/** Gets the parse tree resulting from parsing `re`, if such has been constructed. */ +RegExpTerm getParsedRegExp(StrConst re) { result.getRegex() = re and result.isRootTerm() } diff --git a/python/ql/src/semmle/python/regex.qll b/python/ql/src/semmle/python/regex.qll index 365c36d0d2f6..ab4713ab683d 100644 --- a/python/ql/src/semmle/python/regex.qll +++ b/python/ql/src/semmle/python/regex.qll @@ -121,8 +121,57 @@ deprecated string mode_from_mode_object(Value obj) { abstract class RegexString extends Expr { RegexString() { (this instanceof Bytes or this instanceof Unicode) } + override string toString() { + result = this.(Bytes).getText() + or + result = this.(Unicode).getText() + } + + /** result is true for those start chars that actually mark a start of a char set. */ + boolean char_set_start(int pos) { + exists(int index | + char_set_delimiter(index, pos) = true and + ( + index = 1 and result = true // if a '[' is first in the string (among brackets), it starts a char set + or + index > 1 and + not char_set_delimiter(index - 1, _) = false and + result = false + or + exists(int p1 | + char_set_delimiter(index - 1, p1) = false and // if it is preceded by a closing bracket, it starts a char set + if + exists(int p2 | + p1 = p2 + 1 + or + this.getChar(p2 + 1) = "^" and + p1 = p2 + 2 + | + char_set_delimiter(index - 2, p2) = true // but the closing bracket only closes... + ) + then + exists(int p2 | char_set_delimiter(index - 2, p2) = true | + result = char_set_start(p2).booleanNot() // ...if it is not the first in a char set + ) + else result = true + ) + ) + ) + } + + /** result denotes if the index is a left bracket */ + boolean char_set_delimiter(int index, int pos) { + pos = rank[index](int p | this.nonEscapedCharAt(p) = "[" or this.nonEscapedCharAt(p) = "]") and + ( + this.nonEscapedCharAt(pos) = "[" and result = true + or + this.nonEscapedCharAt(pos) = "]" and result = false + ) + } + + /** Hold is a character set starts between `start` and `end`. */ predicate char_set_start(int start, int end) { - this.nonEscapedCharAt(start) = "[" and + this.char_set_start(start) = true and ( this.getChar(start + 1) = "^" and end = start + 2 or @@ -143,6 +192,83 @@ abstract class RegexString extends Expr { ) } + /** An indexed version of `char_set_token/3` */ + private predicate char_set_token(int charset_start, int index, int token_start, int token_end) { + token_start = + rank[index](int start, int end | this.char_set_token(charset_start, start, end) | start) and + this.char_set_token(charset_start, token_start, token_end) + } + + /** Either a char or a - */ + private predicate char_set_token(int charset_start, int start, int end) { + this.char_set_start(charset_start, start) and + ( + this.escapedCharacter(start, end) + or + exists(this.nonEscapedCharAt(start)) and end = start + 1 + ) + or + this.char_set_token(charset_start, _, start) and + ( + this.escapedCharacter(start, end) + or + exists(this.nonEscapedCharAt(start)) and + end = start + 1 and + not this.getChar(start) = "]" + ) + } + + /** + * Holds if the character set starting at `charset_start` contains either + * a character or a range found between `start` and `end`. + */ + predicate char_set_child(int charset_start, int start, int end) { + this.char_set_token(charset_start, start, end) and + not exists(int range_start, int range_end | + this.charRange(charset_start, range_start, _, _, range_end) and + range_start <= start and + range_end >= end + ) + or + this.charRange(charset_start, start, _, _, end) + } + + /** + * Holds if the character set starting at `charset_start` contains a character range + * with lower bound found between `start` and `lower_end` + * and upper bound found between `upper_start` and `end`. + */ + predicate charRange(int charset_start, int start, int lower_end, int upper_start, int end) { + exists(int index | + this.charRangeEnd(charset_start, index) = true and + this.char_set_token(charset_start, index - 2, start, lower_end) and + this.char_set_token(charset_start, index, upper_start, end) + ) + } + + private boolean charRangeEnd(int charset_start, int index) { + this.char_set_token(charset_start, index, _, _) and + ( + index in [1, 2] and result = false + or + index > 2 and + exists(int connector_start | + this.char_set_token(charset_start, index - 1, connector_start, _) and + this.nonEscapedCharAt(connector_start) = "-" and + result = + this.charRangeEnd(charset_start, index - 2) + .booleanNot() + .booleanAnd(this.charRangeEnd(charset_start, index - 1).booleanNot()) + ) + or + not exists(int connector_start | + this.char_set_token(charset_start, index - 1, connector_start, _) and + this.nonEscapedCharAt(connector_start) = "-" + ) and + result = false + ) + } + predicate escapingChar(int pos) { this.escaping(pos) = true } private boolean escaping(int pos) { @@ -164,14 +290,14 @@ abstract class RegexString extends Expr { string nonEscapedCharAt(int i) { result = this.getText().charAt(i) and - not this.escapingChar(i - 1) + not exists(int x, int y | this.escapedCharacter(x, y) and i in [x .. y - 1]) } private predicate isOptionDivider(int i) { this.nonEscapedCharAt(i) = "|" } - private predicate isGroupEnd(int i) { this.nonEscapedCharAt(i) = ")" } + private predicate isGroupEnd(int i) { this.nonEscapedCharAt(i) = ")" and not this.inCharSet(i) } - private predicate isGroupStart(int i) { this.nonEscapedCharAt(i) = "(" } + private predicate isGroupStart(int i) { this.nonEscapedCharAt(i) = "(" and not this.inCharSet(i) } predicate failedToParse(int i) { exists(this.getChar(i)) and @@ -192,16 +318,25 @@ abstract class RegexString extends Expr { not exists(int i | start + 2 < i and i < end - 1 | this.getChar(i) = "}") } - private predicate escapedCharacter(int start, int end) { + /** + * Holds if an escaped character is found between `start` and `end`. + * Escaped characters include hex values, octal values and named escapes, + * but excludes backreferences. + */ + predicate escapedCharacter(int start, int end) { this.escapingChar(start) and - not exists(this.getText().substring(start + 1, end + 1).toInt()) and + not this.numbered_backreference(start, _, _) and ( // hex value \xhh this.getChar(start + 1) = "x" and end = start + 4 or // octal value \ooo end in [start + 2 .. start + 4] and - exists(this.getText().substring(start + 1, end).toInt()) + this.getText().substring(start + 1, end).toInt() >= 0 and + not ( + end < start + 4 and + exists(this.getText().substring(start + 1, end + 1).toInt()) + ) or // 16-bit hex value \uhhhh this.getChar(start + 1) = "u" and end = start + 6 @@ -213,11 +348,13 @@ abstract class RegexString extends Expr { or // escape not handled above, update when adding a new case not this.getChar(start + 1) in ["x", "u", "U", "N"] and + not exists(this.getChar(start + 1).toInt()) and end = start + 2 ) } - private predicate inCharSet(int index) { + /** Holds if `index` is inside a character set. */ + predicate inCharSet(int index) { exists(int x, int y | this.charSet(x, y) and index in [x + 1 .. y - 2]) } @@ -238,7 +375,7 @@ abstract class RegexString extends Expr { or start = z - 2 or - start > y and start < z - 2 and not c = "-" + start > y and start < z - 2 and not this.charRange(_, _, start, end, _) ) or not this.inCharSet(start) and @@ -246,7 +383,7 @@ abstract class RegexString extends Expr { not c = "[" and not c = ")" and not c = "|" and - not this.qualifier(start, _, _) + not this.qualifier(start, _, _, _) ) } @@ -257,7 +394,8 @@ abstract class RegexString extends Expr { or this.escapedCharacter(start, end) ) and - not exists(int x, int y | this.group_start(x, y) and x <= start and y >= end) + not exists(int x, int y | this.group_start(x, y) and x <= start and y >= end) and + not exists(int x, int y | this.backreference(x, y) and x <= start and y >= end) } predicate normalCharacter(int start, int end) { @@ -302,12 +440,13 @@ abstract class RegexString extends Expr { or this.negativeAssertionGroup(start, end) or - positiveLookaheadAssertionGroup(start, end) + this.positiveLookaheadAssertionGroup(start, end) or this.positiveLookbehindAssertionGroup(start, end) } - private predicate emptyGroup(int start, int end) { + /** Holds if an empty group is found between `start` and `end`. */ + predicate emptyGroup(int start, int end) { exists(int endm1 | end = endm1 + 1 | this.group_start(start, endm1) and this.isGroupEnd(endm1) @@ -340,13 +479,29 @@ abstract class RegexString extends Expr { ) } - private predicate positiveLookaheadAssertionGroup(int start, int end) { + /** Holds if a negative lookahead is found between `start` and `end` */ + predicate negativeLookaheadAssertionGroup(int start, int end) { + exists(int in_start | this.negative_lookahead_assertion_start(start, in_start) | + this.groupContents(start, end, in_start, _) + ) + } + + /** Holds if a negative lookbehind is found between `start` and `end` */ + predicate negativeLookbehindAssertionGroup(int start, int end) { + exists(int in_start | this.negative_lookbehind_assertion_start(start, in_start) | + this.groupContents(start, end, in_start, _) + ) + } + + /** Holds if a positive lookahead is found between `start` and `end` */ + predicate positiveLookaheadAssertionGroup(int start, int end) { exists(int in_start | this.lookahead_assertion_start(start, in_start) | this.groupContents(start, end, in_start, _) ) } - private predicate positiveLookbehindAssertionGroup(int start, int end) { + /** Holds if a positive lookbehind is found between `start` and `end` */ + predicate positiveLookbehindAssertionGroup(int start, int end) { exists(int in_start | this.lookbehind_assertion_start(start, in_start) | this.groupContents(start, end, in_start, _) ) @@ -405,6 +560,8 @@ abstract class RegexString extends Expr { this.getChar(start + 1) = "?" and this.getChar(start + 2) = "P" and this.getChar(start + 3) = "=" and + // Should this be looking for unescaped ")"? + // TODO: test this end = min(int i | i > start + 4 and this.getChar(i) = "?") } @@ -495,6 +652,7 @@ abstract class RegexString extends Expr { private predicate numbered_backreference(int start, int end, int value) { this.escapingChar(start) and + not this.getChar(start + 1) = "0" and exists(string text, string svalue, int len | end = start + len and text = this.getText() and @@ -503,7 +661,7 @@ abstract class RegexString extends Expr { svalue = text.substring(start + 1, start + len) and value = svalue.toInt() and not exists(text.substring(start + 1, start + len + 1).toInt()) and - value != 0 + value > 0 ) } @@ -527,43 +685,55 @@ abstract class RegexString extends Expr { this.group(start, end) or this.charSet(start, end) + or + this.backreference(start, end) } - private predicate qualifier(int start, int end, boolean maybe_empty) { - this.short_qualifier(start, end, maybe_empty) and not this.getChar(end) = "?" + private predicate qualifier(int start, int end, boolean maybe_empty, boolean may_repeat_forever) { + this.short_qualifier(start, end, maybe_empty, may_repeat_forever) and + not this.getChar(end) = "?" or - exists(int short_end | this.short_qualifier(start, short_end, maybe_empty) | + exists(int short_end | this.short_qualifier(start, short_end, maybe_empty, may_repeat_forever) | if this.getChar(short_end) = "?" then end = short_end + 1 else end = short_end ) } - private predicate short_qualifier(int start, int end, boolean maybe_empty) { + private predicate short_qualifier( + int start, int end, boolean maybe_empty, boolean may_repeat_forever + ) { ( - this.getChar(start) = "+" and maybe_empty = false + this.getChar(start) = "+" and maybe_empty = false and may_repeat_forever = true or - this.getChar(start) = "*" and maybe_empty = true + this.getChar(start) = "*" and maybe_empty = true and may_repeat_forever = true or - this.getChar(start) = "?" and maybe_empty = true + this.getChar(start) = "?" and maybe_empty = true and may_repeat_forever = false ) and end = start + 1 or - exists(int endin | end = endin + 1 | - this.getChar(start) = "{" and - this.getChar(endin) = "}" and - end > start and - exists(string multiples | multiples = this.getText().substring(start + 1, endin) | - multiples.regexpMatch("0+") and maybe_empty = true - or - multiples.regexpMatch("0*,[0-9]*") and maybe_empty = true - or - multiples.regexpMatch("0*[1-9][0-9]*") and maybe_empty = false - or - multiples.regexpMatch("0*[1-9][0-9]*,[0-9]*") and maybe_empty = false - ) and - not exists(int mid | - this.getChar(mid) = "}" and - mid > start and - mid < endin + exists(string lower, string upper | + this.multiples(start, end, lower, upper) and + (if lower = "" or lower.toInt() = 0 then maybe_empty = true else maybe_empty = false) and + if upper = "" then may_repeat_forever = true else may_repeat_forever = false + ) + } + + /** + * Holds if a repetition quantifier is found between `start` and `end`, + * with the given lower and upper bounds. If a bound is omitted, the corresponding + * string is empty. + */ + predicate multiples(int start, int end, string lower, string upper) { + this.getChar(start) = "{" and + this.getChar(end - 1) = "}" and + exists(string inner | inner = this.getText().substring(start + 1, end - 1) | + inner.regexpMatch("[0-9]+") and + lower = inner and + upper = lower + or + inner.regexpMatch("[0-9]*,[0-9]*") and + exists(int commaIndex | commaIndex = inner.indexOf(",") | + lower = inner.prefix(commaIndex) and + upper = inner.suffix(commaIndex + 1) ) ) } @@ -572,19 +742,29 @@ abstract class RegexString extends Expr { * Whether the text in the range start,end is a qualified item, where item is a character, * a character set or a group. */ - predicate qualifiedItem(int start, int end, boolean maybe_empty) { - this.qualifiedPart(start, _, end, maybe_empty) + predicate qualifiedItem(int start, int end, boolean maybe_empty, boolean may_repeat_forever) { + this.qualifiedPart(start, _, end, maybe_empty, may_repeat_forever) } - private predicate qualifiedPart(int start, int part_end, int end, boolean maybe_empty) { + /** + * Holds if a qualified part is found between `start` and `part_end` and the qualifier is + * found between `part_end` and `end`. + * + * `maybe_empty` is true if the part is optional. + * `may_repeat_forever` is true if the part may be repeated unboundedly. + */ + predicate qualifiedPart( + int start, int part_end, int end, boolean maybe_empty, boolean may_repeat_forever + ) { this.baseItem(start, part_end) and - this.qualifier(part_end, end, maybe_empty) + this.qualifier(part_end, end, maybe_empty, may_repeat_forever) } - private predicate item(int start, int end) { - this.qualifiedItem(start, end, _) + /** Holds if the range `start`, `end` contains a character, a quantifier, a character set or a group. */ + predicate item(int start, int end) { + this.qualifiedItem(start, end, _, _) or - this.baseItem(start, end) and not this.qualifier(end, _, _) + this.baseItem(start, end) and not this.qualifier(end, _, _, _) } private predicate subsequence(int start, int end) { @@ -607,7 +787,7 @@ abstract class RegexString extends Expr { */ predicate sequence(int start, int end) { this.sequenceOrQualified(start, end) and - not this.qualifiedItem(start, end, _) + not this.qualifiedItem(start, end, _, _) } private predicate sequenceOrQualified(int start, int end) { @@ -618,7 +798,8 @@ abstract class RegexString extends Expr { private predicate item_start(int start) { this.character(start, _) or this.isGroupStart(start) or - this.charSet(start, _) + this.charSet(start, _) or + this.backreference(start, _) } private predicate item_end(int end) { @@ -628,7 +809,7 @@ abstract class RegexString extends Expr { or this.charSet(_, end) or - this.qualifier(_, end, _) + this.qualifier(_, end, _, _) } private predicate top_level(int start, int end) { @@ -680,14 +861,14 @@ abstract class RegexString extends Expr { or exists(int x | this.firstPart(x, end) | this.emptyMatchAtStartGroup(x, start) or - this.qualifiedItem(x, start, true) or + this.qualifiedItem(x, start, true, _) or this.specialCharacter(x, start, "^") ) or exists(int y | this.firstPart(start, y) | this.item(start, end) or - this.qualifiedPart(start, end, y, _) + this.qualifiedPart(start, end, y, _, _) ) or exists(int x, int y | this.firstPart(x, y) | @@ -704,7 +885,7 @@ abstract class RegexString extends Expr { exists(int y | this.lastPart(start, y) | this.emptyMatchAtEndGroup(end, y) or - this.qualifiedItem(end, y, true) + this.qualifiedItem(end, y, true, _) or this.specialCharacter(end, y, "$") or @@ -716,7 +897,7 @@ abstract class RegexString extends Expr { this.item(start, end) ) or - exists(int y | this.lastPart(start, y) | this.qualifiedPart(start, end, y, _)) + exists(int y | this.lastPart(start, y) | this.qualifiedPart(start, end, y, _, _)) or exists(int x, int y | this.lastPart(x, y) | this.groupContents(x, y, start, end) @@ -733,7 +914,7 @@ abstract class RegexString extends Expr { ( this.character(start, end) or - this.qualifiedItem(start, end, _) + this.qualifiedItem(start, end, _, _) or this.charSet(start, end) ) and @@ -748,7 +929,7 @@ abstract class RegexString extends Expr { ( this.character(start, end) or - this.qualifiedItem(start, end, _) + this.qualifiedItem(start, end, _, _) or this.charSet(start, end) ) and diff --git a/python/ql/src/semmle/python/regex/ExponentialBackTracking.qll b/python/ql/src/semmle/python/regex/ExponentialBackTracking.qll new file mode 100644 index 000000000000..61707b1f392f --- /dev/null +++ b/python/ql/src/semmle/python/regex/ExponentialBackTracking.qll @@ -0,0 +1,342 @@ +/** + * This library implements the analysis described in the following two papers: + * + * James Kirrage, Asiri Rathnayake, Hayo Thielecke: Static Analysis for + * Regular Expression Denial-of-Service Attacks. NSS 2013. + * (http://www.cs.bham.ac.uk/~hxt/research/reg-exp-sec.pdf) + * Asiri Rathnayake, Hayo Thielecke: Static Analysis for Regular Expression + * Exponential Runtime via Substructural Logics. 2014. + * (https://www.cs.bham.ac.uk/~hxt/research/redos_full.pdf) + * + * The basic idea is to search for overlapping cycles in the NFA, that is, + * states `q` such that there are two distinct paths from `q` to itself + * that consume the same word `w`. + * + * For any such state `q`, an attack string can be constructed as follows: + * concatenate a prefix `v` that takes the NFA to `q` with `n` copies of + * the word `w` that leads back to `q` along two different paths, followed + * by a suffix `x` that is _not_ accepted in state `q`. A backtracking + * implementation will need to explore at least 2^n different ways of going + * from `q` back to itself while trying to match the `n` copies of `w` + * before finally giving up. + * + * Now in order to identify overlapping cycles, all we have to do is find + * pumpable forks, that is, states `q` that can transition to two different + * states `r1` and `r2` on the same input symbol `c`, such that there are + * paths from both `r1` and `r2` to `q` that consume the same word. The latter + * condition is equivalent to saying that `(q, q)` is reachable from `(r1, r2)` + * in the product NFA. + * + * This is what the library does. It makes a simple attempt to construct a + * prefix `v` leading into `q`, but only to improve the alert message. + * And the library tries to prove the existence of a suffix that ensures + * rejection. This check might fail, which can cause false positives. + * + * Finally, sometimes it depends on the translation whether the NFA generated + * for a regular expression has a pumpable fork or not. We implement one + * particular translation, which may result in false positives or negatives + * relative to some particular JavaScript engine. + * + * More precisely, the library constructs an NFA from a regular expression `r` + * as follows: + * + * * Every sub-term `t` gives rise to an NFA state `Match(t,i)`, representing + * the state of the automaton before attempting to match the `i`th character in `t`. + * * There is one accepting state `Accept(r)`. + * * There is a special `AcceptAnySuffix(r)` state, which accepts any suffix string + * by using an epsilon transition to `Accept(r)` and an any transition to itself. + * * Transitions between states may be labelled with epsilon, or an abstract + * input symbol. + * * Each abstract input symbol represents a set of concrete input characters: + * either a single character, a set of characters represented by a + * character class, or the set of all characters. + * * The product automaton is constructed lazily, starting with pair states + * `(q, q)` where `q` is a fork, and proceding along an over-approximate + * step relation. + * * The over-approximate step relation allows transitions along pairs of + * abstract input symbols where the symbols have overlap in the characters they accept. + * * Once a trace of pairs of abstract input symbols that leads from a fork + * back to itself has been identified, we attempt to construct a concrete + * string corresponding to it, which may fail. + * * Lastly we ensure that any state reached by repeating `n` copies of `w` has + * a suffix `x` (possible empty) that is most likely __not__ accepted. + */ + +import ReDoSUtil + +/** + * Holds if state `s` might be inside a backtracking repetition. + */ +pragma[noinline] +predicate stateInsideBacktracking(State s) { + s.getRepr().getParent*() instanceof MaybeBacktrackingRepetition +} + +/** + * A infinitely repeating quantifier that might backtrack. + */ +class MaybeBacktrackingRepetition extends InfiniteRepetitionQuantifier { + MaybeBacktrackingRepetition() { + exists(RegExpTerm child | + child instanceof RegExpAlt or + child instanceof RegExpQuantifier + | + child.getParent+() = this + ) + } +} + +/** + * A state in the product automaton. + */ +newtype TStatePair = + /** + * We lazily only construct those states that we are actually + * going to need: `(q, q)` for every fork state `q`, and any + * pair of states that can be reached from a pair that we have + * already constructed. To cut down on the number of states, + * we only represent states `(q1, q2)` where `q1` is lexicographically + * no bigger than `q2`. + * + * States are only constructed if both states in the pair are + * inside a repetition that might backtrack. + */ + MkStatePair(State q1, State q2) { + isFork(q1, _, _, _, _) and q2 = q1 + or + (step(_, _, _, q1, q2) or step(_, _, _, q2, q1)) and + rankState(q1) <= rankState(q2) + } + +/** + * Gets a unique number for a `state`. + * Is used to create an ordering of states, where states with the same `toString()` will be ordered differently. + */ +int rankState(State state) { + state = + rank[result](State s, Location l | + l = s.getRepr().getLocation() + | + s order by l.getStartLine(), l.getStartColumn(), s.toString() + ) +} + +/** + * A state in the product automaton. + */ +class StatePair extends TStatePair { + State q1; + State q2; + + StatePair() { this = MkStatePair(q1, q2) } + + /** Gets a textual representation of this element. */ + string toString() { result = "(" + q1 + ", " + q2 + ")" } + + /** Gets the first component of the state pair. */ + State getLeft() { result = q1 } + + /** Gets the second component of the state pair. */ + State getRight() { result = q2 } +} + +/** + * Holds for all constructed state pairs. + * + * Used in `statePairDist` + */ +predicate isStatePair(StatePair p) { any() } + +/** + * Holds if there are transitions from the components of `q` to the corresponding + * components of `r`. + * + * Used in `statePairDist` + */ +predicate delta2(StatePair q, StatePair r) { step(q, _, _, r) } + +/** + * Gets the minimum length of a path from `q` to `r` in the + * product automaton. + */ +int statePairDist(StatePair q, StatePair r) = + shortestDistances(isStatePair/1, delta2/2)(q, r, result) + +/** + * Holds if there are transitions from `q` to `r1` and from `q` to `r2` + * labelled with `s1` and `s2`, respectively, where `s1` and `s2` do not + * trivially have an empty intersection. + * + * This predicate only holds for states associated with regular expressions + * that have at least one repetition quantifier in them (otherwise the + * expression cannot be vulnerable to ReDoS attacks anyway). + */ +pragma[noopt] +predicate isFork(State q, InputSymbol s1, InputSymbol s2, State r1, State r2) { + stateInsideBacktracking(q) and + exists(State q1, State q2 | + q1 = epsilonSucc*(q) and + delta(q1, s1, r1) and + q2 = epsilonSucc*(q) and + delta(q2, s2, r2) and + // Use pragma[noopt] to prevent intersect(s1,s2) from being the starting point of the join. + // From (s1,s2) it would find a huge number of intermediate state pairs (q1,q2) originating from different literals, + // and discover at the end that no `q` can reach both `q1` and `q2` by epsilon transitions. + exists(intersect(s1, s2)) + | + s1 != s2 + or + r1 != r2 + or + r1 = r2 and q1 != q2 + or + // If q can reach itself by epsilon transitions, then there are two distinct paths to the q1/q2 state: + // one that uses the loop and one that doesn't. The engine will separately attempt to match with each path, + // despite ending in the same state. The "fork" thus arises from the choice of whether to use the loop or not. + // To avoid every state in the loop becoming a fork state, + // we arbitrarily pick the InfiniteRepetitionQuantifier state as the canonical fork state for the loop + // (every epsilon-loop must contain such a state). + // + // We additionally require that the there exists another InfiniteRepetitionQuantifier `mid` on the path from `q` to itself. + // This is done to avoid flagging regular expressions such as `/(a?)*b/` - that only has polynomial runtime, and is detected by `js/polynomial-redos`. + // The below code is therefore a heuritic, that only flags regular expressions such as `/(a*)*b/`, + // and does not flag regular expressions such as `/(a?b?)c/`, but the latter pattern is not used frequently. + r1 = r2 and + q1 = q2 and + epsilonSucc+(q) = q and + exists(RegExpTerm term | term = q.getRepr() | term instanceof InfiniteRepetitionQuantifier) and + // One of the mid states is an infinite quantifier itself + exists(State mid, RegExpTerm term | + mid = epsilonSucc+(q) and + term = mid.getRepr() and + term instanceof InfiniteRepetitionQuantifier and + q = epsilonSucc+(mid) and + not mid = q + ) + ) and + stateInsideBacktracking(r1) and + stateInsideBacktracking(r2) +} + +/** + * Gets the state pair `(q1, q2)` or `(q2, q1)`; note that only + * one or the other is defined. + */ +StatePair mkStatePair(State q1, State q2) { + result = MkStatePair(q1, q2) or result = MkStatePair(q2, q1) +} + +/** + * Holds if there are transitions from the components of `q` to the corresponding + * components of `r` labelled with `s1` and `s2`, respectively. + */ +predicate step(StatePair q, InputSymbol s1, InputSymbol s2, StatePair r) { + exists(State r1, State r2 | step(q, s1, s2, r1, r2) and r = mkStatePair(r1, r2)) +} + +/** + * Holds if there are transitions from the components of `q` to `r1` and `r2` + * labelled with `s1` and `s2`, respectively. + * + * We only consider transitions where the resulting states `(r1, r2)` are both + * inside a repetition that might backtrack. + */ +pragma[noopt] +predicate step(StatePair q, InputSymbol s1, InputSymbol s2, State r1, State r2) { + exists(State q1, State q2 | q.getLeft() = q1 and q.getRight() = q2 | + deltaClosed(q1, s1, r1) and + deltaClosed(q2, s2, r2) and + // use noopt to force the join on `intersect` to happen last. + exists(intersect(s1, s2)) + ) and + stateInsideBacktracking(r1) and + stateInsideBacktracking(r2) +} + +private newtype TTrace = + Nil() or + Step(InputSymbol s1, InputSymbol s2, TTrace t) { + exists(StatePair p | + isReachableFromFork(_, p, t, _) and + step(p, s1, s2, _) + ) + or + t = Nil() and isFork(_, s1, s2, _, _) + } + +/** + * A list of pairs of input symbols that describe a path in the product automaton + * starting from some fork state. + */ +class Trace extends TTrace { + /** Gets a textual representation of this element. */ + string toString() { + this = Nil() and result = "Nil()" + or + exists(InputSymbol s1, InputSymbol s2, Trace t | this = Step(s1, s2, t) | + result = "Step(" + s1 + ", " + s2 + ", " + t + ")" + ) + } +} + +/** + * Gets a string corresponding to the trace `t`. + */ +string concretise(Trace t) { + t = Nil() and result = "" + or + exists(InputSymbol s1, InputSymbol s2, Trace rest | t = Step(s1, s2, rest) | + result = concretise(rest) + intersect(s1, s2) + ) +} + +/** + * Holds if `r` is reachable from `(fork, fork)` under input `w`, and there is + * a path from `r` back to `(fork, fork)` with `rem` steps. + */ +predicate isReachableFromFork(State fork, StatePair r, Trace w, int rem) { + // base case + exists(InputSymbol s1, InputSymbol s2, State q1, State q2 | + isFork(fork, s1, s2, q1, q2) and + r = MkStatePair(q1, q2) and + w = Step(s1, s2, Nil()) and + rem = statePairDist(r, MkStatePair(fork, fork)) + ) + or + // recursive case + exists(StatePair p, Trace v, InputSymbol s1, InputSymbol s2 | + isReachableFromFork(fork, p, v, rem + 1) and + step(p, s1, s2, r) and + w = Step(s1, s2, v) and + rem >= statePairDist(r, MkStatePair(fork, fork)) + ) +} + +/** + * Gets a state in the product automaton from which `(fork, fork)` is + * reachable in zero or more epsilon transitions. + */ +StatePair getAForkPair(State fork) { + isFork(fork, _, _, _, _) and + result = MkStatePair(epsilonPred*(fork), epsilonPred*(fork)) +} + +/** + * Holds if `fork` is a pumpable fork with word `w`. + */ +predicate isPumpable(State fork, string w) { + exists(StatePair q, Trace t | + isReachableFromFork(fork, q, t, _) and + q = getAForkPair(fork) and + w = concretise(t) + ) +} + +/** + * An instantiation of `ReDoSConfiguration` for exponential backtracking. + */ +class ExponentialReDoSConfiguration extends ReDoSConfiguration { + ExponentialReDoSConfiguration() { this = "ExponentialReDoSConfiguration" } + + override predicate isReDoSCandidate(State state, string pump) { isPumpable(state, pump) } +} diff --git a/python/ql/src/semmle/python/regex/ReDoSUtil.qll b/python/ql/src/semmle/python/regex/ReDoSUtil.qll new file mode 100644 index 000000000000..40b05825bc6d --- /dev/null +++ b/python/ql/src/semmle/python/regex/ReDoSUtil.qll @@ -0,0 +1,1034 @@ +/** + * Provides classes for working with regular expressions that can + * perform backtracking in superlinear/exponential time. + * + * This module contains a number of utility predicates for compiling a regular expression into a NFA and reasoning about this NFA. + * + * The `ReDoSConfiguration` contains a `isReDoSCandidate` predicate that is used to + * to determine which states the prefix/suffix search should happen on. + * There is only meant to exist one `ReDoSConfiguration` at a time. + * + * The predicate `hasReDoSResult` outputs a de-duplicated set of + * states that will cause backtracking (a rejecting suffix exists). + */ + +import RegExpTreeView + +/** + * A configuration for which parts of a regular expression should be considered relevant for + * the different predicates in `ReDoS.qll`. + * Used to adjust the computations for either superlinear or exponential backtracking. + */ +abstract class ReDoSConfiguration extends string { + bindingset[this] + ReDoSConfiguration() { any() } + + /** + * Holds if `state` with the pump string `pump` is a candidate for a + * ReDoS vulnerable state. + * This is used to determine which states are considered for the prefix/suffix construction. + */ + abstract predicate isReDoSCandidate(State state, string pump); +} + +/** + * Holds if repeating `pump' starting at `state` is a candidate for causing backtracking. + * No check whether a rejected suffix exists has been made. + */ +private predicate isReDoSCandidate(State state, string pump) { + any(ReDoSConfiguration conf).isReDoSCandidate(state, pump) and + ( + not any(ReDoSConfiguration conf).isReDoSCandidate(epsilonSucc+(state), _) + or + epsilonSucc+(state) = state and + state = + max(State s, Location l | + s = epsilonSucc+(state) and + l = s.getRepr().getLocation() and + any(ReDoSConfiguration conf).isReDoSCandidate(s, _) and + s.getRepr() instanceof InfiniteRepetitionQuantifier + | + s order by l.getStartLine(), l.getStartColumn(), l.getEndColumn(), l.getEndLine() + ) + ) +} + +/** + * Gets the char after `c` (from a simplified ASCII table). + */ +private string nextChar(string c) { exists(int code | code = ascii(c) | code + 1 = ascii(result)) } + +/** + * Gets an approximation for the ASCII code for `char`. + * Only the easily printable chars are included (so no newline, tab, null, etc). + */ +private int ascii(string char) { + char = + rank[result](string c | + c = + "! \"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~" + .charAt(_) + ) +} + +/** + * A branch in a disjunction that is the root node in a literal, or a literal + * whose root node is not a disjunction. + */ +class RegExpRoot extends RegExpTerm { + RegExpParent parent; + + RegExpRoot() { + exists(RegExpAlt alt | + alt.isRootTerm() and + this = alt.getAChild() and + parent = alt.getParent() + ) + or + this.isRootTerm() and + not this instanceof RegExpAlt and + parent = this.getParent() + } + + /** + * Holds if this root term is relevant to the ReDoS analysis. + */ + predicate isRelevant() { + // there is at least one repetition + getRoot(any(InfiniteRepetitionQuantifier q)) = this and + // there are no lookbehinds + not exists(RegExpLookbehind lbh | getRoot(lbh) = this) and + // is actually used as a RegExp + isUsedAsRegExp() and + // not excluded for library specific reasons + not isExcluded(getRootTerm().getParent()) + } +} + +/** + * A constant in a regular expression that represents valid Unicode character(s). + */ +private class RegexpCharacterConstant extends RegExpConstant { + RegexpCharacterConstant() { this.isCharacter() } +} + +/** + * Holds if `term` is the chosen canonical representative for all terms with string representation `str`. + * + * Using canonical representatives gives a huge performance boost when working with tuples containing multiple `InputSymbol`s. + * The number of `InputSymbol`s is decreased by 3 orders of magnitude or more in some larger benchmarks. + */ +private predicate isCanonicalTerm(RegExpTerm term, string str) { + term = + rank[1](RegExpTerm t, Location loc, File file | + loc = t.getLocation() and + file = t.getFile() and + str = t.getRawValue() + | + t order by t.getFile().getRelativePath(), loc.getStartLine(), loc.getStartColumn() + ) +} + +/** + * An abstract input symbol, representing a set of concrete characters. + */ +private newtype TInputSymbol = + /** An input symbol corresponding to character `c`. */ + Char(string c) { + c = any(RegexpCharacterConstant cc | getRoot(cc).isRelevant()).getValue().charAt(_) + } or + /** + * An input symbol representing all characters matched by + * a (non-universal) character class that has string representation `charClassString`. + */ + CharClass(string charClassString) { + exists(RegExpTerm term | term.getRawValue() = charClassString | getRoot(term).isRelevant()) and + exists(RegExpTerm recc | isCanonicalTerm(recc, charClassString) | + recc instanceof RegExpCharacterClass and + not recc.(RegExpCharacterClass).isUniversalClass() + or + recc instanceof RegExpCharacterClassEscape + ) + } or + /** An input symbol representing all characters matched by `.`. */ + Dot() or + /** An input symbol representing all characters. */ + Any() or + /** An epsilon transition in the automaton. */ + Epsilon() + +/** + * Gets the canonical CharClass for `term`. + */ +CharClass getCanonicalCharClass(RegExpTerm term) { + exists(string str | isCanonicalTerm(term, str) | result = CharClass(str)) +} + +/** + * Holds if `a` and `b` are input symbols from the same regexp. + */ +private predicate sharesRoot(TInputSymbol a, TInputSymbol b) { + exists(RegExpRoot root | + belongsTo(a, root) and + belongsTo(b, root) + ) +} + +/** + * Holds if the `a` is an input symbol from a regexp that has root `root`. + */ +private predicate belongsTo(TInputSymbol a, RegExpRoot root) { + exists(State s | getRoot(s.getRepr()) = root | + delta(s, a, _) + or + delta(_, a, s) + ) +} + +/** + * An abstract input symbol, representing a set of concrete characters. + */ +class InputSymbol extends TInputSymbol { + InputSymbol() { not this instanceof Epsilon } + + /** + * Gets a string representation of this input symbol. + */ + string toString() { + this = Char(result) + or + this = CharClass(result) + or + this = Dot() and result = "." + or + this = Any() and result = "[^]" + } +} + +/** + * An abstract input symbol that represents a character class. + */ +abstract private class CharacterClass extends InputSymbol { + /** + * Gets a character that is relevant for intersection-tests involving this + * character class. + * + * Specifically, this is any of the characters mentioned explicitly in the + * character class, offset by one if it is inverted. For character class escapes, + * the result is as if the class had been written out as a series of intervals. + * + * This set is large enough to ensure that for any two intersecting character + * classes, one contains a relevant character from the other. + */ + abstract string getARelevantChar(); + + /** + * Holds if this character class matches `char`. + */ + bindingset[char] + abstract predicate matches(string char); + + /** + * Gets a character matched by this character class. + */ + string choose() { result = getARelevantChar() and matches(result) } +} + +/** + * Provides implementations for `CharacterClass`. + */ +private module CharacterClasses { + /** + * Holds if the character class `cc` has a child (constant or range) that matches `char`. + */ + pragma[noinline] + predicate hasChildThatMatches(RegExpCharacterClass cc, string char) { + exists(getCanonicalCharClass(cc)) and + exists(RegExpTerm child | child = cc.getAChild() | + char = child.(RegexpCharacterConstant).getValue() + or + rangeMatchesOnLetterOrDigits(child, char) + or + not rangeMatchesOnLetterOrDigits(child, _) and + char = getARelevantChar() and + exists(string lo, string hi | child.(RegExpCharacterRange).isRange(lo, hi) | + lo <= char and + char <= hi + ) + or + exists(RegExpCharacterClassEscape escape | escape = child | + escape.getValue() = escape.getValue().toLowerCase() and + classEscapeMatches(escape.getValue(), char) + or + char = getARelevantChar() and + escape.getValue() = escape.getValue().toUpperCase() and + not classEscapeMatches(escape.getValue().toLowerCase(), char) + ) + ) + } + + /** + * Holds if `range` is a range on lower-case, upper-case, or digits, and matches `char`. + * This predicate is used to restrict the searchspace for ranges by only joining `getAnyPossiblyMatchedChar` + * on a few ranges. + */ + private predicate rangeMatchesOnLetterOrDigits(RegExpCharacterRange range, string char) { + exists(string lo, string hi | + range.isRange(lo, hi) and lo = lowercaseLetter() and hi = lowercaseLetter() + | + lo <= char and + char <= hi and + char = lowercaseLetter() + ) + or + exists(string lo, string hi | + range.isRange(lo, hi) and lo = upperCaseLetter() and hi = upperCaseLetter() + | + lo <= char and + char <= hi and + char = upperCaseLetter() + ) + or + exists(string lo, string hi | range.isRange(lo, hi) and lo = digit() and hi = digit() | + lo <= char and + char <= hi and + char = digit() + ) + } + + private string lowercaseLetter() { result = "abdcefghijklmnopqrstuvwxyz".charAt(_) } + + private string upperCaseLetter() { result = "ABCDEFGHIJKLMNOPQRSTUVWXYZ".charAt(_) } + + private string digit() { result = [0 .. 9].toString() } + + /** + * Gets a char that could be matched by a regular expression. + * Includes all printable ascii chars, all constants mentioned in a regexp, and all chars matches by the regexp `/\s|\d|\w/`. + */ + string getARelevantChar() { + exists(ascii(result)) + or + exists(RegexpCharacterConstant c | result = c.getValue().charAt(_)) + or + classEscapeMatches(_, result) + } + + /** + * Gets a char that is mentioned in the character class `c`. + */ + private string getAMentionedChar(RegExpCharacterClass c) { + exists(RegExpTerm child | child = c.getAChild() | + result = child.(RegexpCharacterConstant).getValue() + or + child.(RegExpCharacterRange).isRange(result, _) + or + child.(RegExpCharacterRange).isRange(_, result) + or + exists(RegExpCharacterClassEscape escape | child = escape | + result = min(string s | classEscapeMatches(escape.getValue().toLowerCase(), s)) + or + result = max(string s | classEscapeMatches(escape.getValue().toLowerCase(), s)) + ) + ) + } + + /** + * An implementation of `CharacterClass` for positive (non inverted) character classes. + */ + private class PositiveCharacterClass extends CharacterClass { + RegExpCharacterClass cc; + + PositiveCharacterClass() { this = getCanonicalCharClass(cc) and not cc.isInverted() } + + override string getARelevantChar() { result = getAMentionedChar(cc) } + + override predicate matches(string char) { hasChildThatMatches(cc, char) } + } + + /** + * An implementation of `CharacterClass` for inverted character classes. + */ + private class InvertedCharacterClass extends CharacterClass { + RegExpCharacterClass cc; + + InvertedCharacterClass() { this = getCanonicalCharClass(cc) and cc.isInverted() } + + override string getARelevantChar() { + result = nextChar(getAMentionedChar(cc)) or + nextChar(result) = getAMentionedChar(cc) + } + + bindingset[char] + override predicate matches(string char) { not hasChildThatMatches(cc, char) } + } + + /** + * Holds if the character class escape `clazz` (\d, \s, or \w) matches `char`. + */ + pragma[noinline] + private predicate classEscapeMatches(string clazz, string char) { + clazz = "d" and + char = "0123456789".charAt(_) + or + clazz = "s" and + ( + char = [" ", "\t", "\r", "\n"] + or + char = getARelevantChar() and + char.regexpMatch("\\u000b|\\u000c") // \v|\f (vertical tab | form feed) + ) + or + clazz = "w" and + char = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_".charAt(_) + } + + /** + * An implementation of `CharacterClass` for \d, \s, and \w. + */ + private class PositiveCharacterClassEscape extends CharacterClass { + RegExpCharacterClassEscape cc; + + PositiveCharacterClassEscape() { + this = getCanonicalCharClass(cc) and cc.getValue() = ["d", "s", "w"] + } + + override string getARelevantChar() { + cc.getValue() = "d" and + result = ["0", "9"] + or + cc.getValue() = "s" and + result = [" "] + or + cc.getValue() = "w" and + result = ["a", "Z", "_", "0", "9"] + } + + override predicate matches(string char) { classEscapeMatches(cc.getValue(), char) } + + override string choose() { + cc.getValue() = "d" and + result = "9" + or + cc.getValue() = "s" and + result = [" "] + or + cc.getValue() = "w" and + result = "a" + } + } + + /** + * An implementation of `CharacterClass` for \D, \S, and \W. + */ + private class NegativeCharacterClassEscape extends CharacterClass { + RegExpCharacterClassEscape cc; + + NegativeCharacterClassEscape() { + this = getCanonicalCharClass(cc) and cc.getValue() = ["D", "S", "W"] + } + + override string getARelevantChar() { + cc.getValue() = "D" and + result = ["a", "Z", "!"] + or + cc.getValue() = "S" and + result = ["a", "9", "!"] + or + cc.getValue() = "W" and + result = [" ", "!"] + } + + bindingset[char] + override predicate matches(string char) { + not classEscapeMatches(cc.getValue().toLowerCase(), char) + } + } +} + +private class EdgeLabel extends TInputSymbol { + string toString() { + this = Epsilon() and result = "" + or + exists(InputSymbol s | this = s and result = s.toString()) + } +} + +/** + * Gets the state before matching `t`. + */ +pragma[inline] +private State before(RegExpTerm t) { result = Match(t, 0) } + +/** + * Gets a state the NFA may be in after matching `t`. + */ +private State after(RegExpTerm t) { + exists(RegExpAlt alt | t = alt.getAChild() | result = after(alt)) + or + exists(RegExpSequence seq, int i | t = seq.getChild(i) | + result = before(seq.getChild(i + 1)) + or + i + 1 = seq.getNumChild() and result = after(seq) + ) + or + exists(RegExpGroup grp | t = grp.getAChild() | result = after(grp)) + or + exists(RegExpStar star | t = star.getAChild() | result = before(star)) + or + exists(RegExpPlus plus | t = plus.getAChild() | + result = before(plus) or + result = after(plus) + ) + or + exists(RegExpOpt opt | t = opt.getAChild() | result = after(opt)) + or + exists(RegExpRoot root | t = root | result = AcceptAnySuffix(root)) +} + +/** + * Holds if the NFA has a transition from `q1` to `q2` labelled with `lbl`. + */ +predicate delta(State q1, EdgeLabel lbl, State q2) { + exists(RegexpCharacterConstant s, int i | + q1 = Match(s, i) and + lbl = Char(s.getValue().charAt(i)) and + ( + q2 = Match(s, i + 1) + or + s.getValue().length() = i + 1 and + q2 = after(s) + ) + ) + or + exists(RegExpDot dot | q1 = before(dot) and q2 = after(dot) | + if dot.getLiteral().isDotAll() then lbl = Any() else lbl = Dot() + ) + or + exists(RegExpCharacterClass cc | + cc.isUniversalClass() and q1 = before(cc) and lbl = Any() and q2 = after(cc) + or + q1 = before(cc) and + lbl = CharClass(cc.getRawValue()) and + q2 = after(cc) + ) + or + exists(RegExpCharacterClassEscape cc | + q1 = before(cc) and + lbl = CharClass(cc.getRawValue()) and + q2 = after(cc) + ) + or + exists(RegExpAlt alt | lbl = Epsilon() | q1 = before(alt) and q2 = before(alt.getAChild())) + or + exists(RegExpSequence seq | lbl = Epsilon() | q1 = before(seq) and q2 = before(seq.getChild(0))) + or + exists(RegExpGroup grp | lbl = Epsilon() | q1 = before(grp) and q2 = before(grp.getChild(0))) + or + exists(RegExpStar star | lbl = Epsilon() | + q1 = before(star) and q2 = before(star.getChild(0)) + or + q1 = before(star) and q2 = after(star) + ) + or + exists(RegExpPlus plus | lbl = Epsilon() | q1 = before(plus) and q2 = before(plus.getChild(0))) + or + exists(RegExpOpt opt | lbl = Epsilon() | + q1 = before(opt) and q2 = before(opt.getChild(0)) + or + q1 = before(opt) and q2 = after(opt) + ) + or + exists(RegExpRoot root | q1 = AcceptAnySuffix(root) | + lbl = Any() and q2 = q1 + or + lbl = Epsilon() and q2 = Accept(root) + ) + or + exists(RegExpRoot root | q1 = Match(root, 0) | lbl = Any() and q2 = q1) + or + exists(RegExpDollar dollar | q1 = before(dollar) | + lbl = Epsilon() and q2 = Accept(getRoot(dollar)) + ) +} + +/** + * Gets a state that `q` has an epsilon transition to. + */ +State epsilonSucc(State q) { delta(q, Epsilon(), result) } + +/** + * Gets a state that has an epsilon transition to `q`. + */ +State epsilonPred(State q) { q = epsilonSucc(result) } + +/** + * Holds if there is a state `q` that can be reached from `q1` + * along epsilon edges, such that there is a transition from + * `q` to `q2` that consumes symbol `s`. + */ +predicate deltaClosed(State q1, InputSymbol s, State q2) { delta(epsilonSucc*(q1), s, q2) } + +/** + * Gets the root containing the given term, that is, the root of the literal, + * or a branch of the root disjunction. + */ +RegExpRoot getRoot(RegExpTerm term) { + result = term or + result = getRoot(term.getParent()) +} + +private newtype TState = + Match(RegExpTerm t, int i) { + getRoot(t).isRelevant() and + ( + i = 0 + or + exists(t.(RegexpCharacterConstant).getValue().charAt(i)) + ) + } or + Accept(RegExpRoot l) { l.isRelevant() } or + AcceptAnySuffix(RegExpRoot l) { l.isRelevant() } + +/** + * Gets a state that is about to match the regular expression `t`. + */ +State mkMatch(RegExpTerm t) { result = Match(t, 0) } + +/** + * A state in the NFA corresponding to a regular expression. + * + * Each regular expression literal `l` has one accepting state + * `Accept(l)`, one state that accepts all suffixes `AcceptAnySuffix(l)`, + * and a state `Match(t, i)` for every subterm `t`, + * which represents the state of the NFA before starting to + * match `t`, or the `i`th character in `t` if `t` is a constant. + */ +class State extends TState { + RegExpTerm repr; + + State() { + this = Match(repr, _) or + this = Accept(repr) or + this = AcceptAnySuffix(repr) + } + + /** + * Gets a string representation for this state in a regular expression. + */ + string toString() { + exists(int i | this = Match(repr, i) | result = "Match(" + repr + "," + i + ")") + or + this instanceof Accept and + result = "Accept(" + repr + ")" + or + this instanceof AcceptAnySuffix and + result = "AcceptAny(" + repr + ")" + } + + /** + * Gets the location for this state. + */ + Location getLocation() { result = repr.getLocation() } + + /** + * Gets the term represented by this state. + */ + RegExpTerm getRepr() { result = repr } +} + +/** + * Gets the minimum char that is matched by both the character classes `c` and `d`. + */ +private string getMinOverlapBetweenCharacterClasses(CharacterClass c, CharacterClass d) { + result = min(getAOverlapBetweenCharacterClasses(c, d)) +} + +/** + * Gets a char that is matched by both the character classes `c` and `d`. + * And `c` and `d` is not the same character class. + */ +private string getAOverlapBetweenCharacterClasses(CharacterClass c, CharacterClass d) { + sharesRoot(c, d) and + result = [c.getARelevantChar(), d.getARelevantChar()] and + c.matches(result) and + d.matches(result) and + not c = d +} + +/** + * Gets a character that is represented by both `c` and `d`. + */ +string intersect(InputSymbol c, InputSymbol d) { + (sharesRoot(c, d) or [c, d] = Any()) and + ( + c = Char(result) and + d = getAnInputSymbolMatching(result) + or + result = getMinOverlapBetweenCharacterClasses(c, d) + or + result = c.(CharacterClass).choose() and + ( + d = c + or + d = Dot() and + not (result = "\n" or result = "\r") + or + d = Any() + ) + or + (c = Dot() or c = Any()) and + (d = Dot() or d = Any()) and + result = "a" + ) + or + result = intersect(d, c) +} + +/** + * Gets a symbol that matches `char`. + */ +bindingset[char] +InputSymbol getAnInputSymbolMatching(string char) { + result = Char(char) + or + result.(CharacterClass).matches(char) + or + result = Dot() and + not (char = "\n" or char = "\r") + or + result = Any() +} + +/** + * Predicates for constructing a prefix string that leads to a given state. + */ +private module PrefixConstruction { + /** + * Holds if `state` starts the string matched by the regular expression. + */ + private predicate isStartState(State state) { + state instanceof StateInPumpableRegexp and + ( + state = Match(any(RegExpRoot r), _) + or + exists(RegExpCaret car | state = after(car)) + ) + } + + /** + * Holds if `state` is the textually last start state for the regular expression. + */ + private predicate lastStartState(State state) { + exists(RegExpRoot root | + state = + max(State s, Location l | + isStartState(s) and getRoot(s.getRepr()) = root and l = s.getRepr().getLocation() + | + s + order by + l.getStartLine(), l.getStartColumn(), s.getRepr().toString(), l.getEndColumn(), + l.getEndLine() + ) + ) + } + + /** + * Holds if there exists any transition (Epsilon() or other) from `a` to `b`. + */ + private predicate existsTransition(State a, State b) { delta(a, _, b) } + + /** + * Gets the minimum number of transitions it takes to reach `state` from the `start` state. + */ + int prefixLength(State start, State state) = + shortestDistances(lastStartState/1, existsTransition/2)(start, state, result) + + /** + * Gets the minimum number of transitions it takes to reach `state` from the start state. + */ + private int lengthFromStart(State state) { result = prefixLength(_, state) } + + /** + * Gets a string for which the regular expression will reach `state`. + * + * Has at most one result for any given `state`. + * This predicate will not always have a result even if there is a ReDoS issue in + * the regular expression. + */ + string prefix(State state) { + lastStartState(state) and + result = "" + or + // the search stops past the last redos candidate state. + lengthFromStart(state) <= max(lengthFromStart(any(State s | isReDoSCandidate(s, _)))) and + exists(State prev | + // select a unique predecessor (by an arbitrary measure) + prev = + min(State s, Location loc | + lengthFromStart(s) = lengthFromStart(state) - 1 and + loc = s.getRepr().getLocation() and + delta(s, _, state) + | + s + order by + loc.getStartLine(), loc.getStartColumn(), loc.getEndLine(), loc.getEndColumn(), + s.getRepr().toString() + ) + | + // greedy search for the shortest prefix + result = prefix(prev) and delta(prev, Epsilon(), state) + or + not delta(prev, Epsilon(), state) and + result = prefix(prev) + getCanonicalEdgeChar(prev, state) + ) + } + + /** + * Gets a canonical char for which there exists a transition from `prev` to `next` in the NFA. + */ + private string getCanonicalEdgeChar(State prev, State next) { + result = + min(string c | delta(prev, any(InputSymbol symbol | c = intersect(Any(), symbol)), next)) + } + + /** + * A state within a regular expression that has a pumpable state. + */ + class StateInPumpableRegexp extends State { + pragma[noinline] + StateInPumpableRegexp() { + exists(State s | isReDoSCandidate(s, _) | getRoot(s.getRepr()) = getRoot(this.getRepr())) + } + } +} + +/** + * Predicates for testing the presence of a rejecting suffix. + * + * These predicates are used to ensure that the all states reached from the fork + * by repeating `w` have a rejecting suffix. + * + * For example, a regexp like `/^(a+)+/` will accept any string as long the prefix is + * some number of `"a"`s, and it is therefore not possible to construct a rejecting suffix. + * + * A regexp like `/(a+)+$/` or `/(a+)+b/` trivially has a rejecting suffix, + * as the suffix "X" will cause both the regular expressions to be rejected. + * + * The string `w` is repeated any number of times because it needs to be + * infinitely repeatedable for the attack to work. + * For the regular expression `/((ab)+)*abab/` the accepting state is not reachable from the fork + * using epsilon transitions. But any attempt at repeating `w` will end in a state that accepts all suffixes. + */ +private module SuffixConstruction { + import PrefixConstruction + + /** + * Holds if all states reachable from `fork` by repeating `w` + * are likely rejectable by appending some suffix. + */ + predicate reachesOnlyRejectableSuffixes(State fork, string w) { + isReDoSCandidate(fork, w) and + forex(State next | next = process(fork, w, w.length() - 1) | isLikelyRejectable(next)) + } + + /** + * Holds if there likely exists a suffix starting from `s` that leads to the regular expression being rejected. + * This predicate might find impossible suffixes when searching for suffixes of length > 1, which can cause FPs. + */ + pragma[noinline] + private predicate isLikelyRejectable(StateInPumpableRegexp s) { + // exists a reject edge with some char. + hasRejectEdge(s) + or + hasEdgeToLikelyRejectable(s) + or + // stopping here is rejection + isRejectState(s) + } + + /** + * Holds if `s` is not an accept state, and there is no epsilon transition to an accept state. + */ + predicate isRejectState(StateInPumpableRegexp s) { not epsilonSucc*(s) = Accept(_) } + + /** + * Holds if there is likely a non-empty suffix leading to rejection starting in `s`. + */ + pragma[noopt] + predicate hasEdgeToLikelyRejectable(StateInPumpableRegexp s) { + // all edges (at least one) with some char leads to another state that is rejectable. + // the `next` states might not share a common suffix, which can cause FPs. + exists(string char | char = hasEdgeToLikelyRejectableHelper(s) | + // noopt to force `hasEdgeToLikelyRejectableHelper` to be first in the join-order. + exists(State next | deltaClosedChar(s, char, next) | isLikelyRejectable(next)) and + forall(State next | deltaClosedChar(s, char, next) | isLikelyRejectable(next)) + ) + } + + /** + * Gets a char for there exists a transition away from `s`, + * and `s` has not been found to be rejectable by `hasRejectEdge` or `isRejectState`. + */ + pragma[noinline] + private string hasEdgeToLikelyRejectableHelper(StateInPumpableRegexp s) { + not hasRejectEdge(s) and + not isRejectState(s) and + deltaClosedChar(s, result, _) + } + + /** + * Holds if there is a state `next` that can be reached from `prev` + * along epsilon edges, such that there is a transition from + * `prev` to `next` that the character symbol `char`. + */ + predicate deltaClosedChar(StateInPumpableRegexp prev, string char, StateInPumpableRegexp next) { + deltaClosed(prev, getAnInputSymbolMatchingRelevant(char), next) + } + + pragma[noinline] + InputSymbol getAnInputSymbolMatchingRelevant(string char) { + char = relevant(_) and + result = getAnInputSymbolMatching(char) + } + + /** + * Gets a char used for finding possible suffixes inside `root`. + */ + pragma[noinline] + private string relevant(RegExpRoot root) { + exists(ascii(result)) + or + exists(InputSymbol s | belongsTo(s, root) | result = intersect(s, _)) + or + // The characters from `hasSimpleRejectEdge`. Only `\n` is really needed (as `\n` is not in the `ascii` relation). + // The three chars must be kept in sync with `hasSimpleRejectEdge`. + result = ["|", "\n", "Z"] + } + + /** + * Holds if there exists a `char` such that there is no edge from `s` labeled `char` in our NFA. + * The NFA does not model reject states, so the above is the same as saying there is a reject edge. + */ + private predicate hasRejectEdge(State s) { + hasSimpleRejectEdge(s) + or + not hasSimpleRejectEdge(s) and + exists(string char | char = relevant(getRoot(s.getRepr())) | not deltaClosedChar(s, char, _)) + } + + /** + * Holds if there is no edge from `s` labeled with "|", "\n", or "Z" in our NFA. + * This predicate is used as a cheap pre-processing to speed up `hasRejectEdge`. + */ + private predicate hasSimpleRejectEdge(State s) { + // The three chars were chosen arbitrarily. The three chars must be kept in sync with `relevant`. + exists(string char | char = ["|", "\n", "Z"] | not deltaClosedChar(s, char, _)) + } + + /** + * Gets a state that can be reached from pumpable `fork` consuming all + * chars in `w` any number of times followed by the first `i+1` characters of `w`. + */ + pragma[noopt] + private State process(State fork, string w, int i) { + exists(State prev | prev = getProcessPrevious(fork, i, w) | + exists(string char, InputSymbol sym | + char = w.charAt(i) and + deltaClosed(prev, sym, result) and + // noopt to prevent joining `prev` with all possible `chars` that could transition away from `prev`. + // Instead only join with the set of `chars` where a relevant `InputSymbol` has already been found. + sym = getAProcessInputSymbol(char) + ) + ) + } + + /** + * Gets a state that can be reached from pumpable `fork` consuming all + * chars in `w` any number of times followed by the first `i` characters of `w`. + */ + private State getProcessPrevious(State fork, int i, string w) { + isReDoSCandidate(fork, w) and + ( + i = 0 and result = fork + or + result = process(fork, w, i - 1) + or + // repeat until fixpoint + i = 0 and + result = process(fork, w, w.length() - 1) + ) + } + + /** + * Gets an InputSymbol that matches `char`. + * The predicate is specialized to only have a result for the `char`s that are relevant for the `process` predicate. + */ + private InputSymbol getAProcessInputSymbol(string char) { + char = getAProcessChar() and + result = getAnInputSymbolMatching(char) + } + + /** + * Gets a `char` that occurs in a `pump` string. + */ + private string getAProcessChar() { result = any(string s | isReDoSCandidate(_, s)).charAt(_) } +} + +/** + * Gets the result of backslash-escaping newlines, carriage-returns and + * backslashes in `s`. + */ +bindingset[s] +private string escape(string s) { + result = + s.replaceAll("\\", "\\\\") + .replaceAll("\n", "\\n") + .replaceAll("\r", "\\r") + .replaceAll("\t", "\\t") +} + +/** + * Gets `str` with the last `i` characters moved to the front. + * + * We use this to adjust the pump string to match with the beginning of + * a RegExpTerm, so it doesn't start in the middle of a constant. + */ +bindingset[str, i] +private string rotate(string str, int i) { + result = str.suffix(str.length() - i) + str.prefix(str.length() - i) +} + +/** + * Holds if `term` may cause superlinear backtracking on strings containing many repetitions of `pump`. + * Gets the shortest string that causes superlinear backtracking. + */ +private predicate isReDoSAttackable(RegExpTerm term, string pump, State s) { + exists(int i, string c | s = Match(term, i) | + c = + min(string w | + any(ReDoSConfiguration conf).isReDoSCandidate(s, w) and + SuffixConstruction::reachesOnlyRejectableSuffixes(s, w) + | + w order by w.length(), w + ) and + pump = escape(rotate(c, i)) + ) +} + +/** + * Holds if the state `s` (represented by the term `t`) can have backtracking with repetitions of `pump`. + * + * `prefixMsg` contains a friendly message for a prefix that reaches `s` (or `prefixMsg` is the empty string if the prefix is empty or if no prefix could be found). + */ +predicate hasReDoSResult(RegExpTerm t, string pump, State s, string prefixMsg) { + isReDoSAttackable(t, pump, s) and + ( + prefixMsg = "starting with '" + escape(PrefixConstruction::prefix(s)) + "' and " and + not PrefixConstruction::prefix(s) = "" + or + PrefixConstruction::prefix(s) = "" and prefixMsg = "" + or + not exists(PrefixConstruction::prefix(s)) and prefixMsg = "" + ) +} diff --git a/python/ql/src/semmle/python/regex/RegExpTreeView.qll b/python/ql/src/semmle/python/regex/RegExpTreeView.qll new file mode 100644 index 000000000000..addd192cafaa --- /dev/null +++ b/python/ql/src/semmle/python/regex/RegExpTreeView.qll @@ -0,0 +1,15 @@ +/** + * This module should provide a class hierarchy corresponding to a parse tree of regular expressions. + */ + +import python +import semmle.python.RegexTreeView + +/** + * Holds if the regular expression should not be considered. + * + * For javascript we make the pragmatic performance optimization to ignore files we did not extract. + */ +predicate isExcluded(RegExpParent parent) { + not exists(parent.getRegex().getLocation().getFile().getRelativePath()) +} diff --git a/python/ql/src/semmle/python/regex/SuperlinearBackTracking.qll b/python/ql/src/semmle/python/regex/SuperlinearBackTracking.qll new file mode 100644 index 000000000000..0bbff12b49d7 --- /dev/null +++ b/python/ql/src/semmle/python/regex/SuperlinearBackTracking.qll @@ -0,0 +1,449 @@ +/** + * Provides classes for working with regular expressions that can + * perform backtracking in superlinear time. + */ + +import ReDoSUtil + +/* + * This module implements the analysis described in the paper: + * Valentin Wustholz, Oswaldo Olivo, Marijn J. H. Heule, and Isil Dillig: + * Static Detection of DoS Vulnerabilities in + * Programs that use Regular Expressions + * (Extended Version). + * (https://arxiv.org/pdf/1701.04045.pdf) + * + * Theorem 3 from the paper describes the basic idea. + * + * The following explains the idea using variables and predicate names that are used in the implementation: + * We consider a pair of repetitions, which we will call `pivot` and `succ`. + * + * We create a product automaton of 3-tuples of states (see `StateTuple`). + * There exists a transition `(a,b,c) -> (d,e,f)` in the product automaton + * iff there exists three transitions in the NFA `a->d, b->e, c->f` where those three + * transitions all match a shared character `char`. (see `getAThreewayIntersect`) + * + * We start a search in the product automaton at `(pivot, pivot, succ)`, + * and search for a series of transitions (a `Trace`), such that we end + * at `(pivot, succ, succ)` (see `isReachableFromStartTuple`). + * + * For example, consider the regular expression `/^\d*5\w*$/`. + * The search will start at the tuple `(\d*, \d*, \w*)` and search + * for a path to `(\d*, \w*, \w*)`. + * This path exists, and consists of a single transition in the product automaton, + * where the three corresponding NFA edges all match the character `"5"`. + * + * The start-state in the NFA has an any-transition to itself, this allows us to + * flag regular expressions such as `/a*$/` - which does not have a start anchor - + * and can thus start matching anywhere. + * + * The implementation is not perfect. + * It has the same suffix detection issue as the `js/redos` query, which can cause false positives. + * It also doesn't find all transitions in the product automaton, which can cause false negatives. + */ + +/** + * An instantiaion of `ReDoSConfiguration` for superlinear ReDoS. + */ +class SuperLinearReDoSConfiguration extends ReDoSConfiguration { + SuperLinearReDoSConfiguration() { this = "SuperLinearReDoSConfiguration" } + + override predicate isReDoSCandidate(State state, string pump) { isPumpable(_, state, pump) } +} + +/** + * Gets any root (start) state of a regular expression. + */ +private State getRootState() { result = mkMatch(any(RegExpRoot r)) } + +private newtype TStateTuple = + MkStateTuple(State q1, State q2, State q3) { + // starts at (pivot, pivot, succ) + isStartLoops(q1, q3) and q1 = q2 + or + step(_, _, _, _, q1, q2, q3) and FeasibleTuple::isFeasibleTuple(q1, q2, q3) + } + +/** + * A state in the product automaton. + * The product automaton contains 3-tuples of states. + * + * We lazily only construct those states that we are actually + * going to need. + * Either a start state `(pivot, pivot, succ)`, or a state + * where there exists a transition from an already existing state. + * + * The exponential variant of this query (`js/redos`) uses an optimization + * trick where `q1 <= q2`. This trick cannot be used here as the order + * of the elements matter. + */ +class StateTuple extends TStateTuple { + State q1; + State q2; + State q3; + + StateTuple() { this = MkStateTuple(q1, q2, q3) } + + /** + * Gest a string repesentation of this tuple. + */ + string toString() { result = "(" + q1 + ", " + q2 + ", " + q3 + ")" } + + /** + * Holds if this tuple is `(r1, r2, r3)`. + */ + pragma[noinline] + predicate isTuple(State r1, State r2, State r3) { r1 = q1 and r2 = q2 and r3 = q3 } +} + +/** + * A module for determining feasible tuples for the product automaton. + * + * The implementation is split into many predicates for performance reasons. + */ +private module FeasibleTuple { + /** + * Holds if the tuple `(r1, r2, r3)` might be on path from a start-state to an end-state in the product automaton. + */ + pragma[inline] + predicate isFeasibleTuple(State r1, State r2, State r3) { + // The first element is either inside a repetition (or the start state itself) + isRepetitionOrStart(r1) and + // The last element is inside a repetition + stateInsideRepetition(r3) and + // The states are reachable in the NFA in the order r1 -> r2 -> r3 + delta+(r1) = r2 and + delta+(r2) = r3 and + // The first element can reach a beginning (the "pivot" state in a `(pivot, succ)` pair). + canReachABeginning(r1) and + // The last element can reach a target (the "succ" state in a `(pivot, succ)` pair). + canReachATarget(r3) + } + + /** + * Holds if `s` is either inside a repetition, or is the start state (which is a repetition). + */ + pragma[noinline] + private predicate isRepetitionOrStart(State s) { stateInsideRepetition(s) or s = getRootState() } + + /** + * Holds if state `s` might be inside a backtracking repetition. + */ + pragma[noinline] + private predicate stateInsideRepetition(State s) { + s.getRepr().getParent*() instanceof InfiniteRepetitionQuantifier + } + + /** + * Holds if there exists a path in the NFA from `s` to a "pivot" state + * (from a `(pivot, succ)` pair that starts the search). + */ + pragma[noinline] + private predicate canReachABeginning(State s) { + delta+(s) = any(State pivot | isStartLoops(pivot, _)) + } + + /** + * Holds if there exists a path in the NFA from `s` to a "succ" state + * (from a `(pivot, succ)` pair that starts the search). + */ + pragma[noinline] + private predicate canReachATarget(State s) { delta+(s) = any(State succ | isStartLoops(_, succ)) } +} + +/** + * Holds if `pivot` and `succ` are a pair of loops that could be the beginning of a quadratic blowup. + * + * There is a slight implementation difference compared to the paper: this predicate requires that `pivot != succ`. + * The case where `pivot = succ` causes exponential backtracking and is handled by the `js/redos` query. + */ +predicate isStartLoops(State pivot, State succ) { + pivot != succ and + succ.getRepr() instanceof InfiniteRepetitionQuantifier and + delta+(pivot) = succ and + ( + pivot.getRepr() instanceof InfiniteRepetitionQuantifier + or + pivot = mkMatch(any(RegExpRoot root)) + ) +} + +/** + * Gets a state for which there exists a transition in the NFA from `s'. + */ +State delta(State s) { delta(s, _, result) } + +/** + * Holds if there are transitions from the components of `q` to the corresponding + * components of `r` labelled with `s1`, `s2`, and `s3`, respectively. + */ +pragma[noinline] +predicate step(StateTuple q, InputSymbol s1, InputSymbol s2, InputSymbol s3, StateTuple r) { + exists(State r1, State r2, State r3 | + step(q, s1, s2, s3, r1, r2, r3) and r = MkStateTuple(r1, r2, r3) + ) +} + +/** + * Holds if there are transitions from the components of `q` to `r1`, `r2`, and `r3 + * labelled with `s1`, `s2`, and `s3`, respectively. + */ +pragma[noopt] +predicate step( + StateTuple q, InputSymbol s1, InputSymbol s2, InputSymbol s3, State r1, State r2, State r3 +) { + exists(State q1, State q2, State q3 | q.isTuple(q1, q2, q3) | + deltaClosed(q1, s1, r1) and + deltaClosed(q2, s2, r2) and + deltaClosed(q3, s3, r3) and + // use noopt to force the join on `getAThreewayIntersect` to happen last. + exists(getAThreewayIntersect(s1, s2, s3)) + ) +} + +/** + * Gets a char that is matched by all the edges `s1`, `s2`, and `s3`. + * + * The result is not complete, and might miss some combination of edges that share some character. + */ +pragma[noinline] +string getAThreewayIntersect(InputSymbol s1, InputSymbol s2, InputSymbol s3) { + result = minAndMaxIntersect(s1, s2) and result = [intersect(s2, s3), intersect(s1, s3)] + or + result = minAndMaxIntersect(s1, s3) and result = [intersect(s2, s3), intersect(s1, s2)] + or + result = minAndMaxIntersect(s2, s3) and result = [intersect(s1, s2), intersect(s1, s3)] +} + +/** + * Gets the minimum and maximum characters that intersect between `a` and `b`. + * This predicate is used to limit the size of `getAThreewayIntersect`. + */ +pragma[noinline] +string minAndMaxIntersect(InputSymbol a, InputSymbol b) { + result = [min(intersect(a, b)), max(intersect(a, b))] +} + +private newtype TTrace = + Nil() or + Step(InputSymbol s1, InputSymbol s2, InputSymbol s3, TTrace t) { + exists(StateTuple p | + isReachableFromStartTuple(_, _, p, t, _) and + step(p, s1, s2, s3, _) + ) + or + exists(State pivot, State succ | isStartLoops(pivot, succ) | + t = Nil() and step(MkStateTuple(pivot, pivot, succ), s1, s2, s3, _) + ) + } + +/** + * A list of tuples of input symbols that describe a path in the product automaton + * starting from some start state. + */ +class Trace extends TTrace { + /** + * Gets a string representation of this Trace that can be used for debug purposes. + */ + string toString() { + this = Nil() and result = "Nil()" + or + exists(InputSymbol s1, InputSymbol s2, InputSymbol s3, Trace t | this = Step(s1, s2, s3, t) | + result = "Step(" + s1 + ", " + s2 + ", " + s3 + ", " + t + ")" + ) + } +} + +/** + * Gets a string corresponding to the trace `t`. + */ +string concretise(Trace t) { + t = Nil() and result = "" + or + exists(InputSymbol s1, InputSymbol s2, InputSymbol s3, Trace rest | t = Step(s1, s2, s3, rest) | + result = concretise(rest) + getAThreewayIntersect(s1, s2, s3) + ) +} + +/** + * Holds if there exists a transition from `r` to `q` in the product automaton. + * Notice that the arguments are flipped, and thus the direction is backwards. + */ +pragma[noinline] +predicate tupleDeltaBackwards(StateTuple q, StateTuple r) { step(r, _, _, _, q) } + +/** + * Holds if `tuple` is an end state in our search. + * That means there exists a pair of loops `(pivot, succ)` such that `tuple = (pivot, succ, succ)`. + */ +predicate isEndTuple(StateTuple tuple) { tuple = getAnEndTuple(_, _) } + +/** + * Gets the minimum length of a path from `r` to some an end state `end`. + * + * The implementation searches backwards from the end-tuple. + * This approach was chosen because it is way more efficient if the first predicate given to `shortestDistances` is small. + * The `end` argument must always be an end state. + */ +int distBackFromEnd(StateTuple r, StateTuple end) = + shortestDistances(isEndTuple/1, tupleDeltaBackwards/2)(end, r, result) + +/** + * Holds if there exists a pair of repetitions `(pivot, succ)` in the regular expression such that: + * `tuple` is reachable from `(pivot, pivot, succ)` in the product automaton, + * and there is a distance of `dist` from `tuple` to the nearest end-tuple `(pivot, succ, succ)`, + * and a path from a start-state to `tuple` follows the transitions in `trace`. + */ +predicate isReachableFromStartTuple(State pivot, State succ, StateTuple tuple, Trace trace, int dist) { + // base case. The first step is inlined to start the search after all possible 1-steps, and not just the ones with the shortest path. + exists(InputSymbol s1, InputSymbol s2, InputSymbol s3, State q1, State q2, State q3 | + isStartLoops(pivot, succ) and + step(MkStateTuple(pivot, pivot, succ), s1, s2, s3, tuple) and + tuple = MkStateTuple(q1, q2, q3) and + trace = Step(s1, s2, s3, Nil()) and + dist = distBackFromEnd(tuple, MkStateTuple(pivot, succ, succ)) + ) + or + // recursive case + exists(StateTuple p, Trace v, InputSymbol s1, InputSymbol s2, InputSymbol s3 | + isReachableFromStartTuple(pivot, succ, p, v, dist + 1) and + dist = isReachableFromStartTupleHelper(pivot, succ, tuple, p, s1, s2, s3) and + trace = Step(s1, s2, s3, v) + ) +} + +/** + * Helper predicate for the recursive case in `isReachableFromStartTuple`. + */ +pragma[noinline] +private int isReachableFromStartTupleHelper( + State pivot, State succ, StateTuple r, StateTuple p, InputSymbol s1, InputSymbol s2, + InputSymbol s3 +) { + result = distBackFromEnd(r, MkStateTuple(pivot, succ, succ)) and + step(p, s1, s2, s3, r) +} + +/** + * Gets the tuple `(pivot, succ, succ)` from the product automaton. + */ +StateTuple getAnEndTuple(State pivot, State succ) { + isStartLoops(pivot, succ) and + result = MkStateTuple(pivot, succ, succ) +} + +/** + * Holds if matching repetitions of `pump` can: + * 1) Transition from `pivot` back to `pivot`. + * 2) Transition from `pivot` to `succ`. + * 3) Transition from `succ` to `succ`. + * + * From theorem 3 in the paper linked in the top of this file we can therefore conclude that + * the regular expression has polynomial backtracking - if a rejecting suffix exists. + * + * This predicate is used by `SuperLinearReDoSConfiguration`, and the final results are + * available in the `hasReDoSResult` predicate. + */ +predicate isPumpable(State pivot, State succ, string pump) { + exists(StateTuple q, Trace t | + isReachableFromStartTuple(pivot, succ, q, t, _) and + q = getAnEndTuple(pivot, succ) and + pump = concretise(t) + ) +} + +/** + * Holds if repetitions of `pump` at `t` will cause polynomial backtracking. + */ +predicate polynimalReDoS(RegExpTerm t, string pump, string prefixMsg, RegExpTerm prev) { + exists(State s, State pivot | + hasReDoSResult(t, pump, s, prefixMsg) and + isPumpable(pivot, s, _) and + prev = pivot.getRepr() + ) +} + +/** + * Holds if `t` matches at least an epsilon symbol. + * + * That is, this term does not restrict the language of the enclosing regular expression. + * + * This is implemented as an under-approximation, and this predicate does not hold for sub-patterns in particular. + */ +private predicate matchesEpsilon(RegExpTerm t) { + t instanceof RegExpStar + or + t instanceof RegExpOpt + or + t.(RegExpRange).getLowerBound() = 0 + or + exists(RegExpTerm child | + child = t.getAChild() and + matchesEpsilon(child) + | + t instanceof RegExpAlt or + t instanceof RegExpGroup or + t instanceof RegExpPlus or + t instanceof RegExpRange + ) + or + matchesEpsilon(t.(RegExpBackRef).getGroup()) + or + forex(RegExpTerm child | child = t.(RegExpSequence).getAChild() | matchesEpsilon(child)) +} + +/** + * Gets a message for why `term` can cause polynomial backtracking. + */ +string getReasonString(RegExpTerm term, string pump, string prefixMsg, RegExpTerm prev) { + polynimalReDoS(term, pump, prefixMsg, prev) and + result = + "Strings " + prefixMsg + "with many repetitions of '" + pump + + "' can start matching anywhere after the start of the preceeding " + prev +} + +/** + * A term that may cause a regular expression engine to perform a + * polynomial number of match attempts, relative to the input length. + */ +class PolynomialBackTrackingTerm extends InfiniteRepetitionQuantifier { + string reason; + string pump; + string prefixMsg; + RegExpTerm prev; + + PolynomialBackTrackingTerm() { + reason = getReasonString(this, pump, prefixMsg, prev) and + // there might be many reasons for this term to have polynomial backtracking - we pick the shortest one. + reason = min(string msg | msg = getReasonString(this, _, _, _) | msg order by msg.length(), msg) + } + + /** + * Holds if all non-empty successors to the polynomial backtracking term matches the end of the line. + */ + predicate isAtEndLine() { + forall(RegExpTerm succ | this.getSuccessor+() = succ and not matchesEpsilon(succ) | + succ instanceof RegExpDollar + ) + } + + /** + * Gets the string that should be repeated to cause this regular expression to perform polynomially. + */ + string getPumpString() { result = pump } + + /** + * Gets a message for which prefix a matching string must start with for this term to cause polynomial backtracking. + */ + string getPrefixMessage() { result = prefixMsg } + + /** + * Gets a predecessor to `this`, which also loops on the pump string, and thereby causes polynomial backtracking. + */ + RegExpTerm getPreviousLoop() { result = prev } + + /** + * Gets the reason for the number of match attempts. + */ + string getReason() { result = reason } +} diff --git a/python/ql/src/semmle/python/security/dataflow/PolynomialReDoS.qll b/python/ql/src/semmle/python/security/dataflow/PolynomialReDoS.qll new file mode 100644 index 000000000000..eace61442eda --- /dev/null +++ b/python/ql/src/semmle/python/security/dataflow/PolynomialReDoS.qll @@ -0,0 +1,177 @@ +/** + * Provides a taint-tracking configuration for detecting polynomial regular expression denial of service (ReDoS) + * vulnerabilities. + */ + +import python +import semmle.python.dataflow.new.DataFlow +import semmle.python.dataflow.new.DataFlow2 +import semmle.python.dataflow.new.TaintTracking +import semmle.python.Concepts +import semmle.python.dataflow.new.RemoteFlowSources +import semmle.python.dataflow.new.BarrierGuards +import semmle.python.RegexTreeView +import semmle.python.ApiGraphs + +/** A configuration for finding uses of compiled regexes. */ +class RegexDefinitionConfiguration extends DataFlow2::Configuration { + RegexDefinitionConfiguration() { this = "RegexDefinitionConfiguration" } + + override predicate isSource(DataFlow::Node source) { source instanceof RegexDefinitonSource } + + override predicate isSink(DataFlow::Node sink) { sink instanceof RegexDefinitionSink } +} + +/** A regex compilation. */ +class RegexDefinitonSource extends DataFlow::CallCfgNode { + DataFlow::Node regexNode; + + RegexDefinitonSource() { + this = API::moduleImport("re").getMember("compile").getACall() and + regexNode in [this.getArg(0), this.getArgByName("pattern")] + } + + /** Gets the regex that is being compiled by this node. */ + RegExpTerm getRegExp() { result.getRegex() = regexNode.asExpr() and result.isRootTerm() } + + /** Gets the data flow node for the regex being compiled by this node. */ + DataFlow::Node getRegexNode() { result = regexNode } +} + +/** A use of a compiled regex. */ +class RegexDefinitionSink extends DataFlow::Node { + RegexExecutionMethod method; + DataFlow::CallCfgNode executingCall; + + RegexDefinitionSink() { + exists(DataFlow::AttrRead reMethod | + executingCall.getFunction() = reMethod and + reMethod.getAttributeName() = method and + this = reMethod.getObject() + ) + } + + /** Gets the method used to execute the regex. */ + RegexExecutionMethod getMethod() { result = method } + + /** Gets the data flow node for the executing call. */ + DataFlow::CallCfgNode getExecutingCall() { result = executingCall } +} + +/** + * A taint-tracking configuration for detecting regular expression denial-of-service vulnerabilities. + */ +class PolynomialReDoSConfiguration extends TaintTracking::Configuration { + PolynomialReDoSConfiguration() { this = "PolynomialReDoSConfiguration" } + + override predicate isSource(DataFlow::Node source) { source instanceof RemoteFlowSource } + + override predicate isSink(DataFlow::Node sink) { sink instanceof PolynomialReDoSSink } +} + +/** A data flow node executing a regex. */ +abstract class RegexExecution extends DataFlow::Node { + /** Gets the data flow node for the regex being compiled by this node. */ + abstract DataFlow::Node getRegexNode(); + + /** Gets a dataflow node for the string to be searched or matched against. */ + abstract DataFlow::Node getString(); +} + +private class RegexExecutionMethod extends string { + RegexExecutionMethod() { + this in ["match", "fullmatch", "search", "split", "findall", "finditer", "sub", "subn"] + } +} + +/** Gets the index of the argument representing the string to be searched by a regex. */ +int stringArg(RegexExecutionMethod method) { + method in ["match", "fullmatch", "search", "split", "findall", "finditer"] and + result = 1 + or + method in ["sub", "subn"] and + result = 2 +} + +/** + * A class to find `re` methods immediately executing an expression. + * + * See `RegexExecutionMethods` + */ +class DirectRegex extends DataFlow::CallCfgNode, RegexExecution { + RegexExecutionMethod method; + + DirectRegex() { this = API::moduleImport("re").getMember(method).getACall() } + + override DataFlow::Node getRegexNode() { + result in [this.getArg(0), this.getArgByName("pattern")] + } + + override DataFlow::Node getString() { + result in [this.getArg(stringArg(method)), this.getArgByName("string")] + } +} + +/** + * A class to find `re` methods immediately executing a compiled expression by `re.compile`. + * + * Given the following example: + * + * ```py + * pattern = re.compile(input) + * pattern.match(s) + * ``` + * + * This class will identify that `re.compile` compiles `input` and afterwards + * executes `re`'s `match`. As a result, `this` will refer to `pattern.match(s)` + * and `this.getRegexNode()` will return the node for `input` (`re.compile`'s first argument) + * + * + * See `RegexExecutionMethods` + * + * See https://docs.python.org/3/library/re.html#regular-expression-objects + */ +private class CompiledRegex extends DataFlow::CallCfgNode, RegexExecution { + DataFlow::Node regexNode; + RegexExecutionMethod method; + + CompiledRegex() { + exists( + RegexDefinitionConfiguration conf, RegexDefinitonSource source, RegexDefinitionSink sink + | + conf.hasFlow(source, sink) and + regexNode = source.getRegexNode() and + method = sink.getMethod() and + this = sink.getExecutingCall() + ) + } + + override DataFlow::Node getRegexNode() { result = regexNode } + + override DataFlow::Node getString() { + result in [this.getArg(stringArg(method) - 1), this.getArgByName("string")] + } +} + +/** + * A data flow sink node for polynomial regular expression denial-of-service vulnerabilities. + */ +class PolynomialReDoSSink extends DataFlow::Node { + RegExpTerm t; + + PolynomialReDoSSink() { + exists(RegexExecution re | + re.getRegexNode().asExpr() = t.getRegex() and + this = re.getString() + ) and + t.isRootTerm() + } + + /** Gets the regex that is being executed by this node. */ + RegExpTerm getRegExp() { result = t } + + /** + * Gets the node to highlight in the alert message. + */ + DataFlow::Node getHighlight() { result = this } +} diff --git a/python/ql/test/library-tests/regex/Qualified.expected b/python/ql/test/library-tests/regex/Qualified.expected index 30019f943eb9..b698a1d09557 100644 --- a/python/ql/test/library-tests/regex/Qualified.expected +++ b/python/ql/test/library-tests/regex/Qualified.expected @@ -1,15 +1,15 @@ -| (?!not-this)^[A-Z_]+$ | 13 | 20 | false | -| (?:(?:\n\r?)\|^)( *)\\S | 7 | 9 | true | -| (?:(?:\n\r?)\|^)( *)\\S | 14 | 16 | true | -| (?:[^%]\|^)?%\\((\\w*)\\)[a-z] | 0 | 11 | true | -| (?:[^%]\|^)?%\\((\\w*)\\)[a-z] | 15 | 18 | true | -| (?P[\\w]+)\| | 9 | 14 | false | -| \\A[+-]?\\d+ | 2 | 7 | true | -| \\A[+-]?\\d+ | 7 | 10 | false | -| \\[(?P[^[]*)\\]\\((?P[^)]*) | 10 | 15 | true | -| \\[(?P[^[]*)\\]\\((?P[^)]*) | 28 | 33 | true | -| ^[A-Z_]+$(?[\\w]+)\| | 9 | 14 | false | true | +| \\A[+-]?\\d+ | 2 | 7 | true | false | +| \\A[+-]?\\d+ | 7 | 10 | false | true | +| \\[(?P[^[]*)\\]\\((?P[^)]*) | 10 | 15 | true | true | +| \\[(?P[^[]*)\\]\\((?P[^)]*) | 28 | 33 | true | true | +| ^[A-Z_]+$(?[\w]+)|') #$ charSet=9:13 +re.compile(r'\|\[\][123]|\{\}') #$ charSet=6:11 +re.compile(r'[^A-Z]') #$ charSet=0:6 +re.compile("[]]") #$ charSet=0:3 +re.compile("[][]") #$ charSet=0:4 +re.compile("[^][^]") #$ charSet=0:6 +re.compile("[.][.]") #$ charSet=0:3 charSet=3:6 +re.compile("[[]]") #$ charSet=0:3 +re.compile("[^]]") #$ charSet=0:4 +re.compile("[^-]") #$ charSet=0:4 +re.compile("[]-[]") #$ charSet=0:5 +re.compile("[^]-[]") #$ charSet=0:6 + +re.compile("]]][[[[]") #$ charSet=3:8 + + +#ODASA-3985 +#Half Surrogate pairs +re.compile(u'[\uD800-\uDBFF][\uDC00-\uDFFF]') #$ charSet=0:5 charSet=5:10 +#Outside BMP +re.compile(u'[\U00010000-\U0010ffff]') #$ charSet=0:5 + +#Misparsed on LGTM +re.compile(r"\[(?P[^[]*)\]\((?P[^)]*)") #$ charSet=10:14 charSet=28:32 + + # parses wrongly, sees this \|/ as a char set start +re.compile(r'''(?:[\s;,"'<>(){}|[\]@=+*]|:(?![/\\]))+''') #$ charSet=3:25 charSet=30:35 diff --git a/python/ql/test/library-tests/regex/escapedCharacterTest.py b/python/ql/test/library-tests/regex/escapedCharacterTest.py new file mode 100644 index 000000000000..e6add851a8c7 --- /dev/null +++ b/python/ql/test/library-tests/regex/escapedCharacterTest.py @@ -0,0 +1,23 @@ +import re + +re.compile(r'\b') #$ escapedCharacter=0:2 +re.compile(r'''\b''') #$ escapedCharacter=0:2 +re.compile(r"\b") #$ escapedCharacter=0:2 +re.compile(u"\b") # not escape +re.compile("\b") # not escape +re.compile(r'\\\b') #$ escapedCharacter=0:2 escapedCharacter=2:4 +re.compile(r'[\---]') #$ escapedCharacter=1:3 +re.compile(r'[--\-]') #$ escapedCharacter=3:5 +re.compile(r'[\--\-]') #$ escapedCharacter=1:3 escapedCharacter=4:6 +re.compile(r'[0\-9-A-Z]') #$ escapedCharacter=2:4 +re.compile(r'[\0-\09]') #$ escapedCharacter=1:3 escapedCharacter=4:7 +re.compile(r'[\0123-5]') #$ escapedCharacter=1:5 + +#ODASA-3985 +#Half Surrogate pairs +re.compile(u'[\uD800-\uDBFF][\uDC00-\uDFFF]') # not escapes +#Outside BMP +re.compile(u'[\U00010000-\U0010ffff]') # not escapes + +#Misparsed on LGTM +re.compile(r"\[(?P[^[]*)\]\((?P[^)]*)") #$ escapedCharacter=0:2 escapedCharacter=16:18 escapedCharacter=18:20 diff --git a/python/ql/test/library-tests/regex/groupTest.py b/python/ql/test/library-tests/regex/groupTest.py new file mode 100644 index 000000000000..2cb96801f776 --- /dev/null +++ b/python/ql/test/library-tests/regex/groupTest.py @@ -0,0 +1,4 @@ +import re + +re.compile(r'(?P\w+) (?P\w+)') #$ group=0:14 group=15:30 +re.compile(r'([)(])') #$ group=0:6 diff --git a/python/ql/test/query-tests/Security/CWE-730/KnownCVEs.py b/python/ql/test/query-tests/Security/CWE-730/KnownCVEs.py new file mode 100644 index 000000000000..14a8ff528090 --- /dev/null +++ b/python/ql/test/query-tests/Security/CWE-730/KnownCVEs.py @@ -0,0 +1,94 @@ +import re + +# linear +# https://github.com/github/codeql-python-CVE-coverage/issues/439 +rex_blame = re.compile(r'\s*(\d+)\s*(\S+) (.*)') + +# https://github.com/github/codeql-python-CVE-coverage/issues/402 +whitespace = br"[\000\011\012\014\015\040]" +whitespace_optional = whitespace + b"*" +newline_only = br"[\r\n]+" +newline = whitespace_optional + newline_only + whitespace_optional +toFlag = re.compile(newline) + +# https://github.com/github/codeql-python-CVE-coverage/issues/400 +re.compile(r'[+-]?(\d+)*\.\d+%?') +re.compile(r'"""\s+(?:.|\n)*?\s+"""') +re.compile(r'(\{\s+)(\S+)(\s+[^}]+\s+\}\s)') +re.compile(r'".*``.*``.*"') +re.compile(r'(\s*)(?:(.+)(\s*)(=)(\s*))?(.+)(\()(.*)(\))(\s*)') +re.compile(r'(%config)(\s*\(\s*)(\w+)(\s*=\s*)(.*?)(\s*\)\s*)') +re.compile(r'(%new)(\s*)(\()(\s*.*?\s*)(\))') +re.compile(r'(\$)(evoque|overlay)(\{(%)?)(\s*[#\w\-"\'.]+[^=,%}]+?)?') +re.compile(r'(\.\w+\b)(\s*=\s*)([^;]*)(\s*;)') + +# linear +# https://github.com/github/codeql-python-CVE-coverage/issues/392 +simple_email_re = re.compile(r"^\S+@[a-zA-Z0-9._-]+\.[a-zA-Z0-9._-]+$") + +# https://github.com/github/codeql-python-CVE-coverage/issues/249 +rx = re.compile('(?:.*,)*[ \t]*([^ \t]+)[ \t]+' + 'realm=(["\']?)([^"\']*)\\2', re.I) + +# https://github.com/github/codeql-python-CVE-coverage/issues/248 +gauntlet = re.compile( + r"""^([-/:,#%.'"\s!\w]|\w-\w|'[\s\w]+'\s*|"[\s\w]+"|\([\d,%\.\s]+\))*$""", + flags=re.U + ) + +# https://github.com/github/codeql-python-CVE-coverage/issues/227 +# from .compat import tobytes + +WS = "[ \t]" +OWS = WS + "{0,}?" + +# RFC 7230 Section 3.2.6 "Field Value Components": +# tchar = "!" / "#" / "$" / "%" / "&" / "'" / "*" +# / "+" / "-" / "." / "^" / "_" / "`" / "|" / "~" +# / DIGIT / ALPHA +# obs-text = %x80-FF +TCHAR = r"[!#$%&'*+\-.^_`|~0-9A-Za-z]" +OBS_TEXT = r"\x80-\xff" +TOKEN = TCHAR + "{1,}" +# RFC 5234 Appendix B.1 "Core Rules": +# VCHAR = %x21-7E +# ; visible (printing) characters +VCHAR = r"\x21-\x7e" +# header-field = field-name ":" OWS field-value OWS +# field-name = token +# field-value = *( field-content / obs-fold ) +# field-content = field-vchar [ 1*( SP / HTAB ) field-vchar ] +# field-vchar = VCHAR / obs-text +# Errata from: https://www.rfc-editor.org/errata_search.php?rfc=7230&eid=4189 +# changes field-content to: +# +# field-content = field-vchar [ 1*( SP / HTAB / field-vchar ) +# field-vchar ] + +FIELD_VCHAR = "[" + VCHAR + OBS_TEXT + "]" +FIELD_CONTENT = FIELD_VCHAR + "([ \t" + VCHAR + OBS_TEXT + "]+" + FIELD_VCHAR + "){,1}" +FIELD_VALUE = "(" + FIELD_CONTENT + "){0,}" + +HEADER_FIELD = re.compile( + # tobytes( + "^(?P" + TOKEN + "):" + OWS + "(?P" + FIELD_VALUE + ")" + OWS + "$" + # ) + ) + +# https://github.com/github/codeql-python-CVE-coverage/issues/224 +pattern = re.compile( + r'^(:?(([a-zA-Z]{1})|([a-zA-Z]{1}[a-zA-Z]{1})|' # domain pt.1 + r'([a-zA-Z]{1}[0-9]{1})|([0-9]{1}[a-zA-Z]{1})|' # domain pt.2 + r'([a-zA-Z0-9][-_a-zA-Z0-9]{0,61}[a-zA-Z0-9]))\.)+' # domain pt.3 + r'([a-zA-Z]{2,13}|(xn--[a-zA-Z0-9]{2,30}))$' # TLD +) + +# https://github.com/github/codeql-python-CVE-coverage/issues/189 +URL_REGEX = ( + r'(?i)\b((?:[a-z][\w-]+:(?:/{1,3}|[a-z0-9%])|www\d{0,3}[.]|' + r'[a-z0-9.\-]+[.][a-z]{2,4}/)(?:[^\s()<>]+|\(([^\s()<>]+|' + r'(\([^\s()<>]+\)))*\))+(?:\(([^\s()<>]+|(\([^\s()<>]+\)))*\)|' + r'[^\s`!()\[\]{};:\'".,<>?«»“”‘’]))' # "emacs! +) + +url = re.compile(URL_REGEX) diff --git a/python/ql/test/query-tests/Security/CWE-730/PolynomialBackTracking.expected b/python/ql/test/query-tests/Security/CWE-730/PolynomialBackTracking.expected new file mode 100644 index 000000000000..fc8af14d6aca --- /dev/null +++ b/python/ql/test/query-tests/Security/CWE-730/PolynomialBackTracking.expected @@ -0,0 +1,46 @@ +| KnownCVEs.py:5:24:5:47 | Str | KnownCVEs.py:5:32:5:34 | \\d+ | Strings with many repetitions of '9' can start matching anywhere after the start of the preceeding \\s*(\\d+)\\s*(\\S+) (.*) | +| KnownCVEs.py:5:24:5:47 | Str | KnownCVEs.py:5:40:5:42 | \\S+ | Strings starting with '9' and with many repetitions of '00' can start matching anywhere after the start of the preceeding \\d+ | +| KnownCVEs.py:5:24:5:47 | \\s*(\\d+)\\s*(\\S+) (.*) | KnownCVEs.py:5:32:5:34 | \\d+ | Strings with many repetitions of '9' can start matching anywhere after the start of the preceeding \\s*(\\d+)\\s*(\\S+) (.*) | +| KnownCVEs.py:5:24:5:47 | \\s*(\\d+)\\s*(\\S+) (.*) | KnownCVEs.py:5:40:5:42 | \\S+ | Strings starting with '9' and with many repetitions of '00' can start matching anywhere after the start of the preceeding \\d+ | +| KnownCVEs.py:15:12:15:32 | Str | KnownCVEs.py:15:22:15:24 | \\d+ | Strings with many repetitions of '9' can start matching anywhere after the start of the preceeding (\\d+)* | +| KnownCVEs.py:15:12:15:32 | [+-]?(\\d+)*\\.\\d+%? | KnownCVEs.py:15:22:15:24 | \\d+ | Strings with many repetitions of '9' can start matching anywhere after the start of the preceeding (\\d+)* | +| KnownCVEs.py:16:12:16:36 | """\\s+(?:.\|\\n)*?\\s+""" | KnownCVEs.py:16:32:16:34 | \\s+ | Strings starting with '""" ' and with many repetitions of '\\n' can start matching anywhere after the start of the preceeding \\s+ | +| KnownCVEs.py:16:12:16:36 | Str | KnownCVEs.py:16:32:16:34 | \\s+ | Strings starting with '""" ' and with many repetitions of '\\n' can start matching anywhere after the start of the preceeding \\s+ | +| KnownCVEs.py:17:12:17:43 | (\\{\\s+)(\\S+)(\\s+[^}]+\\s+\\}\\s) | KnownCVEs.py:17:32:17:36 | [^}]+ | Strings starting with '{ ! ' and with many repetitions of ' ' can start matching anywhere after the start of the preceeding \\s+ | +| KnownCVEs.py:17:12:17:43 | (\\{\\s+)(\\S+)(\\s+[^}]+\\s+\\}\\s) | KnownCVEs.py:17:37:17:39 | \\s+ | Strings starting with '{ ! \|' and with many repetitions of ' ' can start matching anywhere after the start of the preceeding \\s+ | +| KnownCVEs.py:17:12:17:43 | Str | KnownCVEs.py:17:32:17:36 | [^}]+ | Strings starting with '{ ! ' and with many repetitions of ' ' can start matching anywhere after the start of the preceeding \\s+ | +| KnownCVEs.py:17:12:17:43 | Str | KnownCVEs.py:17:37:17:39 | \\s+ | Strings starting with '{ ! \|' and with many repetitions of ' ' can start matching anywhere after the start of the preceeding \\s+ | +| KnownCVEs.py:18:12:18:26 | ".*``.*``.*" | KnownCVEs.py:18:17:18:18 | .* | Strings starting with '"' and with many repetitions of '"' can start matching anywhere after the start of the preceeding ".*``.*``.*" | +| KnownCVEs.py:18:12:18:26 | ".*``.*``.*" | KnownCVEs.py:18:21:18:22 | .* | Strings starting with '"``' and with many repetitions of '``' can start matching anywhere after the start of the preceeding .* | +| KnownCVEs.py:18:12:18:26 | ".*``.*``.*" | KnownCVEs.py:18:25:18:26 | .* | Strings starting with '"````' and with many repetitions of '``' can start matching anywhere after the start of the preceeding .* | +| KnownCVEs.py:18:12:18:26 | Str | KnownCVEs.py:18:17:18:18 | .* | Strings starting with '"' and with many repetitions of '"' can start matching anywhere after the start of the preceeding ".*``.*``.*" | +| KnownCVEs.py:18:12:18:26 | Str | KnownCVEs.py:18:21:18:22 | .* | Strings starting with '"``' and with many repetitions of '``' can start matching anywhere after the start of the preceeding .* | +| KnownCVEs.py:18:12:18:26 | Str | KnownCVEs.py:18:25:18:26 | .* | Strings starting with '"````' and with many repetitions of '``' can start matching anywhere after the start of the preceeding .* | +| KnownCVEs.py:19:12:19:62 | (\\s*)(?:(.+)(\\s*)(=)(\\s*))?(.+)(\\()(.*)(\\))(\\s*) | KnownCVEs.py:19:25:19:26 | .+ | Strings with many repetitions of ' ' can start matching anywhere after the start of the preceeding \\s* | +| KnownCVEs.py:19:12:19:62 | (\\s*)(?:(.+)(\\s*)(=)(\\s*))?(.+)(\\()(.*)(\\))(\\s*) | KnownCVEs.py:19:29:19:31 | \\s* | Strings starting with 'a' and with many repetitions of ' ' can start matching anywhere after the start of the preceeding .+ | +| KnownCVEs.py:19:12:19:62 | (\\s*)(?:(.+)(\\s*)(=)(\\s*))?(.+)(\\()(.*)(\\))(\\s*) | KnownCVEs.py:19:44:19:45 | .+ | Strings with many repetitions of ' ' can start matching anywhere after the start of the preceeding .+ | +| KnownCVEs.py:19:12:19:62 | (\\s*)(?:(.+)(\\s*)(=)(\\s*))?(.+)(\\()(.*)(\\))(\\s*) | KnownCVEs.py:19:52:19:53 | .* | Strings starting with 'a(' and with many repetitions of 'a(a' can start matching anywhere after the start of the preceeding .+ | +| KnownCVEs.py:19:12:19:62 | Str | KnownCVEs.py:19:25:19:26 | .+ | Strings with many repetitions of ' ' can start matching anywhere after the start of the preceeding \\s* | +| KnownCVEs.py:19:12:19:62 | Str | KnownCVEs.py:19:29:19:31 | \\s* | Strings starting with 'a' and with many repetitions of ' ' can start matching anywhere after the start of the preceeding .+ | +| KnownCVEs.py:19:12:19:62 | Str | KnownCVEs.py:19:44:19:45 | .+ | Strings with many repetitions of ' ' can start matching anywhere after the start of the preceeding .+ | +| KnownCVEs.py:19:12:19:62 | Str | KnownCVEs.py:19:52:19:53 | .* | Strings starting with 'a(' and with many repetitions of 'a(a' can start matching anywhere after the start of the preceeding .+ | +| KnownCVEs.py:20:12:20:62 | (%config)(\\s*\\(\\s*)(\\w+)(\\s*=\\s*)(.*?)(\\s*\\)\\s*) | KnownCVEs.py:20:55:20:57 | \\s* | Strings starting with '%config(a=' and with many repetitions of ' ' can start matching anywhere after the start of the preceeding .*? | +| KnownCVEs.py:20:12:20:62 | Str | KnownCVEs.py:20:55:20:57 | \\s* | Strings starting with '%config(a=' and with many repetitions of ' ' can start matching anywhere after the start of the preceeding .*? | +| KnownCVEs.py:21:12:21:44 | (%new)(\\s*)(\\()(\\s*.*?\\s*)(\\)) | KnownCVEs.py:21:38:21:40 | \\s* | Strings starting with '%new(' and with many repetitions of ' ' can start matching anywhere after the start of the preceeding .*? | +| KnownCVEs.py:21:12:21:44 | Str | KnownCVEs.py:21:38:21:40 | \\s* | Strings starting with '%new(' and with many repetitions of ' ' can start matching anywhere after the start of the preceeding .*? | +| KnownCVEs.py:23:12:23:45 | (\\.\\w+\\b)(\\s*=\\s*)([^;]*)(\\s*;) | KnownCVEs.py:23:42:23:44 | \\s* | Strings starting with '.ab=' and with many repetitions of ' ' can start matching anywhere after the start of the preceeding \\s* | +| KnownCVEs.py:23:12:23:45 | Str | KnownCVEs.py:23:42:23:44 | \\s* | Strings starting with '.ab=' and with many repetitions of ' ' can start matching anywhere after the start of the preceeding \\s* | +| KnownCVEs.py:27:30:27:70 | Str | KnownCVEs.py:27:56:27:70 | [a-zA-Z0-9._-]+ | Strings with many repetitions of '-.' can start matching anywhere after the start of the preceeding [a-zA-Z0-9._-]+ | +| KnownCVEs.py:27:30:27:70 | ^\\S+@[a-zA-Z0-9._-]+\\.[a-zA-Z0-9._-]+$ | KnownCVEs.py:27:56:27:70 | [a-zA-Z0-9._-]+ | Strings with many repetitions of '-.' can start matching anywhere after the start of the preceeding [a-zA-Z0-9._-]+ | +| KnownCVEs.py:30:17:31:49 | (?:.*,)*[ \t]*([^ \t]+)[ \t]+realm=(["']?)([^"']*)\\2 | KnownCVEs.py:30:24:31:25 | .* | Strings with many repetitions of ',' can start matching anywhere after the start of the preceeding (?:.*,)* | +| KnownCVEs.py:30:17:31:49 | (?:.*,)*[ \t]*([^ \t]+)[ \t]+realm=(["']?)([^"']*)\\2 | KnownCVEs.py:30:35:31:40 | [^ \t]+ | Strings with many repetitions of '!' can start matching anywhere after the start of the preceeding .* | +| KnownCVEs.py:30:17:31:49 | (?:.*,)*[ \t]*([^ \t]+)[ \t]+realm=(["']?)([^"']*)\\2 | KnownCVEs.py:30:61:31:66 | [^"']* | Strings starting with '!\\trealm=' and with many repetitions of '!\\trealm=!' can start matching anywhere after the start of the preceeding .* | +| KnownCVEs.py:30:17:31:49 | Str | KnownCVEs.py:30:24:31:25 | .* | Strings with many repetitions of ',' can start matching anywhere after the start of the preceeding (?:.*,)* | +| KnownCVEs.py:30:17:31:49 | Str | KnownCVEs.py:30:35:31:40 | [^ \t]+ | Strings with many repetitions of '!' can start matching anywhere after the start of the preceeding .* | +| KnownCVEs.py:30:17:31:49 | Str | KnownCVEs.py:30:61:31:66 | [^"']* | Strings starting with '!\\trealm=' and with many repetitions of '!\\trealm=!' can start matching anywhere after the start of the preceeding .* | +| KnownCVEs.py:35:13:35:85 | Str | KnownCVEs.py:35:18:35:81 | ([-/:,#%.'"\\s!\\w]\|\\w-\\w\|'[\\s\\w]+'\\s*\|"[\\s\\w]+"\|\\([\\d,%\\.\\s]+\\))* | Strings with many repetitions of '\\t' can start matching anywhere after the start of the preceeding \\s* | +| KnownCVEs.py:35:13:35:85 | Str | KnownCVEs.py:35:43:35:49 | [\\s\\w]+ | Strings with many repetitions of '\\t''' can start matching anywhere after the start of the preceeding \\s* | +| KnownCVEs.py:35:13:35:85 | Str | KnownCVEs.py:35:56:35:62 | [\\s\\w]+ | Strings with many repetitions of '\\t""' can start matching anywhere after the start of the preceeding ([-/:,#%.'"\\s!\\w]\|\\w-\\w\|'[\\s\\w]+'\\s*\|"[\\s\\w]+"\|\\([\\d,%\\.\\s]+\\))* | +| KnownCVEs.py:35:13:35:85 | ^([-/:,#%.'"\\s!\\w]\|\\w-\\w\|'[\\s\\w]+'\\s*\|"[\\s\\w]+"\|\\([\\d,%\\.\\s]+\\))*$ | KnownCVEs.py:35:18:35:81 | ([-/:,#%.'"\\s!\\w]\|\\w-\\w\|'[\\s\\w]+'\\s*\|"[\\s\\w]+"\|\\([\\d,%\\.\\s]+\\))* | Strings with many repetitions of '\\t' can start matching anywhere after the start of the preceeding \\s* | +| KnownCVEs.py:35:13:35:85 | ^([-/:,#%.'"\\s!\\w]\|\\w-\\w\|'[\\s\\w]+'\\s*\|"[\\s\\w]+"\|\\([\\d,%\\.\\s]+\\))*$ | KnownCVEs.py:35:43:35:49 | [\\s\\w]+ | Strings with many repetitions of '\\t''' can start matching anywhere after the start of the preceeding \\s* | +| KnownCVEs.py:35:13:35:85 | ^([-/:,#%.'"\\s!\\w]\|\\w-\\w\|'[\\s\\w]+'\\s*\|"[\\s\\w]+"\|\\([\\d,%\\.\\s]+\\))*$ | KnownCVEs.py:35:56:35:62 | [\\s\\w]+ | Strings with many repetitions of '\\t""' can start matching anywhere after the start of the preceeding ([-/:,#%.'"\\s!\\w]\|\\w-\\w\|'[\\s\\w]+'\\s*\|"[\\s\\w]+"\|\\([\\d,%\\.\\s]+\\))* | diff --git a/python/ql/test/query-tests/Security/CWE-730/PolynomialBackTracking.qlref b/python/ql/test/query-tests/Security/CWE-730/PolynomialBackTracking.qlref new file mode 100644 index 000000000000..d3f6ed149436 --- /dev/null +++ b/python/ql/test/query-tests/Security/CWE-730/PolynomialBackTracking.qlref @@ -0,0 +1 @@ +Security/CWE-730/PolynomialBackTracking.ql diff --git a/python/ql/test/query-tests/Security/CWE-730/ReDoS.expected b/python/ql/test/query-tests/Security/CWE-730/ReDoS.expected new file mode 100644 index 000000000000..1286abcc0a70 --- /dev/null +++ b/python/ql/test/query-tests/Security/CWE-730/ReDoS.expected @@ -0,0 +1,98 @@ +| KnownCVEs.py:15:22:15:24 | \\d+ | This part of the regular expression may cause exponential backtracking on strings containing many repetitions of '9'. | +| KnownCVEs.py:30:24:31:25 | .* | This part of the regular expression may cause exponential backtracking on strings containing many repetitions of ','. | +| KnownCVEs.py:35:18:35:81 | ([-/:,#%.'"\\s!\\w]\|\\w-\\w\|'[\\s\\w]+'\\s*\|"[\\s\\w]+"\|\\([\\d,%\\.\\s]+\\))* | This part of the regular expression may cause exponential backtracking on strings containing many repetitions of '"\\t"'. | +| redos.py:6:28:6:42 | (?:__\|[\\s\\S])+? | This part of the regular expression may cause exponential backtracking on strings containing many repetitions of '__'. | +| redos.py:6:52:6:68 | (?:\\*\\*\|[\\s\\S])+? | This part of the regular expression may cause exponential backtracking on strings containing many repetitions of '**'. | +| redos.py:21:34:21:53 | (?:[^"\\\\]\|\\\\\\\\\|\\\\.)+ | This part of the regular expression may cause exponential backtracking on strings containing many repetitions of '\\\\\\\\'. | +| redos.py:21:57:21:76 | (?:[^'\\\\]\|\\\\\\\\\|\\\\.)+ | This part of the regular expression may cause exponential backtracking on strings containing many repetitions of '\\\\\\\\'. | +| redos.py:21:81:21:100 | (?:[^)\\\\]\|\\\\\\\\\|\\\\.)+ | This part of the regular expression may cause exponential backtracking on strings containing many repetitions of '\\\\\\\\'. | +| redos.py:33:64:33:65 | .* | This part of the regular expression may cause exponential backtracking on strings containing many repetitions of '\|\|\\n'. | +| redos.py:38:33:38:42 | (\\\\\\/\|.)*? | This part of the regular expression may cause exponential backtracking on strings containing many repetitions of '\\\\/'. | +| redos.py:43:37:43:38 | .* | This part of the regular expression may cause exponential backtracking on strings containing many repetitions of '#'. | +| redos.py:49:41:49:43 | .*? | This part of the regular expression may cause exponential backtracking on strings starting with '"' and containing many repetitions of '""'. | +| redos.py:49:47:49:49 | .*? | This part of the regular expression may cause exponential backtracking on strings starting with ''' and containing many repetitions of ''''. | +| redos.py:54:47:54:49 | .*? | This part of the regular expression may cause exponential backtracking on strings containing many repetitions of ']['. | +| redos.py:54:80:54:82 | .*? | This part of the regular expression may cause exponential backtracking on strings containing many repetitions of ']['. | +| redos.py:60:25:60:30 | [a-z]+ | This part of the regular expression may cause exponential backtracking on strings containing many repetitions of 'a'. | +| redos.py:61:25:61:30 | [a-z]* | This part of the regular expression may cause exponential backtracking on strings containing many repetitions of 'a'. | +| redos.py:62:53:62:64 | [a-zA-Z0-9]+ | This part of the regular expression may cause exponential backtracking on strings containing many repetitions of '0'. | +| redos.py:63:26:63:33 | ([a-z])+ | This part of the regular expression may cause exponential backtracking on strings containing many repetitions of 'aa'. | +| redos.py:68:26:68:41 | [\\w#:.~>+()\\s-]+ | This part of the regular expression may cause exponential backtracking on strings containing many repetitions of '\\t'. | +| redos.py:68:48:68:50 | .*? | This part of the regular expression may cause exponential backtracking on strings starting with '[' and containing many repetitions of ']['. | +| redos.py:73:29:73:36 | (\\\\?.)*? | This part of the regular expression may cause exponential backtracking on strings starting with '"' and containing many repetitions of '\\\\a'. | +| redos.py:76:24:76:31 | (b\|a?b)* | This part of the regular expression may cause exponential backtracking on strings containing many repetitions of 'b'. | +| redos.py:79:24:79:31 | (a\|aa?)* | This part of the regular expression may cause exponential backtracking on strings containing many repetitions of 'a'. | +| redos.py:91:24:91:31 | (a\|aa?)* | This part of the regular expression may cause exponential backtracking on strings containing many repetitions of 'a'. | +| redos.py:97:25:97:38 | ([\\s\\S]\|[^a])* | This part of the regular expression may cause exponential backtracking on strings containing many repetitions of '`'. | +| redos.py:103:25:103:33 | (.\|[^a])* | This part of the regular expression may cause exponential backtracking on strings containing many repetitions of '`'. | +| redos.py:109:25:109:33 | (b\|[^a])* | This part of the regular expression may cause exponential backtracking on strings containing many repetitions of 'b'. | +| redos.py:112:25:112:33 | (G\|[^a])* | This part of the regular expression may cause exponential backtracking on strings containing many repetitions of 'G'. | +| redos.py:115:25:115:37 | ([0-9]\|[^a])* | This part of the regular expression may cause exponential backtracking on strings containing many repetitions of '0'. | +| redos.py:127:25:127:38 | ([a-z]\|[d-h])* | This part of the regular expression may cause exponential backtracking on strings containing many repetitions of 'd'. | +| redos.py:130:25:130:40 | ([^a-z]\|[^0-9])* | This part of the regular expression may cause exponential backtracking on strings containing many repetitions of '/'. | +| redos.py:133:25:133:35 | (\\d\|[0-9])* | This part of the regular expression may cause exponential backtracking on strings containing many repetitions of '0'. | +| redos.py:136:25:136:32 | (\\s\|\\s)* | This part of the regular expression may cause exponential backtracking on strings containing many repetitions of ' '. | +| redos.py:139:25:139:31 | (\\w\|G)* | This part of the regular expression may cause exponential backtracking on strings containing many repetitions of 'G'. | +| redos.py:145:25:145:32 | (\\d\|\\w)* | This part of the regular expression may cause exponential backtracking on strings containing many repetitions of '0'. | +| redos.py:148:25:148:31 | (\\d\|5)* | This part of the regular expression may cause exponential backtracking on strings containing many repetitions of '5'. | +| redos.py:151:25:151:34 | (\\s\|[\\f])* | This part of the regular expression may cause exponential backtracking on strings containing many repetitions of '\u000c'. | +| redos.py:157:25:157:34 | (\\f\|[\\f])* | This part of the regular expression may cause exponential backtracking on strings containing many repetitions of '\u000c'. | +| redos.py:160:25:160:32 | (\\W\|\\D)* | This part of the regular expression may cause exponential backtracking on strings containing many repetitions of ' '. | +| redos.py:163:25:163:32 | (\\S\|\\w)* | This part of the regular expression may cause exponential backtracking on strings containing many repetitions of '0'. | +| redos.py:166:25:166:34 | (\\S\|[\\w])* | This part of the regular expression may cause exponential backtracking on strings containing many repetitions of '0'. | +| redos.py:169:25:169:37 | (1s\|[\\da-z])* | This part of the regular expression may cause exponential backtracking on strings containing many repetitions of '1s'. | +| redos.py:172:25:172:33 | (0\|[\\d])* | This part of the regular expression may cause exponential backtracking on strings containing many repetitions of '0'. | +| redos.py:175:26:175:30 | [\\d]+ | This part of the regular expression may cause exponential backtracking on strings containing many repetitions of '0'. | +| redos.py:187:26:187:31 | [^>a]+ | This part of the regular expression may cause exponential backtracking on strings containing many repetitions of '='. | +| redos.py:190:27:190:29 | \\s* | This part of the regular expression may cause exponential backtracking on strings starting with '\\n' and containing many repetitions of '\\n'. | +| redos.py:193:28:193:30 | \\s+ | This part of the regular expression may cause exponential backtracking on strings containing many repetitions of ' '. | +| redos.py:196:78:196:89 | [ a-zA-Z{}]+ | This part of the regular expression may cause exponential backtracking on strings starting with '{[A(A)A:' and containing many repetitions of ' A:'. | +| redos.py:196:91:196:92 | ,? | This part of the regular expression may cause exponential backtracking on strings starting with '{[A(A)A: ' and containing many repetitions of ',A: '. | +| redos.py:199:25:199:26 | a+ | This part of the regular expression may cause exponential backtracking on strings containing many repetitions of 'a'. | +| redos.py:199:28:199:29 | b+ | This part of the regular expression may cause exponential backtracking on strings containing many repetitions of 'b'. | +| redos.py:202:26:202:32 | (a+a?)* | This part of the regular expression may cause exponential backtracking on strings containing many repetitions of 'a'. | +| redos.py:202:27:202:28 | a+ | This part of the regular expression may cause exponential backtracking on strings containing many repetitions of 'a'. | +| redos.py:205:25:205:26 | a+ | This part of the regular expression may cause exponential backtracking on strings containing many repetitions of 'a'. | +| redos.py:211:25:211:26 | a+ | This part of the regular expression may cause exponential backtracking on strings containing many repetitions of 'a'. | +| redos.py:217:25:217:27 | \\n+ | This part of the regular expression may cause exponential backtracking on strings containing many repetitions of '\\n'. | +| redos.py:220:25:220:29 | [^X]+ | This part of the regular expression may cause exponential backtracking on strings containing many repetitions of 'W'. | +| redos.py:223:30:223:30 | b | This part of the regular expression may cause exponential backtracking on strings starting with 'W' and containing many repetitions of 'bW'. | +| redos.py:229:30:229:30 | b | This part of the regular expression may cause exponential backtracking on strings starting with 'W' and containing many repetitions of 'bW'. | +| redos.py:241:27:241:27 | b | This part of the regular expression may cause exponential backtracking on strings starting with 'a' and containing many repetitions of 'ba'. | +| redos.py:247:25:247:31 | [\\n\\s]+ | This part of the regular expression may cause exponential backtracking on strings containing many repetitions of '\\n'. | +| redos.py:256:25:256:27 | \\w* | This part of the regular expression may cause exponential backtracking on strings containing many repetitions of 'foobarbazfoobarbazfoobarbazfoobarbazfoobarbazfoobarbaz'. | +| redos.py:256:37:256:39 | \\w* | This part of the regular expression may cause exponential backtracking on strings starting with 'foobarbaz' and containing many repetitions of 'foobarbazfoobarbazfoobarbazfoobarbazfoobarbazfoobarbaz'. | +| redos.py:256:49:256:51 | \\w* | This part of the regular expression may cause exponential backtracking on strings starting with 'foobarbazfoobarbaz' and containing many repetitions of 'foobarbazfoobarbazfoobarbazfoobarbazfoobarbazfoobarbaz'. | +| redos.py:256:61:256:63 | \\w* | This part of the regular expression may cause exponential backtracking on strings starting with 'foobarbazfoobarbazfoobarbaz' and containing many repetitions of 'foobarbazfoobarbazfoobarbazfoobarbazfoobarbazfoobarbaz'. | +| redos.py:259:24:259:126 | (.thisisagoddamnlongstringforstresstestingthequery\|\\sthisisagoddamnlongstringforstresstestingthequery)* | This part of the regular expression may cause exponential backtracking on strings containing many repetitions of ' thisisagoddamnlongstringforstresstestingthequery'. | +| redos.py:262:24:262:87 | (thisisagoddamnlongstringforstresstestingthequery\|this\\w+query)* | This part of the regular expression may cause exponential backtracking on strings containing many repetitions of 'thisisagoddamnlongstringforstresstestingthequery'. | +| redos.py:262:78:262:80 | \\w+ | This part of the regular expression may cause exponential backtracking on strings starting with 'this' and containing many repetitions of 'aquerythis'. | +| redos.py:274:31:274:32 | b+ | This part of the regular expression may cause exponential backtracking on strings containing many repetitions of 'b'. | +| redos.py:277:48:277:50 | \\s* | This part of the regular expression may cause exponential backtracking on strings containing many repetitions of '"" a='. | +| redos.py:283:26:283:27 | a+ | This part of the regular expression may cause exponential backtracking on strings containing many repetitions of 'a'. | +| redos.py:286:26:286:27 | a+ | This part of the regular expression may cause exponential backtracking on strings containing many repetitions of 'a'. | +| redos.py:292:26:292:27 | a+ | This part of the regular expression may cause exponential backtracking on strings containing many repetitions of 'a'. | +| redos.py:295:35:295:36 | a+ | This part of the regular expression may cause exponential backtracking on strings containing many repetitions of 'a'. | +| redos.py:301:100:301:101 | e+ | This part of the regular expression may cause exponential backtracking on strings starting with ';00000000000000' and containing many repetitions of 'e'. | +| redos.py:304:28:304:29 | c+ | This part of the regular expression may cause exponential backtracking on strings starting with 'ab' and containing many repetitions of 'c'. | +| redos.py:307:28:307:30 | \\s+ | This part of the regular expression may cause exponential backtracking on strings containing many repetitions of ' '. | +| redos.py:310:26:310:34 | ([^/]\|X)+ | This part of the regular expression may cause exponential backtracking on strings containing many repetitions of 'X'. | +| redos.py:313:30:313:34 | [^Y]+ | This part of the regular expression may cause exponential backtracking on strings containing many repetitions of 'Xx'. | +| redos.py:316:25:316:26 | a* | This part of the regular expression may cause exponential backtracking on strings containing many repetitions of 'a'. | +| redos.py:319:28:319:33 | [\\w-]* | This part of the regular expression may cause exponential backtracking on strings starting with 'foo' and containing many repetitions of '-'. | +| redos.py:322:25:322:29 | (ab)* | This part of the regular expression may cause exponential backtracking on strings containing many repetitions of 'ab'. | +| redos.py:325:24:325:30 | (a?a?)* | This part of the regular expression may cause exponential backtracking on strings containing many repetitions of 'a'. | +| redos.py:334:24:334:32 | (?:a\|a?)+ | This part of the regular expression may cause exponential backtracking on strings containing many repetitions of 'a'. | +| redos.py:340:27:340:55 | (([a-c]\|[c-d])T(e?e?e?e?\|X))+ | This part of the regular expression may cause exponential backtracking on strings starting with 'PRE' and containing many repetitions of 'cTX'. | +| redos.py:343:26:343:29 | (a)+ | This part of the regular expression may cause exponential backtracking on strings containing many repetitions of 'aa'. | +| redos.py:346:26:346:27 | b+ | This part of the regular expression may cause exponential backtracking on strings containing many repetitions of 'bb'. | +| redos.py:352:25:352:26 | a* | This part of the regular expression may cause exponential backtracking on strings containing many repetitions of 'a'. | +| redos.py:353:25:353:26 | a+ | This part of the regular expression may cause exponential backtracking on strings containing many repetitions of 'a'. | +| redos.py:354:25:354:26 | a* | This part of the regular expression may cause exponential backtracking on strings containing many repetitions of 'a'. | +| redos.py:355:25:355:26 | a+ | This part of the regular expression may cause exponential backtracking on strings containing many repetitions of 'a'. | +| redos.py:362:25:362:40 | ((?:a{\|-)\|\\w\\{)+ | This part of the regular expression may cause exponential backtracking on strings containing many repetitions of 'a{'. | +| redos.py:363:25:363:43 | ((?:a{0\|-)\|\\w\\{\\d)+ | This part of the regular expression may cause exponential backtracking on strings containing many repetitions of 'a{0'. | +| redos.py:364:25:364:45 | ((?:a{0,\|-)\|\\w\\{\\d,)+ | This part of the regular expression may cause exponential backtracking on strings containing many repetitions of 'a{0,'. | +| redos.py:365:25:365:48 | ((?:a{0,2\|-)\|\\w\\{\\d,\\d)+ | This part of the regular expression may cause exponential backtracking on strings containing many repetitions of 'a{0,2'. | +| unittests.py:5:17:5:23 | (\u00c6\|\\\u00c6)+ | This part of the regular expression may cause exponential backtracking on strings starting with 'X' and containing many repetitions of '\u00c6'. | +| unittests.py:9:16:9:24 | (?:.\|\\n)* | This part of the regular expression may cause exponential backtracking on strings containing many repetitions of '\\n'. | diff --git a/python/ql/test/query-tests/Security/CWE-730/ReDoS.qlref b/python/ql/test/query-tests/Security/CWE-730/ReDoS.qlref new file mode 100644 index 000000000000..4c19d395edbe --- /dev/null +++ b/python/ql/test/query-tests/Security/CWE-730/ReDoS.qlref @@ -0,0 +1 @@ +Security/CWE-730/ReDoS.ql diff --git a/python/ql/test/query-tests/Security/CWE-730/ReDoSRegexParsing.expected b/python/ql/test/query-tests/Security/CWE-730/ReDoSRegexParsing.expected new file mode 100644 index 000000000000..6ec71cec7b91 --- /dev/null +++ b/python/ql/test/query-tests/Security/CWE-730/ReDoSRegexParsing.expected @@ -0,0 +1,2526 @@ +| redos.py:6:23:6:23 | ^ | caret | +| redos.py:6:23:6:46 | ^\\b_((?:__\|[\\s\\S])+?)_\\b | sequence | +| redos.py:6:23:6:77 | ^\\b_((?:__\|[\\s\\S])+?)_\\b\|^\\*((?:\\*\\*\|[\\s\\S])+?)\\*(?!\\*) | alt | +| redos.py:6:24:6:25 | \\b | constant | +| redos.py:6:26:6:26 | _ | constant | +| redos.py:6:27:6:43 | ((?:__\|[\\s\\S])+?) | group | +| redos.py:6:28:6:40 | (?:__\|[\\s\\S]) | group | +| redos.py:6:28:6:42 | (?:__\|[\\s\\S])+? | plus | +| redos.py:6:28:6:42 | (?:__\|[\\s\\S])+? | quantifier | +| redos.py:6:31:6:31 | _ | constant | +| redos.py:6:31:6:32 | __ | sequence | +| redos.py:6:31:6:39 | __\|[\\s\\S] | alt | +| redos.py:6:32:6:32 | _ | constant | +| redos.py:6:34:6:39 | [\\s\\S] | char | +| redos.py:6:35:6:36 | \\s | charEsc | +| redos.py:6:37:6:38 | \\S | charEsc | +| redos.py:6:44:6:44 | _ | constant | +| redos.py:6:45:6:46 | \\b | constant | +| redos.py:6:48:6:48 | ^ | caret | +| redos.py:6:48:6:77 | ^\\*((?:\\*\\*\|[\\s\\S])+?)\\*(?!\\*) | sequence | +| redos.py:6:49:6:50 | \\* | constant | +| redos.py:6:51:6:69 | ((?:\\*\\*\|[\\s\\S])+?) | group | +| redos.py:6:52:6:66 | (?:\\*\\*\|[\\s\\S]) | group | +| redos.py:6:52:6:68 | (?:\\*\\*\|[\\s\\S])+? | plus | +| redos.py:6:52:6:68 | (?:\\*\\*\|[\\s\\S])+? | quantifier | +| redos.py:6:55:6:56 | \\* | constant | +| redos.py:6:55:6:58 | \\*\\* | sequence | +| redos.py:6:55:6:65 | \\*\\*\|[\\s\\S] | alt | +| redos.py:6:57:6:58 | \\* | constant | +| redos.py:6:60:6:65 | [\\s\\S] | char | +| redos.py:6:61:6:62 | \\s | charEsc | +| redos.py:6:63:6:64 | \\S | charEsc | +| redos.py:6:70:6:71 | \\* | constant | +| redos.py:6:72:6:77 | (?!\\*) | group | +| redos.py:6:75:6:76 | \\* | constant | +| redos.py:11:24:11:24 | ^ | caret | +| redos.py:11:24:11:45 | ^\\b_((?:__\|[^_])+?)_\\b | sequence | +| redos.py:11:24:11:74 | ^\\b_((?:__\|[^_])+?)_\\b\|^\\*((?:\\*\\*\|[^*])+?)\\*(?!\\*) | alt | +| redos.py:11:25:11:26 | \\b | constant | +| redos.py:11:27:11:27 | _ | constant | +| redos.py:11:28:11:42 | ((?:__\|[^_])+?) | group | +| redos.py:11:29:11:39 | (?:__\|[^_]) | group | +| redos.py:11:29:11:41 | (?:__\|[^_])+? | plus | +| redos.py:11:29:11:41 | (?:__\|[^_])+? | quantifier | +| redos.py:11:32:11:32 | _ | constant | +| redos.py:11:32:11:33 | __ | sequence | +| redos.py:11:32:11:38 | __\|[^_] | alt | +| redos.py:11:33:11:33 | _ | constant | +| redos.py:11:35:11:38 | [^_] | char | +| redos.py:11:37:11:37 | _ | constant | +| redos.py:11:43:11:43 | _ | constant | +| redos.py:11:44:11:45 | \\b | constant | +| redos.py:11:47:11:47 | ^ | caret | +| redos.py:11:47:11:74 | ^\\*((?:\\*\\*\|[^*])+?)\\*(?!\\*) | sequence | +| redos.py:11:48:11:49 | \\* | constant | +| redos.py:11:50:11:66 | ((?:\\*\\*\|[^*])+?) | group | +| redos.py:11:51:11:63 | (?:\\*\\*\|[^*]) | group | +| redos.py:11:51:11:65 | (?:\\*\\*\|[^*])+? | plus | +| redos.py:11:51:11:65 | (?:\\*\\*\|[^*])+? | quantifier | +| redos.py:11:54:11:55 | \\* | constant | +| redos.py:11:54:11:57 | \\*\\* | sequence | +| redos.py:11:54:11:62 | \\*\\*\|[^*] | alt | +| redos.py:11:56:11:57 | \\* | constant | +| redos.py:11:59:11:62 | [^*] | char | +| redos.py:11:61:11:61 | * | constant | +| redos.py:11:67:11:68 | \\* | constant | +| redos.py:11:69:11:74 | (?!\\*) | group | +| redos.py:11:72:11:73 | \\* | constant | +| redos.py:16:24:16:28 | (.*,) | group | +| redos.py:16:24:16:29 | (.*,)+ | plus | +| redos.py:16:24:16:29 | (.*,)+ | quantifier | +| redos.py:16:24:16:31 | (.*,)+.+ | sequence | +| redos.py:16:25:16:25 | . | dot | +| redos.py:16:25:16:26 | .* | quantifier | +| redos.py:16:25:16:26 | .* | star | +| redos.py:16:25:16:27 | .*, | sequence | +| redos.py:16:27:16:27 | , | constant | +| redos.py:16:30:16:30 | . | dot | +| redos.py:16:30:16:31 | .+ | plus | +| redos.py:16:30:16:31 | .+ | quantifier | +| redos.py:21:23:21:23 | ^ | caret | +| redos.py:21:23:21:105 | ^(?:\\s+(?:"(?:[^"\\\\]\|\\\\\\\\\|\\\\.)+"\|'(?:[^'\\\\]\|\\\\\\\\\|\\\\.)+'\|\\((?:[^)\\\\]\|\\\\\\\\\|\\\\.)+\\)))? | sequence | +| redos.py:21:24:21:104 | (?:\\s+(?:"(?:[^"\\\\]\|\\\\\\\\\|\\\\.)+"\|'(?:[^'\\\\]\|\\\\\\\\\|\\\\.)+'\|\\((?:[^)\\\\]\|\\\\\\\\\|\\\\.)+\\))) | group | +| redos.py:21:24:21:105 | (?:\\s+(?:"(?:[^"\\\\]\|\\\\\\\\\|\\\\.)+"\|'(?:[^'\\\\]\|\\\\\\\\\|\\\\.)+'\|\\((?:[^)\\\\]\|\\\\\\\\\|\\\\.)+\\)))? | opt | +| redos.py:21:24:21:105 | (?:\\s+(?:"(?:[^"\\\\]\|\\\\\\\\\|\\\\.)+"\|'(?:[^'\\\\]\|\\\\\\\\\|\\\\.)+'\|\\((?:[^)\\\\]\|\\\\\\\\\|\\\\.)+\\)))? | quantifier | +| redos.py:21:27:21:28 | \\s | charEsc | +| redos.py:21:27:21:29 | \\s+ | plus | +| redos.py:21:27:21:29 | \\s+ | quantifier | +| redos.py:21:27:21:103 | \\s+(?:"(?:[^"\\\\]\|\\\\\\\\\|\\\\.)+"\|'(?:[^'\\\\]\|\\\\\\\\\|\\\\.)+'\|\\((?:[^)\\\\]\|\\\\\\\\\|\\\\.)+\\)) | sequence | +| redos.py:21:30:21:103 | (?:"(?:[^"\\\\]\|\\\\\\\\\|\\\\.)+"\|'(?:[^'\\\\]\|\\\\\\\\\|\\\\.)+'\|\\((?:[^)\\\\]\|\\\\\\\\\|\\\\.)+\\)) | group | +| redos.py:21:33:21:33 | " | constant | +| redos.py:21:33:21:54 | "(?:[^"\\\\]\|\\\\\\\\\|\\\\.)+" | sequence | +| redos.py:21:33:21:102 | "(?:[^"\\\\]\|\\\\\\\\\|\\\\.)+"\|'(?:[^'\\\\]\|\\\\\\\\\|\\\\.)+'\|\\((?:[^)\\\\]\|\\\\\\\\\|\\\\.)+\\) | alt | +| redos.py:21:34:21:52 | (?:[^"\\\\]\|\\\\\\\\\|\\\\.) | group | +| redos.py:21:34:21:53 | (?:[^"\\\\]\|\\\\\\\\\|\\\\.)+ | plus | +| redos.py:21:34:21:53 | (?:[^"\\\\]\|\\\\\\\\\|\\\\.)+ | quantifier | +| redos.py:21:37:21:42 | [^"\\\\] | char | +| redos.py:21:37:21:51 | [^"\\\\]\|\\\\\\\\\|\\\\. | alt | +| redos.py:21:39:21:39 | " | constant | +| redos.py:21:40:21:41 | \\\\ | constant | +| redos.py:21:44:21:45 | \\\\ | constant | +| redos.py:21:44:21:47 | \\\\\\\\ | sequence | +| redos.py:21:46:21:47 | \\\\ | constant | +| redos.py:21:49:21:50 | \\\\ | constant | +| redos.py:21:49:21:51 | \\\\. | sequence | +| redos.py:21:51:21:51 | . | dot | +| redos.py:21:54:21:54 | " | constant | +| redos.py:21:56:21:56 | ' | constant | +| redos.py:21:56:21:77 | '(?:[^'\\\\]\|\\\\\\\\\|\\\\.)+' | sequence | +| redos.py:21:57:21:75 | (?:[^'\\\\]\|\\\\\\\\\|\\\\.) | group | +| redos.py:21:57:21:76 | (?:[^'\\\\]\|\\\\\\\\\|\\\\.)+ | plus | +| redos.py:21:57:21:76 | (?:[^'\\\\]\|\\\\\\\\\|\\\\.)+ | quantifier | +| redos.py:21:60:21:65 | [^'\\\\] | char | +| redos.py:21:60:21:74 | [^'\\\\]\|\\\\\\\\\|\\\\. | alt | +| redos.py:21:62:21:62 | ' | constant | +| redos.py:21:63:21:64 | \\\\ | constant | +| redos.py:21:67:21:68 | \\\\ | constant | +| redos.py:21:67:21:70 | \\\\\\\\ | sequence | +| redos.py:21:69:21:70 | \\\\ | constant | +| redos.py:21:72:21:73 | \\\\ | constant | +| redos.py:21:72:21:74 | \\\\. | sequence | +| redos.py:21:74:21:74 | . | dot | +| redos.py:21:77:21:77 | ' | constant | +| redos.py:21:79:21:80 | \\( | constant | +| redos.py:21:79:21:102 | \\((?:[^)\\\\]\|\\\\\\\\\|\\\\.)+\\) | sequence | +| redos.py:21:81:21:99 | (?:[^)\\\\]\|\\\\\\\\\|\\\\.) | group | +| redos.py:21:81:21:100 | (?:[^)\\\\]\|\\\\\\\\\|\\\\.)+ | plus | +| redos.py:21:81:21:100 | (?:[^)\\\\]\|\\\\\\\\\|\\\\.)+ | quantifier | +| redos.py:21:84:21:89 | [^)\\\\] | char | +| redos.py:21:84:21:98 | [^)\\\\]\|\\\\\\\\\|\\\\. | alt | +| redos.py:21:86:21:86 | ) | constant | +| redos.py:21:87:21:88 | \\\\ | constant | +| redos.py:21:91:21:92 | \\\\ | constant | +| redos.py:21:91:21:94 | \\\\\\\\ | sequence | +| redos.py:21:93:21:94 | \\\\ | constant | +| redos.py:21:96:21:97 | \\\\ | constant | +| redos.py:21:96:21:98 | \\\\. | sequence | +| redos.py:21:98:21:98 | . | dot | +| redos.py:21:101:21:102 | \\) | constant | +| redos.py:25:24:25:25 | \\( | constant | +| redos.py:25:24:25:68 | \\(\\*(?:[\\s\\S]*?\\(\\*[\\s\\S]*?\\*\\))*[\\s\\S]*?\\*\\) | sequence | +| redos.py:25:26:25:27 | \\* | constant | +| redos.py:25:28:25:55 | (?:[\\s\\S]*?\\(\\*[\\s\\S]*?\\*\\)) | group | +| redos.py:25:28:25:56 | (?:[\\s\\S]*?\\(\\*[\\s\\S]*?\\*\\))* | quantifier | +| redos.py:25:28:25:56 | (?:[\\s\\S]*?\\(\\*[\\s\\S]*?\\*\\))* | star | +| redos.py:25:31:25:36 | [\\s\\S] | char | +| redos.py:25:31:25:38 | [\\s\\S]*? | quantifier | +| redos.py:25:31:25:38 | [\\s\\S]*? | star | +| redos.py:25:31:25:54 | [\\s\\S]*?\\(\\*[\\s\\S]*?\\*\\) | sequence | +| redos.py:25:32:25:33 | \\s | charEsc | +| redos.py:25:34:25:35 | \\S | charEsc | +| redos.py:25:39:25:40 | \\( | constant | +| redos.py:25:41:25:42 | \\* | constant | +| redos.py:25:43:25:48 | [\\s\\S] | char | +| redos.py:25:43:25:50 | [\\s\\S]*? | quantifier | +| redos.py:25:43:25:50 | [\\s\\S]*? | star | +| redos.py:25:44:25:45 | \\s | charEsc | +| redos.py:25:46:25:47 | \\S | charEsc | +| redos.py:25:51:25:52 | \\* | constant | +| redos.py:25:53:25:54 | \\) | constant | +| redos.py:25:57:25:62 | [\\s\\S] | char | +| redos.py:25:57:25:64 | [\\s\\S]*? | quantifier | +| redos.py:25:57:25:64 | [\\s\\S]*? | star | +| redos.py:25:58:25:59 | \\s | charEsc | +| redos.py:25:60:25:61 | \\S | charEsc | +| redos.py:25:65:25:66 | \\* | constant | +| redos.py:25:67:25:68 | \\) | constant | +| redos.py:30:24:30:24 | ^ | caret | +| redos.py:30:24:30:84 | ^ *(\\S.*\\\|.*)\\n *([-:]+ *\\\|[-\| :]*)\\n((?:.*\\\|.*(?:\\n\|$))*)\\n* | sequence | +| redos.py:30:25:30:25 | | constant | +| redos.py:30:25:30:26 | * | quantifier | +| redos.py:30:25:30:26 | * | star | +| redos.py:30:27:30:36 | (\\S.*\\\|.*) | group | +| redos.py:30:28:30:29 | \\S | charEsc | +| redos.py:30:28:30:35 | \\S.*\\\|.* | sequence | +| redos.py:30:30:30:30 | . | dot | +| redos.py:30:30:30:31 | .* | quantifier | +| redos.py:30:30:30:31 | .* | star | +| redos.py:30:32:30:33 | \\\| | constant | +| redos.py:30:34:30:34 | . | dot | +| redos.py:30:34:30:35 | .* | quantifier | +| redos.py:30:34:30:35 | .* | star | +| redos.py:30:37:30:38 | \\n | constant | +| redos.py:30:39:30:39 | | constant | +| redos.py:30:39:30:40 | * | quantifier | +| redos.py:30:39:30:40 | * | star | +| redos.py:30:41:30:58 | ([-:]+ *\\\|[-\| :]*) | group | +| redos.py:30:42:30:45 | [-:] | char | +| redos.py:30:42:30:46 | [-:]+ | plus | +| redos.py:30:42:30:46 | [-:]+ | quantifier | +| redos.py:30:42:30:57 | [-:]+ *\\\|[-\| :]* | sequence | +| redos.py:30:43:30:43 | - | constant | +| redos.py:30:44:30:44 | : | constant | +| redos.py:30:47:30:47 | | constant | +| redos.py:30:47:30:48 | * | quantifier | +| redos.py:30:47:30:48 | * | star | +| redos.py:30:49:30:50 | \\\| | constant | +| redos.py:30:51:30:56 | [-\| :] | char | +| redos.py:30:51:30:57 | [-\| :]* | quantifier | +| redos.py:30:51:30:57 | [-\| :]* | star | +| redos.py:30:52:30:52 | - | constant | +| redos.py:30:53:30:53 | \| | constant | +| redos.py:30:54:30:54 | | constant | +| redos.py:30:55:30:55 | : | constant | +| redos.py:30:59:30:60 | \\n | constant | +| redos.py:30:61:30:81 | ((?:.*\\\|.*(?:\\n\|$))*) | group | +| redos.py:30:62:30:79 | (?:.*\\\|.*(?:\\n\|$)) | group | +| redos.py:30:62:30:80 | (?:.*\\\|.*(?:\\n\|$))* | quantifier | +| redos.py:30:62:30:80 | (?:.*\\\|.*(?:\\n\|$))* | star | +| redos.py:30:65:30:65 | . | dot | +| redos.py:30:65:30:66 | .* | quantifier | +| redos.py:30:65:30:66 | .* | star | +| redos.py:30:65:30:78 | .*\\\|.*(?:\\n\|$) | sequence | +| redos.py:30:67:30:68 | \\\| | constant | +| redos.py:30:69:30:69 | . | dot | +| redos.py:30:69:30:70 | .* | quantifier | +| redos.py:30:69:30:70 | .* | star | +| redos.py:30:71:30:78 | (?:\\n\|$) | group | +| redos.py:30:74:30:75 | \\n | constant | +| redos.py:30:74:30:77 | \\n\|$ | alt | +| redos.py:30:77:30:77 | $ | dollar | +| redos.py:30:82:30:83 | \\n | constant | +| redos.py:30:82:30:84 | \\n* | quantifier | +| redos.py:30:82:30:84 | \\n* | star | +| redos.py:33:23:33:23 | ^ | caret | +| redos.py:33:23:33:81 | ^ *(\\S.*\\\|.*)\\n *([-:]+ *\\\|[-\| :]*)\\n((?:.*\\\|.*(?:\\n\|$))*)a | sequence | +| redos.py:33:24:33:24 | | constant | +| redos.py:33:24:33:25 | * | quantifier | +| redos.py:33:24:33:25 | * | star | +| redos.py:33:26:33:35 | (\\S.*\\\|.*) | group | +| redos.py:33:27:33:28 | \\S | charEsc | +| redos.py:33:27:33:34 | \\S.*\\\|.* | sequence | +| redos.py:33:29:33:29 | . | dot | +| redos.py:33:29:33:30 | .* | quantifier | +| redos.py:33:29:33:30 | .* | star | +| redos.py:33:31:33:32 | \\\| | constant | +| redos.py:33:33:33:33 | . | dot | +| redos.py:33:33:33:34 | .* | quantifier | +| redos.py:33:33:33:34 | .* | star | +| redos.py:33:36:33:37 | \\n | constant | +| redos.py:33:38:33:38 | | constant | +| redos.py:33:38:33:39 | * | quantifier | +| redos.py:33:38:33:39 | * | star | +| redos.py:33:40:33:57 | ([-:]+ *\\\|[-\| :]*) | group | +| redos.py:33:41:33:44 | [-:] | char | +| redos.py:33:41:33:45 | [-:]+ | plus | +| redos.py:33:41:33:45 | [-:]+ | quantifier | +| redos.py:33:41:33:56 | [-:]+ *\\\|[-\| :]* | sequence | +| redos.py:33:42:33:42 | - | constant | +| redos.py:33:43:33:43 | : | constant | +| redos.py:33:46:33:46 | | constant | +| redos.py:33:46:33:47 | * | quantifier | +| redos.py:33:46:33:47 | * | star | +| redos.py:33:48:33:49 | \\\| | constant | +| redos.py:33:50:33:55 | [-\| :] | char | +| redos.py:33:50:33:56 | [-\| :]* | quantifier | +| redos.py:33:50:33:56 | [-\| :]* | star | +| redos.py:33:51:33:51 | - | constant | +| redos.py:33:52:33:52 | \| | constant | +| redos.py:33:53:33:53 | | constant | +| redos.py:33:54:33:54 | : | constant | +| redos.py:33:58:33:59 | \\n | constant | +| redos.py:33:60:33:80 | ((?:.*\\\|.*(?:\\n\|$))*) | group | +| redos.py:33:61:33:78 | (?:.*\\\|.*(?:\\n\|$)) | group | +| redos.py:33:61:33:79 | (?:.*\\\|.*(?:\\n\|$))* | quantifier | +| redos.py:33:61:33:79 | (?:.*\\\|.*(?:\\n\|$))* | star | +| redos.py:33:64:33:64 | . | dot | +| redos.py:33:64:33:65 | .* | quantifier | +| redos.py:33:64:33:65 | .* | star | +| redos.py:33:64:33:77 | .*\\\|.*(?:\\n\|$) | sequence | +| redos.py:33:66:33:67 | \\\| | constant | +| redos.py:33:68:33:68 | . | dot | +| redos.py:33:68:33:69 | .* | quantifier | +| redos.py:33:68:33:69 | .* | star | +| redos.py:33:70:33:77 | (?:\\n\|$) | group | +| redos.py:33:73:33:74 | \\n | constant | +| redos.py:33:73:33:76 | \\n\|$ | alt | +| redos.py:33:76:33:76 | $ | dollar | +| redos.py:33:81:33:81 | a | constant | +| redos.py:38:23:38:24 | \\/ | constant | +| redos.py:38:23:38:58 | \\/(?![ *])(\\\\\\/\|.)*?\\/[gim]*(?=\\W\|$) | sequence | +| redos.py:38:25:38:32 | (?![ *]) | group | +| redos.py:38:28:38:31 | [ *] | char | +| redos.py:38:29:38:29 | | constant | +| redos.py:38:30:38:30 | * | constant | +| redos.py:38:33:38:40 | (\\\\\\/\|.) | group | +| redos.py:38:33:38:42 | (\\\\\\/\|.)*? | quantifier | +| redos.py:38:33:38:42 | (\\\\\\/\|.)*? | star | +| redos.py:38:34:38:35 | \\\\ | constant | +| redos.py:38:34:38:37 | \\\\\\/ | sequence | +| redos.py:38:34:38:39 | \\\\\\/\|. | alt | +| redos.py:38:36:38:37 | \\/ | constant | +| redos.py:38:39:38:39 | . | dot | +| redos.py:38:43:38:44 | \\/ | constant | +| redos.py:38:45:38:49 | [gim] | char | +| redos.py:38:45:38:50 | [gim]* | quantifier | +| redos.py:38:45:38:50 | [gim]* | star | +| redos.py:38:46:38:46 | g | constant | +| redos.py:38:47:38:47 | i | constant | +| redos.py:38:48:38:48 | m | constant | +| redos.py:38:51:38:58 | (?=\\W\|$) | group | +| redos.py:38:54:38:55 | \\W | charEsc | +| redos.py:38:54:38:57 | \\W\|$ | alt | +| redos.py:38:57:38:57 | $ | dollar | +| redos.py:43:23:43:23 | ^ | caret | +| redos.py:43:23:43:41 | ^([\\s\\[\\{\\(]\|#.*)*$ | sequence | +| redos.py:43:24:43:39 | ([\\s\\[\\{\\(]\|#.*) | group | +| redos.py:43:24:43:40 | ([\\s\\[\\{\\(]\|#.*)* | quantifier | +| redos.py:43:24:43:40 | ([\\s\\[\\{\\(]\|#.*)* | star | +| redos.py:43:25:43:34 | [\\s\\[\\{\\(] | char | +| redos.py:43:25:43:38 | [\\s\\[\\{\\(]\|#.* | alt | +| redos.py:43:26:43:27 | \\s | charEsc | +| redos.py:43:28:43:29 | \\[ | constant | +| redos.py:43:30:43:31 | \\{ | constant | +| redos.py:43:32:43:33 | \\( | constant | +| redos.py:43:36:43:36 | # | constant | +| redos.py:43:36:43:38 | #.* | sequence | +| redos.py:43:37:43:37 | . | dot | +| redos.py:43:37:43:38 | .* | quantifier | +| redos.py:43:37:43:38 | .* | star | +| redos.py:43:41:43:41 | $ | dollar | +| redos.py:46:24:46:35 | (\\r\\n\|\\r\|\\n) | group | +| redos.py:46:24:46:36 | (\\r\\n\|\\r\|\\n)+ | plus | +| redos.py:46:24:46:36 | (\\r\\n\|\\r\|\\n)+ | quantifier | +| redos.py:46:25:46:26 | \\r | constant | +| redos.py:46:25:46:28 | \\r\\n | sequence | +| redos.py:46:25:46:34 | \\r\\n\|\\r\|\\n | alt | +| redos.py:46:27:46:28 | \\n | constant | +| redos.py:46:30:46:31 | \\r | constant | +| redos.py:46:33:46:34 | \\n | constant | +| redos.py:49:30:49:54 | ((?:[^"']\|".*?"\|'.*?')*?) | group | +| redos.py:49:30:49:63 | ((?:[^"']\|".*?"\|'.*?')*?)([(,)]\|$) | sequence | +| redos.py:49:31:49:51 | (?:[^"']\|".*?"\|'.*?') | group | +| redos.py:49:31:49:53 | (?:[^"']\|".*?"\|'.*?')*? | quantifier | +| redos.py:49:31:49:53 | (?:[^"']\|".*?"\|'.*?')*? | star | +| redos.py:49:34:49:38 | [^"'] | char | +| redos.py:49:34:49:50 | [^"']\|".*?"\|'.*?' | alt | +| redos.py:49:36:49:36 | " | constant | +| redos.py:49:37:49:37 | ' | constant | +| redos.py:49:40:49:40 | " | constant | +| redos.py:49:40:49:44 | ".*?" | sequence | +| redos.py:49:41:49:41 | . | dot | +| redos.py:49:41:49:43 | .*? | quantifier | +| redos.py:49:41:49:43 | .*? | star | +| redos.py:49:44:49:44 | " | constant | +| redos.py:49:46:49:46 | ' | constant | +| redos.py:49:46:49:50 | '.*?' | sequence | +| redos.py:49:47:49:47 | . | dot | +| redos.py:49:47:49:49 | .*? | quantifier | +| redos.py:49:47:49:49 | .*? | star | +| redos.py:49:50:49:50 | ' | constant | +| redos.py:49:55:49:63 | ([(,)]\|$) | group | +| redos.py:49:56:49:60 | [(,)] | char | +| redos.py:49:56:49:62 | [(,)]\|$ | alt | +| redos.py:49:57:49:57 | ( | constant | +| redos.py:49:58:49:58 | , | constant | +| redos.py:49:59:49:59 | ) | constant | +| redos.py:49:62:49:62 | $ | dollar | +| redos.py:54:23:54:23 | ^ | caret | +| redos.py:54:23:54:89 | ^[\\_$a-z][\\_$a-z0-9]*(\\[.*?\\])*(\\.[\\_$a-z][\\_$a-z0-9]*(\\[.*?\\])*)*$ | sequence | +| redos.py:54:24:54:31 | [\\_$a-z] | char | +| redos.py:54:25:54:26 | \\_ | constant | +| redos.py:54:27:54:27 | $ | constant | +| redos.py:54:28:54:28 | a | constant | +| redos.py:54:28:54:30 | a-z | charRange | +| redos.py:54:30:54:30 | z | constant | +| redos.py:54:32:54:42 | [\\_$a-z0-9] | char | +| redos.py:54:32:54:43 | [\\_$a-z0-9]* | quantifier | +| redos.py:54:32:54:43 | [\\_$a-z0-9]* | star | +| redos.py:54:33:54:34 | \\_ | constant | +| redos.py:54:35:54:35 | $ | constant | +| redos.py:54:36:54:36 | a | constant | +| redos.py:54:36:54:38 | a-z | charRange | +| redos.py:54:38:54:38 | z | constant | +| redos.py:54:39:54:39 | 0 | constant | +| redos.py:54:39:54:41 | 0-9 | charRange | +| redos.py:54:41:54:41 | 9 | constant | +| redos.py:54:44:54:52 | (\\[.*?\\]) | group | +| redos.py:54:44:54:53 | (\\[.*?\\])* | quantifier | +| redos.py:54:44:54:53 | (\\[.*?\\])* | star | +| redos.py:54:45:54:46 | \\[ | constant | +| redos.py:54:45:54:51 | \\[.*?\\] | sequence | +| redos.py:54:47:54:47 | . | dot | +| redos.py:54:47:54:49 | .*? | quantifier | +| redos.py:54:47:54:49 | .*? | star | +| redos.py:54:50:54:51 | \\] | constant | +| redos.py:54:54:54:87 | (\\.[\\_$a-z][\\_$a-z0-9]*(\\[.*?\\])*) | group | +| redos.py:54:54:54:88 | (\\.[\\_$a-z][\\_$a-z0-9]*(\\[.*?\\])*)* | quantifier | +| redos.py:54:54:54:88 | (\\.[\\_$a-z][\\_$a-z0-9]*(\\[.*?\\])*)* | star | +| redos.py:54:55:54:56 | \\. | constant | +| redos.py:54:55:54:86 | \\.[\\_$a-z][\\_$a-z0-9]*(\\[.*?\\])* | sequence | +| redos.py:54:57:54:64 | [\\_$a-z] | char | +| redos.py:54:58:54:59 | \\_ | constant | +| redos.py:54:60:54:60 | $ | constant | +| redos.py:54:61:54:61 | a | constant | +| redos.py:54:61:54:63 | a-z | charRange | +| redos.py:54:63:54:63 | z | constant | +| redos.py:54:65:54:75 | [\\_$a-z0-9] | char | +| redos.py:54:65:54:76 | [\\_$a-z0-9]* | quantifier | +| redos.py:54:65:54:76 | [\\_$a-z0-9]* | star | +| redos.py:54:66:54:67 | \\_ | constant | +| redos.py:54:68:54:68 | $ | constant | +| redos.py:54:69:54:69 | a | constant | +| redos.py:54:69:54:71 | a-z | charRange | +| redos.py:54:71:54:71 | z | constant | +| redos.py:54:72:54:72 | 0 | constant | +| redos.py:54:72:54:74 | 0-9 | charRange | +| redos.py:54:74:54:74 | 9 | constant | +| redos.py:54:77:54:85 | (\\[.*?\\]) | group | +| redos.py:54:77:54:86 | (\\[.*?\\])* | quantifier | +| redos.py:54:77:54:86 | (\\[.*?\\])* | star | +| redos.py:54:78:54:79 | \\[ | constant | +| redos.py:54:78:54:84 | \\[.*?\\] | sequence | +| redos.py:54:80:54:80 | . | dot | +| redos.py:54:80:54:82 | .*? | quantifier | +| redos.py:54:80:54:82 | .*? | star | +| redos.py:54:83:54:84 | \\] | constant | +| redos.py:54:89:54:89 | $ | dollar | +| redos.py:57:24:57:28 | (a\|.) | group | +| redos.py:57:24:57:29 | (a\|.)* | quantifier | +| redos.py:57:24:57:29 | (a\|.)* | star | +| redos.py:57:25:57:25 | a | constant | +| redos.py:57:25:57:27 | a\|. | alt | +| redos.py:57:27:57:27 | . | dot | +| redos.py:60:23:60:23 | ^ | caret | +| redos.py:60:23:60:33 | ^([a-z]+)+$ | sequence | +| redos.py:60:24:60:31 | ([a-z]+) | group | +| redos.py:60:24:60:32 | ([a-z]+)+ | plus | +| redos.py:60:24:60:32 | ([a-z]+)+ | quantifier | +| redos.py:60:25:60:29 | [a-z] | char | +| redos.py:60:25:60:30 | [a-z]+ | plus | +| redos.py:60:25:60:30 | [a-z]+ | quantifier | +| redos.py:60:26:60:26 | a | constant | +| redos.py:60:26:60:28 | a-z | charRange | +| redos.py:60:28:60:28 | z | constant | +| redos.py:60:33:60:33 | $ | dollar | +| redos.py:61:23:61:23 | ^ | caret | +| redos.py:61:23:61:33 | ^([a-z]*)*$ | sequence | +| redos.py:61:24:61:31 | ([a-z]*) | group | +| redos.py:61:24:61:32 | ([a-z]*)* | quantifier | +| redos.py:61:24:61:32 | ([a-z]*)* | star | +| redos.py:61:25:61:29 | [a-z] | char | +| redos.py:61:25:61:30 | [a-z]* | quantifier | +| redos.py:61:25:61:30 | [a-z]* | star | +| redos.py:61:26:61:26 | a | constant | +| redos.py:61:26:61:28 | a-z | charRange | +| redos.py:61:28:61:28 | z | constant | +| redos.py:61:33:61:33 | $ | dollar | +| redos.py:62:23:62:23 | ^ | caret | +| redos.py:62:23:62:132 | ^([a-zA-Z0-9])(([\\\\-.]\|[_]+)?([a-zA-Z0-9]+))*(@){1}[a-z0-9]+[.]{1}(([a-z]{2,3})\|([a-z]{2,3}[.]{1}[a-z]{2,3}))$ | sequence | +| redos.py:62:24:62:36 | ([a-zA-Z0-9]) | group | +| redos.py:62:25:62:35 | [a-zA-Z0-9] | char | +| redos.py:62:26:62:26 | a | constant | +| redos.py:62:26:62:28 | a-z | charRange | +| redos.py:62:28:62:28 | z | constant | +| redos.py:62:29:62:29 | A | constant | +| redos.py:62:29:62:31 | A-Z | charRange | +| redos.py:62:31:62:31 | Z | constant | +| redos.py:62:32:62:32 | 0 | constant | +| redos.py:62:32:62:34 | 0-9 | charRange | +| redos.py:62:34:62:34 | 9 | constant | +| redos.py:62:37:62:66 | (([\\\\-.]\|[_]+)?([a-zA-Z0-9]+)) | group | +| redos.py:62:37:62:67 | (([\\\\-.]\|[_]+)?([a-zA-Z0-9]+))* | quantifier | +| redos.py:62:37:62:67 | (([\\\\-.]\|[_]+)?([a-zA-Z0-9]+))* | star | +| redos.py:62:38:62:50 | ([\\\\-.]\|[_]+) | group | +| redos.py:62:38:62:51 | ([\\\\-.]\|[_]+)? | opt | +| redos.py:62:38:62:51 | ([\\\\-.]\|[_]+)? | quantifier | +| redos.py:62:38:62:65 | ([\\\\-.]\|[_]+)?([a-zA-Z0-9]+) | sequence | +| redos.py:62:39:62:44 | [\\\\-.] | char | +| redos.py:62:39:62:49 | [\\\\-.]\|[_]+ | alt | +| redos.py:62:40:62:41 | \\\\ | constant | +| redos.py:62:40:62:43 | \\\\-. | charRange | +| redos.py:62:43:62:43 | . | constant | +| redos.py:62:46:62:48 | [_] | char | +| redos.py:62:46:62:49 | [_]+ | plus | +| redos.py:62:46:62:49 | [_]+ | quantifier | +| redos.py:62:47:62:47 | _ | constant | +| redos.py:62:52:62:65 | ([a-zA-Z0-9]+) | group | +| redos.py:62:53:62:63 | [a-zA-Z0-9] | char | +| redos.py:62:53:62:64 | [a-zA-Z0-9]+ | plus | +| redos.py:62:53:62:64 | [a-zA-Z0-9]+ | quantifier | +| redos.py:62:54:62:54 | a | constant | +| redos.py:62:54:62:56 | a-z | charRange | +| redos.py:62:56:62:56 | z | constant | +| redos.py:62:57:62:57 | A | constant | +| redos.py:62:57:62:59 | A-Z | charRange | +| redos.py:62:59:62:59 | Z | constant | +| redos.py:62:60:62:60 | 0 | constant | +| redos.py:62:60:62:62 | 0-9 | charRange | +| redos.py:62:62:62:62 | 9 | constant | +| redos.py:62:68:62:70 | (@) | group | +| redos.py:62:68:62:73 | (@){1} | quantifier | +| redos.py:62:68:62:73 | (@){1} | range | +| redos.py:62:69:62:69 | @ | constant | +| redos.py:62:74:62:81 | [a-z0-9] | char | +| redos.py:62:74:62:82 | [a-z0-9]+ | plus | +| redos.py:62:74:62:82 | [a-z0-9]+ | quantifier | +| redos.py:62:75:62:75 | a | constant | +| redos.py:62:75:62:77 | a-z | charRange | +| redos.py:62:77:62:77 | z | constant | +| redos.py:62:78:62:78 | 0 | constant | +| redos.py:62:78:62:80 | 0-9 | charRange | +| redos.py:62:80:62:80 | 9 | constant | +| redos.py:62:83:62:85 | [.] | char | +| redos.py:62:83:62:88 | [.]{1} | quantifier | +| redos.py:62:83:62:88 | [.]{1} | range | +| redos.py:62:84:62:84 | . | constant | +| redos.py:62:89:62:131 | (([a-z]{2,3})\|([a-z]{2,3}[.]{1}[a-z]{2,3})) | group | +| redos.py:62:90:62:101 | ([a-z]{2,3}) | group | +| redos.py:62:90:62:130 | ([a-z]{2,3})\|([a-z]{2,3}[.]{1}[a-z]{2,3}) | alt | +| redos.py:62:91:62:95 | [a-z] | char | +| redos.py:62:91:62:100 | [a-z]{2,3} | quantifier | +| redos.py:62:91:62:100 | [a-z]{2,3} | range | +| redos.py:62:92:62:92 | a | constant | +| redos.py:62:92:62:94 | a-z | charRange | +| redos.py:62:94:62:94 | z | constant | +| redos.py:62:103:62:130 | ([a-z]{2,3}[.]{1}[a-z]{2,3}) | group | +| redos.py:62:104:62:108 | [a-z] | char | +| redos.py:62:104:62:113 | [a-z]{2,3} | quantifier | +| redos.py:62:104:62:113 | [a-z]{2,3} | range | +| redos.py:62:104:62:129 | [a-z]{2,3}[.]{1}[a-z]{2,3} | sequence | +| redos.py:62:105:62:105 | a | constant | +| redos.py:62:105:62:107 | a-z | charRange | +| redos.py:62:107:62:107 | z | constant | +| redos.py:62:114:62:116 | [.] | char | +| redos.py:62:114:62:119 | [.]{1} | quantifier | +| redos.py:62:114:62:119 | [.]{1} | range | +| redos.py:62:115:62:115 | . | constant | +| redos.py:62:120:62:124 | [a-z] | char | +| redos.py:62:120:62:129 | [a-z]{2,3} | quantifier | +| redos.py:62:120:62:129 | [a-z]{2,3} | range | +| redos.py:62:121:62:121 | a | constant | +| redos.py:62:121:62:123 | a-z | charRange | +| redos.py:62:123:62:123 | z | constant | +| redos.py:62:132:62:132 | $ | dollar | +| redos.py:63:24:63:24 | ^ | caret | +| redos.py:63:24:63:50 | ^(([a-z])+.)+[A-Z]([a-z])+$ | sequence | +| redos.py:63:25:63:35 | (([a-z])+.) | group | +| redos.py:63:25:63:36 | (([a-z])+.)+ | plus | +| redos.py:63:25:63:36 | (([a-z])+.)+ | quantifier | +| redos.py:63:26:63:32 | ([a-z]) | group | +| redos.py:63:26:63:33 | ([a-z])+ | plus | +| redos.py:63:26:63:33 | ([a-z])+ | quantifier | +| redos.py:63:26:63:34 | ([a-z])+. | sequence | +| redos.py:63:27:63:31 | [a-z] | char | +| redos.py:63:28:63:28 | a | constant | +| redos.py:63:28:63:30 | a-z | charRange | +| redos.py:63:30:63:30 | z | constant | +| redos.py:63:34:63:34 | . | dot | +| redos.py:63:37:63:41 | [A-Z] | char | +| redos.py:63:38:63:38 | A | constant | +| redos.py:63:38:63:40 | A-Z | charRange | +| redos.py:63:40:63:40 | Z | constant | +| redos.py:63:42:63:48 | ([a-z]) | group | +| redos.py:63:42:63:49 | ([a-z])+ | plus | +| redos.py:63:42:63:49 | ([a-z])+ | quantifier | +| redos.py:63:43:63:47 | [a-z] | char | +| redos.py:63:44:63:44 | a | constant | +| redos.py:63:44:63:46 | a-z | charRange | +| redos.py:63:46:63:46 | z | constant | +| redos.py:63:50:63:50 | $ | dollar | +| redos.py:68:24:68:55 | (([\\w#:.~>+()\\s-]+\|\\*\|\\[.*?\\])+) | group | +| redos.py:68:24:68:63 | (([\\w#:.~>+()\\s-]+\|\\*\|\\[.*?\\])+)\\s*(,\|$) | sequence | +| redos.py:68:25:68:53 | ([\\w#:.~>+()\\s-]+\|\\*\|\\[.*?\\]) | group | +| redos.py:68:25:68:54 | ([\\w#:.~>+()\\s-]+\|\\*\|\\[.*?\\])+ | plus | +| redos.py:68:25:68:54 | ([\\w#:.~>+()\\s-]+\|\\*\|\\[.*?\\])+ | quantifier | +| redos.py:68:26:68:40 | [\\w#:.~>+()\\s-] | char | +| redos.py:68:26:68:41 | [\\w#:.~>+()\\s-]+ | plus | +| redos.py:68:26:68:41 | [\\w#:.~>+()\\s-]+ | quantifier | +| redos.py:68:26:68:52 | [\\w#:.~>+()\\s-]+\|\\*\|\\[.*?\\] | alt | +| redos.py:68:27:68:28 | \\w | charEsc | +| redos.py:68:29:68:29 | # | constant | +| redos.py:68:30:68:30 | : | constant | +| redos.py:68:31:68:31 | . | constant | +| redos.py:68:32:68:32 | ~ | constant | +| redos.py:68:33:68:33 | > | constant | +| redos.py:68:34:68:34 | + | constant | +| redos.py:68:35:68:35 | ( | constant | +| redos.py:68:36:68:36 | ) | constant | +| redos.py:68:37:68:38 | \\s | charEsc | +| redos.py:68:39:68:39 | - | constant | +| redos.py:68:43:68:44 | \\* | constant | +| redos.py:68:46:68:47 | \\[ | constant | +| redos.py:68:46:68:52 | \\[.*?\\] | sequence | +| redos.py:68:48:68:48 | . | dot | +| redos.py:68:48:68:50 | .*? | quantifier | +| redos.py:68:48:68:50 | .*? | star | +| redos.py:68:51:68:52 | \\] | constant | +| redos.py:68:56:68:57 | \\s | charEsc | +| redos.py:68:56:68:58 | \\s* | quantifier | +| redos.py:68:56:68:58 | \\s* | star | +| redos.py:68:59:68:63 | (,\|$) | group | +| redos.py:68:60:68:60 | , | constant | +| redos.py:68:60:68:62 | ,\|$ | alt | +| redos.py:68:62:68:62 | $ | dollar | +| redos.py:73:24:73:28 | ("\|') | group | +| redos.py:73:24:73:38 | ("\|')(\\\\?.)*?\\1 | sequence | +| redos.py:73:25:73:25 | " | constant | +| redos.py:73:25:73:27 | "\|' | alt | +| redos.py:73:27:73:27 | ' | constant | +| redos.py:73:29:73:34 | (\\\\?.) | group | +| redos.py:73:29:73:36 | (\\\\?.)*? | quantifier | +| redos.py:73:29:73:36 | (\\\\?.)*? | star | +| redos.py:73:30:73:31 | \\\\ | constant | +| redos.py:73:30:73:32 | \\\\? | opt | +| redos.py:73:30:73:32 | \\\\? | quantifier | +| redos.py:73:30:73:33 | \\\\?. | sequence | +| redos.py:73:33:73:33 | . | dot | +| redos.py:76:24:76:30 | (b\|a?b) | group | +| redos.py:76:24:76:31 | (b\|a?b)* | quantifier | +| redos.py:76:24:76:31 | (b\|a?b)* | star | +| redos.py:76:24:76:32 | (b\|a?b)*c | sequence | +| redos.py:76:25:76:25 | b | constant | +| redos.py:76:25:76:29 | b\|a?b | alt | +| redos.py:76:27:76:27 | a | constant | +| redos.py:76:27:76:28 | a? | opt | +| redos.py:76:27:76:28 | a? | quantifier | +| redos.py:76:27:76:29 | a?b | sequence | +| redos.py:76:29:76:29 | b | constant | +| redos.py:76:32:76:32 | c | constant | +| redos.py:79:24:79:30 | (a\|aa?) | group | +| redos.py:79:24:79:31 | (a\|aa?)* | quantifier | +| redos.py:79:24:79:31 | (a\|aa?)* | star | +| redos.py:79:24:79:32 | (a\|aa?)*b | sequence | +| redos.py:79:25:79:25 | a | constant | +| redos.py:79:25:79:29 | a\|aa? | alt | +| redos.py:79:27:79:27 | a | constant | +| redos.py:79:27:79:29 | aa? | sequence | +| redos.py:79:28:79:28 | a | constant | +| redos.py:79:28:79:29 | a? | opt | +| redos.py:79:28:79:29 | a? | quantifier | +| redos.py:79:32:79:32 | b | constant | +| redos.py:82:24:82:29 | (.\|\\n) | group | +| redos.py:82:24:82:30 | (.\|\\n)* | quantifier | +| redos.py:82:24:82:30 | (.\|\\n)* | star | +| redos.py:82:24:82:31 | (.\|\\n)*! | sequence | +| redos.py:82:25:82:25 | . | dot | +| redos.py:82:25:82:28 | .\|\\n | alt | +| redos.py:82:27:82:28 | \\n | constant | +| redos.py:82:31:82:31 | ! | constant | +| redos.py:85:24:85:29 | (.\|\\n) | group | +| redos.py:85:24:85:30 | (.\|\\n)* | quantifier | +| redos.py:85:24:85:30 | (.\|\\n)* | star | +| redos.py:85:24:85:31 | (.\|\\n)*! | sequence | +| redos.py:85:25:85:25 | . | dot | +| redos.py:85:25:85:28 | .\|\\n | alt | +| redos.py:85:27:85:28 | \\n | constant | +| redos.py:85:31:85:31 | ! | constant | +| redos.py:88:24:88:31 | ([\\w.]+) | group | +| redos.py:88:24:88:32 | ([\\w.]+)* | quantifier | +| redos.py:88:24:88:32 | ([\\w.]+)* | star | +| redos.py:88:25:88:29 | [\\w.] | char | +| redos.py:88:25:88:30 | [\\w.]+ | plus | +| redos.py:88:25:88:30 | [\\w.]+ | quantifier | +| redos.py:88:26:88:27 | \\w | charEsc | +| redos.py:88:28:88:28 | . | constant | +| redos.py:91:24:91:30 | (a\|aa?) | group | +| redos.py:91:24:91:31 | (a\|aa?)* | quantifier | +| redos.py:91:24:91:31 | (a\|aa?)* | star | +| redos.py:91:24:91:32 | (a\|aa?)*b | sequence | +| redos.py:91:25:91:25 | a | constant | +| redos.py:91:25:91:29 | a\|aa? | alt | +| redos.py:91:27:91:27 | a | constant | +| redos.py:91:27:91:29 | aa? | sequence | +| redos.py:91:28:91:28 | a | constant | +| redos.py:91:28:91:29 | a? | opt | +| redos.py:91:28:91:29 | a? | quantifier | +| redos.py:91:32:91:32 | b | constant | +| redos.py:97:24:97:39 | (([\\s\\S]\|[^a])*) | group | +| redos.py:97:24:97:40 | (([\\s\\S]\|[^a])*)" | sequence | +| redos.py:97:25:97:37 | ([\\s\\S]\|[^a]) | group | +| redos.py:97:25:97:38 | ([\\s\\S]\|[^a])* | quantifier | +| redos.py:97:25:97:38 | ([\\s\\S]\|[^a])* | star | +| redos.py:97:26:97:31 | [\\s\\S] | char | +| redos.py:97:26:97:36 | [\\s\\S]\|[^a] | alt | +| redos.py:97:27:97:28 | \\s | charEsc | +| redos.py:97:29:97:30 | \\S | charEsc | +| redos.py:97:33:97:36 | [^a] | char | +| redos.py:97:35:97:35 | a | constant | +| redos.py:97:40:97:40 | " | constant | +| redos.py:100:25:100:32 | ([^"']+) | group | +| redos.py:100:25:100:33 | ([^"']+)* | quantifier | +| redos.py:100:25:100:33 | ([^"']+)* | star | +| redos.py:100:26:100:30 | [^"'] | char | +| redos.py:100:26:100:31 | [^"']+ | plus | +| redos.py:100:26:100:31 | [^"']+ | quantifier | +| redos.py:100:28:100:28 | " | constant | +| redos.py:100:29:100:29 | ' | constant | +| redos.py:103:24:103:34 | ((.\|[^a])*) | group | +| redos.py:103:24:103:35 | ((.\|[^a])*)" | sequence | +| redos.py:103:25:103:32 | (.\|[^a]) | group | +| redos.py:103:25:103:33 | (.\|[^a])* | quantifier | +| redos.py:103:25:103:33 | (.\|[^a])* | star | +| redos.py:103:26:103:26 | . | dot | +| redos.py:103:26:103:31 | .\|[^a] | alt | +| redos.py:103:28:103:31 | [^a] | char | +| redos.py:103:30:103:30 | a | constant | +| redos.py:103:35:103:35 | " | constant | +| redos.py:106:25:106:35 | ((a\|[^a])*) | group | +| redos.py:106:25:106:36 | ((a\|[^a])*)" | sequence | +| redos.py:106:26:106:33 | (a\|[^a]) | group | +| redos.py:106:26:106:34 | (a\|[^a])* | quantifier | +| redos.py:106:26:106:34 | (a\|[^a])* | star | +| redos.py:106:27:106:27 | a | constant | +| redos.py:106:27:106:32 | a\|[^a] | alt | +| redos.py:106:29:106:32 | [^a] | char | +| redos.py:106:31:106:31 | a | constant | +| redos.py:106:36:106:36 | " | constant | +| redos.py:109:24:109:34 | ((b\|[^a])*) | group | +| redos.py:109:24:109:35 | ((b\|[^a])*)" | sequence | +| redos.py:109:25:109:32 | (b\|[^a]) | group | +| redos.py:109:25:109:33 | (b\|[^a])* | quantifier | +| redos.py:109:25:109:33 | (b\|[^a])* | star | +| redos.py:109:26:109:26 | b | constant | +| redos.py:109:26:109:31 | b\|[^a] | alt | +| redos.py:109:28:109:31 | [^a] | char | +| redos.py:109:30:109:30 | a | constant | +| redos.py:109:35:109:35 | " | constant | +| redos.py:112:24:112:34 | ((G\|[^a])*) | group | +| redos.py:112:24:112:35 | ((G\|[^a])*)" | sequence | +| redos.py:112:25:112:32 | (G\|[^a]) | group | +| redos.py:112:25:112:33 | (G\|[^a])* | quantifier | +| redos.py:112:25:112:33 | (G\|[^a])* | star | +| redos.py:112:26:112:26 | G | constant | +| redos.py:112:26:112:31 | G\|[^a] | alt | +| redos.py:112:28:112:31 | [^a] | char | +| redos.py:112:30:112:30 | a | constant | +| redos.py:112:35:112:35 | " | constant | +| redos.py:115:24:115:38 | (([0-9]\|[^a])*) | group | +| redos.py:115:24:115:39 | (([0-9]\|[^a])*)" | sequence | +| redos.py:115:25:115:36 | ([0-9]\|[^a]) | group | +| redos.py:115:25:115:37 | ([0-9]\|[^a])* | quantifier | +| redos.py:115:25:115:37 | ([0-9]\|[^a])* | star | +| redos.py:115:26:115:30 | [0-9] | char | +| redos.py:115:26:115:35 | [0-9]\|[^a] | alt | +| redos.py:115:27:115:27 | 0 | constant | +| redos.py:115:27:115:29 | 0-9 | charRange | +| redos.py:115:29:115:29 | 9 | constant | +| redos.py:115:32:115:35 | [^a] | char | +| redos.py:115:34:115:34 | a | constant | +| redos.py:115:39:115:39 | " | constant | +| redos.py:118:24:118:118 | (?:=(?:([!#\\$%&'\\*\\+\\-\\.\\^_`\\\|~0-9A-Za-z]+)\|"((?:\\\\[\\x00-\\x7f]\|[^\\x00-\\x08\\x0a-\\x1f\\x7f"])*)")) | group | +| redos.py:118:24:118:119 | (?:=(?:([!#\\$%&'\\*\\+\\-\\.\\^_`\\\|~0-9A-Za-z]+)\|"((?:\\\\[\\x00-\\x7f]\|[^\\x00-\\x08\\x0a-\\x1f\\x7f"])*)"))? | opt | +| redos.py:118:24:118:119 | (?:=(?:([!#\\$%&'\\*\\+\\-\\.\\^_`\\\|~0-9A-Za-z]+)\|"((?:\\\\[\\x00-\\x7f]\|[^\\x00-\\x08\\x0a-\\x1f\\x7f"])*)"))? | quantifier | +| redos.py:118:27:118:27 | = | constant | +| redos.py:118:27:118:117 | =(?:([!#\\$%&'\\*\\+\\-\\.\\^_`\\\|~0-9A-Za-z]+)\|"((?:\\\\[\\x00-\\x7f]\|[^\\x00-\\x08\\x0a-\\x1f\\x7f"])*)") | sequence | +| redos.py:118:28:118:117 | (?:([!#\\$%&'\\*\\+\\-\\.\\^_`\\\|~0-9A-Za-z]+)\|"((?:\\\\[\\x00-\\x7f]\|[^\\x00-\\x08\\x0a-\\x1f\\x7f"])*)") | group | +| redos.py:118:31:118:66 | ([!#\\$%&'\\*\\+\\-\\.\\^_`\\\|~0-9A-Za-z]+) | group | +| redos.py:118:31:118:116 | ([!#\\$%&'\\*\\+\\-\\.\\^_`\\\|~0-9A-Za-z]+)\|"((?:\\\\[\\x00-\\x7f]\|[^\\x00-\\x08\\x0a-\\x1f\\x7f"])*)" | alt | +| redos.py:118:32:118:64 | [!#\\$%&'\\*\\+\\-\\.\\^_`\\\|~0-9A-Za-z] | char | +| redos.py:118:32:118:65 | [!#\\$%&'\\*\\+\\-\\.\\^_`\\\|~0-9A-Za-z]+ | plus | +| redos.py:118:32:118:65 | [!#\\$%&'\\*\\+\\-\\.\\^_`\\\|~0-9A-Za-z]+ | quantifier | +| redos.py:118:33:118:33 | ! | constant | +| redos.py:118:34:118:34 | # | constant | +| redos.py:118:35:118:36 | \\$ | constant | +| redos.py:118:37:118:37 | % | constant | +| redos.py:118:38:118:38 | & | constant | +| redos.py:118:39:118:39 | ' | constant | +| redos.py:118:40:118:41 | \\* | constant | +| redos.py:118:42:118:43 | \\+ | constant | +| redos.py:118:44:118:45 | \\- | constant | +| redos.py:118:46:118:47 | \\. | constant | +| redos.py:118:48:118:49 | \\^ | constant | +| redos.py:118:50:118:50 | _ | constant | +| redos.py:118:51:118:51 | ` | constant | +| redos.py:118:52:118:53 | \\\| | constant | +| redos.py:118:54:118:54 | ~ | constant | +| redos.py:118:55:118:55 | 0 | constant | +| redos.py:118:55:118:57 | 0-9 | charRange | +| redos.py:118:57:118:57 | 9 | constant | +| redos.py:118:58:118:58 | A | constant | +| redos.py:118:58:118:60 | A-Z | charRange | +| redos.py:118:60:118:60 | Z | constant | +| redos.py:118:61:118:61 | a | constant | +| redos.py:118:61:118:63 | a-z | charRange | +| redos.py:118:63:118:63 | z | constant | +| redos.py:118:68:118:68 | " | constant | +| redos.py:118:68:118:116 | "((?:\\\\[\\x00-\\x7f]\|[^\\x00-\\x08\\x0a-\\x1f\\x7f"])*)" | sequence | +| redos.py:118:69:118:115 | ((?:\\\\[\\x00-\\x7f]\|[^\\x00-\\x08\\x0a-\\x1f\\x7f"])*) | group | +| redos.py:118:70:118:113 | (?:\\\\[\\x00-\\x7f]\|[^\\x00-\\x08\\x0a-\\x1f\\x7f"]) | group | +| redos.py:118:70:118:114 | (?:\\\\[\\x00-\\x7f]\|[^\\x00-\\x08\\x0a-\\x1f\\x7f"])* | quantifier | +| redos.py:118:70:118:114 | (?:\\\\[\\x00-\\x7f]\|[^\\x00-\\x08\\x0a-\\x1f\\x7f"])* | star | +| redos.py:118:73:118:74 | \\\\ | constant | +| redos.py:118:73:118:85 | \\\\[\\x00-\\x7f] | sequence | +| redos.py:118:73:118:112 | \\\\[\\x00-\\x7f]\|[^\\x00-\\x08\\x0a-\\x1f\\x7f"] | alt | +| redos.py:118:75:118:85 | [\\x00-\\x7f] | char | +| redos.py:118:76:118:79 | \\x00 | constant | +| redos.py:118:76:118:84 | \\x00-\\x7f | charRange | +| redos.py:118:81:118:84 | \\x7f | constant | +| redos.py:118:87:118:112 | [^\\x00-\\x08\\x0a-\\x1f\\x7f"] | char | +| redos.py:118:89:118:92 | \\x00 | constant | +| redos.py:118:89:118:97 | \\x00-\\x08 | charRange | +| redos.py:118:94:118:97 | \\x08 | constant | +| redos.py:118:98:118:101 | \\x0a | constant | +| redos.py:118:98:118:106 | \\x0a-\\x1f | charRange | +| redos.py:118:103:118:106 | \\x1f | constant | +| redos.py:118:107:118:110 | \\x7f | constant | +| redos.py:118:111:118:111 | " | constant | +| redos.py:118:116:118:116 | " | constant | +| redos.py:121:24:121:24 | " | constant | +| redos.py:121:24:121:72 | "((?:\\\\[\\x00-\\x7f]\|[^\\x00-\\x08\\x0a-\\x1f\\x7f"])*)" | sequence | +| redos.py:121:25:121:71 | ((?:\\\\[\\x00-\\x7f]\|[^\\x00-\\x08\\x0a-\\x1f\\x7f"])*) | group | +| redos.py:121:26:121:69 | (?:\\\\[\\x00-\\x7f]\|[^\\x00-\\x08\\x0a-\\x1f\\x7f"]) | group | +| redos.py:121:26:121:70 | (?:\\\\[\\x00-\\x7f]\|[^\\x00-\\x08\\x0a-\\x1f\\x7f"])* | quantifier | +| redos.py:121:26:121:70 | (?:\\\\[\\x00-\\x7f]\|[^\\x00-\\x08\\x0a-\\x1f\\x7f"])* | star | +| redos.py:121:29:121:30 | \\\\ | constant | +| redos.py:121:29:121:41 | \\\\[\\x00-\\x7f] | sequence | +| redos.py:121:29:121:68 | \\\\[\\x00-\\x7f]\|[^\\x00-\\x08\\x0a-\\x1f\\x7f"] | alt | +| redos.py:121:31:121:41 | [\\x00-\\x7f] | char | +| redos.py:121:32:121:35 | \\x00 | constant | +| redos.py:121:32:121:40 | \\x00-\\x7f | charRange | +| redos.py:121:37:121:40 | \\x7f | constant | +| redos.py:121:43:121:68 | [^\\x00-\\x08\\x0a-\\x1f\\x7f"] | char | +| redos.py:121:45:121:48 | \\x00 | constant | +| redos.py:121:45:121:53 | \\x00-\\x08 | charRange | +| redos.py:121:50:121:53 | \\x08 | constant | +| redos.py:121:54:121:57 | \\x0a | constant | +| redos.py:121:54:121:62 | \\x0a-\\x1f | charRange | +| redos.py:121:59:121:62 | \\x1f | constant | +| redos.py:121:63:121:66 | \\x7f | constant | +| redos.py:121:67:121:67 | " | constant | +| redos.py:121:72:121:72 | " | constant | +| redos.py:124:24:124:24 | " | constant | +| redos.py:124:24:124:74 | "((?:\\\\[\\x00-\\x7f]\|[^\\x00-\\x08\\x0a-\\x1f\\x7f"\\\\])*)" | sequence | +| redos.py:124:25:124:73 | ((?:\\\\[\\x00-\\x7f]\|[^\\x00-\\x08\\x0a-\\x1f\\x7f"\\\\])*) | group | +| redos.py:124:26:124:71 | (?:\\\\[\\x00-\\x7f]\|[^\\x00-\\x08\\x0a-\\x1f\\x7f"\\\\]) | group | +| redos.py:124:26:124:72 | (?:\\\\[\\x00-\\x7f]\|[^\\x00-\\x08\\x0a-\\x1f\\x7f"\\\\])* | quantifier | +| redos.py:124:26:124:72 | (?:\\\\[\\x00-\\x7f]\|[^\\x00-\\x08\\x0a-\\x1f\\x7f"\\\\])* | star | +| redos.py:124:29:124:30 | \\\\ | constant | +| redos.py:124:29:124:41 | \\\\[\\x00-\\x7f] | sequence | +| redos.py:124:29:124:70 | \\\\[\\x00-\\x7f]\|[^\\x00-\\x08\\x0a-\\x1f\\x7f"\\\\] | alt | +| redos.py:124:31:124:41 | [\\x00-\\x7f] | char | +| redos.py:124:32:124:35 | \\x00 | constant | +| redos.py:124:32:124:40 | \\x00-\\x7f | charRange | +| redos.py:124:37:124:40 | \\x7f | constant | +| redos.py:124:43:124:70 | [^\\x00-\\x08\\x0a-\\x1f\\x7f"\\\\] | char | +| redos.py:124:45:124:48 | \\x00 | constant | +| redos.py:124:45:124:53 | \\x00-\\x08 | charRange | +| redos.py:124:50:124:53 | \\x08 | constant | +| redos.py:124:54:124:57 | \\x0a | constant | +| redos.py:124:54:124:62 | \\x0a-\\x1f | charRange | +| redos.py:124:59:124:62 | \\x1f | constant | +| redos.py:124:63:124:66 | \\x7f | constant | +| redos.py:124:67:124:67 | " | constant | +| redos.py:124:68:124:69 | \\\\ | constant | +| redos.py:124:74:124:74 | " | constant | +| redos.py:127:24:127:39 | (([a-z]\|[d-h])*) | group | +| redos.py:127:24:127:40 | (([a-z]\|[d-h])*)" | sequence | +| redos.py:127:25:127:37 | ([a-z]\|[d-h]) | group | +| redos.py:127:25:127:38 | ([a-z]\|[d-h])* | quantifier | +| redos.py:127:25:127:38 | ([a-z]\|[d-h])* | star | +| redos.py:127:26:127:30 | [a-z] | char | +| redos.py:127:26:127:36 | [a-z]\|[d-h] | alt | +| redos.py:127:27:127:27 | a | constant | +| redos.py:127:27:127:29 | a-z | charRange | +| redos.py:127:29:127:29 | z | constant | +| redos.py:127:32:127:36 | [d-h] | char | +| redos.py:127:33:127:33 | d | constant | +| redos.py:127:33:127:35 | d-h | charRange | +| redos.py:127:35:127:35 | h | constant | +| redos.py:127:40:127:40 | " | constant | +| redos.py:130:24:130:41 | (([^a-z]\|[^0-9])*) | group | +| redos.py:130:24:130:42 | (([^a-z]\|[^0-9])*)" | sequence | +| redos.py:130:25:130:39 | ([^a-z]\|[^0-9]) | group | +| redos.py:130:25:130:40 | ([^a-z]\|[^0-9])* | quantifier | +| redos.py:130:25:130:40 | ([^a-z]\|[^0-9])* | star | +| redos.py:130:26:130:31 | [^a-z] | char | +| redos.py:130:26:130:38 | [^a-z]\|[^0-9] | alt | +| redos.py:130:28:130:28 | a | constant | +| redos.py:130:28:130:30 | a-z | charRange | +| redos.py:130:30:130:30 | z | constant | +| redos.py:130:33:130:38 | [^0-9] | char | +| redos.py:130:35:130:35 | 0 | constant | +| redos.py:130:35:130:37 | 0-9 | charRange | +| redos.py:130:37:130:37 | 9 | constant | +| redos.py:130:42:130:42 | " | constant | +| redos.py:133:24:133:36 | ((\\d\|[0-9])*) | group | +| redos.py:133:24:133:37 | ((\\d\|[0-9])*)" | sequence | +| redos.py:133:25:133:34 | (\\d\|[0-9]) | group | +| redos.py:133:25:133:35 | (\\d\|[0-9])* | quantifier | +| redos.py:133:25:133:35 | (\\d\|[0-9])* | star | +| redos.py:133:26:133:27 | \\d | charEsc | +| redos.py:133:26:133:33 | \\d\|[0-9] | alt | +| redos.py:133:29:133:33 | [0-9] | char | +| redos.py:133:30:133:30 | 0 | constant | +| redos.py:133:30:133:32 | 0-9 | charRange | +| redos.py:133:32:133:32 | 9 | constant | +| redos.py:133:37:133:37 | " | constant | +| redos.py:136:24:136:33 | ((\\s\|\\s)*) | group | +| redos.py:136:24:136:34 | ((\\s\|\\s)*)" | sequence | +| redos.py:136:25:136:31 | (\\s\|\\s) | group | +| redos.py:136:25:136:32 | (\\s\|\\s)* | quantifier | +| redos.py:136:25:136:32 | (\\s\|\\s)* | star | +| redos.py:136:26:136:27 | \\s | charEsc | +| redos.py:136:26:136:30 | \\s\|\\s | alt | +| redos.py:136:29:136:30 | \\s | charEsc | +| redos.py:136:34:136:34 | " | constant | +| redos.py:139:24:139:32 | ((\\w\|G)*) | group | +| redos.py:139:24:139:33 | ((\\w\|G)*)" | sequence | +| redos.py:139:25:139:30 | (\\w\|G) | group | +| redos.py:139:25:139:31 | (\\w\|G)* | quantifier | +| redos.py:139:25:139:31 | (\\w\|G)* | star | +| redos.py:139:26:139:27 | \\w | charEsc | +| redos.py:139:26:139:29 | \\w\|G | alt | +| redos.py:139:29:139:29 | G | constant | +| redos.py:139:33:139:33 | " | constant | +| redos.py:142:25:142:34 | ((\\s\|\\d)*) | group | +| redos.py:142:25:142:35 | ((\\s\|\\d)*)" | sequence | +| redos.py:142:26:142:32 | (\\s\|\\d) | group | +| redos.py:142:26:142:33 | (\\s\|\\d)* | quantifier | +| redos.py:142:26:142:33 | (\\s\|\\d)* | star | +| redos.py:142:27:142:28 | \\s | charEsc | +| redos.py:142:27:142:31 | \\s\|\\d | alt | +| redos.py:142:30:142:31 | \\d | charEsc | +| redos.py:142:35:142:35 | " | constant | +| redos.py:145:24:145:33 | ((\\d\|\\w)*) | group | +| redos.py:145:24:145:34 | ((\\d\|\\w)*)" | sequence | +| redos.py:145:25:145:31 | (\\d\|\\w) | group | +| redos.py:145:25:145:32 | (\\d\|\\w)* | quantifier | +| redos.py:145:25:145:32 | (\\d\|\\w)* | star | +| redos.py:145:26:145:27 | \\d | charEsc | +| redos.py:145:26:145:30 | \\d\|\\w | alt | +| redos.py:145:29:145:30 | \\w | charEsc | +| redos.py:145:34:145:34 | " | constant | +| redos.py:148:24:148:32 | ((\\d\|5)*) | group | +| redos.py:148:24:148:33 | ((\\d\|5)*)" | sequence | +| redos.py:148:25:148:30 | (\\d\|5) | group | +| redos.py:148:25:148:31 | (\\d\|5)* | quantifier | +| redos.py:148:25:148:31 | (\\d\|5)* | star | +| redos.py:148:26:148:27 | \\d | charEsc | +| redos.py:148:26:148:29 | \\d\|5 | alt | +| redos.py:148:29:148:29 | 5 | constant | +| redos.py:148:33:148:33 | " | constant | +| redos.py:151:24:151:35 | ((\\s\|[\\f])*) | group | +| redos.py:151:24:151:36 | ((\\s\|[\\f])*)" | sequence | +| redos.py:151:25:151:33 | (\\s\|[\\f]) | group | +| redos.py:151:25:151:34 | (\\s\|[\\f])* | quantifier | +| redos.py:151:25:151:34 | (\\s\|[\\f])* | star | +| redos.py:151:26:151:27 | \\s | charEsc | +| redos.py:151:26:151:32 | \\s\|[\\f] | alt | +| redos.py:151:29:151:32 | [\\f] | char | +| redos.py:151:30:151:31 | \\f | constant | +| redos.py:151:36:151:36 | " | constant | +| redos.py:154:24:154:39 | ((\\s\|[\\v]\|\\\\v)*) | group | +| redos.py:154:24:154:40 | ((\\s\|[\\v]\|\\\\v)*)" | sequence | +| redos.py:154:25:154:37 | (\\s\|[\\v]\|\\\\v) | group | +| redos.py:154:25:154:38 | (\\s\|[\\v]\|\\\\v)* | quantifier | +| redos.py:154:25:154:38 | (\\s\|[\\v]\|\\\\v)* | star | +| redos.py:154:26:154:27 | \\s | charEsc | +| redos.py:154:26:154:36 | \\s\|[\\v]\|\\\\v | alt | +| redos.py:154:29:154:32 | [\\v] | char | +| redos.py:154:30:154:31 | \\v | constant | +| redos.py:154:34:154:35 | \\\\ | constant | +| redos.py:154:34:154:36 | \\\\v | sequence | +| redos.py:154:36:154:36 | v | constant | +| redos.py:154:40:154:40 | " | constant | +| redos.py:157:24:157:35 | ((\\f\|[\\f])*) | group | +| redos.py:157:24:157:36 | ((\\f\|[\\f])*)" | sequence | +| redos.py:157:25:157:33 | (\\f\|[\\f]) | group | +| redos.py:157:25:157:34 | (\\f\|[\\f])* | quantifier | +| redos.py:157:25:157:34 | (\\f\|[\\f])* | star | +| redos.py:157:26:157:27 | \\f | constant | +| redos.py:157:26:157:32 | \\f\|[\\f] | alt | +| redos.py:157:29:157:32 | [\\f] | char | +| redos.py:157:30:157:31 | \\f | constant | +| redos.py:157:36:157:36 | " | constant | +| redos.py:160:24:160:33 | ((\\W\|\\D)*) | group | +| redos.py:160:24:160:34 | ((\\W\|\\D)*)" | sequence | +| redos.py:160:25:160:31 | (\\W\|\\D) | group | +| redos.py:160:25:160:32 | (\\W\|\\D)* | quantifier | +| redos.py:160:25:160:32 | (\\W\|\\D)* | star | +| redos.py:160:26:160:27 | \\W | charEsc | +| redos.py:160:26:160:30 | \\W\|\\D | alt | +| redos.py:160:29:160:30 | \\D | charEsc | +| redos.py:160:34:160:34 | " | constant | +| redos.py:163:24:163:33 | ((\\S\|\\w)*) | group | +| redos.py:163:24:163:34 | ((\\S\|\\w)*)" | sequence | +| redos.py:163:25:163:31 | (\\S\|\\w) | group | +| redos.py:163:25:163:32 | (\\S\|\\w)* | quantifier | +| redos.py:163:25:163:32 | (\\S\|\\w)* | star | +| redos.py:163:26:163:27 | \\S | charEsc | +| redos.py:163:26:163:30 | \\S\|\\w | alt | +| redos.py:163:29:163:30 | \\w | charEsc | +| redos.py:163:34:163:34 | " | constant | +| redos.py:166:24:166:35 | ((\\S\|[\\w])*) | group | +| redos.py:166:24:166:36 | ((\\S\|[\\w])*)" | sequence | +| redos.py:166:25:166:33 | (\\S\|[\\w]) | group | +| redos.py:166:25:166:34 | (\\S\|[\\w])* | quantifier | +| redos.py:166:25:166:34 | (\\S\|[\\w])* | star | +| redos.py:166:26:166:27 | \\S | charEsc | +| redos.py:166:26:166:32 | \\S\|[\\w] | alt | +| redos.py:166:29:166:32 | [\\w] | char | +| redos.py:166:30:166:31 | \\w | charEsc | +| redos.py:166:36:166:36 | " | constant | +| redos.py:169:24:169:38 | ((1s\|[\\da-z])*) | group | +| redos.py:169:24:169:39 | ((1s\|[\\da-z])*)" | sequence | +| redos.py:169:25:169:36 | (1s\|[\\da-z]) | group | +| redos.py:169:25:169:37 | (1s\|[\\da-z])* | quantifier | +| redos.py:169:25:169:37 | (1s\|[\\da-z])* | star | +| redos.py:169:26:169:26 | 1 | constant | +| redos.py:169:26:169:27 | 1s | sequence | +| redos.py:169:26:169:35 | 1s\|[\\da-z] | alt | +| redos.py:169:27:169:27 | s | constant | +| redos.py:169:29:169:35 | [\\da-z] | char | +| redos.py:169:30:169:31 | \\d | charEsc | +| redos.py:169:32:169:32 | a | constant | +| redos.py:169:32:169:34 | a-z | charRange | +| redos.py:169:34:169:34 | z | constant | +| redos.py:169:39:169:39 | " | constant | +| redos.py:172:24:172:34 | ((0\|[\\d])*) | group | +| redos.py:172:24:172:35 | ((0\|[\\d])*)" | sequence | +| redos.py:172:25:172:32 | (0\|[\\d]) | group | +| redos.py:172:25:172:33 | (0\|[\\d])* | quantifier | +| redos.py:172:25:172:33 | (0\|[\\d])* | star | +| redos.py:172:26:172:26 | 0 | constant | +| redos.py:172:26:172:31 | 0\|[\\d] | alt | +| redos.py:172:28:172:31 | [\\d] | char | +| redos.py:172:29:172:30 | \\d | charEsc | +| redos.py:172:35:172:35 | " | constant | +| redos.py:175:24:175:33 | (([\\d]+)*) | group | +| redos.py:175:24:175:34 | (([\\d]+)*)" | sequence | +| redos.py:175:25:175:31 | ([\\d]+) | group | +| redos.py:175:25:175:32 | ([\\d]+)* | quantifier | +| redos.py:175:25:175:32 | ([\\d]+)* | star | +| redos.py:175:26:175:29 | [\\d] | char | +| redos.py:175:26:175:30 | [\\d]+ | plus | +| redos.py:175:26:175:30 | [\\d]+ | quantifier | +| redos.py:175:27:175:28 | \\d | charEsc | +| redos.py:175:34:175:34 | " | constant | +| redos.py:178:25:178:36 | (\\d+(X\\d+)?) | group | +| redos.py:178:25:178:37 | (\\d+(X\\d+)?)+ | plus | +| redos.py:178:25:178:37 | (\\d+(X\\d+)?)+ | quantifier | +| redos.py:178:26:178:27 | \\d | charEsc | +| redos.py:178:26:178:28 | \\d+ | plus | +| redos.py:178:26:178:28 | \\d+ | quantifier | +| redos.py:178:26:178:35 | \\d+(X\\d+)? | sequence | +| redos.py:178:29:178:34 | (X\\d+) | group | +| redos.py:178:29:178:35 | (X\\d+)? | opt | +| redos.py:178:29:178:35 | (X\\d+)? | quantifier | +| redos.py:178:30:178:30 | X | constant | +| redos.py:178:30:178:33 | X\\d+ | sequence | +| redos.py:178:31:178:32 | \\d | charEsc | +| redos.py:178:31:178:33 | \\d+ | plus | +| redos.py:178:31:178:33 | \\d+ | quantifier | +| redos.py:181:25:181:42 | ([0-9]+(X[0-9]*)?) | group | +| redos.py:181:25:181:43 | ([0-9]+(X[0-9]*)?)* | quantifier | +| redos.py:181:25:181:43 | ([0-9]+(X[0-9]*)?)* | star | +| redos.py:181:26:181:30 | [0-9] | char | +| redos.py:181:26:181:31 | [0-9]+ | plus | +| redos.py:181:26:181:31 | [0-9]+ | quantifier | +| redos.py:181:26:181:41 | [0-9]+(X[0-9]*)? | sequence | +| redos.py:181:27:181:27 | 0 | constant | +| redos.py:181:27:181:29 | 0-9 | charRange | +| redos.py:181:29:181:29 | 9 | constant | +| redos.py:181:32:181:40 | (X[0-9]*) | group | +| redos.py:181:32:181:41 | (X[0-9]*)? | opt | +| redos.py:181:32:181:41 | (X[0-9]*)? | quantifier | +| redos.py:181:33:181:33 | X | constant | +| redos.py:181:33:181:39 | X[0-9]* | sequence | +| redos.py:181:34:181:38 | [0-9] | char | +| redos.py:181:34:181:39 | [0-9]* | quantifier | +| redos.py:181:34:181:39 | [0-9]* | star | +| redos.py:181:35:181:35 | 0 | constant | +| redos.py:181:35:181:37 | 0-9 | charRange | +| redos.py:181:37:181:37 | 9 | constant | +| redos.py:184:25:184:25 | ^ | caret | +| redos.py:184:25:184:38 | ^([^>]+)*(>\|$) | sequence | +| redos.py:184:26:184:32 | ([^>]+) | group | +| redos.py:184:26:184:33 | ([^>]+)* | quantifier | +| redos.py:184:26:184:33 | ([^>]+)* | star | +| redos.py:184:27:184:30 | [^>] | char | +| redos.py:184:27:184:31 | [^>]+ | plus | +| redos.py:184:27:184:31 | [^>]+ | quantifier | +| redos.py:184:29:184:29 | > | constant | +| redos.py:184:34:184:38 | (>\|$) | group | +| redos.py:184:35:184:35 | > | constant | +| redos.py:184:35:184:37 | >\|$ | alt | +| redos.py:184:37:184:37 | $ | dollar | +| redos.py:187:24:187:24 | ^ | caret | +| redos.py:187:24:187:38 | ^([^>a]+)*(>\|$) | sequence | +| redos.py:187:25:187:32 | ([^>a]+) | group | +| redos.py:187:25:187:33 | ([^>a]+)* | quantifier | +| redos.py:187:25:187:33 | ([^>a]+)* | star | +| redos.py:187:26:187:30 | [^>a] | char | +| redos.py:187:26:187:31 | [^>a]+ | plus | +| redos.py:187:26:187:31 | [^>a]+ | quantifier | +| redos.py:187:28:187:28 | > | constant | +| redos.py:187:29:187:29 | a | constant | +| redos.py:187:34:187:38 | (>\|$) | group | +| redos.py:187:35:187:35 | > | constant | +| redos.py:187:35:187:37 | >\|$ | alt | +| redos.py:187:37:187:37 | $ | dollar | +| redos.py:190:24:190:30 | (\\n\\s*) | group | +| redos.py:190:24:190:31 | (\\n\\s*)+ | plus | +| redos.py:190:24:190:31 | (\\n\\s*)+ | quantifier | +| redos.py:190:24:190:32 | (\\n\\s*)+$ | sequence | +| redos.py:190:25:190:26 | \\n | constant | +| redos.py:190:25:190:29 | \\n\\s* | sequence | +| redos.py:190:27:190:28 | \\s | charEsc | +| redos.py:190:27:190:29 | \\s* | quantifier | +| redos.py:190:27:190:29 | \\s* | star | +| redos.py:190:32:190:32 | $ | dollar | +| redos.py:193:24:193:24 | ^ | caret | +| redos.py:193:24:193:73 | ^(?:\\s+\|#.*\|\\(\\?#[^)]*\\))*(?:[?*+]\|{\\d+(?:,\\d*)?}) | sequence | +| redos.py:193:25:193:48 | (?:\\s+\|#.*\|\\(\\?#[^)]*\\)) | group | +| redos.py:193:25:193:49 | (?:\\s+\|#.*\|\\(\\?#[^)]*\\))* | quantifier | +| redos.py:193:25:193:49 | (?:\\s+\|#.*\|\\(\\?#[^)]*\\))* | star | +| redos.py:193:28:193:29 | \\s | charEsc | +| redos.py:193:28:193:30 | \\s+ | plus | +| redos.py:193:28:193:30 | \\s+ | quantifier | +| redos.py:193:28:193:47 | \\s+\|#.*\|\\(\\?#[^)]*\\) | alt | +| redos.py:193:32:193:32 | # | constant | +| redos.py:193:32:193:34 | #.* | sequence | +| redos.py:193:33:193:33 | . | dot | +| redos.py:193:33:193:34 | .* | quantifier | +| redos.py:193:33:193:34 | .* | star | +| redos.py:193:36:193:37 | \\( | constant | +| redos.py:193:36:193:47 | \\(\\?#[^)]*\\) | sequence | +| redos.py:193:38:193:39 | \\? | constant | +| redos.py:193:40:193:40 | # | constant | +| redos.py:193:41:193:44 | [^)] | char | +| redos.py:193:41:193:45 | [^)]* | quantifier | +| redos.py:193:41:193:45 | [^)]* | star | +| redos.py:193:43:193:43 | ) | constant | +| redos.py:193:46:193:47 | \\) | constant | +| redos.py:193:50:193:73 | (?:[?*+]\|{\\d+(?:,\\d*)?}) | group | +| redos.py:193:53:193:57 | [?*+] | char | +| redos.py:193:53:193:72 | [?*+]\|{\\d+(?:,\\d*)?} | alt | +| redos.py:193:54:193:54 | ? | constant | +| redos.py:193:55:193:55 | * | constant | +| redos.py:193:56:193:56 | + | constant | +| redos.py:193:59:193:59 | { | constant | +| redos.py:193:59:193:72 | {\\d+(?:,\\d*)?} | sequence | +| redos.py:193:60:193:61 | \\d | charEsc | +| redos.py:193:60:193:62 | \\d+ | plus | +| redos.py:193:60:193:62 | \\d+ | quantifier | +| redos.py:193:63:193:70 | (?:,\\d*) | group | +| redos.py:193:63:193:71 | (?:,\\d*)? | opt | +| redos.py:193:63:193:71 | (?:,\\d*)? | quantifier | +| redos.py:193:66:193:66 | , | constant | +| redos.py:193:66:193:69 | ,\\d* | sequence | +| redos.py:193:67:193:68 | \\d | charEsc | +| redos.py:193:67:193:69 | \\d* | quantifier | +| redos.py:193:67:193:69 | \\d* | star | +| redos.py:193:72:193:72 | } | constant | +| redos.py:196:24:196:25 | \\{ | constant | +| redos.py:196:24:196:103 | \\{\\[\\s*([a-zA-Z]+)\\(([a-zA-Z]+)\\)((\\s*([a-zA-Z]+)\\: ?([ a-zA-Z{}]+),?)+)*\\s*\\]\\} | sequence | +| redos.py:196:26:196:27 | \\[ | constant | +| redos.py:196:28:196:29 | \\s | charEsc | +| redos.py:196:28:196:30 | \\s* | quantifier | +| redos.py:196:28:196:30 | \\s* | star | +| redos.py:196:31:196:41 | ([a-zA-Z]+) | group | +| redos.py:196:32:196:39 | [a-zA-Z] | char | +| redos.py:196:32:196:40 | [a-zA-Z]+ | plus | +| redos.py:196:32:196:40 | [a-zA-Z]+ | quantifier | +| redos.py:196:33:196:33 | a | constant | +| redos.py:196:33:196:35 | a-z | charRange | +| redos.py:196:35:196:35 | z | constant | +| redos.py:196:36:196:36 | A | constant | +| redos.py:196:36:196:38 | A-Z | charRange | +| redos.py:196:38:196:38 | Z | constant | +| redos.py:196:42:196:43 | \\( | constant | +| redos.py:196:44:196:54 | ([a-zA-Z]+) | group | +| redos.py:196:45:196:52 | [a-zA-Z] | char | +| redos.py:196:45:196:53 | [a-zA-Z]+ | plus | +| redos.py:196:45:196:53 | [a-zA-Z]+ | quantifier | +| redos.py:196:46:196:46 | a | constant | +| redos.py:196:46:196:48 | a-z | charRange | +| redos.py:196:48:196:48 | z | constant | +| redos.py:196:49:196:49 | A | constant | +| redos.py:196:49:196:51 | A-Z | charRange | +| redos.py:196:51:196:51 | Z | constant | +| redos.py:196:55:196:56 | \\) | constant | +| redos.py:196:57:196:95 | ((\\s*([a-zA-Z]+)\\: ?([ a-zA-Z{}]+),?)+) | group | +| redos.py:196:57:196:96 | ((\\s*([a-zA-Z]+)\\: ?([ a-zA-Z{}]+),?)+)* | quantifier | +| redos.py:196:57:196:96 | ((\\s*([a-zA-Z]+)\\: ?([ a-zA-Z{}]+),?)+)* | star | +| redos.py:196:58:196:93 | (\\s*([a-zA-Z]+)\\: ?([ a-zA-Z{}]+),?) | group | +| redos.py:196:58:196:94 | (\\s*([a-zA-Z]+)\\: ?([ a-zA-Z{}]+),?)+ | plus | +| redos.py:196:58:196:94 | (\\s*([a-zA-Z]+)\\: ?([ a-zA-Z{}]+),?)+ | quantifier | +| redos.py:196:59:196:60 | \\s | charEsc | +| redos.py:196:59:196:61 | \\s* | quantifier | +| redos.py:196:59:196:61 | \\s* | star | +| redos.py:196:59:196:92 | \\s*([a-zA-Z]+)\\: ?([ a-zA-Z{}]+),? | sequence | +| redos.py:196:62:196:72 | ([a-zA-Z]+) | group | +| redos.py:196:63:196:70 | [a-zA-Z] | char | +| redos.py:196:63:196:71 | [a-zA-Z]+ | plus | +| redos.py:196:63:196:71 | [a-zA-Z]+ | quantifier | +| redos.py:196:64:196:64 | a | constant | +| redos.py:196:64:196:66 | a-z | charRange | +| redos.py:196:66:196:66 | z | constant | +| redos.py:196:67:196:67 | A | constant | +| redos.py:196:67:196:69 | A-Z | charRange | +| redos.py:196:69:196:69 | Z | constant | +| redos.py:196:73:196:74 | \\: | constant | +| redos.py:196:75:196:75 | | constant | +| redos.py:196:75:196:76 | ? | opt | +| redos.py:196:75:196:76 | ? | quantifier | +| redos.py:196:77:196:90 | ([ a-zA-Z{}]+) | group | +| redos.py:196:78:196:88 | [ a-zA-Z{}] | char | +| redos.py:196:78:196:89 | [ a-zA-Z{}]+ | plus | +| redos.py:196:78:196:89 | [ a-zA-Z{}]+ | quantifier | +| redos.py:196:79:196:79 | | constant | +| redos.py:196:80:196:80 | a | constant | +| redos.py:196:80:196:82 | a-z | charRange | +| redos.py:196:82:196:82 | z | constant | +| redos.py:196:83:196:83 | A | constant | +| redos.py:196:83:196:85 | A-Z | charRange | +| redos.py:196:85:196:85 | Z | constant | +| redos.py:196:86:196:86 | { | constant | +| redos.py:196:87:196:87 | } | constant | +| redos.py:196:91:196:91 | , | constant | +| redos.py:196:91:196:92 | ,? | opt | +| redos.py:196:91:196:92 | ,? | quantifier | +| redos.py:196:97:196:98 | \\s | charEsc | +| redos.py:196:97:196:99 | \\s* | quantifier | +| redos.py:196:97:196:99 | \\s* | star | +| redos.py:196:100:196:101 | \\] | constant | +| redos.py:196:102:196:103 | \\} | constant | +| redos.py:199:24:199:33 | (a+\|b+\|c+) | group | +| redos.py:199:24:199:34 | (a+\|b+\|c+)* | quantifier | +| redos.py:199:24:199:34 | (a+\|b+\|c+)* | star | +| redos.py:199:24:199:35 | (a+\|b+\|c+)*c | sequence | +| redos.py:199:25:199:25 | a | constant | +| redos.py:199:25:199:26 | a+ | plus | +| redos.py:199:25:199:26 | a+ | quantifier | +| redos.py:199:25:199:32 | a+\|b+\|c+ | alt | +| redos.py:199:28:199:28 | b | constant | +| redos.py:199:28:199:29 | b+ | plus | +| redos.py:199:28:199:29 | b+ | quantifier | +| redos.py:199:31:199:31 | c | constant | +| redos.py:199:31:199:32 | c+ | plus | +| redos.py:199:31:199:32 | c+ | quantifier | +| redos.py:199:35:199:35 | c | constant | +| redos.py:202:24:202:37 | (((a+a?)*)+b+) | group | +| redos.py:202:25:202:33 | ((a+a?)*) | group | +| redos.py:202:25:202:34 | ((a+a?)*)+ | plus | +| redos.py:202:25:202:34 | ((a+a?)*)+ | quantifier | +| redos.py:202:25:202:36 | ((a+a?)*)+b+ | sequence | +| redos.py:202:26:202:31 | (a+a?) | group | +| redos.py:202:26:202:32 | (a+a?)* | quantifier | +| redos.py:202:26:202:32 | (a+a?)* | star | +| redos.py:202:27:202:27 | a | constant | +| redos.py:202:27:202:28 | a+ | plus | +| redos.py:202:27:202:28 | a+ | quantifier | +| redos.py:202:27:202:30 | a+a? | sequence | +| redos.py:202:29:202:29 | a | constant | +| redos.py:202:29:202:30 | a? | opt | +| redos.py:202:29:202:30 | a? | quantifier | +| redos.py:202:35:202:35 | b | constant | +| redos.py:202:35:202:36 | b+ | plus | +| redos.py:202:35:202:36 | b+ | quantifier | +| redos.py:205:24:205:27 | (a+) | group | +| redos.py:205:24:205:28 | (a+)+ | plus | +| redos.py:205:24:205:28 | (a+)+ | quantifier | +| redos.py:205:24:205:32 | (a+)+bbbb | sequence | +| redos.py:205:25:205:25 | a | constant | +| redos.py:205:25:205:26 | a+ | plus | +| redos.py:205:25:205:26 | a+ | quantifier | +| redos.py:205:29:205:29 | b | constant | +| redos.py:205:30:205:30 | b | constant | +| redos.py:205:31:205:31 | b | constant | +| redos.py:205:32:205:32 | b | constant | +| redos.py:208:25:208:28 | (a+) | group | +| redos.py:208:25:208:29 | (a+)+ | plus | +| redos.py:208:25:208:29 | (a+)+ | quantifier | +| redos.py:208:25:208:37 | (a+)+aaaaa*a+ | sequence | +| redos.py:208:26:208:26 | a | constant | +| redos.py:208:26:208:27 | a+ | plus | +| redos.py:208:26:208:27 | a+ | quantifier | +| redos.py:208:30:208:30 | a | constant | +| redos.py:208:31:208:31 | a | constant | +| redos.py:208:32:208:32 | a | constant | +| redos.py:208:33:208:33 | a | constant | +| redos.py:208:34:208:34 | a | constant | +| redos.py:208:34:208:35 | a* | quantifier | +| redos.py:208:34:208:35 | a* | star | +| redos.py:208:36:208:36 | a | constant | +| redos.py:208:36:208:37 | a+ | plus | +| redos.py:208:36:208:37 | a+ | quantifier | +| redos.py:211:24:211:27 | (a+) | group | +| redos.py:211:24:211:28 | (a+)+ | plus | +| redos.py:211:24:211:28 | (a+)+ | quantifier | +| redos.py:211:24:211:34 | (a+)+aaaaa$ | sequence | +| redos.py:211:25:211:25 | a | constant | +| redos.py:211:25:211:26 | a+ | plus | +| redos.py:211:25:211:26 | a+ | quantifier | +| redos.py:211:29:211:29 | a | constant | +| redos.py:211:30:211:30 | a | constant | +| redos.py:211:31:211:31 | a | constant | +| redos.py:211:32:211:32 | a | constant | +| redos.py:211:33:211:33 | a | constant | +| redos.py:211:34:211:34 | $ | dollar | +| redos.py:214:25:214:29 | (\\n+) | group | +| redos.py:214:25:214:30 | (\\n+)+ | plus | +| redos.py:214:25:214:30 | (\\n+)+ | quantifier | +| redos.py:214:25:214:34 | (\\n+)+\\n\\n | sequence | +| redos.py:214:26:214:27 | \\n | constant | +| redos.py:214:26:214:28 | \\n+ | plus | +| redos.py:214:26:214:28 | \\n+ | quantifier | +| redos.py:214:31:214:32 | \\n | constant | +| redos.py:214:33:214:34 | \\n | constant | +| redos.py:217:24:217:28 | (\\n+) | group | +| redos.py:217:24:217:29 | (\\n+)+ | plus | +| redos.py:217:24:217:29 | (\\n+)+ | quantifier | +| redos.py:217:24:217:34 | (\\n+)+\\n\\n$ | sequence | +| redos.py:217:25:217:26 | \\n | constant | +| redos.py:217:25:217:27 | \\n+ | plus | +| redos.py:217:25:217:27 | \\n+ | quantifier | +| redos.py:217:30:217:31 | \\n | constant | +| redos.py:217:32:217:33 | \\n | constant | +| redos.py:217:34:217:34 | $ | dollar | +| redos.py:220:24:220:30 | ([^X]+) | group | +| redos.py:220:24:220:31 | ([^X]+)* | quantifier | +| redos.py:220:24:220:31 | ([^X]+)* | star | +| redos.py:220:24:220:32 | ([^X]+)*$ | sequence | +| redos.py:220:25:220:28 | [^X] | char | +| redos.py:220:25:220:29 | [^X]+ | plus | +| redos.py:220:25:220:29 | [^X]+ | quantifier | +| redos.py:220:27:220:27 | X | constant | +| redos.py:220:32:220:32 | $ | dollar | +| redos.py:223:24:223:33 | (([^X]b)+) | group | +| redos.py:223:24:223:34 | (([^X]b)+)* | quantifier | +| redos.py:223:24:223:34 | (([^X]b)+)* | star | +| redos.py:223:24:223:35 | (([^X]b)+)*$ | sequence | +| redos.py:223:25:223:31 | ([^X]b) | group | +| redos.py:223:25:223:32 | ([^X]b)+ | plus | +| redos.py:223:25:223:32 | ([^X]b)+ | quantifier | +| redos.py:223:26:223:29 | [^X] | char | +| redos.py:223:26:223:30 | [^X]b | sequence | +| redos.py:223:28:223:28 | X | constant | +| redos.py:223:30:223:30 | b | constant | +| redos.py:223:35:223:35 | $ | dollar | +| redos.py:226:25:226:34 | (([^X]b)+) | group | +| redos.py:226:25:226:35 | (([^X]b)+)* | quantifier | +| redos.py:226:25:226:35 | (([^X]b)+)* | star | +| redos.py:226:25:226:44 | (([^X]b)+)*($\|[^X]b) | sequence | +| redos.py:226:26:226:32 | ([^X]b) | group | +| redos.py:226:26:226:33 | ([^X]b)+ | plus | +| redos.py:226:26:226:33 | ([^X]b)+ | quantifier | +| redos.py:226:27:226:30 | [^X] | char | +| redos.py:226:27:226:31 | [^X]b | sequence | +| redos.py:226:29:226:29 | X | constant | +| redos.py:226:31:226:31 | b | constant | +| redos.py:226:36:226:44 | ($\|[^X]b) | group | +| redos.py:226:37:226:37 | $ | dollar | +| redos.py:226:37:226:43 | $\|[^X]b | alt | +| redos.py:226:39:226:42 | [^X] | char | +| redos.py:226:39:226:43 | [^X]b | sequence | +| redos.py:226:41:226:41 | X | constant | +| redos.py:226:43:226:43 | b | constant | +| redos.py:229:24:229:33 | (([^X]b)+) | group | +| redos.py:229:24:229:34 | (([^X]b)+)* | quantifier | +| redos.py:229:24:229:34 | (([^X]b)+)* | star | +| redos.py:229:24:229:43 | (([^X]b)+)*($\|[^X]c) | sequence | +| redos.py:229:25:229:31 | ([^X]b) | group | +| redos.py:229:25:229:32 | ([^X]b)+ | plus | +| redos.py:229:25:229:32 | ([^X]b)+ | quantifier | +| redos.py:229:26:229:29 | [^X] | char | +| redos.py:229:26:229:30 | [^X]b | sequence | +| redos.py:229:28:229:28 | X | constant | +| redos.py:229:30:229:30 | b | constant | +| redos.py:229:35:229:43 | ($\|[^X]c) | group | +| redos.py:229:36:229:36 | $ | dollar | +| redos.py:229:36:229:42 | $\|[^X]c | alt | +| redos.py:229:38:229:41 | [^X] | char | +| redos.py:229:38:229:42 | [^X]c | sequence | +| redos.py:229:40:229:40 | X | constant | +| redos.py:229:42:229:42 | c | constant | +| redos.py:232:25:232:31 | ((ab)+) | group | +| redos.py:232:25:232:32 | ((ab)+)* | quantifier | +| redos.py:232:25:232:32 | ((ab)+)* | star | +| redos.py:232:25:232:38 | ((ab)+)*ababab | sequence | +| redos.py:232:26:232:29 | (ab) | group | +| redos.py:232:26:232:30 | (ab)+ | plus | +| redos.py:232:26:232:30 | (ab)+ | quantifier | +| redos.py:232:27:232:27 | a | constant | +| redos.py:232:27:232:28 | ab | sequence | +| redos.py:232:28:232:28 | b | constant | +| redos.py:232:33:232:33 | a | constant | +| redos.py:232:34:232:34 | b | constant | +| redos.py:232:35:232:35 | a | constant | +| redos.py:232:36:232:36 | b | constant | +| redos.py:232:37:232:37 | a | constant | +| redos.py:232:38:232:38 | b | constant | +| redos.py:235:25:235:31 | ((ab)+) | group | +| redos.py:235:25:235:32 | ((ab)+)* | quantifier | +| redos.py:235:25:235:32 | ((ab)+)* | star | +| redos.py:235:25:235:46 | ((ab)+)*abab(ab)*(ab)+ | sequence | +| redos.py:235:26:235:29 | (ab) | group | +| redos.py:235:26:235:30 | (ab)+ | plus | +| redos.py:235:26:235:30 | (ab)+ | quantifier | +| redos.py:235:27:235:27 | a | constant | +| redos.py:235:27:235:28 | ab | sequence | +| redos.py:235:28:235:28 | b | constant | +| redos.py:235:33:235:33 | a | constant | +| redos.py:235:34:235:34 | b | constant | +| redos.py:235:35:235:35 | a | constant | +| redos.py:235:36:235:36 | b | constant | +| redos.py:235:37:235:40 | (ab) | group | +| redos.py:235:37:235:41 | (ab)* | quantifier | +| redos.py:235:37:235:41 | (ab)* | star | +| redos.py:235:38:235:38 | a | constant | +| redos.py:235:38:235:39 | ab | sequence | +| redos.py:235:39:235:39 | b | constant | +| redos.py:235:42:235:45 | (ab) | group | +| redos.py:235:42:235:46 | (ab)+ | plus | +| redos.py:235:42:235:46 | (ab)+ | quantifier | +| redos.py:235:43:235:43 | a | constant | +| redos.py:235:43:235:44 | ab | sequence | +| redos.py:235:44:235:44 | b | constant | +| redos.py:238:25:238:31 | ((ab)+) | group | +| redos.py:238:25:238:32 | ((ab)+)* | quantifier | +| redos.py:238:25:238:32 | ((ab)+)* | star | +| redos.py:238:26:238:29 | (ab) | group | +| redos.py:238:26:238:30 | (ab)+ | plus | +| redos.py:238:26:238:30 | (ab)+ | quantifier | +| redos.py:238:27:238:27 | a | constant | +| redos.py:238:27:238:28 | ab | sequence | +| redos.py:238:28:238:28 | b | constant | +| redos.py:241:24:241:30 | ((ab)+) | group | +| redos.py:241:24:241:31 | ((ab)+)* | quantifier | +| redos.py:241:24:241:31 | ((ab)+)* | star | +| redos.py:241:24:241:32 | ((ab)+)*$ | sequence | +| redos.py:241:25:241:28 | (ab) | group | +| redos.py:241:25:241:29 | (ab)+ | plus | +| redos.py:241:25:241:29 | (ab)+ | quantifier | +| redos.py:241:26:241:26 | a | constant | +| redos.py:241:26:241:27 | ab | sequence | +| redos.py:241:27:241:27 | b | constant | +| redos.py:241:32:241:32 | $ | dollar | +| redos.py:244:25:244:31 | ((ab)+) | group | +| redos.py:244:25:244:32 | ((ab)+)* | quantifier | +| redos.py:244:25:244:32 | ((ab)+)* | star | +| redos.py:244:25:244:56 | ((ab)+)*[a1][b1][a2][b2][a3][b3] | sequence | +| redos.py:244:26:244:29 | (ab) | group | +| redos.py:244:26:244:30 | (ab)+ | plus | +| redos.py:244:26:244:30 | (ab)+ | quantifier | +| redos.py:244:27:244:27 | a | constant | +| redos.py:244:27:244:28 | ab | sequence | +| redos.py:244:28:244:28 | b | constant | +| redos.py:244:33:244:36 | [a1] | char | +| redos.py:244:34:244:34 | a | constant | +| redos.py:244:35:244:35 | 1 | constant | +| redos.py:244:37:244:40 | [b1] | char | +| redos.py:244:38:244:38 | b | constant | +| redos.py:244:39:244:39 | 1 | constant | +| redos.py:244:41:244:44 | [a2] | char | +| redos.py:244:42:244:42 | a | constant | +| redos.py:244:43:244:43 | 2 | constant | +| redos.py:244:45:244:48 | [b2] | char | +| redos.py:244:46:244:46 | b | constant | +| redos.py:244:47:244:47 | 2 | constant | +| redos.py:244:49:244:52 | [a3] | char | +| redos.py:244:50:244:50 | a | constant | +| redos.py:244:51:244:51 | 3 | constant | +| redos.py:244:53:244:56 | [b3] | char | +| redos.py:244:54:244:54 | b | constant | +| redos.py:244:55:244:55 | 3 | constant | +| redos.py:247:24:247:32 | ([\\n\\s]+) | group | +| redos.py:247:24:247:33 | ([\\n\\s]+)* | quantifier | +| redos.py:247:24:247:33 | ([\\n\\s]+)* | star | +| redos.py:247:24:247:36 | ([\\n\\s]+)*(.) | sequence | +| redos.py:247:25:247:30 | [\\n\\s] | char | +| redos.py:247:25:247:31 | [\\n\\s]+ | plus | +| redos.py:247:25:247:31 | [\\n\\s]+ | quantifier | +| redos.py:247:26:247:27 | \\n | constant | +| redos.py:247:28:247:29 | \\s | charEsc | +| redos.py:247:34:247:36 | (.) | group | +| redos.py:247:35:247:35 | . | dot | +| redos.py:250:25:250:31 | (A*A*X) | group | +| redos.py:250:25:250:32 | (A*A*X)* | quantifier | +| redos.py:250:25:250:32 | (A*A*X)* | star | +| redos.py:250:26:250:26 | A | constant | +| redos.py:250:26:250:27 | A* | quantifier | +| redos.py:250:26:250:27 | A* | star | +| redos.py:250:26:250:30 | A*A*X | sequence | +| redos.py:250:28:250:28 | A | constant | +| redos.py:250:28:250:29 | A* | quantifier | +| redos.py:250:28:250:29 | A* | star | +| redos.py:250:30:250:30 | X | constant | +| redos.py:253:25:253:34 | ([^\\\\\\]]+) | group | +| redos.py:253:25:253:35 | ([^\\\\\\]]+)* | quantifier | +| redos.py:253:25:253:35 | ([^\\\\\\]]+)* | star | +| redos.py:253:26:253:32 | [^\\\\\\]] | char | +| redos.py:253:26:253:33 | [^\\\\\\]]+ | plus | +| redos.py:253:26:253:33 | [^\\\\\\]]+ | quantifier | +| redos.py:253:28:253:29 | \\\\ | constant | +| redos.py:253:30:253:31 | \\] | constant | +| redos.py:256:24:256:100 | (\\w*foobarbaz\\w*foobarbaz\\w*foobarbaz\\w*foobarbaz\\s*foobarbaz\\d*foobarbaz\\w*) | group | +| redos.py:256:24:256:101 | (\\w*foobarbaz\\w*foobarbaz\\w*foobarbaz\\w*foobarbaz\\s*foobarbaz\\d*foobarbaz\\w*)+ | plus | +| redos.py:256:24:256:101 | (\\w*foobarbaz\\w*foobarbaz\\w*foobarbaz\\w*foobarbaz\\s*foobarbaz\\d*foobarbaz\\w*)+ | quantifier | +| redos.py:256:24:256:102 | (\\w*foobarbaz\\w*foobarbaz\\w*foobarbaz\\w*foobarbaz\\s*foobarbaz\\d*foobarbaz\\w*)+- | sequence | +| redos.py:256:25:256:26 | \\w | charEsc | +| redos.py:256:25:256:27 | \\w* | quantifier | +| redos.py:256:25:256:27 | \\w* | star | +| redos.py:256:25:256:99 | \\w*foobarbaz\\w*foobarbaz\\w*foobarbaz\\w*foobarbaz\\s*foobarbaz\\d*foobarbaz\\w* | sequence | +| redos.py:256:28:256:28 | f | constant | +| redos.py:256:29:256:29 | o | constant | +| redos.py:256:30:256:30 | o | constant | +| redos.py:256:31:256:31 | b | constant | +| redos.py:256:32:256:32 | a | constant | +| redos.py:256:33:256:33 | r | constant | +| redos.py:256:34:256:34 | b | constant | +| redos.py:256:35:256:35 | a | constant | +| redos.py:256:36:256:36 | z | constant | +| redos.py:256:37:256:38 | \\w | charEsc | +| redos.py:256:37:256:39 | \\w* | quantifier | +| redos.py:256:37:256:39 | \\w* | star | +| redos.py:256:40:256:40 | f | constant | +| redos.py:256:41:256:41 | o | constant | +| redos.py:256:42:256:42 | o | constant | +| redos.py:256:43:256:43 | b | constant | +| redos.py:256:44:256:44 | a | constant | +| redos.py:256:45:256:45 | r | constant | +| redos.py:256:46:256:46 | b | constant | +| redos.py:256:47:256:47 | a | constant | +| redos.py:256:48:256:48 | z | constant | +| redos.py:256:49:256:50 | \\w | charEsc | +| redos.py:256:49:256:51 | \\w* | quantifier | +| redos.py:256:49:256:51 | \\w* | star | +| redos.py:256:52:256:52 | f | constant | +| redos.py:256:53:256:53 | o | constant | +| redos.py:256:54:256:54 | o | constant | +| redos.py:256:55:256:55 | b | constant | +| redos.py:256:56:256:56 | a | constant | +| redos.py:256:57:256:57 | r | constant | +| redos.py:256:58:256:58 | b | constant | +| redos.py:256:59:256:59 | a | constant | +| redos.py:256:60:256:60 | z | constant | +| redos.py:256:61:256:62 | \\w | charEsc | +| redos.py:256:61:256:63 | \\w* | quantifier | +| redos.py:256:61:256:63 | \\w* | star | +| redos.py:256:64:256:64 | f | constant | +| redos.py:256:65:256:65 | o | constant | +| redos.py:256:66:256:66 | o | constant | +| redos.py:256:67:256:67 | b | constant | +| redos.py:256:68:256:68 | a | constant | +| redos.py:256:69:256:69 | r | constant | +| redos.py:256:70:256:70 | b | constant | +| redos.py:256:71:256:71 | a | constant | +| redos.py:256:72:256:72 | z | constant | +| redos.py:256:73:256:74 | \\s | charEsc | +| redos.py:256:73:256:75 | \\s* | quantifier | +| redos.py:256:73:256:75 | \\s* | star | +| redos.py:256:76:256:76 | f | constant | +| redos.py:256:77:256:77 | o | constant | +| redos.py:256:78:256:78 | o | constant | +| redos.py:256:79:256:79 | b | constant | +| redos.py:256:80:256:80 | a | constant | +| redos.py:256:81:256:81 | r | constant | +| redos.py:256:82:256:82 | b | constant | +| redos.py:256:83:256:83 | a | constant | +| redos.py:256:84:256:84 | z | constant | +| redos.py:256:85:256:86 | \\d | charEsc | +| redos.py:256:85:256:87 | \\d* | quantifier | +| redos.py:256:85:256:87 | \\d* | star | +| redos.py:256:88:256:88 | f | constant | +| redos.py:256:89:256:89 | o | constant | +| redos.py:256:90:256:90 | o | constant | +| redos.py:256:91:256:91 | b | constant | +| redos.py:256:92:256:92 | a | constant | +| redos.py:256:93:256:93 | r | constant | +| redos.py:256:94:256:94 | b | constant | +| redos.py:256:95:256:95 | a | constant | +| redos.py:256:96:256:96 | z | constant | +| redos.py:256:97:256:98 | \\w | charEsc | +| redos.py:256:97:256:99 | \\w* | quantifier | +| redos.py:256:97:256:99 | \\w* | star | +| redos.py:256:102:256:102 | - | constant | +| redos.py:259:24:259:125 | (.thisisagoddamnlongstringforstresstestingthequery\|\\sthisisagoddamnlongstringforstresstestingthequery) | group | +| redos.py:259:24:259:126 | (.thisisagoddamnlongstringforstresstestingthequery\|\\sthisisagoddamnlongstringforstresstestingthequery)* | quantifier | +| redos.py:259:24:259:126 | (.thisisagoddamnlongstringforstresstestingthequery\|\\sthisisagoddamnlongstringforstresstestingthequery)* | star | +| redos.py:259:24:259:127 | (.thisisagoddamnlongstringforstresstestingthequery\|\\sthisisagoddamnlongstringforstresstestingthequery)*- | sequence | +| redos.py:259:25:259:25 | . | dot | +| redos.py:259:25:259:73 | .thisisagoddamnlongstringforstresstestingthequery | sequence | +| redos.py:259:25:259:124 | .thisisagoddamnlongstringforstresstestingthequery\|\\sthisisagoddamnlongstringforstresstestingthequery | alt | +| redos.py:259:26:259:26 | t | constant | +| redos.py:259:27:259:27 | h | constant | +| redos.py:259:28:259:28 | i | constant | +| redos.py:259:29:259:29 | s | constant | +| redos.py:259:30:259:30 | i | constant | +| redos.py:259:31:259:31 | s | constant | +| redos.py:259:32:259:32 | a | constant | +| redos.py:259:33:259:33 | g | constant | +| redos.py:259:34:259:34 | o | constant | +| redos.py:259:35:259:35 | d | constant | +| redos.py:259:36:259:36 | d | constant | +| redos.py:259:37:259:37 | a | constant | +| redos.py:259:38:259:38 | m | constant | +| redos.py:259:39:259:39 | n | constant | +| redos.py:259:40:259:40 | l | constant | +| redos.py:259:41:259:41 | o | constant | +| redos.py:259:42:259:42 | n | constant | +| redos.py:259:43:259:43 | g | constant | +| redos.py:259:44:259:44 | s | constant | +| redos.py:259:45:259:45 | t | constant | +| redos.py:259:46:259:46 | r | constant | +| redos.py:259:47:259:47 | i | constant | +| redos.py:259:48:259:48 | n | constant | +| redos.py:259:49:259:49 | g | constant | +| redos.py:259:50:259:50 | f | constant | +| redos.py:259:51:259:51 | o | constant | +| redos.py:259:52:259:52 | r | constant | +| redos.py:259:53:259:53 | s | constant | +| redos.py:259:54:259:54 | t | constant | +| redos.py:259:55:259:55 | r | constant | +| redos.py:259:56:259:56 | e | constant | +| redos.py:259:57:259:57 | s | constant | +| redos.py:259:58:259:58 | s | constant | +| redos.py:259:59:259:59 | t | constant | +| redos.py:259:60:259:60 | e | constant | +| redos.py:259:61:259:61 | s | constant | +| redos.py:259:62:259:62 | t | constant | +| redos.py:259:63:259:63 | i | constant | +| redos.py:259:64:259:64 | n | constant | +| redos.py:259:65:259:65 | g | constant | +| redos.py:259:66:259:66 | t | constant | +| redos.py:259:67:259:67 | h | constant | +| redos.py:259:68:259:68 | e | constant | +| redos.py:259:69:259:69 | q | constant | +| redos.py:259:70:259:70 | u | constant | +| redos.py:259:71:259:71 | e | constant | +| redos.py:259:72:259:72 | r | constant | +| redos.py:259:73:259:73 | y | constant | +| redos.py:259:75:259:76 | \\s | charEsc | +| redos.py:259:75:259:124 | \\sthisisagoddamnlongstringforstresstestingthequery | sequence | +| redos.py:259:77:259:77 | t | constant | +| redos.py:259:78:259:78 | h | constant | +| redos.py:259:79:259:79 | i | constant | +| redos.py:259:80:259:80 | s | constant | +| redos.py:259:81:259:81 | i | constant | +| redos.py:259:82:259:82 | s | constant | +| redos.py:259:83:259:83 | a | constant | +| redos.py:259:84:259:84 | g | constant | +| redos.py:259:85:259:85 | o | constant | +| redos.py:259:86:259:86 | d | constant | +| redos.py:259:87:259:87 | d | constant | +| redos.py:259:88:259:88 | a | constant | +| redos.py:259:89:259:89 | m | constant | +| redos.py:259:90:259:90 | n | constant | +| redos.py:259:91:259:91 | l | constant | +| redos.py:259:92:259:92 | o | constant | +| redos.py:259:93:259:93 | n | constant | +| redos.py:259:94:259:94 | g | constant | +| redos.py:259:95:259:95 | s | constant | +| redos.py:259:96:259:96 | t | constant | +| redos.py:259:97:259:97 | r | constant | +| redos.py:259:98:259:98 | i | constant | +| redos.py:259:99:259:99 | n | constant | +| redos.py:259:100:259:100 | g | constant | +| redos.py:259:101:259:101 | f | constant | +| redos.py:259:102:259:102 | o | constant | +| redos.py:259:103:259:103 | r | constant | +| redos.py:259:104:259:104 | s | constant | +| redos.py:259:105:259:105 | t | constant | +| redos.py:259:106:259:106 | r | constant | +| redos.py:259:107:259:107 | e | constant | +| redos.py:259:108:259:108 | s | constant | +| redos.py:259:109:259:109 | s | constant | +| redos.py:259:110:259:110 | t | constant | +| redos.py:259:111:259:111 | e | constant | +| redos.py:259:112:259:112 | s | constant | +| redos.py:259:113:259:113 | t | constant | +| redos.py:259:114:259:114 | i | constant | +| redos.py:259:115:259:115 | n | constant | +| redos.py:259:116:259:116 | g | constant | +| redos.py:259:117:259:117 | t | constant | +| redos.py:259:118:259:118 | h | constant | +| redos.py:259:119:259:119 | e | constant | +| redos.py:259:120:259:120 | q | constant | +| redos.py:259:121:259:121 | u | constant | +| redos.py:259:122:259:122 | e | constant | +| redos.py:259:123:259:123 | r | constant | +| redos.py:259:124:259:124 | y | constant | +| redos.py:259:127:259:127 | - | constant | +| redos.py:262:24:262:86 | (thisisagoddamnlongstringforstresstestingthequery\|this\\w+query) | group | +| redos.py:262:24:262:87 | (thisisagoddamnlongstringforstresstestingthequery\|this\\w+query)* | quantifier | +| redos.py:262:24:262:87 | (thisisagoddamnlongstringforstresstestingthequery\|this\\w+query)* | star | +| redos.py:262:24:262:88 | (thisisagoddamnlongstringforstresstestingthequery\|this\\w+query)*- | sequence | +| redos.py:262:25:262:25 | t | constant | +| redos.py:262:25:262:72 | thisisagoddamnlongstringforstresstestingthequery | sequence | +| redos.py:262:25:262:85 | thisisagoddamnlongstringforstresstestingthequery\|this\\w+query | alt | +| redos.py:262:26:262:26 | h | constant | +| redos.py:262:27:262:27 | i | constant | +| redos.py:262:28:262:28 | s | constant | +| redos.py:262:29:262:29 | i | constant | +| redos.py:262:30:262:30 | s | constant | +| redos.py:262:31:262:31 | a | constant | +| redos.py:262:32:262:32 | g | constant | +| redos.py:262:33:262:33 | o | constant | +| redos.py:262:34:262:34 | d | constant | +| redos.py:262:35:262:35 | d | constant | +| redos.py:262:36:262:36 | a | constant | +| redos.py:262:37:262:37 | m | constant | +| redos.py:262:38:262:38 | n | constant | +| redos.py:262:39:262:39 | l | constant | +| redos.py:262:40:262:40 | o | constant | +| redos.py:262:41:262:41 | n | constant | +| redos.py:262:42:262:42 | g | constant | +| redos.py:262:43:262:43 | s | constant | +| redos.py:262:44:262:44 | t | constant | +| redos.py:262:45:262:45 | r | constant | +| redos.py:262:46:262:46 | i | constant | +| redos.py:262:47:262:47 | n | constant | +| redos.py:262:48:262:48 | g | constant | +| redos.py:262:49:262:49 | f | constant | +| redos.py:262:50:262:50 | o | constant | +| redos.py:262:51:262:51 | r | constant | +| redos.py:262:52:262:52 | s | constant | +| redos.py:262:53:262:53 | t | constant | +| redos.py:262:54:262:54 | r | constant | +| redos.py:262:55:262:55 | e | constant | +| redos.py:262:56:262:56 | s | constant | +| redos.py:262:57:262:57 | s | constant | +| redos.py:262:58:262:58 | t | constant | +| redos.py:262:59:262:59 | e | constant | +| redos.py:262:60:262:60 | s | constant | +| redos.py:262:61:262:61 | t | constant | +| redos.py:262:62:262:62 | i | constant | +| redos.py:262:63:262:63 | n | constant | +| redos.py:262:64:262:64 | g | constant | +| redos.py:262:65:262:65 | t | constant | +| redos.py:262:66:262:66 | h | constant | +| redos.py:262:67:262:67 | e | constant | +| redos.py:262:68:262:68 | q | constant | +| redos.py:262:69:262:69 | u | constant | +| redos.py:262:70:262:70 | e | constant | +| redos.py:262:71:262:71 | r | constant | +| redos.py:262:72:262:72 | y | constant | +| redos.py:262:74:262:74 | t | constant | +| redos.py:262:74:262:85 | this\\w+query | sequence | +| redos.py:262:75:262:75 | h | constant | +| redos.py:262:76:262:76 | i | constant | +| redos.py:262:77:262:77 | s | constant | +| redos.py:262:78:262:79 | \\w | charEsc | +| redos.py:262:78:262:80 | \\w+ | plus | +| redos.py:262:78:262:80 | \\w+ | quantifier | +| redos.py:262:81:262:81 | q | constant | +| redos.py:262:82:262:82 | u | constant | +| redos.py:262:83:262:83 | e | constant | +| redos.py:262:84:262:84 | r | constant | +| redos.py:262:85:262:85 | y | constant | +| redos.py:262:88:262:88 | - | constant | +| redos.py:265:25:265:126 | (thisisagoddamnlongstringforstresstestingthequery\|imanotherbutunrelatedstringcomparedtotheotherstring) | group | +| redos.py:265:25:265:127 | (thisisagoddamnlongstringforstresstestingthequery\|imanotherbutunrelatedstringcomparedtotheotherstring)* | quantifier | +| redos.py:265:25:265:127 | (thisisagoddamnlongstringforstresstestingthequery\|imanotherbutunrelatedstringcomparedtotheotherstring)* | star | +| redos.py:265:25:265:128 | (thisisagoddamnlongstringforstresstestingthequery\|imanotherbutunrelatedstringcomparedtotheotherstring)*- | sequence | +| redos.py:265:26:265:26 | t | constant | +| redos.py:265:26:265:73 | thisisagoddamnlongstringforstresstestingthequery | sequence | +| redos.py:265:26:265:125 | thisisagoddamnlongstringforstresstestingthequery\|imanotherbutunrelatedstringcomparedtotheotherstring | alt | +| redos.py:265:27:265:27 | h | constant | +| redos.py:265:28:265:28 | i | constant | +| redos.py:265:29:265:29 | s | constant | +| redos.py:265:30:265:30 | i | constant | +| redos.py:265:31:265:31 | s | constant | +| redos.py:265:32:265:32 | a | constant | +| redos.py:265:33:265:33 | g | constant | +| redos.py:265:34:265:34 | o | constant | +| redos.py:265:35:265:35 | d | constant | +| redos.py:265:36:265:36 | d | constant | +| redos.py:265:37:265:37 | a | constant | +| redos.py:265:38:265:38 | m | constant | +| redos.py:265:39:265:39 | n | constant | +| redos.py:265:40:265:40 | l | constant | +| redos.py:265:41:265:41 | o | constant | +| redos.py:265:42:265:42 | n | constant | +| redos.py:265:43:265:43 | g | constant | +| redos.py:265:44:265:44 | s | constant | +| redos.py:265:45:265:45 | t | constant | +| redos.py:265:46:265:46 | r | constant | +| redos.py:265:47:265:47 | i | constant | +| redos.py:265:48:265:48 | n | constant | +| redos.py:265:49:265:49 | g | constant | +| redos.py:265:50:265:50 | f | constant | +| redos.py:265:51:265:51 | o | constant | +| redos.py:265:52:265:52 | r | constant | +| redos.py:265:53:265:53 | s | constant | +| redos.py:265:54:265:54 | t | constant | +| redos.py:265:55:265:55 | r | constant | +| redos.py:265:56:265:56 | e | constant | +| redos.py:265:57:265:57 | s | constant | +| redos.py:265:58:265:58 | s | constant | +| redos.py:265:59:265:59 | t | constant | +| redos.py:265:60:265:60 | e | constant | +| redos.py:265:61:265:61 | s | constant | +| redos.py:265:62:265:62 | t | constant | +| redos.py:265:63:265:63 | i | constant | +| redos.py:265:64:265:64 | n | constant | +| redos.py:265:65:265:65 | g | constant | +| redos.py:265:66:265:66 | t | constant | +| redos.py:265:67:265:67 | h | constant | +| redos.py:265:68:265:68 | e | constant | +| redos.py:265:69:265:69 | q | constant | +| redos.py:265:70:265:70 | u | constant | +| redos.py:265:71:265:71 | e | constant | +| redos.py:265:72:265:72 | r | constant | +| redos.py:265:73:265:73 | y | constant | +| redos.py:265:75:265:75 | i | constant | +| redos.py:265:75:265:125 | imanotherbutunrelatedstringcomparedtotheotherstring | sequence | +| redos.py:265:76:265:76 | m | constant | +| redos.py:265:77:265:77 | a | constant | +| redos.py:265:78:265:78 | n | constant | +| redos.py:265:79:265:79 | o | constant | +| redos.py:265:80:265:80 | t | constant | +| redos.py:265:81:265:81 | h | constant | +| redos.py:265:82:265:82 | e | constant | +| redos.py:265:83:265:83 | r | constant | +| redos.py:265:84:265:84 | b | constant | +| redos.py:265:85:265:85 | u | constant | +| redos.py:265:86:265:86 | t | constant | +| redos.py:265:87:265:87 | u | constant | +| redos.py:265:88:265:88 | n | constant | +| redos.py:265:89:265:89 | r | constant | +| redos.py:265:90:265:90 | e | constant | +| redos.py:265:91:265:91 | l | constant | +| redos.py:265:92:265:92 | a | constant | +| redos.py:265:93:265:93 | t | constant | +| redos.py:265:94:265:94 | e | constant | +| redos.py:265:95:265:95 | d | constant | +| redos.py:265:96:265:96 | s | constant | +| redos.py:265:97:265:97 | t | constant | +| redos.py:265:98:265:98 | r | constant | +| redos.py:265:99:265:99 | i | constant | +| redos.py:265:100:265:100 | n | constant | +| redos.py:265:101:265:101 | g | constant | +| redos.py:265:102:265:102 | c | constant | +| redos.py:265:103:265:103 | o | constant | +| redos.py:265:104:265:104 | m | constant | +| redos.py:265:105:265:105 | p | constant | +| redos.py:265:106:265:106 | a | constant | +| redos.py:265:107:265:107 | r | constant | +| redos.py:265:108:265:108 | e | constant | +| redos.py:265:109:265:109 | d | constant | +| redos.py:265:110:265:110 | t | constant | +| redos.py:265:111:265:111 | o | constant | +| redos.py:265:112:265:112 | t | constant | +| redos.py:265:113:265:113 | h | constant | +| redos.py:265:114:265:114 | e | constant | +| redos.py:265:115:265:115 | o | constant | +| redos.py:265:116:265:116 | t | constant | +| redos.py:265:117:265:117 | h | constant | +| redos.py:265:118:265:118 | e | constant | +| redos.py:265:119:265:119 | r | constant | +| redos.py:265:120:265:120 | s | constant | +| redos.py:265:121:265:121 | t | constant | +| redos.py:265:122:265:122 | r | constant | +| redos.py:265:123:265:123 | i | constant | +| redos.py:265:124:265:124 | n | constant | +| redos.py:265:125:265:125 | g | constant | +| redos.py:265:128:265:128 | - | constant | +| redos.py:268:25:268:25 | f | constant | +| redos.py:268:25:268:62 | foo([\\uDC66\\uDC67]\|[\\uDC68\\uDC69])*foo | sequence | +| redos.py:268:26:268:26 | o | constant | +| redos.py:268:27:268:27 | o | constant | +| redos.py:268:28:268:58 | ([\\uDC66\\uDC67]\|[\\uDC68\\uDC69]) | group | +| redos.py:268:28:268:59 | ([\\uDC66\\uDC67]\|[\\uDC68\\uDC69])* | quantifier | +| redos.py:268:28:268:59 | ([\\uDC66\\uDC67]\|[\\uDC68\\uDC69])* | star | +| redos.py:268:29:268:42 | [\\uDC66\\uDC67] | char | +| redos.py:268:29:268:57 | [\\uDC66\\uDC67]\|[\\uDC68\\uDC69] | alt | +| redos.py:268:30:268:35 | \\uDC66 | constant | +| redos.py:268:36:268:41 | \\uDC67 | constant | +| redos.py:268:44:268:57 | [\\uDC68\\uDC69] | char | +| redos.py:268:45:268:50 | \\uDC68 | constant | +| redos.py:268:51:268:56 | \\uDC69 | constant | +| redos.py:268:60:268:60 | f | constant | +| redos.py:268:61:268:61 | o | constant | +| redos.py:268:62:268:62 | o | constant | +| redos.py:271:25:271:25 | f | constant | +| redos.py:271:25:271:64 | foo((\\uDC66\|\\uDC67)\|(\\uDC68\|\\uDC69))*foo | sequence | +| redos.py:271:26:271:26 | o | constant | +| redos.py:271:27:271:27 | o | constant | +| redos.py:271:28:271:60 | ((\\uDC66\|\\uDC67)\|(\\uDC68\|\\uDC69)) | group | +| redos.py:271:28:271:61 | ((\\uDC66\|\\uDC67)\|(\\uDC68\|\\uDC69))* | quantifier | +| redos.py:271:28:271:61 | ((\\uDC66\|\\uDC67)\|(\\uDC68\|\\uDC69))* | star | +| redos.py:271:29:271:43 | (\\uDC66\|\\uDC67) | group | +| redos.py:271:29:271:59 | (\\uDC66\|\\uDC67)\|(\\uDC68\|\\uDC69) | alt | +| redos.py:271:30:271:35 | \\uDC66 | constant | +| redos.py:271:30:271:42 | \\uDC66\|\\uDC67 | alt | +| redos.py:271:37:271:42 | \\uDC67 | constant | +| redos.py:271:45:271:59 | (\\uDC68\|\\uDC69) | group | +| redos.py:271:46:271:51 | \\uDC68 | constant | +| redos.py:271:46:271:58 | \\uDC68\|\\uDC69 | alt | +| redos.py:271:53:271:58 | \\uDC69 | constant | +| redos.py:271:62:271:62 | f | constant | +| redos.py:271:63:271:63 | o | constant | +| redos.py:271:64:271:64 | o | constant | +| redos.py:274:24:274:24 | a | constant | +| redos.py:274:24:274:29 | a{2,3} | quantifier | +| redos.py:274:24:274:29 | a{2,3} | range | +| redos.py:274:24:274:35 | a{2,3}(b+)+X | sequence | +| redos.py:274:30:274:33 | (b+) | group | +| redos.py:274:30:274:34 | (b+)+ | plus | +| redos.py:274:30:274:34 | (b+)+ | quantifier | +| redos.py:274:31:274:31 | b | constant | +| redos.py:274:31:274:32 | b+ | plus | +| redos.py:274:31:274:32 | b+ | quantifier | +| redos.py:274:35:274:35 | X | constant | +| redos.py:277:24:277:24 | ^ | caret | +| redos.py:277:24:277:99 | ^<(\\w+)((?:\\s+\\w+(?:\\s*=\\s*(?:(?:"[^"]*")\|(?:'[^']*')\|[^>\\s]+))?)*)\\s*(\\/?)> | sequence | +| redos.py:277:25:277:25 | < | constant | +| redos.py:277:26:277:30 | (\\w+) | group | +| redos.py:277:27:277:28 | \\w | charEsc | +| redos.py:277:27:277:29 | \\w+ | plus | +| redos.py:277:27:277:29 | \\w+ | quantifier | +| redos.py:277:31:277:90 | ((?:\\s+\\w+(?:\\s*=\\s*(?:(?:"[^"]*")\|(?:'[^']*')\|[^>\\s]+))?)*) | group | +| redos.py:277:32:277:88 | (?:\\s+\\w+(?:\\s*=\\s*(?:(?:"[^"]*")\|(?:'[^']*')\|[^>\\s]+))?) | group | +| redos.py:277:32:277:89 | (?:\\s+\\w+(?:\\s*=\\s*(?:(?:"[^"]*")\|(?:'[^']*')\|[^>\\s]+))?)* | quantifier | +| redos.py:277:32:277:89 | (?:\\s+\\w+(?:\\s*=\\s*(?:(?:"[^"]*")\|(?:'[^']*')\|[^>\\s]+))?)* | star | +| redos.py:277:35:277:36 | \\s | charEsc | +| redos.py:277:35:277:37 | \\s+ | plus | +| redos.py:277:35:277:37 | \\s+ | quantifier | +| redos.py:277:35:277:87 | \\s+\\w+(?:\\s*=\\s*(?:(?:"[^"]*")\|(?:'[^']*')\|[^>\\s]+))? | sequence | +| redos.py:277:38:277:39 | \\w | charEsc | +| redos.py:277:38:277:40 | \\w+ | plus | +| redos.py:277:38:277:40 | \\w+ | quantifier | +| redos.py:277:41:277:86 | (?:\\s*=\\s*(?:(?:"[^"]*")\|(?:'[^']*')\|[^>\\s]+)) | group | +| redos.py:277:41:277:87 | (?:\\s*=\\s*(?:(?:"[^"]*")\|(?:'[^']*')\|[^>\\s]+))? | opt | +| redos.py:277:41:277:87 | (?:\\s*=\\s*(?:(?:"[^"]*")\|(?:'[^']*')\|[^>\\s]+))? | quantifier | +| redos.py:277:44:277:45 | \\s | charEsc | +| redos.py:277:44:277:46 | \\s* | quantifier | +| redos.py:277:44:277:46 | \\s* | star | +| redos.py:277:44:277:85 | \\s*=\\s*(?:(?:"[^"]*")\|(?:'[^']*')\|[^>\\s]+) | sequence | +| redos.py:277:47:277:47 | = | constant | +| redos.py:277:48:277:49 | \\s | charEsc | +| redos.py:277:48:277:50 | \\s* | quantifier | +| redos.py:277:48:277:50 | \\s* | star | +| redos.py:277:51:277:85 | (?:(?:"[^"]*")\|(?:'[^']*')\|[^>\\s]+) | group | +| redos.py:277:54:277:64 | (?:"[^"]*") | group | +| redos.py:277:54:277:84 | (?:"[^"]*")\|(?:'[^']*')\|[^>\\s]+ | alt | +| redos.py:277:57:277:57 | " | constant | +| redos.py:277:57:277:63 | "[^"]*" | sequence | +| redos.py:277:58:277:61 | [^"] | char | +| redos.py:277:58:277:62 | [^"]* | quantifier | +| redos.py:277:58:277:62 | [^"]* | star | +| redos.py:277:60:277:60 | " | constant | +| redos.py:277:63:277:63 | " | constant | +| redos.py:277:66:277:76 | (?:'[^']*') | group | +| redos.py:277:69:277:69 | ' | constant | +| redos.py:277:69:277:75 | '[^']*' | sequence | +| redos.py:277:70:277:73 | [^'] | char | +| redos.py:277:70:277:74 | [^']* | quantifier | +| redos.py:277:70:277:74 | [^']* | star | +| redos.py:277:72:277:72 | ' | constant | +| redos.py:277:75:277:75 | ' | constant | +| redos.py:277:78:277:83 | [^>\\s] | char | +| redos.py:277:78:277:84 | [^>\\s]+ | plus | +| redos.py:277:78:277:84 | [^>\\s]+ | quantifier | +| redos.py:277:80:277:80 | > | constant | +| redos.py:277:81:277:82 | \\s | charEsc | +| redos.py:277:91:277:92 | \\s | charEsc | +| redos.py:277:91:277:93 | \\s* | quantifier | +| redos.py:277:91:277:93 | \\s* | star | +| redos.py:277:94:277:98 | (\\/?) | group | +| redos.py:277:95:277:96 | \\/ | constant | +| redos.py:277:95:277:97 | \\/? | opt | +| redos.py:277:95:277:97 | \\/? | quantifier | +| redos.py:277:99:277:99 | > | constant | +| redos.py:280:25:280:28 | (a+) | group | +| redos.py:280:25:280:29 | (a+)* | quantifier | +| redos.py:280:25:280:29 | (a+)* | star | +| redos.py:280:25:280:48 | (a+)*[\\s\\S][\\s\\S][\\s\\S]? | sequence | +| redos.py:280:26:280:26 | a | constant | +| redos.py:280:26:280:27 | a+ | plus | +| redos.py:280:26:280:27 | a+ | quantifier | +| redos.py:280:30:280:35 | [\\s\\S] | char | +| redos.py:280:31:280:32 | \\s | charEsc | +| redos.py:280:33:280:34 | \\S | charEsc | +| redos.py:280:36:280:41 | [\\s\\S] | char | +| redos.py:280:37:280:38 | \\s | charEsc | +| redos.py:280:39:280:40 | \\S | charEsc | +| redos.py:280:42:280:47 | [\\s\\S] | char | +| redos.py:280:42:280:48 | [\\s\\S]? | opt | +| redos.py:280:42:280:48 | [\\s\\S]? | quantifier | +| redos.py:280:43:280:44 | \\s | charEsc | +| redos.py:280:45:280:46 | \\S | charEsc | +| redos.py:283:25:283:28 | (a+) | group | +| redos.py:283:25:283:29 | (a+)* | quantifier | +| redos.py:283:25:283:29 | (a+)* | star | +| redos.py:283:25:283:40 | (a+)*[\\s\\S]{2,3} | sequence | +| redos.py:283:26:283:26 | a | constant | +| redos.py:283:26:283:27 | a+ | plus | +| redos.py:283:26:283:27 | a+ | quantifier | +| redos.py:283:30:283:35 | [\\s\\S] | char | +| redos.py:283:30:283:40 | [\\s\\S]{2,3} | quantifier | +| redos.py:283:30:283:40 | [\\s\\S]{2,3} | range | +| redos.py:283:31:283:32 | \\s | charEsc | +| redos.py:283:33:283:34 | \\S | charEsc | +| redos.py:286:25:286:28 | (a+) | group | +| redos.py:286:25:286:29 | (a+)* | quantifier | +| redos.py:286:25:286:29 | (a+)* | star | +| redos.py:286:25:286:44 | (a+)*([\\s\\S]{2,}\|X)$ | sequence | +| redos.py:286:26:286:26 | a | constant | +| redos.py:286:26:286:27 | a+ | plus | +| redos.py:286:26:286:27 | a+ | quantifier | +| redos.py:286:30:286:43 | ([\\s\\S]{2,}\|X) | group | +| redos.py:286:31:286:36 | [\\s\\S] | char | +| redos.py:286:31:286:40 | [\\s\\S]{2,} | quantifier | +| redos.py:286:31:286:40 | [\\s\\S]{2,} | range | +| redos.py:286:31:286:42 | [\\s\\S]{2,}\|X | alt | +| redos.py:286:32:286:33 | \\s | charEsc | +| redos.py:286:34:286:35 | \\S | charEsc | +| redos.py:286:42:286:42 | X | constant | +| redos.py:286:44:286:44 | $ | dollar | +| redos.py:289:25:289:28 | (a+) | group | +| redos.py:289:25:289:29 | (a+)* | quantifier | +| redos.py:289:25:289:29 | (a+)* | star | +| redos.py:289:25:289:41 | (a+)*([\\s\\S]*\|X)$ | sequence | +| redos.py:289:26:289:26 | a | constant | +| redos.py:289:26:289:27 | a+ | plus | +| redos.py:289:26:289:27 | a+ | quantifier | +| redos.py:289:30:289:40 | ([\\s\\S]*\|X) | group | +| redos.py:289:31:289:36 | [\\s\\S] | char | +| redos.py:289:31:289:37 | [\\s\\S]* | quantifier | +| redos.py:289:31:289:37 | [\\s\\S]* | star | +| redos.py:289:31:289:39 | [\\s\\S]*\|X | alt | +| redos.py:289:32:289:33 | \\s | charEsc | +| redos.py:289:34:289:35 | \\S | charEsc | +| redos.py:289:39:289:39 | X | constant | +| redos.py:289:41:289:41 | $ | dollar | +| redos.py:292:24:292:39 | ((a+)*$\|[\\s\\S]+) | group | +| redos.py:292:25:292:28 | (a+) | group | +| redos.py:292:25:292:29 | (a+)* | quantifier | +| redos.py:292:25:292:29 | (a+)* | star | +| redos.py:292:25:292:30 | (a+)*$ | sequence | +| redos.py:292:25:292:38 | (a+)*$\|[\\s\\S]+ | alt | +| redos.py:292:26:292:26 | a | constant | +| redos.py:292:26:292:27 | a+ | plus | +| redos.py:292:26:292:27 | a+ | quantifier | +| redos.py:292:30:292:30 | $ | dollar | +| redos.py:292:32:292:37 | [\\s\\S] | char | +| redos.py:292:32:292:38 | [\\s\\S]+ | plus | +| redos.py:292:32:292:38 | [\\s\\S]+ | quantifier | +| redos.py:292:33:292:34 | \\s | charEsc | +| redos.py:292:35:292:36 | \\S | charEsc | +| redos.py:295:25:295:40 | ([\\s\\S]+\|(a+)*$) | group | +| redos.py:295:26:295:31 | [\\s\\S] | char | +| redos.py:295:26:295:32 | [\\s\\S]+ | plus | +| redos.py:295:26:295:32 | [\\s\\S]+ | quantifier | +| redos.py:295:26:295:39 | [\\s\\S]+\|(a+)*$ | alt | +| redos.py:295:27:295:28 | \\s | charEsc | +| redos.py:295:29:295:30 | \\S | charEsc | +| redos.py:295:34:295:37 | (a+) | group | +| redos.py:295:34:295:38 | (a+)* | quantifier | +| redos.py:295:34:295:38 | (a+)* | star | +| redos.py:295:34:295:39 | (a+)*$ | sequence | +| redos.py:295:35:295:35 | a | constant | +| redos.py:295:35:295:36 | a+ | plus | +| redos.py:295:35:295:36 | a+ | quantifier | +| redos.py:295:39:295:39 | $ | dollar | +| redos.py:298:25:298:33 | ((;\|^)a+) | group | +| redos.py:298:25:298:34 | ((;\|^)a+)+ | plus | +| redos.py:298:25:298:34 | ((;\|^)a+)+ | quantifier | +| redos.py:298:25:298:35 | ((;\|^)a+)+$ | sequence | +| redos.py:298:26:298:30 | (;\|^) | group | +| redos.py:298:26:298:32 | (;\|^)a+ | sequence | +| redos.py:298:27:298:27 | ; | constant | +| redos.py:298:27:298:29 | ;\|^ | alt | +| redos.py:298:29:298:29 | ^ | caret | +| redos.py:298:31:298:31 | a | constant | +| redos.py:298:31:298:32 | a+ | plus | +| redos.py:298:31:298:32 | a+ | quantifier | +| redos.py:298:35:298:35 | $ | dollar | +| redos.py:301:24:301:28 | (^\|;) | group | +| redos.py:301:24:301:104 | (^\|;)(0\|1)(0\|1)(0\|1)(0\|1)(0\|1)(0\|1)(0\|1)(0\|1)(0\|1)(0\|1)(0\|1)(0\|1)(0\|1)(0\|1)(e+)+f | sequence | +| redos.py:301:25:301:25 | ^ | caret | +| redos.py:301:25:301:27 | ^\|; | alt | +| redos.py:301:27:301:27 | ; | constant | +| redos.py:301:29:301:33 | (0\|1) | group | +| redos.py:301:30:301:30 | 0 | constant | +| redos.py:301:30:301:32 | 0\|1 | alt | +| redos.py:301:32:301:32 | 1 | constant | +| redos.py:301:34:301:38 | (0\|1) | group | +| redos.py:301:35:301:35 | 0 | constant | +| redos.py:301:35:301:37 | 0\|1 | alt | +| redos.py:301:37:301:37 | 1 | constant | +| redos.py:301:39:301:43 | (0\|1) | group | +| redos.py:301:40:301:40 | 0 | constant | +| redos.py:301:40:301:42 | 0\|1 | alt | +| redos.py:301:42:301:42 | 1 | constant | +| redos.py:301:44:301:48 | (0\|1) | group | +| redos.py:301:45:301:45 | 0 | constant | +| redos.py:301:45:301:47 | 0\|1 | alt | +| redos.py:301:47:301:47 | 1 | constant | +| redos.py:301:49:301:53 | (0\|1) | group | +| redos.py:301:50:301:50 | 0 | constant | +| redos.py:301:50:301:52 | 0\|1 | alt | +| redos.py:301:52:301:52 | 1 | constant | +| redos.py:301:54:301:58 | (0\|1) | group | +| redos.py:301:55:301:55 | 0 | constant | +| redos.py:301:55:301:57 | 0\|1 | alt | +| redos.py:301:57:301:57 | 1 | constant | +| redos.py:301:59:301:63 | (0\|1) | group | +| redos.py:301:60:301:60 | 0 | constant | +| redos.py:301:60:301:62 | 0\|1 | alt | +| redos.py:301:62:301:62 | 1 | constant | +| redos.py:301:64:301:68 | (0\|1) | group | +| redos.py:301:65:301:65 | 0 | constant | +| redos.py:301:65:301:67 | 0\|1 | alt | +| redos.py:301:67:301:67 | 1 | constant | +| redos.py:301:69:301:73 | (0\|1) | group | +| redos.py:301:70:301:70 | 0 | constant | +| redos.py:301:70:301:72 | 0\|1 | alt | +| redos.py:301:72:301:72 | 1 | constant | +| redos.py:301:74:301:78 | (0\|1) | group | +| redos.py:301:75:301:75 | 0 | constant | +| redos.py:301:75:301:77 | 0\|1 | alt | +| redos.py:301:77:301:77 | 1 | constant | +| redos.py:301:79:301:83 | (0\|1) | group | +| redos.py:301:80:301:80 | 0 | constant | +| redos.py:301:80:301:82 | 0\|1 | alt | +| redos.py:301:82:301:82 | 1 | constant | +| redos.py:301:84:301:88 | (0\|1) | group | +| redos.py:301:85:301:85 | 0 | constant | +| redos.py:301:85:301:87 | 0\|1 | alt | +| redos.py:301:87:301:87 | 1 | constant | +| redos.py:301:89:301:93 | (0\|1) | group | +| redos.py:301:90:301:90 | 0 | constant | +| redos.py:301:90:301:92 | 0\|1 | alt | +| redos.py:301:92:301:92 | 1 | constant | +| redos.py:301:94:301:98 | (0\|1) | group | +| redos.py:301:95:301:95 | 0 | constant | +| redos.py:301:95:301:97 | 0\|1 | alt | +| redos.py:301:97:301:97 | 1 | constant | +| redos.py:301:99:301:102 | (e+) | group | +| redos.py:301:99:301:103 | (e+)+ | plus | +| redos.py:301:99:301:103 | (e+)+ | quantifier | +| redos.py:301:100:301:100 | e | constant | +| redos.py:301:100:301:101 | e+ | plus | +| redos.py:301:100:301:101 | e+ | quantifier | +| redos.py:301:104:301:104 | f | constant | +| redos.py:304:24:304:24 | ^ | caret | +| redos.py:304:24:304:32 | ^ab(c+)+$ | sequence | +| redos.py:304:25:304:25 | a | constant | +| redos.py:304:26:304:26 | b | constant | +| redos.py:304:27:304:30 | (c+) | group | +| redos.py:304:27:304:31 | (c+)+ | plus | +| redos.py:304:27:304:31 | (c+)+ | quantifier | +| redos.py:304:28:304:28 | c | constant | +| redos.py:304:28:304:29 | c+ | plus | +| redos.py:304:28:304:29 | c+ | quantifier | +| redos.py:304:32:304:32 | $ | dollar | +| redos.py:307:24:307:33 | (\\d(\\s+)*) | group | +| redos.py:307:24:307:37 | (\\d(\\s+)*){20} | quantifier | +| redos.py:307:24:307:37 | (\\d(\\s+)*){20} | range | +| redos.py:307:25:307:26 | \\d | charEsc | +| redos.py:307:25:307:32 | \\d(\\s+)* | sequence | +| redos.py:307:27:307:31 | (\\s+) | group | +| redos.py:307:27:307:32 | (\\s+)* | quantifier | +| redos.py:307:27:307:32 | (\\s+)* | star | +| redos.py:307:28:307:29 | \\s | charEsc | +| redos.py:307:28:307:30 | \\s+ | plus | +| redos.py:307:28:307:30 | \\s+ | quantifier | +| redos.py:310:25:310:35 | (([^/]\|X)+) | group | +| redos.py:310:25:310:48 | (([^/]\|X)+)(\\/[\\s\\S]*)*$ | sequence | +| redos.py:310:26:310:33 | ([^/]\|X) | group | +| redos.py:310:26:310:34 | ([^/]\|X)+ | plus | +| redos.py:310:26:310:34 | ([^/]\|X)+ | quantifier | +| redos.py:310:27:310:30 | [^/] | char | +| redos.py:310:27:310:32 | [^/]\|X | alt | +| redos.py:310:29:310:29 | / | constant | +| redos.py:310:32:310:32 | X | constant | +| redos.py:310:36:310:46 | (\\/[\\s\\S]*) | group | +| redos.py:310:36:310:47 | (\\/[\\s\\S]*)* | quantifier | +| redos.py:310:36:310:47 | (\\/[\\s\\S]*)* | star | +| redos.py:310:37:310:38 | \\/ | constant | +| redos.py:310:37:310:45 | \\/[\\s\\S]* | sequence | +| redos.py:310:39:310:44 | [\\s\\S] | char | +| redos.py:310:39:310:45 | [\\s\\S]* | quantifier | +| redos.py:310:39:310:45 | [\\s\\S]* | star | +| redos.py:310:40:310:41 | \\s | charEsc | +| redos.py:310:42:310:43 | \\S | charEsc | +| redos.py:310:48:310:48 | $ | dollar | +| redos.py:313:25:313:25 | ^ | caret | +| redos.py:313:25:313:44 | ^((x([^Y]+)?)*(Y\|$)) | sequence | +| redos.py:313:26:313:44 | ((x([^Y]+)?)*(Y\|$)) | group | +| redos.py:313:27:313:37 | (x([^Y]+)?) | group | +| redos.py:313:27:313:38 | (x([^Y]+)?)* | quantifier | +| redos.py:313:27:313:38 | (x([^Y]+)?)* | star | +| redos.py:313:27:313:43 | (x([^Y]+)?)*(Y\|$) | sequence | +| redos.py:313:28:313:28 | x | constant | +| redos.py:313:28:313:36 | x([^Y]+)? | sequence | +| redos.py:313:29:313:35 | ([^Y]+) | group | +| redos.py:313:29:313:36 | ([^Y]+)? | opt | +| redos.py:313:29:313:36 | ([^Y]+)? | quantifier | +| redos.py:313:30:313:33 | [^Y] | char | +| redos.py:313:30:313:34 | [^Y]+ | plus | +| redos.py:313:30:313:34 | [^Y]+ | quantifier | +| redos.py:313:32:313:32 | Y | constant | +| redos.py:313:39:313:43 | (Y\|$) | group | +| redos.py:313:40:313:40 | Y | constant | +| redos.py:313:40:313:42 | Y\|$ | alt | +| redos.py:313:42:313:42 | $ | dollar | +| redos.py:316:24:316:27 | (a*) | group | +| redos.py:316:24:316:28 | (a*)+ | plus | +| redos.py:316:24:316:28 | (a*)+ | quantifier | +| redos.py:316:24:316:29 | (a*)+b | sequence | +| redos.py:316:25:316:25 | a | constant | +| redos.py:316:25:316:26 | a* | quantifier | +| redos.py:316:25:316:26 | a* | star | +| redos.py:316:29:316:29 | b | constant | +| redos.py:319:24:319:24 | f | constant | +| redos.py:319:24:319:38 | foo([\\w-]*)+bar | sequence | +| redos.py:319:25:319:25 | o | constant | +| redos.py:319:26:319:26 | o | constant | +| redos.py:319:27:319:34 | ([\\w-]*) | group | +| redos.py:319:27:319:35 | ([\\w-]*)+ | plus | +| redos.py:319:27:319:35 | ([\\w-]*)+ | quantifier | +| redos.py:319:28:319:32 | [\\w-] | char | +| redos.py:319:28:319:33 | [\\w-]* | quantifier | +| redos.py:319:28:319:33 | [\\w-]* | star | +| redos.py:319:29:319:30 | \\w | charEsc | +| redos.py:319:31:319:31 | - | constant | +| redos.py:319:36:319:36 | b | constant | +| redos.py:319:37:319:37 | a | constant | +| redos.py:319:38:319:38 | r | constant | +| redos.py:322:24:322:30 | ((ab)*) | group | +| redos.py:322:24:322:31 | ((ab)*)+ | plus | +| redos.py:322:24:322:31 | ((ab)*)+ | quantifier | +| redos.py:322:24:322:32 | ((ab)*)+c | sequence | +| redos.py:322:25:322:28 | (ab) | group | +| redos.py:322:25:322:29 | (ab)* | quantifier | +| redos.py:322:25:322:29 | (ab)* | star | +| redos.py:322:26:322:26 | a | constant | +| redos.py:322:26:322:27 | ab | sequence | +| redos.py:322:27:322:27 | b | constant | +| redos.py:322:32:322:32 | c | constant | +| redos.py:325:24:325:29 | (a?a?) | group | +| redos.py:325:24:325:30 | (a?a?)* | quantifier | +| redos.py:325:24:325:30 | (a?a?)* | star | +| redos.py:325:24:325:31 | (a?a?)*b | sequence | +| redos.py:325:25:325:25 | a | constant | +| redos.py:325:25:325:26 | a? | opt | +| redos.py:325:25:325:26 | a? | quantifier | +| redos.py:325:25:325:28 | a?a? | sequence | +| redos.py:325:27:325:27 | a | constant | +| redos.py:325:27:325:28 | a? | opt | +| redos.py:325:27:325:28 | a? | quantifier | +| redos.py:325:31:325:31 | b | constant | +| redos.py:328:25:328:28 | (a?) | group | +| redos.py:328:25:328:29 | (a?)* | quantifier | +| redos.py:328:25:328:29 | (a?)* | star | +| redos.py:328:25:328:30 | (a?)*b | sequence | +| redos.py:328:26:328:26 | a | constant | +| redos.py:328:26:328:27 | a? | opt | +| redos.py:328:26:328:27 | a? | quantifier | +| redos.py:328:30:328:30 | b | constant | +| redos.py:331:24:331:29 | (c?a?) | group | +| redos.py:331:24:331:30 | (c?a?)* | quantifier | +| redos.py:331:24:331:30 | (c?a?)* | star | +| redos.py:331:24:331:31 | (c?a?)*b | sequence | +| redos.py:331:25:331:25 | c | constant | +| redos.py:331:25:331:26 | c? | opt | +| redos.py:331:25:331:26 | c? | quantifier | +| redos.py:331:25:331:28 | c?a? | sequence | +| redos.py:331:27:331:27 | a | constant | +| redos.py:331:27:331:28 | a? | opt | +| redos.py:331:27:331:28 | a? | quantifier | +| redos.py:331:31:331:31 | b | constant | +| redos.py:334:24:334:31 | (?:a\|a?) | group | +| redos.py:334:24:334:32 | (?:a\|a?)+ | plus | +| redos.py:334:24:334:32 | (?:a\|a?)+ | quantifier | +| redos.py:334:24:334:33 | (?:a\|a?)+b | sequence | +| redos.py:334:27:334:27 | a | constant | +| redos.py:334:27:334:30 | a\|a? | alt | +| redos.py:334:29:334:29 | a | constant | +| redos.py:334:29:334:30 | a? | opt | +| redos.py:334:29:334:30 | a? | quantifier | +| redos.py:334:33:334:33 | b | constant | +| redos.py:337:24:337:29 | (a?b?) | group | +| redos.py:337:24:337:30 | (a?b?)* | quantifier | +| redos.py:337:24:337:30 | (a?b?)* | star | +| redos.py:337:24:337:31 | (a?b?)*$ | sequence | +| redos.py:337:25:337:25 | a | constant | +| redos.py:337:25:337:26 | a? | opt | +| redos.py:337:25:337:26 | a? | quantifier | +| redos.py:337:25:337:28 | a?b? | sequence | +| redos.py:337:27:337:27 | b | constant | +| redos.py:337:27:337:28 | b? | opt | +| redos.py:337:27:337:28 | b? | quantifier | +| redos.py:337:31:337:31 | $ | dollar | +| redos.py:340:24:340:24 | P | constant | +| redos.py:340:24:340:69 | PRE(([a-c]\|[c-d])T(e?e?e?e?\|X))+(cTcT\|cTXcTX$) | sequence | +| redos.py:340:25:340:25 | R | constant | +| redos.py:340:26:340:26 | E | constant | +| redos.py:340:27:340:54 | (([a-c]\|[c-d])T(e?e?e?e?\|X)) | group | +| redos.py:340:27:340:55 | (([a-c]\|[c-d])T(e?e?e?e?\|X))+ | plus | +| redos.py:340:27:340:55 | (([a-c]\|[c-d])T(e?e?e?e?\|X))+ | quantifier | +| redos.py:340:28:340:40 | ([a-c]\|[c-d]) | group | +| redos.py:340:28:340:53 | ([a-c]\|[c-d])T(e?e?e?e?\|X) | sequence | +| redos.py:340:29:340:33 | [a-c] | char | +| redos.py:340:29:340:39 | [a-c]\|[c-d] | alt | +| redos.py:340:30:340:30 | a | constant | +| redos.py:340:30:340:32 | a-c | charRange | +| redos.py:340:32:340:32 | c | constant | +| redos.py:340:35:340:39 | [c-d] | char | +| redos.py:340:36:340:36 | c | constant | +| redos.py:340:36:340:38 | c-d | charRange | +| redos.py:340:38:340:38 | d | constant | +| redos.py:340:41:340:41 | T | constant | +| redos.py:340:42:340:53 | (e?e?e?e?\|X) | group | +| redos.py:340:43:340:43 | e | constant | +| redos.py:340:43:340:44 | e? | opt | +| redos.py:340:43:340:44 | e? | quantifier | +| redos.py:340:43:340:50 | e?e?e?e? | sequence | +| redos.py:340:43:340:52 | e?e?e?e?\|X | alt | +| redos.py:340:45:340:45 | e | constant | +| redos.py:340:45:340:46 | e? | opt | +| redos.py:340:45:340:46 | e? | quantifier | +| redos.py:340:47:340:47 | e | constant | +| redos.py:340:47:340:48 | e? | opt | +| redos.py:340:47:340:48 | e? | quantifier | +| redos.py:340:49:340:49 | e | constant | +| redos.py:340:49:340:50 | e? | opt | +| redos.py:340:49:340:50 | e? | quantifier | +| redos.py:340:52:340:52 | X | constant | +| redos.py:340:56:340:69 | (cTcT\|cTXcTX$) | group | +| redos.py:340:57:340:57 | c | constant | +| redos.py:340:57:340:60 | cTcT | sequence | +| redos.py:340:57:340:68 | cTcT\|cTXcTX$ | alt | +| redos.py:340:58:340:58 | T | constant | +| redos.py:340:59:340:59 | c | constant | +| redos.py:340:60:340:60 | T | constant | +| redos.py:340:62:340:62 | c | constant | +| redos.py:340:62:340:68 | cTXcTX$ | sequence | +| redos.py:340:63:340:63 | T | constant | +| redos.py:340:64:340:64 | X | constant | +| redos.py:340:65:340:65 | c | constant | +| redos.py:340:66:340:66 | T | constant | +| redos.py:340:67:340:67 | X | constant | +| redos.py:340:68:340:68 | $ | dollar | +| redos.py:343:24:343:24 | ^ | caret | +| redos.py:343:24:343:34 | ^((a)+\\w)+$ | sequence | +| redos.py:343:25:343:32 | ((a)+\\w) | group | +| redos.py:343:25:343:33 | ((a)+\\w)+ | plus | +| redos.py:343:25:343:33 | ((a)+\\w)+ | quantifier | +| redos.py:343:26:343:28 | (a) | group | +| redos.py:343:26:343:29 | (a)+ | plus | +| redos.py:343:26:343:29 | (a)+ | quantifier | +| redos.py:343:26:343:31 | (a)+\\w | sequence | +| redos.py:343:27:343:27 | a | constant | +| redos.py:343:30:343:31 | \\w | charEsc | +| redos.py:343:34:343:34 | $ | dollar | +| redos.py:346:24:346:24 | ^ | caret | +| redos.py:346:24:346:31 | ^(b+.)+$ | sequence | +| redos.py:346:25:346:29 | (b+.) | group | +| redos.py:346:25:346:30 | (b+.)+ | plus | +| redos.py:346:25:346:30 | (b+.)+ | quantifier | +| redos.py:346:26:346:26 | b | constant | +| redos.py:346:26:346:27 | b+ | plus | +| redos.py:346:26:346:27 | b+ | quantifier | +| redos.py:346:26:346:28 | b+. | sequence | +| redos.py:346:28:346:28 | . | dot | +| redos.py:346:31:346:31 | $ | dollar | +| redos.py:349:25:349:25 | a | constant | +| redos.py:349:25:349:26 | a* | quantifier | +| redos.py:349:25:349:26 | a* | star | +| redos.py:349:25:349:27 | a*b | sequence | +| redos.py:349:27:349:27 | b | constant | +| redos.py:352:24:352:27 | (a*) | group | +| redos.py:352:24:352:28 | (a*)* | quantifier | +| redos.py:352:24:352:28 | (a*)* | star | +| redos.py:352:24:352:29 | (a*)*b | sequence | +| redos.py:352:25:352:25 | a | constant | +| redos.py:352:25:352:26 | a* | quantifier | +| redos.py:352:25:352:26 | a* | star | +| redos.py:352:29:352:29 | b | constant | +| redos.py:353:24:353:27 | (a+) | group | +| redos.py:353:24:353:28 | (a+)* | quantifier | +| redos.py:353:24:353:28 | (a+)* | star | +| redos.py:353:24:353:29 | (a+)*b | sequence | +| redos.py:353:25:353:25 | a | constant | +| redos.py:353:25:353:26 | a+ | plus | +| redos.py:353:25:353:26 | a+ | quantifier | +| redos.py:353:29:353:29 | b | constant | +| redos.py:354:24:354:27 | (a*) | group | +| redos.py:354:24:354:28 | (a*)+ | plus | +| redos.py:354:24:354:28 | (a*)+ | quantifier | +| redos.py:354:24:354:29 | (a*)+b | sequence | +| redos.py:354:25:354:25 | a | constant | +| redos.py:354:25:354:26 | a* | quantifier | +| redos.py:354:25:354:26 | a* | star | +| redos.py:354:29:354:29 | b | constant | +| redos.py:355:24:355:27 | (a+) | group | +| redos.py:355:24:355:28 | (a+)+ | plus | +| redos.py:355:24:355:28 | (a+)+ | quantifier | +| redos.py:355:24:355:29 | (a+)+b | sequence | +| redos.py:355:25:355:25 | a | constant | +| redos.py:355:25:355:26 | a+ | plus | +| redos.py:355:25:355:26 | a+ | quantifier | +| redos.py:355:29:355:29 | b | constant | +| redos.py:358:25:358:29 | (a\|b) | group | +| redos.py:358:25:358:30 | (a\|b)+ | plus | +| redos.py:358:25:358:30 | (a\|b)+ | quantifier | +| redos.py:358:26:358:26 | a | constant | +| redos.py:358:26:358:28 | a\|b | alt | +| redos.py:358:28:358:28 | b | constant | +| redos.py:359:25:359:61 | (?:[\\s;,"'<>(){}\|[\\]@=+*]\|:(?![/\\\\])) | group | +| redos.py:359:25:359:62 | (?:[\\s;,"'<>(){}\|[\\]@=+*]\|:(?![/\\\\]))+ | plus | +| redos.py:359:25:359:62 | (?:[\\s;,"'<>(){}\|[\\]@=+*]\|:(?![/\\\\]))+ | quantifier | +| redos.py:359:28:359:49 | [\\s;,"'<>(){}\|[\\]@=+*] | char | +| redos.py:359:28:359:60 | [\\s;,"'<>(){}\|[\\]@=+*]\|:(?![/\\\\]) | alt | +| redos.py:359:29:359:30 | \\s | charEsc | +| redos.py:359:31:359:31 | ; | constant | +| redos.py:359:32:359:32 | , | constant | +| redos.py:359:33:359:33 | " | constant | +| redos.py:359:34:359:34 | ' | constant | +| redos.py:359:35:359:35 | < | constant | +| redos.py:359:36:359:36 | > | constant | +| redos.py:359:37:359:37 | ( | constant | +| redos.py:359:38:359:38 | ) | constant | +| redos.py:359:39:359:39 | { | constant | +| redos.py:359:40:359:40 | } | constant | +| redos.py:359:41:359:41 | \| | constant | +| redos.py:359:42:359:42 | [ | constant | +| redos.py:359:43:359:44 | \\] | constant | +| redos.py:359:45:359:45 | @ | constant | +| redos.py:359:46:359:46 | = | constant | +| redos.py:359:47:359:47 | + | constant | +| redos.py:359:48:359:48 | * | constant | +| redos.py:359:51:359:51 | : | constant | +| redos.py:359:51:359:60 | :(?![/\\\\]) | sequence | +| redos.py:359:52:359:60 | (?![/\\\\]) | group | +| redos.py:359:55:359:59 | [/\\\\] | char | +| redos.py:359:56:359:56 | / | constant | +| redos.py:359:57:359:58 | \\\\ | constant | +| redos.py:362:24:362:24 | ^ | caret | +| redos.py:362:24:362:42 | ^((?:a{\|-)\|\\w\\{)+X$ | sequence | +| redos.py:362:25:362:39 | ((?:a{\|-)\|\\w\\{) | group | +| redos.py:362:25:362:40 | ((?:a{\|-)\|\\w\\{)+ | plus | +| redos.py:362:25:362:40 | ((?:a{\|-)\|\\w\\{)+ | quantifier | +| redos.py:362:26:362:33 | (?:a{\|-) | group | +| redos.py:362:26:362:38 | (?:a{\|-)\|\\w\\{ | alt | +| redos.py:362:29:362:29 | a | constant | +| redos.py:362:29:362:30 | a{ | sequence | +| redos.py:362:29:362:32 | a{\|- | alt | +| redos.py:362:30:362:30 | { | constant | +| redos.py:362:32:362:32 | - | constant | +| redos.py:362:35:362:36 | \\w | charEsc | +| redos.py:362:35:362:38 | \\w\\{ | sequence | +| redos.py:362:37:362:38 | \\{ | constant | +| redos.py:362:41:362:41 | X | constant | +| redos.py:362:42:362:42 | $ | dollar | +| redos.py:363:24:363:24 | ^ | caret | +| redos.py:363:24:363:45 | ^((?:a{0\|-)\|\\w\\{\\d)+X$ | sequence | +| redos.py:363:25:363:42 | ((?:a{0\|-)\|\\w\\{\\d) | group | +| redos.py:363:25:363:43 | ((?:a{0\|-)\|\\w\\{\\d)+ | plus | +| redos.py:363:25:363:43 | ((?:a{0\|-)\|\\w\\{\\d)+ | quantifier | +| redos.py:363:26:363:34 | (?:a{0\|-) | group | +| redos.py:363:26:363:41 | (?:a{0\|-)\|\\w\\{\\d | alt | +| redos.py:363:29:363:29 | a | constant | +| redos.py:363:29:363:31 | a{0 | sequence | +| redos.py:363:29:363:33 | a{0\|- | alt | +| redos.py:363:30:363:30 | { | constant | +| redos.py:363:31:363:31 | 0 | constant | +| redos.py:363:33:363:33 | - | constant | +| redos.py:363:36:363:37 | \\w | charEsc | +| redos.py:363:36:363:41 | \\w\\{\\d | sequence | +| redos.py:363:38:363:39 | \\{ | constant | +| redos.py:363:40:363:41 | \\d | charEsc | +| redos.py:363:44:363:44 | X | constant | +| redos.py:363:45:363:45 | $ | dollar | +| redos.py:364:24:364:24 | ^ | caret | +| redos.py:364:24:364:47 | ^((?:a{0,\|-)\|\\w\\{\\d,)+X$ | sequence | +| redos.py:364:25:364:44 | ((?:a{0,\|-)\|\\w\\{\\d,) | group | +| redos.py:364:25:364:45 | ((?:a{0,\|-)\|\\w\\{\\d,)+ | plus | +| redos.py:364:25:364:45 | ((?:a{0,\|-)\|\\w\\{\\d,)+ | quantifier | +| redos.py:364:26:364:35 | (?:a{0,\|-) | group | +| redos.py:364:26:364:43 | (?:a{0,\|-)\|\\w\\{\\d, | alt | +| redos.py:364:29:364:29 | a | constant | +| redos.py:364:29:364:32 | a{0, | sequence | +| redos.py:364:29:364:34 | a{0,\|- | alt | +| redos.py:364:30:364:30 | { | constant | +| redos.py:364:31:364:31 | 0 | constant | +| redos.py:364:32:364:32 | , | constant | +| redos.py:364:34:364:34 | - | constant | +| redos.py:364:37:364:38 | \\w | charEsc | +| redos.py:364:37:364:43 | \\w\\{\\d, | sequence | +| redos.py:364:39:364:40 | \\{ | constant | +| redos.py:364:41:364:42 | \\d | charEsc | +| redos.py:364:43:364:43 | , | constant | +| redos.py:364:46:364:46 | X | constant | +| redos.py:364:47:364:47 | $ | dollar | +| redos.py:365:24:365:24 | ^ | caret | +| redos.py:365:24:365:50 | ^((?:a{0,2\|-)\|\\w\\{\\d,\\d)+X$ | sequence | +| redos.py:365:25:365:47 | ((?:a{0,2\|-)\|\\w\\{\\d,\\d) | group | +| redos.py:365:25:365:48 | ((?:a{0,2\|-)\|\\w\\{\\d,\\d)+ | plus | +| redos.py:365:25:365:48 | ((?:a{0,2\|-)\|\\w\\{\\d,\\d)+ | quantifier | +| redos.py:365:26:365:36 | (?:a{0,2\|-) | group | +| redos.py:365:26:365:46 | (?:a{0,2\|-)\|\\w\\{\\d,\\d | alt | +| redos.py:365:29:365:29 | a | constant | +| redos.py:365:29:365:33 | a{0,2 | sequence | +| redos.py:365:29:365:35 | a{0,2\|- | alt | +| redos.py:365:30:365:30 | { | constant | +| redos.py:365:31:365:31 | 0 | constant | +| redos.py:365:32:365:32 | , | constant | +| redos.py:365:33:365:33 | 2 | constant | +| redos.py:365:35:365:35 | - | constant | +| redos.py:365:38:365:39 | \\w | charEsc | +| redos.py:365:38:365:46 | \\w\\{\\d,\\d | sequence | +| redos.py:365:40:365:41 | \\{ | constant | +| redos.py:365:42:365:43 | \\d | charEsc | +| redos.py:365:44:365:44 | , | constant | +| redos.py:365:45:365:46 | \\d | charEsc | +| redos.py:365:49:365:49 | X | constant | +| redos.py:365:50:365:50 | $ | dollar | +| redos.py:368:25:368:25 | ^ | caret | +| redos.py:368:25:368:54 | ^((?:a{0,2}\|-)\|\\w\\{\\d,\\d\\})+X$ | sequence | +| redos.py:368:26:368:51 | ((?:a{0,2}\|-)\|\\w\\{\\d,\\d\\}) | group | +| redos.py:368:26:368:52 | ((?:a{0,2}\|-)\|\\w\\{\\d,\\d\\})+ | plus | +| redos.py:368:26:368:52 | ((?:a{0,2}\|-)\|\\w\\{\\d,\\d\\})+ | quantifier | +| redos.py:368:27:368:38 | (?:a{0,2}\|-) | group | +| redos.py:368:27:368:50 | (?:a{0,2}\|-)\|\\w\\{\\d,\\d\\} | alt | +| redos.py:368:30:368:30 | a | constant | +| redos.py:368:30:368:35 | a{0,2} | quantifier | +| redos.py:368:30:368:35 | a{0,2} | range | +| redos.py:368:30:368:37 | a{0,2}\|- | alt | +| redos.py:368:37:368:37 | - | constant | +| redos.py:368:40:368:41 | \\w | charEsc | +| redos.py:368:40:368:50 | \\w\\{\\d,\\d\\} | sequence | +| redos.py:368:42:368:43 | \\{ | constant | +| redos.py:368:44:368:45 | \\d | charEsc | +| redos.py:368:46:368:46 | , | constant | +| redos.py:368:47:368:48 | \\d | charEsc | +| redos.py:368:49:368:50 | \\} | constant | +| redos.py:368:53:368:53 | X | constant | +| redos.py:368:54:368:54 | $ | dollar | diff --git a/python/ql/test/query-tests/Security/CWE-730/ReDoSRegexParsing.ql b/python/ql/test/query-tests/Security/CWE-730/ReDoSRegexParsing.ql new file mode 100644 index 000000000000..65295f8297e6 --- /dev/null +++ b/python/ql/test/query-tests/Security/CWE-730/ReDoSRegexParsing.ql @@ -0,0 +1,44 @@ +import python +import semmle.python.RegexTreeView + +predicate kind(RegExpTerm term, string kind) { + // kind = "term" // consider this for completeness + // or + term instanceof RegExpQuantifier and kind = "quantifier" + or + term instanceof RegExpPlus and kind = "plus" + or + term instanceof RegExpStar and kind = "star" + or + term instanceof RegExpRange and kind = "range" + or + term instanceof RegExpAlt and kind = "alt" + or + term instanceof RegExpCharacterClass and kind = "char" + or + term instanceof RegExpCharacterClassEscape and kind = "charEsc" + or + term instanceof RegExpLookbehind and kind = "lookbehind" + or + term instanceof RegExpConstant and kind = "constant" + or + term instanceof RegExpCharacterRange and kind = "charRange" + or + term instanceof RegExpGroup and kind = "group" + or + term instanceof RegExpOpt and kind = "opt" + or + term instanceof RegExpDot and kind = "dot" + or + term instanceof RegExpSequence and kind = "sequence" + or + term instanceof RegExpDollar and kind = "dollar" + or + term instanceof RegExpCaret and kind = "caret" +} + +from RegExpTerm rt, string kind +where + rt.getFile().getBaseName() = "redos.py" and + kind(rt, kind) +select rt, kind diff --git a/python/ql/test/query-tests/Security/CWE-730/ReDosRegexHierarchy.expected b/python/ql/test/query-tests/Security/CWE-730/ReDosRegexHierarchy.expected new file mode 100644 index 000000000000..58f939a57cff --- /dev/null +++ b/python/ql/test/query-tests/Security/CWE-730/ReDosRegexHierarchy.expected @@ -0,0 +1,2117 @@ +seqChild +| KnownCVEs.py:5:28:5:48 | \\s*(\\d+)\\s*(\\S+) (.*) | KnownCVEs.py:5:28:5:30 | \\s* | 0 | +| KnownCVEs.py:5:28:5:48 | \\s*(\\d+)\\s*(\\S+) (.*) | KnownCVEs.py:5:31:5:35 | (\\d+) | 1 | +| KnownCVEs.py:5:28:5:48 | \\s*(\\d+)\\s*(\\S+) (.*) | KnownCVEs.py:5:36:5:38 | \\s* | 2 | +| KnownCVEs.py:5:28:5:48 | \\s*(\\d+)\\s*(\\S+) (.*) | KnownCVEs.py:5:39:5:43 | (\\S+) | 3 | +| KnownCVEs.py:5:28:5:48 | \\s*(\\d+)\\s*(\\S+) (.*) | KnownCVEs.py:5:44:5:44 | | 4 | +| KnownCVEs.py:5:28:5:48 | \\s*(\\d+)\\s*(\\S+) (.*) | KnownCVEs.py:5:45:5:48 | (.*) | 5 | +| KnownCVEs.py:15:16:15:33 | [+-]?(\\d+)*\\.\\d+%? | KnownCVEs.py:15:16:15:20 | [+-]? | 0 | +| KnownCVEs.py:15:16:15:33 | [+-]?(\\d+)*\\.\\d+%? | KnownCVEs.py:15:21:15:26 | (\\d+)* | 1 | +| KnownCVEs.py:15:16:15:33 | [+-]?(\\d+)*\\.\\d+%? | KnownCVEs.py:15:27:15:28 | \\. | 2 | +| KnownCVEs.py:15:16:15:33 | [+-]?(\\d+)*\\.\\d+%? | KnownCVEs.py:15:29:15:31 | \\d+ | 3 | +| KnownCVEs.py:15:16:15:33 | [+-]?(\\d+)*\\.\\d+%? | KnownCVEs.py:15:32:15:33 | %? | 4 | +| KnownCVEs.py:16:16:16:37 | """\\s+(?:.\|\\n)*?\\s+""" | KnownCVEs.py:16:16:16:16 | " | 0 | +| KnownCVEs.py:16:16:16:37 | """\\s+(?:.\|\\n)*?\\s+""" | KnownCVEs.py:16:17:16:17 | " | 1 | +| KnownCVEs.py:16:16:16:37 | """\\s+(?:.\|\\n)*?\\s+""" | KnownCVEs.py:16:18:16:18 | " | 2 | +| KnownCVEs.py:16:16:16:37 | """\\s+(?:.\|\\n)*?\\s+""" | KnownCVEs.py:16:19:16:21 | \\s+ | 3 | +| KnownCVEs.py:16:16:16:37 | """\\s+(?:.\|\\n)*?\\s+""" | KnownCVEs.py:16:22:16:31 | (?:.\|\\n)*? | 4 | +| KnownCVEs.py:16:16:16:37 | """\\s+(?:.\|\\n)*?\\s+""" | KnownCVEs.py:16:32:16:34 | \\s+ | 5 | +| KnownCVEs.py:16:16:16:37 | """\\s+(?:.\|\\n)*?\\s+""" | KnownCVEs.py:16:35:16:35 | " | 6 | +| KnownCVEs.py:16:16:16:37 | """\\s+(?:.\|\\n)*?\\s+""" | KnownCVEs.py:16:36:16:36 | " | 7 | +| KnownCVEs.py:16:16:16:37 | """\\s+(?:.\|\\n)*?\\s+""" | KnownCVEs.py:16:37:16:37 | " | 8 | +| KnownCVEs.py:17:16:17:44 | (\\{\\s+)(\\S+)(\\s+[^}]+\\s+\\}\\s) | KnownCVEs.py:17:16:17:22 | (\\{\\s+) | 0 | +| KnownCVEs.py:17:16:17:44 | (\\{\\s+)(\\S+)(\\s+[^}]+\\s+\\}\\s) | KnownCVEs.py:17:23:17:27 | (\\S+) | 1 | +| KnownCVEs.py:17:16:17:44 | (\\{\\s+)(\\S+)(\\s+[^}]+\\s+\\}\\s) | KnownCVEs.py:17:28:17:44 | (\\s+[^}]+\\s+\\}\\s) | 2 | +| KnownCVEs.py:17:17:17:21 | \\{\\s+ | KnownCVEs.py:17:17:17:18 | \\{ | 0 | +| KnownCVEs.py:17:17:17:21 | \\{\\s+ | KnownCVEs.py:17:19:17:21 | \\s+ | 1 | +| KnownCVEs.py:17:29:17:43 | \\s+[^}]+\\s+\\}\\s | KnownCVEs.py:17:29:17:31 | \\s+ | 0 | +| KnownCVEs.py:17:29:17:43 | \\s+[^}]+\\s+\\}\\s | KnownCVEs.py:17:32:17:36 | [^}]+ | 1 | +| KnownCVEs.py:17:29:17:43 | \\s+[^}]+\\s+\\}\\s | KnownCVEs.py:17:37:17:39 | \\s+ | 2 | +| KnownCVEs.py:17:29:17:43 | \\s+[^}]+\\s+\\}\\s | KnownCVEs.py:17:40:17:41 | \\} | 3 | +| KnownCVEs.py:17:29:17:43 | \\s+[^}]+\\s+\\}\\s | KnownCVEs.py:17:42:17:43 | \\s | 4 | +| KnownCVEs.py:18:16:18:27 | ".*``.*``.*" | KnownCVEs.py:18:16:18:16 | " | 0 | +| KnownCVEs.py:18:16:18:27 | ".*``.*``.*" | KnownCVEs.py:18:17:18:18 | .* | 1 | +| KnownCVEs.py:18:16:18:27 | ".*``.*``.*" | KnownCVEs.py:18:19:18:19 | ` | 2 | +| KnownCVEs.py:18:16:18:27 | ".*``.*``.*" | KnownCVEs.py:18:20:18:20 | ` | 3 | +| KnownCVEs.py:18:16:18:27 | ".*``.*``.*" | KnownCVEs.py:18:21:18:22 | .* | 4 | +| KnownCVEs.py:18:16:18:27 | ".*``.*``.*" | KnownCVEs.py:18:23:18:23 | ` | 5 | +| KnownCVEs.py:18:16:18:27 | ".*``.*``.*" | KnownCVEs.py:18:24:18:24 | ` | 6 | +| KnownCVEs.py:18:16:18:27 | ".*``.*``.*" | KnownCVEs.py:18:25:18:26 | .* | 7 | +| KnownCVEs.py:18:16:18:27 | ".*``.*``.*" | KnownCVEs.py:18:27:18:27 | " | 8 | +| KnownCVEs.py:19:16:19:63 | (\\s*)(?:(.+)(\\s*)(=)(\\s*))?(.+)(\\()(.*)(\\))(\\s*) | KnownCVEs.py:19:16:19:20 | (\\s*) | 0 | +| KnownCVEs.py:19:16:19:63 | (\\s*)(?:(.+)(\\s*)(=)(\\s*))?(.+)(\\()(.*)(\\))(\\s*) | KnownCVEs.py:19:21:19:42 | (?:(.+)(\\s*)(=)(\\s*))? | 1 | +| KnownCVEs.py:19:16:19:63 | (\\s*)(?:(.+)(\\s*)(=)(\\s*))?(.+)(\\()(.*)(\\))(\\s*) | KnownCVEs.py:19:43:19:46 | (.+) | 2 | +| KnownCVEs.py:19:16:19:63 | (\\s*)(?:(.+)(\\s*)(=)(\\s*))?(.+)(\\()(.*)(\\))(\\s*) | KnownCVEs.py:19:47:19:50 | (\\() | 3 | +| KnownCVEs.py:19:16:19:63 | (\\s*)(?:(.+)(\\s*)(=)(\\s*))?(.+)(\\()(.*)(\\))(\\s*) | KnownCVEs.py:19:51:19:54 | (.*) | 4 | +| KnownCVEs.py:19:16:19:63 | (\\s*)(?:(.+)(\\s*)(=)(\\s*))?(.+)(\\()(.*)(\\))(\\s*) | KnownCVEs.py:19:55:19:58 | (\\)) | 5 | +| KnownCVEs.py:19:16:19:63 | (\\s*)(?:(.+)(\\s*)(=)(\\s*))?(.+)(\\()(.*)(\\))(\\s*) | KnownCVEs.py:19:59:19:63 | (\\s*) | 6 | +| KnownCVEs.py:19:24:19:40 | (.+)(\\s*)(=)(\\s*) | KnownCVEs.py:19:24:19:27 | (.+) | 0 | +| KnownCVEs.py:19:24:19:40 | (.+)(\\s*)(=)(\\s*) | KnownCVEs.py:19:28:19:32 | (\\s*) | 1 | +| KnownCVEs.py:19:24:19:40 | (.+)(\\s*)(=)(\\s*) | KnownCVEs.py:19:33:19:35 | (=) | 2 | +| KnownCVEs.py:19:24:19:40 | (.+)(\\s*)(=)(\\s*) | KnownCVEs.py:19:36:19:40 | (\\s*) | 3 | +| KnownCVEs.py:20:16:20:63 | (%config)(\\s*\\(\\s*)(\\w+)(\\s*=\\s*)(.*?)(\\s*\\)\\s*) | KnownCVEs.py:20:16:20:24 | (%config) | 0 | +| KnownCVEs.py:20:16:20:63 | (%config)(\\s*\\(\\s*)(\\w+)(\\s*=\\s*)(.*?)(\\s*\\)\\s*) | KnownCVEs.py:20:25:20:34 | (\\s*\\(\\s*) | 1 | +| KnownCVEs.py:20:16:20:63 | (%config)(\\s*\\(\\s*)(\\w+)(\\s*=\\s*)(.*?)(\\s*\\)\\s*) | KnownCVEs.py:20:35:20:39 | (\\w+) | 2 | +| KnownCVEs.py:20:16:20:63 | (%config)(\\s*\\(\\s*)(\\w+)(\\s*=\\s*)(.*?)(\\s*\\)\\s*) | KnownCVEs.py:20:40:20:48 | (\\s*=\\s*) | 3 | +| KnownCVEs.py:20:16:20:63 | (%config)(\\s*\\(\\s*)(\\w+)(\\s*=\\s*)(.*?)(\\s*\\)\\s*) | KnownCVEs.py:20:49:20:53 | (.*?) | 4 | +| KnownCVEs.py:20:16:20:63 | (%config)(\\s*\\(\\s*)(\\w+)(\\s*=\\s*)(.*?)(\\s*\\)\\s*) | KnownCVEs.py:20:54:20:63 | (\\s*\\)\\s*) | 5 | +| KnownCVEs.py:20:17:20:23 | %config | KnownCVEs.py:20:17:20:17 | % | 0 | +| KnownCVEs.py:20:17:20:23 | %config | KnownCVEs.py:20:18:20:18 | c | 1 | +| KnownCVEs.py:20:17:20:23 | %config | KnownCVEs.py:20:19:20:19 | o | 2 | +| KnownCVEs.py:20:17:20:23 | %config | KnownCVEs.py:20:20:20:20 | n | 3 | +| KnownCVEs.py:20:17:20:23 | %config | KnownCVEs.py:20:21:20:21 | f | 4 | +| KnownCVEs.py:20:17:20:23 | %config | KnownCVEs.py:20:22:20:22 | i | 5 | +| KnownCVEs.py:20:17:20:23 | %config | KnownCVEs.py:20:23:20:23 | g | 6 | +| KnownCVEs.py:20:26:20:33 | \\s*\\(\\s* | KnownCVEs.py:20:26:20:28 | \\s* | 0 | +| KnownCVEs.py:20:26:20:33 | \\s*\\(\\s* | KnownCVEs.py:20:29:20:30 | \\( | 1 | +| KnownCVEs.py:20:26:20:33 | \\s*\\(\\s* | KnownCVEs.py:20:31:20:33 | \\s* | 2 | +| KnownCVEs.py:20:41:20:47 | \\s*=\\s* | KnownCVEs.py:20:41:20:43 | \\s* | 0 | +| KnownCVEs.py:20:41:20:47 | \\s*=\\s* | KnownCVEs.py:20:44:20:44 | = | 1 | +| KnownCVEs.py:20:41:20:47 | \\s*=\\s* | KnownCVEs.py:20:45:20:47 | \\s* | 2 | +| KnownCVEs.py:20:55:20:62 | \\s*\\)\\s* | KnownCVEs.py:20:55:20:57 | \\s* | 0 | +| KnownCVEs.py:20:55:20:62 | \\s*\\)\\s* | KnownCVEs.py:20:58:20:59 | \\) | 1 | +| KnownCVEs.py:20:55:20:62 | \\s*\\)\\s* | KnownCVEs.py:20:60:20:62 | \\s* | 2 | +| KnownCVEs.py:21:16:21:45 | (%new)(\\s*)(\\()(\\s*.*?\\s*)(\\)) | KnownCVEs.py:21:16:21:21 | (%new) | 0 | +| KnownCVEs.py:21:16:21:45 | (%new)(\\s*)(\\()(\\s*.*?\\s*)(\\)) | KnownCVEs.py:21:22:21:26 | (\\s*) | 1 | +| KnownCVEs.py:21:16:21:45 | (%new)(\\s*)(\\()(\\s*.*?\\s*)(\\)) | KnownCVEs.py:21:27:21:30 | (\\() | 2 | +| KnownCVEs.py:21:16:21:45 | (%new)(\\s*)(\\()(\\s*.*?\\s*)(\\)) | KnownCVEs.py:21:31:21:41 | (\\s*.*?\\s*) | 3 | +| KnownCVEs.py:21:16:21:45 | (%new)(\\s*)(\\()(\\s*.*?\\s*)(\\)) | KnownCVEs.py:21:42:21:45 | (\\)) | 4 | +| KnownCVEs.py:21:17:21:20 | %new | KnownCVEs.py:21:17:21:17 | % | 0 | +| KnownCVEs.py:21:17:21:20 | %new | KnownCVEs.py:21:18:21:18 | n | 1 | +| KnownCVEs.py:21:17:21:20 | %new | KnownCVEs.py:21:19:21:19 | e | 2 | +| KnownCVEs.py:21:17:21:20 | %new | KnownCVEs.py:21:20:21:20 | w | 3 | +| KnownCVEs.py:21:32:21:40 | \\s*.*?\\s* | KnownCVEs.py:21:32:21:34 | \\s* | 0 | +| KnownCVEs.py:21:32:21:40 | \\s*.*?\\s* | KnownCVEs.py:21:35:21:37 | .*? | 1 | +| KnownCVEs.py:21:32:21:40 | \\s*.*?\\s* | KnownCVEs.py:21:38:21:40 | \\s* | 2 | +| KnownCVEs.py:22:16:22:70 | (\\$)(evoque\|overlay)(\\{(%)?)(\\s*[#\\w\\-"\\'.]+[^=,%}]+?)? | KnownCVEs.py:22:16:22:19 | (\\$) | 0 | +| KnownCVEs.py:22:16:22:70 | (\\$)(evoque\|overlay)(\\{(%)?)(\\s*[#\\w\\-"\\'.]+[^=,%}]+?)? | KnownCVEs.py:22:20:22:35 | (evoque\|overlay) | 1 | +| KnownCVEs.py:22:16:22:70 | (\\$)(evoque\|overlay)(\\{(%)?)(\\s*[#\\w\\-"\\'.]+[^=,%}]+?)? | KnownCVEs.py:22:36:22:43 | (\\{(%)?) | 2 | +| KnownCVEs.py:22:16:22:70 | (\\$)(evoque\|overlay)(\\{(%)?)(\\s*[#\\w\\-"\\'.]+[^=,%}]+?)? | KnownCVEs.py:22:44:22:70 | (\\s*[#\\w\\-"\\'.]+[^=,%}]+?)? | 3 | +| KnownCVEs.py:22:21:22:26 | evoque | KnownCVEs.py:22:21:22:21 | e | 0 | +| KnownCVEs.py:22:21:22:26 | evoque | KnownCVEs.py:22:22:22:22 | v | 1 | +| KnownCVEs.py:22:21:22:26 | evoque | KnownCVEs.py:22:23:22:23 | o | 2 | +| KnownCVEs.py:22:21:22:26 | evoque | KnownCVEs.py:22:24:22:24 | q | 3 | +| KnownCVEs.py:22:21:22:26 | evoque | KnownCVEs.py:22:25:22:25 | u | 4 | +| KnownCVEs.py:22:21:22:26 | evoque | KnownCVEs.py:22:26:22:26 | e | 5 | +| KnownCVEs.py:22:28:22:34 | overlay | KnownCVEs.py:22:28:22:28 | o | 0 | +| KnownCVEs.py:22:28:22:34 | overlay | KnownCVEs.py:22:29:22:29 | v | 1 | +| KnownCVEs.py:22:28:22:34 | overlay | KnownCVEs.py:22:30:22:30 | e | 2 | +| KnownCVEs.py:22:28:22:34 | overlay | KnownCVEs.py:22:31:22:31 | r | 3 | +| KnownCVEs.py:22:28:22:34 | overlay | KnownCVEs.py:22:32:22:32 | l | 4 | +| KnownCVEs.py:22:28:22:34 | overlay | KnownCVEs.py:22:33:22:33 | a | 5 | +| KnownCVEs.py:22:28:22:34 | overlay | KnownCVEs.py:22:34:22:34 | y | 6 | +| KnownCVEs.py:22:37:22:42 | \\{(%)? | KnownCVEs.py:22:37:22:38 | \\{ | 0 | +| KnownCVEs.py:22:37:22:42 | \\{(%)? | KnownCVEs.py:22:39:22:42 | (%)? | 1 | +| KnownCVEs.py:22:45:22:68 | \\s*[#\\w\\-"\\'.]+[^=,%}]+? | KnownCVEs.py:22:45:22:47 | \\s* | 0 | +| KnownCVEs.py:22:45:22:68 | \\s*[#\\w\\-"\\'.]+[^=,%}]+? | KnownCVEs.py:22:48:22:59 | [#\\w\\-"\\'.]+ | 1 | +| KnownCVEs.py:22:45:22:68 | \\s*[#\\w\\-"\\'.]+[^=,%}]+? | KnownCVEs.py:22:60:22:68 | [^=,%}]+? | 2 | +| KnownCVEs.py:23:16:23:46 | (\\.\\w+\\b)(\\s*=\\s*)([^;]*)(\\s*;) | KnownCVEs.py:23:16:23:24 | (\\.\\w+\\b) | 0 | +| KnownCVEs.py:23:16:23:46 | (\\.\\w+\\b)(\\s*=\\s*)([^;]*)(\\s*;) | KnownCVEs.py:23:25:23:33 | (\\s*=\\s*) | 1 | +| KnownCVEs.py:23:16:23:46 | (\\.\\w+\\b)(\\s*=\\s*)([^;]*)(\\s*;) | KnownCVEs.py:23:34:23:40 | ([^;]*) | 2 | +| KnownCVEs.py:23:16:23:46 | (\\.\\w+\\b)(\\s*=\\s*)([^;]*)(\\s*;) | KnownCVEs.py:23:41:23:46 | (\\s*;) | 3 | +| KnownCVEs.py:23:17:23:23 | \\.\\w+\\b | KnownCVEs.py:23:17:23:18 | \\. | 0 | +| KnownCVEs.py:23:17:23:23 | \\.\\w+\\b | KnownCVEs.py:23:19:23:21 | \\w+ | 1 | +| KnownCVEs.py:23:17:23:23 | \\.\\w+\\b | KnownCVEs.py:23:22:23:23 | \\b | 2 | +| KnownCVEs.py:23:26:23:32 | \\s*=\\s* | KnownCVEs.py:23:26:23:28 | \\s* | 0 | +| KnownCVEs.py:23:26:23:32 | \\s*=\\s* | KnownCVEs.py:23:29:23:29 | = | 1 | +| KnownCVEs.py:23:26:23:32 | \\s*=\\s* | KnownCVEs.py:23:30:23:32 | \\s* | 2 | +| KnownCVEs.py:23:42:23:45 | \\s*; | KnownCVEs.py:23:42:23:44 | \\s* | 0 | +| KnownCVEs.py:23:42:23:45 | \\s*; | KnownCVEs.py:23:45:23:45 | ; | 1 | +| KnownCVEs.py:27:34:27:71 | ^\\S+@[a-zA-Z0-9._-]+\\.[a-zA-Z0-9._-]+$ | KnownCVEs.py:27:34:27:34 | ^ | 0 | +| KnownCVEs.py:27:34:27:71 | ^\\S+@[a-zA-Z0-9._-]+\\.[a-zA-Z0-9._-]+$ | KnownCVEs.py:27:35:27:37 | \\S+ | 1 | +| KnownCVEs.py:27:34:27:71 | ^\\S+@[a-zA-Z0-9._-]+\\.[a-zA-Z0-9._-]+$ | KnownCVEs.py:27:38:27:38 | @ | 2 | +| KnownCVEs.py:27:34:27:71 | ^\\S+@[a-zA-Z0-9._-]+\\.[a-zA-Z0-9._-]+$ | KnownCVEs.py:27:39:27:53 | [a-zA-Z0-9._-]+ | 3 | +| KnownCVEs.py:27:34:27:71 | ^\\S+@[a-zA-Z0-9._-]+\\.[a-zA-Z0-9._-]+$ | KnownCVEs.py:27:54:27:55 | \\. | 4 | +| KnownCVEs.py:27:34:27:71 | ^\\S+@[a-zA-Z0-9._-]+\\.[a-zA-Z0-9._-]+$ | KnownCVEs.py:27:56:27:70 | [a-zA-Z0-9._-]+ | 5 | +| KnownCVEs.py:27:34:27:71 | ^\\S+@[a-zA-Z0-9._-]+\\.[a-zA-Z0-9._-]+$ | KnownCVEs.py:27:71:27:71 | $ | 6 | +| KnownCVEs.py:30:21:31:69 | (?:.*,)*[ \t]*([^ \t]+)[ \t]+realm=(["']?)([^"']*)\\2 | KnownCVEs.py:30:21:31:28 | (?:.*,)* | 0 | +| KnownCVEs.py:30:21:31:69 | (?:.*,)*[ \t]*([^ \t]+)[ \t]+realm=(["']?)([^"']*)\\2 | KnownCVEs.py:30:29:31:33 | [ \t]* | 1 | +| KnownCVEs.py:30:21:31:69 | (?:.*,)*[ \t]*([^ \t]+)[ \t]+realm=(["']?)([^"']*)\\2 | KnownCVEs.py:30:34:31:41 | ([^ \t]+) | 2 | +| KnownCVEs.py:30:21:31:69 | (?:.*,)*[ \t]*([^ \t]+)[ \t]+realm=(["']?)([^"']*)\\2 | KnownCVEs.py:30:42:31:46 | [ \t]+ | 3 | +| KnownCVEs.py:30:21:31:69 | (?:.*,)*[ \t]*([^ \t]+)[ \t]+realm=(["']?)([^"']*)\\2 | KnownCVEs.py:30:47:31:47 | r | 4 | +| KnownCVEs.py:30:21:31:69 | (?:.*,)*[ \t]*([^ \t]+)[ \t]+realm=(["']?)([^"']*)\\2 | KnownCVEs.py:30:48:31:48 | e | 5 | +| KnownCVEs.py:30:21:31:69 | (?:.*,)*[ \t]*([^ \t]+)[ \t]+realm=(["']?)([^"']*)\\2 | KnownCVEs.py:30:49:31:49 | a | 6 | +| KnownCVEs.py:30:21:31:69 | (?:.*,)*[ \t]*([^ \t]+)[ \t]+realm=(["']?)([^"']*)\\2 | KnownCVEs.py:30:50:31:50 | l | 7 | +| KnownCVEs.py:30:21:31:69 | (?:.*,)*[ \t]*([^ \t]+)[ \t]+realm=(["']?)([^"']*)\\2 | KnownCVEs.py:30:51:31:51 | m | 8 | +| KnownCVEs.py:30:21:31:69 | (?:.*,)*[ \t]*([^ \t]+)[ \t]+realm=(["']?)([^"']*)\\2 | KnownCVEs.py:30:52:31:52 | = | 9 | +| KnownCVEs.py:30:21:31:69 | (?:.*,)*[ \t]*([^ \t]+)[ \t]+realm=(["']?)([^"']*)\\2 | KnownCVEs.py:30:53:31:59 | (["']?) | 10 | +| KnownCVEs.py:30:21:31:69 | (?:.*,)*[ \t]*([^ \t]+)[ \t]+realm=(["']?)([^"']*)\\2 | KnownCVEs.py:30:60:31:67 | ([^"']*) | 11 | +| KnownCVEs.py:30:21:31:69 | (?:.*,)*[ \t]*([^ \t]+)[ \t]+realm=(["']?)([^"']*)\\2 | KnownCVEs.py:30:68:31:69 | \\2 | 12 | +| KnownCVEs.py:30:24:31:26 | .*, | KnownCVEs.py:30:24:31:25 | .* | 0 | +| KnownCVEs.py:30:24:31:26 | .*, | KnownCVEs.py:30:26:31:26 | , | 1 | +| KnownCVEs.py:35:17:35:82 | ^([-/:,#%.'"\\s!\\w]\|\\w-\\w\|'[\\s\\w]+'\\s*\|"[\\s\\w]+"\|\\([\\d,%\\.\\s]+\\))*$ | KnownCVEs.py:35:17:35:17 | ^ | 0 | +| KnownCVEs.py:35:17:35:82 | ^([-/:,#%.'"\\s!\\w]\|\\w-\\w\|'[\\s\\w]+'\\s*\|"[\\s\\w]+"\|\\([\\d,%\\.\\s]+\\))*$ | KnownCVEs.py:35:18:35:81 | ([-/:,#%.'"\\s!\\w]\|\\w-\\w\|'[\\s\\w]+'\\s*\|"[\\s\\w]+"\|\\([\\d,%\\.\\s]+\\))* | 1 | +| KnownCVEs.py:35:17:35:82 | ^([-/:,#%.'"\\s!\\w]\|\\w-\\w\|'[\\s\\w]+'\\s*\|"[\\s\\w]+"\|\\([\\d,%\\.\\s]+\\))*$ | KnownCVEs.py:35:82:35:82 | $ | 2 | +| KnownCVEs.py:35:36:35:40 | \\w-\\w | KnownCVEs.py:35:36:35:37 | \\w | 0 | +| KnownCVEs.py:35:36:35:40 | \\w-\\w | KnownCVEs.py:35:38:35:38 | - | 1 | +| KnownCVEs.py:35:36:35:40 | \\w-\\w | KnownCVEs.py:35:39:35:40 | \\w | 2 | +| KnownCVEs.py:35:42:35:53 | '[\\s\\w]+'\\s* | KnownCVEs.py:35:42:35:42 | ' | 0 | +| KnownCVEs.py:35:42:35:53 | '[\\s\\w]+'\\s* | KnownCVEs.py:35:43:35:49 | [\\s\\w]+ | 1 | +| KnownCVEs.py:35:42:35:53 | '[\\s\\w]+'\\s* | KnownCVEs.py:35:50:35:50 | ' | 2 | +| KnownCVEs.py:35:42:35:53 | '[\\s\\w]+'\\s* | KnownCVEs.py:35:51:35:53 | \\s* | 3 | +| KnownCVEs.py:35:55:35:63 | "[\\s\\w]+" | KnownCVEs.py:35:55:35:55 | " | 0 | +| KnownCVEs.py:35:55:35:63 | "[\\s\\w]+" | KnownCVEs.py:35:56:35:62 | [\\s\\w]+ | 1 | +| KnownCVEs.py:35:55:35:63 | "[\\s\\w]+" | KnownCVEs.py:35:63:35:63 | " | 2 | +| KnownCVEs.py:35:65:35:79 | \\([\\d,%\\.\\s]+\\) | KnownCVEs.py:35:65:35:66 | \\( | 0 | +| KnownCVEs.py:35:65:35:79 | \\([\\d,%\\.\\s]+\\) | KnownCVEs.py:35:67:35:77 | [\\d,%\\.\\s]+ | 1 | +| KnownCVEs.py:35:65:35:79 | \\([\\d,%\\.\\s]+\\) | KnownCVEs.py:35:78:35:79 | \\) | 2 | +| KnownCVEs.py:80:9:83:185 | ^(:?(([a-zA-Z]{1})\|([a-zA-Z]{1}[a-zA-Z]{1})\|([a-zA-Z]{1}[0-9]{1})\|([0-9]{1}[a-zA-Z]{1})\|([a-zA-Z0-9][-_a-zA-Z0-9]{0,61}[a-zA-Z0-9]))\\.)+([a-zA-Z]{2,13}\|(xn--[a-zA-Z0-9]{2,30}))$ | KnownCVEs.py:80:9:83:9 | ^ | 0 | +| KnownCVEs.py:80:9:83:185 | ^(:?(([a-zA-Z]{1})\|([a-zA-Z]{1}[a-zA-Z]{1})\|([a-zA-Z]{1}[0-9]{1})\|([0-9]{1}[a-zA-Z]{1})\|([a-zA-Z0-9][-_a-zA-Z0-9]{0,61}[a-zA-Z0-9]))\\.)+([a-zA-Z]{2,13}\|(xn--[a-zA-Z0-9]{2,30}))$ | KnownCVEs.py:80:10:83:144 | (:?(([a-zA-Z]{1})\|([a-zA-Z]{1}[a-zA-Z]{1})\|([a-zA-Z]{1}[0-9]{1})\|([0-9]{1}[a-zA-Z]{1})\|([a-zA-Z0-9][-_a-zA-Z0-9]{0,61}[a-zA-Z0-9]))\\.)+ | 1 | +| KnownCVEs.py:80:9:83:185 | ^(:?(([a-zA-Z]{1})\|([a-zA-Z]{1}[a-zA-Z]{1})\|([a-zA-Z]{1}[0-9]{1})\|([0-9]{1}[a-zA-Z]{1})\|([a-zA-Z0-9][-_a-zA-Z0-9]{0,61}[a-zA-Z0-9]))\\.)+([a-zA-Z]{2,13}\|(xn--[a-zA-Z0-9]{2,30}))$ | KnownCVEs.py:80:145:83:184 | ([a-zA-Z]{2,13}\|(xn--[a-zA-Z0-9]{2,30})) | 2 | +| KnownCVEs.py:80:9:83:185 | ^(:?(([a-zA-Z]{1})\|([a-zA-Z]{1}[a-zA-Z]{1})\|([a-zA-Z]{1}[0-9]{1})\|([0-9]{1}[a-zA-Z]{1})\|([a-zA-Z0-9][-_a-zA-Z0-9]{0,61}[a-zA-Z0-9]))\\.)+([a-zA-Z]{2,13}\|(xn--[a-zA-Z0-9]{2,30}))$ | KnownCVEs.py:80:185:83:185 | $ | 3 | +| KnownCVEs.py:80:11:83:142 | :?(([a-zA-Z]{1})\|([a-zA-Z]{1}[a-zA-Z]{1})\|([a-zA-Z]{1}[0-9]{1})\|([0-9]{1}[a-zA-Z]{1})\|([a-zA-Z0-9][-_a-zA-Z0-9]{0,61}[a-zA-Z0-9]))\\. | KnownCVEs.py:80:11:83:12 | :? | 0 | +| KnownCVEs.py:80:11:83:142 | :?(([a-zA-Z]{1})\|([a-zA-Z]{1}[a-zA-Z]{1})\|([a-zA-Z]{1}[0-9]{1})\|([0-9]{1}[a-zA-Z]{1})\|([a-zA-Z0-9][-_a-zA-Z0-9]{0,61}[a-zA-Z0-9]))\\. | KnownCVEs.py:80:13:83:140 | (([a-zA-Z]{1})\|([a-zA-Z]{1}[a-zA-Z]{1})\|([a-zA-Z]{1}[0-9]{1})\|([0-9]{1}[a-zA-Z]{1})\|([a-zA-Z0-9][-_a-zA-Z0-9]{0,61}[a-zA-Z0-9])) | 1 | +| KnownCVEs.py:80:11:83:142 | :?(([a-zA-Z]{1})\|([a-zA-Z]{1}[a-zA-Z]{1})\|([a-zA-Z]{1}[0-9]{1})\|([0-9]{1}[a-zA-Z]{1})\|([a-zA-Z0-9][-_a-zA-Z0-9]{0,61}[a-zA-Z0-9]))\\. | KnownCVEs.py:80:141:83:142 | \\. | 2 | +| KnownCVEs.py:80:29:83:50 | [a-zA-Z]{1}[a-zA-Z]{1} | KnownCVEs.py:80:29:83:39 | [a-zA-Z]{1} | 0 | +| KnownCVEs.py:80:29:83:50 | [a-zA-Z]{1}[a-zA-Z]{1} | KnownCVEs.py:80:40:83:50 | [a-zA-Z]{1} | 1 | +| KnownCVEs.py:80:54:83:72 | [a-zA-Z]{1}[0-9]{1} | KnownCVEs.py:80:54:83:64 | [a-zA-Z]{1} | 0 | +| KnownCVEs.py:80:54:83:72 | [a-zA-Z]{1}[0-9]{1} | KnownCVEs.py:80:65:83:72 | [0-9]{1} | 1 | +| KnownCVEs.py:80:76:83:94 | [0-9]{1}[a-zA-Z]{1} | KnownCVEs.py:80:76:83:83 | [0-9]{1} | 0 | +| KnownCVEs.py:80:76:83:94 | [0-9]{1}[a-zA-Z]{1} | KnownCVEs.py:80:84:83:94 | [a-zA-Z]{1} | 1 | +| KnownCVEs.py:80:98:83:138 | [a-zA-Z0-9][-_a-zA-Z0-9]{0,61}[a-zA-Z0-9] | KnownCVEs.py:80:98:83:108 | [a-zA-Z0-9] | 0 | +| KnownCVEs.py:80:98:83:138 | [a-zA-Z0-9][-_a-zA-Z0-9]{0,61}[a-zA-Z0-9] | KnownCVEs.py:80:109:83:127 | [-_a-zA-Z0-9]{0,61} | 1 | +| KnownCVEs.py:80:98:83:138 | [a-zA-Z0-9][-_a-zA-Z0-9]{0,61}[a-zA-Z0-9] | KnownCVEs.py:80:128:83:138 | [a-zA-Z0-9] | 2 | +| KnownCVEs.py:80:162:83:182 | xn--[a-zA-Z0-9]{2,30} | KnownCVEs.py:80:162:83:162 | x | 0 | +| KnownCVEs.py:80:162:83:182 | xn--[a-zA-Z0-9]{2,30} | KnownCVEs.py:80:163:83:163 | n | 1 | +| KnownCVEs.py:80:162:83:182 | xn--[a-zA-Z0-9]{2,30} | KnownCVEs.py:80:164:83:164 | - | 2 | +| KnownCVEs.py:80:162:83:182 | xn--[a-zA-Z0-9]{2,30} | KnownCVEs.py:80:165:83:165 | - | 3 | +| KnownCVEs.py:80:162:83:182 | xn--[a-zA-Z0-9]{2,30} | KnownCVEs.py:80:166:83:182 | [a-zA-Z0-9]{2,30} | 4 | +| redos.py:6:23:6:46 | ^\\b_((?:__\|[\\s\\S])+?)_\\b | redos.py:6:23:6:23 | ^ | 0 | +| redos.py:6:23:6:46 | ^\\b_((?:__\|[\\s\\S])+?)_\\b | redos.py:6:24:6:25 | \\b | 1 | +| redos.py:6:23:6:46 | ^\\b_((?:__\|[\\s\\S])+?)_\\b | redos.py:6:26:6:26 | _ | 2 | +| redos.py:6:23:6:46 | ^\\b_((?:__\|[\\s\\S])+?)_\\b | redos.py:6:27:6:43 | ((?:__\|[\\s\\S])+?) | 3 | +| redos.py:6:23:6:46 | ^\\b_((?:__\|[\\s\\S])+?)_\\b | redos.py:6:44:6:44 | _ | 4 | +| redos.py:6:23:6:46 | ^\\b_((?:__\|[\\s\\S])+?)_\\b | redos.py:6:45:6:46 | \\b | 5 | +| redos.py:6:31:6:32 | __ | redos.py:6:31:6:31 | _ | 0 | +| redos.py:6:31:6:32 | __ | redos.py:6:32:6:32 | _ | 1 | +| redos.py:6:48:6:77 | ^\\*((?:\\*\\*\|[\\s\\S])+?)\\*(?!\\*) | redos.py:6:48:6:48 | ^ | 0 | +| redos.py:6:48:6:77 | ^\\*((?:\\*\\*\|[\\s\\S])+?)\\*(?!\\*) | redos.py:6:49:6:50 | \\* | 1 | +| redos.py:6:48:6:77 | ^\\*((?:\\*\\*\|[\\s\\S])+?)\\*(?!\\*) | redos.py:6:51:6:69 | ((?:\\*\\*\|[\\s\\S])+?) | 2 | +| redos.py:6:48:6:77 | ^\\*((?:\\*\\*\|[\\s\\S])+?)\\*(?!\\*) | redos.py:6:70:6:71 | \\* | 3 | +| redos.py:6:48:6:77 | ^\\*((?:\\*\\*\|[\\s\\S])+?)\\*(?!\\*) | redos.py:6:72:6:77 | (?!\\*) | 4 | +| redos.py:6:55:6:58 | \\*\\* | redos.py:6:55:6:56 | \\* | 0 | +| redos.py:6:55:6:58 | \\*\\* | redos.py:6:57:6:58 | \\* | 1 | +| redos.py:11:24:11:45 | ^\\b_((?:__\|[^_])+?)_\\b | redos.py:11:24:11:24 | ^ | 0 | +| redos.py:11:24:11:45 | ^\\b_((?:__\|[^_])+?)_\\b | redos.py:11:25:11:26 | \\b | 1 | +| redos.py:11:24:11:45 | ^\\b_((?:__\|[^_])+?)_\\b | redos.py:11:27:11:27 | _ | 2 | +| redos.py:11:24:11:45 | ^\\b_((?:__\|[^_])+?)_\\b | redos.py:11:28:11:42 | ((?:__\|[^_])+?) | 3 | +| redos.py:11:24:11:45 | ^\\b_((?:__\|[^_])+?)_\\b | redos.py:11:43:11:43 | _ | 4 | +| redos.py:11:24:11:45 | ^\\b_((?:__\|[^_])+?)_\\b | redos.py:11:44:11:45 | \\b | 5 | +| redos.py:11:32:11:33 | __ | redos.py:11:32:11:32 | _ | 0 | +| redos.py:11:32:11:33 | __ | redos.py:11:33:11:33 | _ | 1 | +| redos.py:11:47:11:74 | ^\\*((?:\\*\\*\|[^*])+?)\\*(?!\\*) | redos.py:11:47:11:47 | ^ | 0 | +| redos.py:11:47:11:74 | ^\\*((?:\\*\\*\|[^*])+?)\\*(?!\\*) | redos.py:11:48:11:49 | \\* | 1 | +| redos.py:11:47:11:74 | ^\\*((?:\\*\\*\|[^*])+?)\\*(?!\\*) | redos.py:11:50:11:66 | ((?:\\*\\*\|[^*])+?) | 2 | +| redos.py:11:47:11:74 | ^\\*((?:\\*\\*\|[^*])+?)\\*(?!\\*) | redos.py:11:67:11:68 | \\* | 3 | +| redos.py:11:47:11:74 | ^\\*((?:\\*\\*\|[^*])+?)\\*(?!\\*) | redos.py:11:69:11:74 | (?!\\*) | 4 | +| redos.py:11:54:11:57 | \\*\\* | redos.py:11:54:11:55 | \\* | 0 | +| redos.py:11:54:11:57 | \\*\\* | redos.py:11:56:11:57 | \\* | 1 | +| redos.py:16:24:16:31 | (.*,)+.+ | redos.py:16:24:16:29 | (.*,)+ | 0 | +| redos.py:16:24:16:31 | (.*,)+.+ | redos.py:16:30:16:31 | .+ | 1 | +| redos.py:16:25:16:27 | .*, | redos.py:16:25:16:26 | .* | 0 | +| redos.py:16:25:16:27 | .*, | redos.py:16:27:16:27 | , | 1 | +| redos.py:21:23:21:105 | ^(?:\\s+(?:"(?:[^"\\\\]\|\\\\\\\\\|\\\\.)+"\|'(?:[^'\\\\]\|\\\\\\\\\|\\\\.)+'\|\\((?:[^)\\\\]\|\\\\\\\\\|\\\\.)+\\)))? | redos.py:21:23:21:23 | ^ | 0 | +| redos.py:21:23:21:105 | ^(?:\\s+(?:"(?:[^"\\\\]\|\\\\\\\\\|\\\\.)+"\|'(?:[^'\\\\]\|\\\\\\\\\|\\\\.)+'\|\\((?:[^)\\\\]\|\\\\\\\\\|\\\\.)+\\)))? | redos.py:21:24:21:105 | (?:\\s+(?:"(?:[^"\\\\]\|\\\\\\\\\|\\\\.)+"\|'(?:[^'\\\\]\|\\\\\\\\\|\\\\.)+'\|\\((?:[^)\\\\]\|\\\\\\\\\|\\\\.)+\\)))? | 1 | +| redos.py:21:27:21:103 | \\s+(?:"(?:[^"\\\\]\|\\\\\\\\\|\\\\.)+"\|'(?:[^'\\\\]\|\\\\\\\\\|\\\\.)+'\|\\((?:[^)\\\\]\|\\\\\\\\\|\\\\.)+\\)) | redos.py:21:27:21:29 | \\s+ | 0 | +| redos.py:21:27:21:103 | \\s+(?:"(?:[^"\\\\]\|\\\\\\\\\|\\\\.)+"\|'(?:[^'\\\\]\|\\\\\\\\\|\\\\.)+'\|\\((?:[^)\\\\]\|\\\\\\\\\|\\\\.)+\\)) | redos.py:21:30:21:103 | (?:"(?:[^"\\\\]\|\\\\\\\\\|\\\\.)+"\|'(?:[^'\\\\]\|\\\\\\\\\|\\\\.)+'\|\\((?:[^)\\\\]\|\\\\\\\\\|\\\\.)+\\)) | 1 | +| redos.py:21:33:21:54 | "(?:[^"\\\\]\|\\\\\\\\\|\\\\.)+" | redos.py:21:33:21:33 | " | 0 | +| redos.py:21:33:21:54 | "(?:[^"\\\\]\|\\\\\\\\\|\\\\.)+" | redos.py:21:34:21:53 | (?:[^"\\\\]\|\\\\\\\\\|\\\\.)+ | 1 | +| redos.py:21:33:21:54 | "(?:[^"\\\\]\|\\\\\\\\\|\\\\.)+" | redos.py:21:54:21:54 | " | 2 | +| redos.py:21:44:21:47 | \\\\\\\\ | redos.py:21:44:21:45 | \\\\ | 0 | +| redos.py:21:44:21:47 | \\\\\\\\ | redos.py:21:46:21:47 | \\\\ | 1 | +| redos.py:21:49:21:51 | \\\\. | redos.py:21:49:21:50 | \\\\ | 0 | +| redos.py:21:49:21:51 | \\\\. | redos.py:21:51:21:51 | . | 1 | +| redos.py:21:56:21:77 | '(?:[^'\\\\]\|\\\\\\\\\|\\\\.)+' | redos.py:21:56:21:56 | ' | 0 | +| redos.py:21:56:21:77 | '(?:[^'\\\\]\|\\\\\\\\\|\\\\.)+' | redos.py:21:57:21:76 | (?:[^'\\\\]\|\\\\\\\\\|\\\\.)+ | 1 | +| redos.py:21:56:21:77 | '(?:[^'\\\\]\|\\\\\\\\\|\\\\.)+' | redos.py:21:77:21:77 | ' | 2 | +| redos.py:21:67:21:70 | \\\\\\\\ | redos.py:21:67:21:68 | \\\\ | 0 | +| redos.py:21:67:21:70 | \\\\\\\\ | redos.py:21:69:21:70 | \\\\ | 1 | +| redos.py:21:72:21:74 | \\\\. | redos.py:21:72:21:73 | \\\\ | 0 | +| redos.py:21:72:21:74 | \\\\. | redos.py:21:74:21:74 | . | 1 | +| redos.py:21:79:21:102 | \\((?:[^)\\\\]\|\\\\\\\\\|\\\\.)+\\) | redos.py:21:79:21:80 | \\( | 0 | +| redos.py:21:79:21:102 | \\((?:[^)\\\\]\|\\\\\\\\\|\\\\.)+\\) | redos.py:21:81:21:100 | (?:[^)\\\\]\|\\\\\\\\\|\\\\.)+ | 1 | +| redos.py:21:79:21:102 | \\((?:[^)\\\\]\|\\\\\\\\\|\\\\.)+\\) | redos.py:21:101:21:102 | \\) | 2 | +| redos.py:21:91:21:94 | \\\\\\\\ | redos.py:21:91:21:92 | \\\\ | 0 | +| redos.py:21:91:21:94 | \\\\\\\\ | redos.py:21:93:21:94 | \\\\ | 1 | +| redos.py:21:96:21:98 | \\\\. | redos.py:21:96:21:97 | \\\\ | 0 | +| redos.py:21:96:21:98 | \\\\. | redos.py:21:98:21:98 | . | 1 | +| redos.py:25:24:25:68 | \\(\\*(?:[\\s\\S]*?\\(\\*[\\s\\S]*?\\*\\))*[\\s\\S]*?\\*\\) | redos.py:25:24:25:25 | \\( | 0 | +| redos.py:25:24:25:68 | \\(\\*(?:[\\s\\S]*?\\(\\*[\\s\\S]*?\\*\\))*[\\s\\S]*?\\*\\) | redos.py:25:26:25:27 | \\* | 1 | +| redos.py:25:24:25:68 | \\(\\*(?:[\\s\\S]*?\\(\\*[\\s\\S]*?\\*\\))*[\\s\\S]*?\\*\\) | redos.py:25:28:25:56 | (?:[\\s\\S]*?\\(\\*[\\s\\S]*?\\*\\))* | 2 | +| redos.py:25:24:25:68 | \\(\\*(?:[\\s\\S]*?\\(\\*[\\s\\S]*?\\*\\))*[\\s\\S]*?\\*\\) | redos.py:25:57:25:64 | [\\s\\S]*? | 3 | +| redos.py:25:24:25:68 | \\(\\*(?:[\\s\\S]*?\\(\\*[\\s\\S]*?\\*\\))*[\\s\\S]*?\\*\\) | redos.py:25:65:25:66 | \\* | 4 | +| redos.py:25:24:25:68 | \\(\\*(?:[\\s\\S]*?\\(\\*[\\s\\S]*?\\*\\))*[\\s\\S]*?\\*\\) | redos.py:25:67:25:68 | \\) | 5 | +| redos.py:25:31:25:54 | [\\s\\S]*?\\(\\*[\\s\\S]*?\\*\\) | redos.py:25:31:25:38 | [\\s\\S]*? | 0 | +| redos.py:25:31:25:54 | [\\s\\S]*?\\(\\*[\\s\\S]*?\\*\\) | redos.py:25:39:25:40 | \\( | 1 | +| redos.py:25:31:25:54 | [\\s\\S]*?\\(\\*[\\s\\S]*?\\*\\) | redos.py:25:41:25:42 | \\* | 2 | +| redos.py:25:31:25:54 | [\\s\\S]*?\\(\\*[\\s\\S]*?\\*\\) | redos.py:25:43:25:50 | [\\s\\S]*? | 3 | +| redos.py:25:31:25:54 | [\\s\\S]*?\\(\\*[\\s\\S]*?\\*\\) | redos.py:25:51:25:52 | \\* | 4 | +| redos.py:25:31:25:54 | [\\s\\S]*?\\(\\*[\\s\\S]*?\\*\\) | redos.py:25:53:25:54 | \\) | 5 | +| redos.py:30:24:30:84 | ^ *(\\S.*\\\|.*)\\n *([-:]+ *\\\|[-\| :]*)\\n((?:.*\\\|.*(?:\\n\|$))*)\\n* | redos.py:30:24:30:24 | ^ | 0 | +| redos.py:30:24:30:84 | ^ *(\\S.*\\\|.*)\\n *([-:]+ *\\\|[-\| :]*)\\n((?:.*\\\|.*(?:\\n\|$))*)\\n* | redos.py:30:25:30:26 | * | 1 | +| redos.py:30:24:30:84 | ^ *(\\S.*\\\|.*)\\n *([-:]+ *\\\|[-\| :]*)\\n((?:.*\\\|.*(?:\\n\|$))*)\\n* | redos.py:30:27:30:36 | (\\S.*\\\|.*) | 2 | +| redos.py:30:24:30:84 | ^ *(\\S.*\\\|.*)\\n *([-:]+ *\\\|[-\| :]*)\\n((?:.*\\\|.*(?:\\n\|$))*)\\n* | redos.py:30:37:30:38 | \\n | 3 | +| redos.py:30:24:30:84 | ^ *(\\S.*\\\|.*)\\n *([-:]+ *\\\|[-\| :]*)\\n((?:.*\\\|.*(?:\\n\|$))*)\\n* | redos.py:30:39:30:40 | * | 4 | +| redos.py:30:24:30:84 | ^ *(\\S.*\\\|.*)\\n *([-:]+ *\\\|[-\| :]*)\\n((?:.*\\\|.*(?:\\n\|$))*)\\n* | redos.py:30:41:30:58 | ([-:]+ *\\\|[-\| :]*) | 5 | +| redos.py:30:24:30:84 | ^ *(\\S.*\\\|.*)\\n *([-:]+ *\\\|[-\| :]*)\\n((?:.*\\\|.*(?:\\n\|$))*)\\n* | redos.py:30:59:30:60 | \\n | 6 | +| redos.py:30:24:30:84 | ^ *(\\S.*\\\|.*)\\n *([-:]+ *\\\|[-\| :]*)\\n((?:.*\\\|.*(?:\\n\|$))*)\\n* | redos.py:30:61:30:81 | ((?:.*\\\|.*(?:\\n\|$))*) | 7 | +| redos.py:30:24:30:84 | ^ *(\\S.*\\\|.*)\\n *([-:]+ *\\\|[-\| :]*)\\n((?:.*\\\|.*(?:\\n\|$))*)\\n* | redos.py:30:82:30:84 | \\n* | 8 | +| redos.py:30:28:30:35 | \\S.*\\\|.* | redos.py:30:28:30:29 | \\S | 0 | +| redos.py:30:28:30:35 | \\S.*\\\|.* | redos.py:30:30:30:31 | .* | 1 | +| redos.py:30:28:30:35 | \\S.*\\\|.* | redos.py:30:32:30:33 | \\\| | 2 | +| redos.py:30:28:30:35 | \\S.*\\\|.* | redos.py:30:34:30:35 | .* | 3 | +| redos.py:30:42:30:57 | [-:]+ *\\\|[-\| :]* | redos.py:30:42:30:46 | [-:]+ | 0 | +| redos.py:30:42:30:57 | [-:]+ *\\\|[-\| :]* | redos.py:30:47:30:48 | * | 1 | +| redos.py:30:42:30:57 | [-:]+ *\\\|[-\| :]* | redos.py:30:49:30:50 | \\\| | 2 | +| redos.py:30:42:30:57 | [-:]+ *\\\|[-\| :]* | redos.py:30:51:30:57 | [-\| :]* | 3 | +| redos.py:30:65:30:78 | .*\\\|.*(?:\\n\|$) | redos.py:30:65:30:66 | .* | 0 | +| redos.py:30:65:30:78 | .*\\\|.*(?:\\n\|$) | redos.py:30:67:30:68 | \\\| | 1 | +| redos.py:30:65:30:78 | .*\\\|.*(?:\\n\|$) | redos.py:30:69:30:70 | .* | 2 | +| redos.py:30:65:30:78 | .*\\\|.*(?:\\n\|$) | redos.py:30:71:30:78 | (?:\\n\|$) | 3 | +| redos.py:33:23:33:81 | ^ *(\\S.*\\\|.*)\\n *([-:]+ *\\\|[-\| :]*)\\n((?:.*\\\|.*(?:\\n\|$))*)a | redos.py:33:23:33:23 | ^ | 0 | +| redos.py:33:23:33:81 | ^ *(\\S.*\\\|.*)\\n *([-:]+ *\\\|[-\| :]*)\\n((?:.*\\\|.*(?:\\n\|$))*)a | redos.py:33:24:33:25 | * | 1 | +| redos.py:33:23:33:81 | ^ *(\\S.*\\\|.*)\\n *([-:]+ *\\\|[-\| :]*)\\n((?:.*\\\|.*(?:\\n\|$))*)a | redos.py:33:26:33:35 | (\\S.*\\\|.*) | 2 | +| redos.py:33:23:33:81 | ^ *(\\S.*\\\|.*)\\n *([-:]+ *\\\|[-\| :]*)\\n((?:.*\\\|.*(?:\\n\|$))*)a | redos.py:33:36:33:37 | \\n | 3 | +| redos.py:33:23:33:81 | ^ *(\\S.*\\\|.*)\\n *([-:]+ *\\\|[-\| :]*)\\n((?:.*\\\|.*(?:\\n\|$))*)a | redos.py:33:38:33:39 | * | 4 | +| redos.py:33:23:33:81 | ^ *(\\S.*\\\|.*)\\n *([-:]+ *\\\|[-\| :]*)\\n((?:.*\\\|.*(?:\\n\|$))*)a | redos.py:33:40:33:57 | ([-:]+ *\\\|[-\| :]*) | 5 | +| redos.py:33:23:33:81 | ^ *(\\S.*\\\|.*)\\n *([-:]+ *\\\|[-\| :]*)\\n((?:.*\\\|.*(?:\\n\|$))*)a | redos.py:33:58:33:59 | \\n | 6 | +| redos.py:33:23:33:81 | ^ *(\\S.*\\\|.*)\\n *([-:]+ *\\\|[-\| :]*)\\n((?:.*\\\|.*(?:\\n\|$))*)a | redos.py:33:60:33:80 | ((?:.*\\\|.*(?:\\n\|$))*) | 7 | +| redos.py:33:23:33:81 | ^ *(\\S.*\\\|.*)\\n *([-:]+ *\\\|[-\| :]*)\\n((?:.*\\\|.*(?:\\n\|$))*)a | redos.py:33:81:33:81 | a | 8 | +| redos.py:33:27:33:34 | \\S.*\\\|.* | redos.py:33:27:33:28 | \\S | 0 | +| redos.py:33:27:33:34 | \\S.*\\\|.* | redos.py:33:29:33:30 | .* | 1 | +| redos.py:33:27:33:34 | \\S.*\\\|.* | redos.py:33:31:33:32 | \\\| | 2 | +| redos.py:33:27:33:34 | \\S.*\\\|.* | redos.py:33:33:33:34 | .* | 3 | +| redos.py:33:41:33:56 | [-:]+ *\\\|[-\| :]* | redos.py:33:41:33:45 | [-:]+ | 0 | +| redos.py:33:41:33:56 | [-:]+ *\\\|[-\| :]* | redos.py:33:46:33:47 | * | 1 | +| redos.py:33:41:33:56 | [-:]+ *\\\|[-\| :]* | redos.py:33:48:33:49 | \\\| | 2 | +| redos.py:33:41:33:56 | [-:]+ *\\\|[-\| :]* | redos.py:33:50:33:56 | [-\| :]* | 3 | +| redos.py:33:64:33:77 | .*\\\|.*(?:\\n\|$) | redos.py:33:64:33:65 | .* | 0 | +| redos.py:33:64:33:77 | .*\\\|.*(?:\\n\|$) | redos.py:33:66:33:67 | \\\| | 1 | +| redos.py:33:64:33:77 | .*\\\|.*(?:\\n\|$) | redos.py:33:68:33:69 | .* | 2 | +| redos.py:33:64:33:77 | .*\\\|.*(?:\\n\|$) | redos.py:33:70:33:77 | (?:\\n\|$) | 3 | +| redos.py:38:23:38:58 | \\/(?![ *])(\\\\\\/\|.)*?\\/[gim]*(?=\\W\|$) | redos.py:38:23:38:24 | \\/ | 0 | +| redos.py:38:23:38:58 | \\/(?![ *])(\\\\\\/\|.)*?\\/[gim]*(?=\\W\|$) | redos.py:38:25:38:32 | (?![ *]) | 1 | +| redos.py:38:23:38:58 | \\/(?![ *])(\\\\\\/\|.)*?\\/[gim]*(?=\\W\|$) | redos.py:38:33:38:42 | (\\\\\\/\|.)*? | 2 | +| redos.py:38:23:38:58 | \\/(?![ *])(\\\\\\/\|.)*?\\/[gim]*(?=\\W\|$) | redos.py:38:43:38:44 | \\/ | 3 | +| redos.py:38:23:38:58 | \\/(?![ *])(\\\\\\/\|.)*?\\/[gim]*(?=\\W\|$) | redos.py:38:45:38:50 | [gim]* | 4 | +| redos.py:38:23:38:58 | \\/(?![ *])(\\\\\\/\|.)*?\\/[gim]*(?=\\W\|$) | redos.py:38:51:38:58 | (?=\\W\|$) | 5 | +| redos.py:38:34:38:37 | \\\\\\/ | redos.py:38:34:38:35 | \\\\ | 0 | +| redos.py:38:34:38:37 | \\\\\\/ | redos.py:38:36:38:37 | \\/ | 1 | +| redos.py:43:23:43:41 | ^([\\s\\[\\{\\(]\|#.*)*$ | redos.py:43:23:43:23 | ^ | 0 | +| redos.py:43:23:43:41 | ^([\\s\\[\\{\\(]\|#.*)*$ | redos.py:43:24:43:40 | ([\\s\\[\\{\\(]\|#.*)* | 1 | +| redos.py:43:23:43:41 | ^([\\s\\[\\{\\(]\|#.*)*$ | redos.py:43:41:43:41 | $ | 2 | +| redos.py:43:36:43:38 | #.* | redos.py:43:36:43:36 | # | 0 | +| redos.py:43:36:43:38 | #.* | redos.py:43:37:43:38 | .* | 1 | +| redos.py:46:25:46:28 | \\r\\n | redos.py:46:25:46:26 | \\r | 0 | +| redos.py:46:25:46:28 | \\r\\n | redos.py:46:27:46:28 | \\n | 1 | +| redos.py:49:30:49:63 | ((?:[^"']\|".*?"\|'.*?')*?)([(,)]\|$) | redos.py:49:30:49:54 | ((?:[^"']\|".*?"\|'.*?')*?) | 0 | +| redos.py:49:30:49:63 | ((?:[^"']\|".*?"\|'.*?')*?)([(,)]\|$) | redos.py:49:55:49:63 | ([(,)]\|$) | 1 | +| redos.py:49:40:49:44 | ".*?" | redos.py:49:40:49:40 | " | 0 | +| redos.py:49:40:49:44 | ".*?" | redos.py:49:41:49:43 | .*? | 1 | +| redos.py:49:40:49:44 | ".*?" | redos.py:49:44:49:44 | " | 2 | +| redos.py:49:46:49:50 | '.*?' | redos.py:49:46:49:46 | ' | 0 | +| redos.py:49:46:49:50 | '.*?' | redos.py:49:47:49:49 | .*? | 1 | +| redos.py:49:46:49:50 | '.*?' | redos.py:49:50:49:50 | ' | 2 | +| redos.py:54:23:54:89 | ^[\\_$a-z][\\_$a-z0-9]*(\\[.*?\\])*(\\.[\\_$a-z][\\_$a-z0-9]*(\\[.*?\\])*)*$ | redos.py:54:23:54:23 | ^ | 0 | +| redos.py:54:23:54:89 | ^[\\_$a-z][\\_$a-z0-9]*(\\[.*?\\])*(\\.[\\_$a-z][\\_$a-z0-9]*(\\[.*?\\])*)*$ | redos.py:54:24:54:31 | [\\_$a-z] | 1 | +| redos.py:54:23:54:89 | ^[\\_$a-z][\\_$a-z0-9]*(\\[.*?\\])*(\\.[\\_$a-z][\\_$a-z0-9]*(\\[.*?\\])*)*$ | redos.py:54:32:54:43 | [\\_$a-z0-9]* | 2 | +| redos.py:54:23:54:89 | ^[\\_$a-z][\\_$a-z0-9]*(\\[.*?\\])*(\\.[\\_$a-z][\\_$a-z0-9]*(\\[.*?\\])*)*$ | redos.py:54:44:54:53 | (\\[.*?\\])* | 3 | +| redos.py:54:23:54:89 | ^[\\_$a-z][\\_$a-z0-9]*(\\[.*?\\])*(\\.[\\_$a-z][\\_$a-z0-9]*(\\[.*?\\])*)*$ | redos.py:54:54:54:88 | (\\.[\\_$a-z][\\_$a-z0-9]*(\\[.*?\\])*)* | 4 | +| redos.py:54:23:54:89 | ^[\\_$a-z][\\_$a-z0-9]*(\\[.*?\\])*(\\.[\\_$a-z][\\_$a-z0-9]*(\\[.*?\\])*)*$ | redos.py:54:89:54:89 | $ | 5 | +| redos.py:54:45:54:51 | \\[.*?\\] | redos.py:54:45:54:46 | \\[ | 0 | +| redos.py:54:45:54:51 | \\[.*?\\] | redos.py:54:47:54:49 | .*? | 1 | +| redos.py:54:45:54:51 | \\[.*?\\] | redos.py:54:50:54:51 | \\] | 2 | +| redos.py:54:55:54:86 | \\.[\\_$a-z][\\_$a-z0-9]*(\\[.*?\\])* | redos.py:54:55:54:56 | \\. | 0 | +| redos.py:54:55:54:86 | \\.[\\_$a-z][\\_$a-z0-9]*(\\[.*?\\])* | redos.py:54:57:54:64 | [\\_$a-z] | 1 | +| redos.py:54:55:54:86 | \\.[\\_$a-z][\\_$a-z0-9]*(\\[.*?\\])* | redos.py:54:65:54:76 | [\\_$a-z0-9]* | 2 | +| redos.py:54:55:54:86 | \\.[\\_$a-z][\\_$a-z0-9]*(\\[.*?\\])* | redos.py:54:77:54:86 | (\\[.*?\\])* | 3 | +| redos.py:54:78:54:84 | \\[.*?\\] | redos.py:54:78:54:79 | \\[ | 0 | +| redos.py:54:78:54:84 | \\[.*?\\] | redos.py:54:80:54:82 | .*? | 1 | +| redos.py:54:78:54:84 | \\[.*?\\] | redos.py:54:83:54:84 | \\] | 2 | +| redos.py:60:23:60:33 | ^([a-z]+)+$ | redos.py:60:23:60:23 | ^ | 0 | +| redos.py:60:23:60:33 | ^([a-z]+)+$ | redos.py:60:24:60:32 | ([a-z]+)+ | 1 | +| redos.py:60:23:60:33 | ^([a-z]+)+$ | redos.py:60:33:60:33 | $ | 2 | +| redos.py:61:23:61:33 | ^([a-z]*)*$ | redos.py:61:23:61:23 | ^ | 0 | +| redos.py:61:23:61:33 | ^([a-z]*)*$ | redos.py:61:24:61:32 | ([a-z]*)* | 1 | +| redos.py:61:23:61:33 | ^([a-z]*)*$ | redos.py:61:33:61:33 | $ | 2 | +| redos.py:62:23:62:132 | ^([a-zA-Z0-9])(([\\\\-.]\|[_]+)?([a-zA-Z0-9]+))*(@){1}[a-z0-9]+[.]{1}(([a-z]{2,3})\|([a-z]{2,3}[.]{1}[a-z]{2,3}))$ | redos.py:62:23:62:23 | ^ | 0 | +| redos.py:62:23:62:132 | ^([a-zA-Z0-9])(([\\\\-.]\|[_]+)?([a-zA-Z0-9]+))*(@){1}[a-z0-9]+[.]{1}(([a-z]{2,3})\|([a-z]{2,3}[.]{1}[a-z]{2,3}))$ | redos.py:62:24:62:36 | ([a-zA-Z0-9]) | 1 | +| redos.py:62:23:62:132 | ^([a-zA-Z0-9])(([\\\\-.]\|[_]+)?([a-zA-Z0-9]+))*(@){1}[a-z0-9]+[.]{1}(([a-z]{2,3})\|([a-z]{2,3}[.]{1}[a-z]{2,3}))$ | redos.py:62:37:62:67 | (([\\\\-.]\|[_]+)?([a-zA-Z0-9]+))* | 2 | +| redos.py:62:23:62:132 | ^([a-zA-Z0-9])(([\\\\-.]\|[_]+)?([a-zA-Z0-9]+))*(@){1}[a-z0-9]+[.]{1}(([a-z]{2,3})\|([a-z]{2,3}[.]{1}[a-z]{2,3}))$ | redos.py:62:68:62:73 | (@){1} | 3 | +| redos.py:62:23:62:132 | ^([a-zA-Z0-9])(([\\\\-.]\|[_]+)?([a-zA-Z0-9]+))*(@){1}[a-z0-9]+[.]{1}(([a-z]{2,3})\|([a-z]{2,3}[.]{1}[a-z]{2,3}))$ | redos.py:62:74:62:82 | [a-z0-9]+ | 4 | +| redos.py:62:23:62:132 | ^([a-zA-Z0-9])(([\\\\-.]\|[_]+)?([a-zA-Z0-9]+))*(@){1}[a-z0-9]+[.]{1}(([a-z]{2,3})\|([a-z]{2,3}[.]{1}[a-z]{2,3}))$ | redos.py:62:83:62:88 | [.]{1} | 5 | +| redos.py:62:23:62:132 | ^([a-zA-Z0-9])(([\\\\-.]\|[_]+)?([a-zA-Z0-9]+))*(@){1}[a-z0-9]+[.]{1}(([a-z]{2,3})\|([a-z]{2,3}[.]{1}[a-z]{2,3}))$ | redos.py:62:89:62:131 | (([a-z]{2,3})\|([a-z]{2,3}[.]{1}[a-z]{2,3})) | 6 | +| redos.py:62:23:62:132 | ^([a-zA-Z0-9])(([\\\\-.]\|[_]+)?([a-zA-Z0-9]+))*(@){1}[a-z0-9]+[.]{1}(([a-z]{2,3})\|([a-z]{2,3}[.]{1}[a-z]{2,3}))$ | redos.py:62:132:62:132 | $ | 7 | +| redos.py:62:38:62:65 | ([\\\\-.]\|[_]+)?([a-zA-Z0-9]+) | redos.py:62:38:62:51 | ([\\\\-.]\|[_]+)? | 0 | +| redos.py:62:38:62:65 | ([\\\\-.]\|[_]+)?([a-zA-Z0-9]+) | redos.py:62:52:62:65 | ([a-zA-Z0-9]+) | 1 | +| redos.py:62:104:62:129 | [a-z]{2,3}[.]{1}[a-z]{2,3} | redos.py:62:104:62:113 | [a-z]{2,3} | 0 | +| redos.py:62:104:62:129 | [a-z]{2,3}[.]{1}[a-z]{2,3} | redos.py:62:114:62:119 | [.]{1} | 1 | +| redos.py:62:104:62:129 | [a-z]{2,3}[.]{1}[a-z]{2,3} | redos.py:62:120:62:129 | [a-z]{2,3} | 2 | +| redos.py:63:24:63:50 | ^(([a-z])+.)+[A-Z]([a-z])+$ | redos.py:63:24:63:24 | ^ | 0 | +| redos.py:63:24:63:50 | ^(([a-z])+.)+[A-Z]([a-z])+$ | redos.py:63:25:63:36 | (([a-z])+.)+ | 1 | +| redos.py:63:24:63:50 | ^(([a-z])+.)+[A-Z]([a-z])+$ | redos.py:63:37:63:41 | [A-Z] | 2 | +| redos.py:63:24:63:50 | ^(([a-z])+.)+[A-Z]([a-z])+$ | redos.py:63:42:63:49 | ([a-z])+ | 3 | +| redos.py:63:24:63:50 | ^(([a-z])+.)+[A-Z]([a-z])+$ | redos.py:63:50:63:50 | $ | 4 | +| redos.py:63:26:63:34 | ([a-z])+. | redos.py:63:26:63:33 | ([a-z])+ | 0 | +| redos.py:63:26:63:34 | ([a-z])+. | redos.py:63:34:63:34 | . | 1 | +| redos.py:68:24:68:63 | (([\\w#:.~>+()\\s-]+\|\\*\|\\[.*?\\])+)\\s*(,\|$) | redos.py:68:24:68:55 | (([\\w#:.~>+()\\s-]+\|\\*\|\\[.*?\\])+) | 0 | +| redos.py:68:24:68:63 | (([\\w#:.~>+()\\s-]+\|\\*\|\\[.*?\\])+)\\s*(,\|$) | redos.py:68:56:68:58 | \\s* | 1 | +| redos.py:68:24:68:63 | (([\\w#:.~>+()\\s-]+\|\\*\|\\[.*?\\])+)\\s*(,\|$) | redos.py:68:59:68:63 | (,\|$) | 2 | +| redos.py:68:46:68:52 | \\[.*?\\] | redos.py:68:46:68:47 | \\[ | 0 | +| redos.py:68:46:68:52 | \\[.*?\\] | redos.py:68:48:68:50 | .*? | 1 | +| redos.py:68:46:68:52 | \\[.*?\\] | redos.py:68:51:68:52 | \\] | 2 | +| redos.py:73:24:73:38 | ("\|')(\\\\?.)*?\\1 | redos.py:73:24:73:28 | ("\|') | 0 | +| redos.py:73:24:73:38 | ("\|')(\\\\?.)*?\\1 | redos.py:73:29:73:36 | (\\\\?.)*? | 1 | +| redos.py:73:24:73:38 | ("\|')(\\\\?.)*?\\1 | redos.py:73:37:73:38 | \\1 | 2 | +| redos.py:73:30:73:33 | \\\\?. | redos.py:73:30:73:32 | \\\\? | 0 | +| redos.py:73:30:73:33 | \\\\?. | redos.py:73:33:73:33 | . | 1 | +| redos.py:76:24:76:32 | (b\|a?b)*c | redos.py:76:24:76:31 | (b\|a?b)* | 0 | +| redos.py:76:24:76:32 | (b\|a?b)*c | redos.py:76:32:76:32 | c | 1 | +| redos.py:76:27:76:29 | a?b | redos.py:76:27:76:28 | a? | 0 | +| redos.py:76:27:76:29 | a?b | redos.py:76:29:76:29 | b | 1 | +| redos.py:79:24:79:32 | (a\|aa?)*b | redos.py:79:24:79:31 | (a\|aa?)* | 0 | +| redos.py:79:24:79:32 | (a\|aa?)*b | redos.py:79:32:79:32 | b | 1 | +| redos.py:79:27:79:29 | aa? | redos.py:79:27:79:27 | a | 0 | +| redos.py:79:27:79:29 | aa? | redos.py:79:28:79:29 | a? | 1 | +| redos.py:82:24:82:31 | (.\|\\n)*! | redos.py:82:24:82:30 | (.\|\\n)* | 0 | +| redos.py:82:24:82:31 | (.\|\\n)*! | redos.py:82:31:82:31 | ! | 1 | +| redos.py:85:24:85:31 | (.\|\\n)*! | redos.py:85:24:85:30 | (.\|\\n)* | 0 | +| redos.py:85:24:85:31 | (.\|\\n)*! | redos.py:85:31:85:31 | ! | 1 | +| redos.py:91:24:91:32 | (a\|aa?)*b | redos.py:91:24:91:31 | (a\|aa?)* | 0 | +| redos.py:91:24:91:32 | (a\|aa?)*b | redos.py:91:32:91:32 | b | 1 | +| redos.py:91:27:91:29 | aa? | redos.py:91:27:91:27 | a | 0 | +| redos.py:91:27:91:29 | aa? | redos.py:91:28:91:29 | a? | 1 | +| redos.py:97:24:97:40 | (([\\s\\S]\|[^a])*)" | redos.py:97:24:97:39 | (([\\s\\S]\|[^a])*) | 0 | +| redos.py:97:24:97:40 | (([\\s\\S]\|[^a])*)" | redos.py:97:40:97:40 | " | 1 | +| redos.py:103:24:103:35 | ((.\|[^a])*)" | redos.py:103:24:103:34 | ((.\|[^a])*) | 0 | +| redos.py:103:24:103:35 | ((.\|[^a])*)" | redos.py:103:35:103:35 | " | 1 | +| redos.py:106:25:106:36 | ((a\|[^a])*)" | redos.py:106:25:106:35 | ((a\|[^a])*) | 0 | +| redos.py:106:25:106:36 | ((a\|[^a])*)" | redos.py:106:36:106:36 | " | 1 | +| redos.py:109:24:109:35 | ((b\|[^a])*)" | redos.py:109:24:109:34 | ((b\|[^a])*) | 0 | +| redos.py:109:24:109:35 | ((b\|[^a])*)" | redos.py:109:35:109:35 | " | 1 | +| redos.py:112:24:112:35 | ((G\|[^a])*)" | redos.py:112:24:112:34 | ((G\|[^a])*) | 0 | +| redos.py:112:24:112:35 | ((G\|[^a])*)" | redos.py:112:35:112:35 | " | 1 | +| redos.py:115:24:115:39 | (([0-9]\|[^a])*)" | redos.py:115:24:115:38 | (([0-9]\|[^a])*) | 0 | +| redos.py:115:24:115:39 | (([0-9]\|[^a])*)" | redos.py:115:39:115:39 | " | 1 | +| redos.py:118:27:118:117 | =(?:([!#\\$%&'\\*\\+\\-\\.\\^_`\\\|~0-9A-Za-z]+)\|"((?:\\\\[\\x00-\\x7f]\|[^\\x00-\\x08\\x0a-\\x1f\\x7f"])*)") | redos.py:118:27:118:27 | = | 0 | +| redos.py:118:27:118:117 | =(?:([!#\\$%&'\\*\\+\\-\\.\\^_`\\\|~0-9A-Za-z]+)\|"((?:\\\\[\\x00-\\x7f]\|[^\\x00-\\x08\\x0a-\\x1f\\x7f"])*)") | redos.py:118:28:118:117 | (?:([!#\\$%&'\\*\\+\\-\\.\\^_`\\\|~0-9A-Za-z]+)\|"((?:\\\\[\\x00-\\x7f]\|[^\\x00-\\x08\\x0a-\\x1f\\x7f"])*)") | 1 | +| redos.py:118:68:118:116 | "((?:\\\\[\\x00-\\x7f]\|[^\\x00-\\x08\\x0a-\\x1f\\x7f"])*)" | redos.py:118:68:118:68 | " | 0 | +| redos.py:118:68:118:116 | "((?:\\\\[\\x00-\\x7f]\|[^\\x00-\\x08\\x0a-\\x1f\\x7f"])*)" | redos.py:118:69:118:115 | ((?:\\\\[\\x00-\\x7f]\|[^\\x00-\\x08\\x0a-\\x1f\\x7f"])*) | 1 | +| redos.py:118:68:118:116 | "((?:\\\\[\\x00-\\x7f]\|[^\\x00-\\x08\\x0a-\\x1f\\x7f"])*)" | redos.py:118:116:118:116 | " | 2 | +| redos.py:118:73:118:85 | \\\\[\\x00-\\x7f] | redos.py:118:73:118:74 | \\\\ | 0 | +| redos.py:118:73:118:85 | \\\\[\\x00-\\x7f] | redos.py:118:75:118:85 | [\\x00-\\x7f] | 1 | +| redos.py:121:24:121:72 | "((?:\\\\[\\x00-\\x7f]\|[^\\x00-\\x08\\x0a-\\x1f\\x7f"])*)" | redos.py:121:24:121:24 | " | 0 | +| redos.py:121:24:121:72 | "((?:\\\\[\\x00-\\x7f]\|[^\\x00-\\x08\\x0a-\\x1f\\x7f"])*)" | redos.py:121:25:121:71 | ((?:\\\\[\\x00-\\x7f]\|[^\\x00-\\x08\\x0a-\\x1f\\x7f"])*) | 1 | +| redos.py:121:24:121:72 | "((?:\\\\[\\x00-\\x7f]\|[^\\x00-\\x08\\x0a-\\x1f\\x7f"])*)" | redos.py:121:72:121:72 | " | 2 | +| redos.py:121:29:121:41 | \\\\[\\x00-\\x7f] | redos.py:121:29:121:30 | \\\\ | 0 | +| redos.py:121:29:121:41 | \\\\[\\x00-\\x7f] | redos.py:121:31:121:41 | [\\x00-\\x7f] | 1 | +| redos.py:124:24:124:74 | "((?:\\\\[\\x00-\\x7f]\|[^\\x00-\\x08\\x0a-\\x1f\\x7f"\\\\])*)" | redos.py:124:24:124:24 | " | 0 | +| redos.py:124:24:124:74 | "((?:\\\\[\\x00-\\x7f]\|[^\\x00-\\x08\\x0a-\\x1f\\x7f"\\\\])*)" | redos.py:124:25:124:73 | ((?:\\\\[\\x00-\\x7f]\|[^\\x00-\\x08\\x0a-\\x1f\\x7f"\\\\])*) | 1 | +| redos.py:124:24:124:74 | "((?:\\\\[\\x00-\\x7f]\|[^\\x00-\\x08\\x0a-\\x1f\\x7f"\\\\])*)" | redos.py:124:74:124:74 | " | 2 | +| redos.py:124:29:124:41 | \\\\[\\x00-\\x7f] | redos.py:124:29:124:30 | \\\\ | 0 | +| redos.py:124:29:124:41 | \\\\[\\x00-\\x7f] | redos.py:124:31:124:41 | [\\x00-\\x7f] | 1 | +| redos.py:127:24:127:40 | (([a-z]\|[d-h])*)" | redos.py:127:24:127:39 | (([a-z]\|[d-h])*) | 0 | +| redos.py:127:24:127:40 | (([a-z]\|[d-h])*)" | redos.py:127:40:127:40 | " | 1 | +| redos.py:130:24:130:42 | (([^a-z]\|[^0-9])*)" | redos.py:130:24:130:41 | (([^a-z]\|[^0-9])*) | 0 | +| redos.py:130:24:130:42 | (([^a-z]\|[^0-9])*)" | redos.py:130:42:130:42 | " | 1 | +| redos.py:133:24:133:37 | ((\\d\|[0-9])*)" | redos.py:133:24:133:36 | ((\\d\|[0-9])*) | 0 | +| redos.py:133:24:133:37 | ((\\d\|[0-9])*)" | redos.py:133:37:133:37 | " | 1 | +| redos.py:136:24:136:34 | ((\\s\|\\s)*)" | redos.py:136:24:136:33 | ((\\s\|\\s)*) | 0 | +| redos.py:136:24:136:34 | ((\\s\|\\s)*)" | redos.py:136:34:136:34 | " | 1 | +| redos.py:139:24:139:33 | ((\\w\|G)*)" | redos.py:139:24:139:32 | ((\\w\|G)*) | 0 | +| redos.py:139:24:139:33 | ((\\w\|G)*)" | redos.py:139:33:139:33 | " | 1 | +| redos.py:142:25:142:35 | ((\\s\|\\d)*)" | redos.py:142:25:142:34 | ((\\s\|\\d)*) | 0 | +| redos.py:142:25:142:35 | ((\\s\|\\d)*)" | redos.py:142:35:142:35 | " | 1 | +| redos.py:145:24:145:34 | ((\\d\|\\w)*)" | redos.py:145:24:145:33 | ((\\d\|\\w)*) | 0 | +| redos.py:145:24:145:34 | ((\\d\|\\w)*)" | redos.py:145:34:145:34 | " | 1 | +| redos.py:148:24:148:33 | ((\\d\|5)*)" | redos.py:148:24:148:32 | ((\\d\|5)*) | 0 | +| redos.py:148:24:148:33 | ((\\d\|5)*)" | redos.py:148:33:148:33 | " | 1 | +| redos.py:151:24:151:36 | ((\\s\|[\\f])*)" | redos.py:151:24:151:35 | ((\\s\|[\\f])*) | 0 | +| redos.py:151:24:151:36 | ((\\s\|[\\f])*)" | redos.py:151:36:151:36 | " | 1 | +| redos.py:154:24:154:40 | ((\\s\|[\\v]\|\\\\v)*)" | redos.py:154:24:154:39 | ((\\s\|[\\v]\|\\\\v)*) | 0 | +| redos.py:154:24:154:40 | ((\\s\|[\\v]\|\\\\v)*)" | redos.py:154:40:154:40 | " | 1 | +| redos.py:154:34:154:36 | \\\\v | redos.py:154:34:154:35 | \\\\ | 0 | +| redos.py:154:34:154:36 | \\\\v | redos.py:154:36:154:36 | v | 1 | +| redos.py:157:24:157:36 | ((\\f\|[\\f])*)" | redos.py:157:24:157:35 | ((\\f\|[\\f])*) | 0 | +| redos.py:157:24:157:36 | ((\\f\|[\\f])*)" | redos.py:157:36:157:36 | " | 1 | +| redos.py:160:24:160:34 | ((\\W\|\\D)*)" | redos.py:160:24:160:33 | ((\\W\|\\D)*) | 0 | +| redos.py:160:24:160:34 | ((\\W\|\\D)*)" | redos.py:160:34:160:34 | " | 1 | +| redos.py:163:24:163:34 | ((\\S\|\\w)*)" | redos.py:163:24:163:33 | ((\\S\|\\w)*) | 0 | +| redos.py:163:24:163:34 | ((\\S\|\\w)*)" | redos.py:163:34:163:34 | " | 1 | +| redos.py:166:24:166:36 | ((\\S\|[\\w])*)" | redos.py:166:24:166:35 | ((\\S\|[\\w])*) | 0 | +| redos.py:166:24:166:36 | ((\\S\|[\\w])*)" | redos.py:166:36:166:36 | " | 1 | +| redos.py:169:24:169:39 | ((1s\|[\\da-z])*)" | redos.py:169:24:169:38 | ((1s\|[\\da-z])*) | 0 | +| redos.py:169:24:169:39 | ((1s\|[\\da-z])*)" | redos.py:169:39:169:39 | " | 1 | +| redos.py:169:26:169:27 | 1s | redos.py:169:26:169:26 | 1 | 0 | +| redos.py:169:26:169:27 | 1s | redos.py:169:27:169:27 | s | 1 | +| redos.py:172:24:172:35 | ((0\|[\\d])*)" | redos.py:172:24:172:34 | ((0\|[\\d])*) | 0 | +| redos.py:172:24:172:35 | ((0\|[\\d])*)" | redos.py:172:35:172:35 | " | 1 | +| redos.py:175:24:175:34 | (([\\d]+)*)" | redos.py:175:24:175:33 | (([\\d]+)*) | 0 | +| redos.py:175:24:175:34 | (([\\d]+)*)" | redos.py:175:34:175:34 | " | 1 | +| redos.py:178:26:178:35 | \\d+(X\\d+)? | redos.py:178:26:178:28 | \\d+ | 0 | +| redos.py:178:26:178:35 | \\d+(X\\d+)? | redos.py:178:29:178:35 | (X\\d+)? | 1 | +| redos.py:178:30:178:33 | X\\d+ | redos.py:178:30:178:30 | X | 0 | +| redos.py:178:30:178:33 | X\\d+ | redos.py:178:31:178:33 | \\d+ | 1 | +| redos.py:181:26:181:41 | [0-9]+(X[0-9]*)? | redos.py:181:26:181:31 | [0-9]+ | 0 | +| redos.py:181:26:181:41 | [0-9]+(X[0-9]*)? | redos.py:181:32:181:41 | (X[0-9]*)? | 1 | +| redos.py:181:33:181:39 | X[0-9]* | redos.py:181:33:181:33 | X | 0 | +| redos.py:181:33:181:39 | X[0-9]* | redos.py:181:34:181:39 | [0-9]* | 1 | +| redos.py:184:25:184:38 | ^([^>]+)*(>\|$) | redos.py:184:25:184:25 | ^ | 0 | +| redos.py:184:25:184:38 | ^([^>]+)*(>\|$) | redos.py:184:26:184:33 | ([^>]+)* | 1 | +| redos.py:184:25:184:38 | ^([^>]+)*(>\|$) | redos.py:184:34:184:38 | (>\|$) | 2 | +| redos.py:187:24:187:38 | ^([^>a]+)*(>\|$) | redos.py:187:24:187:24 | ^ | 0 | +| redos.py:187:24:187:38 | ^([^>a]+)*(>\|$) | redos.py:187:25:187:33 | ([^>a]+)* | 1 | +| redos.py:187:24:187:38 | ^([^>a]+)*(>\|$) | redos.py:187:34:187:38 | (>\|$) | 2 | +| redos.py:190:24:190:32 | (\\n\\s*)+$ | redos.py:190:24:190:31 | (\\n\\s*)+ | 0 | +| redos.py:190:24:190:32 | (\\n\\s*)+$ | redos.py:190:32:190:32 | $ | 1 | +| redos.py:190:25:190:29 | \\n\\s* | redos.py:190:25:190:26 | \\n | 0 | +| redos.py:190:25:190:29 | \\n\\s* | redos.py:190:27:190:29 | \\s* | 1 | +| redos.py:193:24:193:73 | ^(?:\\s+\|#.*\|\\(\\?#[^)]*\\))*(?:[?*+]\|{\\d+(?:,\\d*)?}) | redos.py:193:24:193:24 | ^ | 0 | +| redos.py:193:24:193:73 | ^(?:\\s+\|#.*\|\\(\\?#[^)]*\\))*(?:[?*+]\|{\\d+(?:,\\d*)?}) | redos.py:193:25:193:49 | (?:\\s+\|#.*\|\\(\\?#[^)]*\\))* | 1 | +| redos.py:193:24:193:73 | ^(?:\\s+\|#.*\|\\(\\?#[^)]*\\))*(?:[?*+]\|{\\d+(?:,\\d*)?}) | redos.py:193:50:193:73 | (?:[?*+]\|{\\d+(?:,\\d*)?}) | 2 | +| redos.py:193:32:193:34 | #.* | redos.py:193:32:193:32 | # | 0 | +| redos.py:193:32:193:34 | #.* | redos.py:193:33:193:34 | .* | 1 | +| redos.py:193:36:193:47 | \\(\\?#[^)]*\\) | redos.py:193:36:193:37 | \\( | 0 | +| redos.py:193:36:193:47 | \\(\\?#[^)]*\\) | redos.py:193:38:193:39 | \\? | 1 | +| redos.py:193:36:193:47 | \\(\\?#[^)]*\\) | redos.py:193:40:193:40 | # | 2 | +| redos.py:193:36:193:47 | \\(\\?#[^)]*\\) | redos.py:193:41:193:45 | [^)]* | 3 | +| redos.py:193:36:193:47 | \\(\\?#[^)]*\\) | redos.py:193:46:193:47 | \\) | 4 | +| redos.py:193:59:193:72 | {\\d+(?:,\\d*)?} | redos.py:193:59:193:59 | { | 0 | +| redos.py:193:59:193:72 | {\\d+(?:,\\d*)?} | redos.py:193:60:193:62 | \\d+ | 1 | +| redos.py:193:59:193:72 | {\\d+(?:,\\d*)?} | redos.py:193:63:193:71 | (?:,\\d*)? | 2 | +| redos.py:193:59:193:72 | {\\d+(?:,\\d*)?} | redos.py:193:72:193:72 | } | 3 | +| redos.py:193:66:193:69 | ,\\d* | redos.py:193:66:193:66 | , | 0 | +| redos.py:193:66:193:69 | ,\\d* | redos.py:193:67:193:69 | \\d* | 1 | +| redos.py:196:24:196:103 | \\{\\[\\s*([a-zA-Z]+)\\(([a-zA-Z]+)\\)((\\s*([a-zA-Z]+)\\: ?([ a-zA-Z{}]+),?)+)*\\s*\\]\\} | redos.py:196:24:196:25 | \\{ | 0 | +| redos.py:196:24:196:103 | \\{\\[\\s*([a-zA-Z]+)\\(([a-zA-Z]+)\\)((\\s*([a-zA-Z]+)\\: ?([ a-zA-Z{}]+),?)+)*\\s*\\]\\} | redos.py:196:26:196:27 | \\[ | 1 | +| redos.py:196:24:196:103 | \\{\\[\\s*([a-zA-Z]+)\\(([a-zA-Z]+)\\)((\\s*([a-zA-Z]+)\\: ?([ a-zA-Z{}]+),?)+)*\\s*\\]\\} | redos.py:196:28:196:30 | \\s* | 2 | +| redos.py:196:24:196:103 | \\{\\[\\s*([a-zA-Z]+)\\(([a-zA-Z]+)\\)((\\s*([a-zA-Z]+)\\: ?([ a-zA-Z{}]+),?)+)*\\s*\\]\\} | redos.py:196:31:196:41 | ([a-zA-Z]+) | 3 | +| redos.py:196:24:196:103 | \\{\\[\\s*([a-zA-Z]+)\\(([a-zA-Z]+)\\)((\\s*([a-zA-Z]+)\\: ?([ a-zA-Z{}]+),?)+)*\\s*\\]\\} | redos.py:196:42:196:43 | \\( | 4 | +| redos.py:196:24:196:103 | \\{\\[\\s*([a-zA-Z]+)\\(([a-zA-Z]+)\\)((\\s*([a-zA-Z]+)\\: ?([ a-zA-Z{}]+),?)+)*\\s*\\]\\} | redos.py:196:44:196:54 | ([a-zA-Z]+) | 5 | +| redos.py:196:24:196:103 | \\{\\[\\s*([a-zA-Z]+)\\(([a-zA-Z]+)\\)((\\s*([a-zA-Z]+)\\: ?([ a-zA-Z{}]+),?)+)*\\s*\\]\\} | redos.py:196:55:196:56 | \\) | 6 | +| redos.py:196:24:196:103 | \\{\\[\\s*([a-zA-Z]+)\\(([a-zA-Z]+)\\)((\\s*([a-zA-Z]+)\\: ?([ a-zA-Z{}]+),?)+)*\\s*\\]\\} | redos.py:196:57:196:96 | ((\\s*([a-zA-Z]+)\\: ?([ a-zA-Z{}]+),?)+)* | 7 | +| redos.py:196:24:196:103 | \\{\\[\\s*([a-zA-Z]+)\\(([a-zA-Z]+)\\)((\\s*([a-zA-Z]+)\\: ?([ a-zA-Z{}]+),?)+)*\\s*\\]\\} | redos.py:196:97:196:99 | \\s* | 8 | +| redos.py:196:24:196:103 | \\{\\[\\s*([a-zA-Z]+)\\(([a-zA-Z]+)\\)((\\s*([a-zA-Z]+)\\: ?([ a-zA-Z{}]+),?)+)*\\s*\\]\\} | redos.py:196:100:196:101 | \\] | 9 | +| redos.py:196:24:196:103 | \\{\\[\\s*([a-zA-Z]+)\\(([a-zA-Z]+)\\)((\\s*([a-zA-Z]+)\\: ?([ a-zA-Z{}]+),?)+)*\\s*\\]\\} | redos.py:196:102:196:103 | \\} | 10 | +| redos.py:196:59:196:92 | \\s*([a-zA-Z]+)\\: ?([ a-zA-Z{}]+),? | redos.py:196:59:196:61 | \\s* | 0 | +| redos.py:196:59:196:92 | \\s*([a-zA-Z]+)\\: ?([ a-zA-Z{}]+),? | redos.py:196:62:196:72 | ([a-zA-Z]+) | 1 | +| redos.py:196:59:196:92 | \\s*([a-zA-Z]+)\\: ?([ a-zA-Z{}]+),? | redos.py:196:73:196:74 | \\: | 2 | +| redos.py:196:59:196:92 | \\s*([a-zA-Z]+)\\: ?([ a-zA-Z{}]+),? | redos.py:196:75:196:76 | ? | 3 | +| redos.py:196:59:196:92 | \\s*([a-zA-Z]+)\\: ?([ a-zA-Z{}]+),? | redos.py:196:77:196:90 | ([ a-zA-Z{}]+) | 4 | +| redos.py:196:59:196:92 | \\s*([a-zA-Z]+)\\: ?([ a-zA-Z{}]+),? | redos.py:196:91:196:92 | ,? | 5 | +| redos.py:199:24:199:35 | (a+\|b+\|c+)*c | redos.py:199:24:199:34 | (a+\|b+\|c+)* | 0 | +| redos.py:199:24:199:35 | (a+\|b+\|c+)*c | redos.py:199:35:199:35 | c | 1 | +| redos.py:202:25:202:36 | ((a+a?)*)+b+ | redos.py:202:25:202:34 | ((a+a?)*)+ | 0 | +| redos.py:202:25:202:36 | ((a+a?)*)+b+ | redos.py:202:35:202:36 | b+ | 1 | +| redos.py:202:27:202:30 | a+a? | redos.py:202:27:202:28 | a+ | 0 | +| redos.py:202:27:202:30 | a+a? | redos.py:202:29:202:30 | a? | 1 | +| redos.py:205:24:205:32 | (a+)+bbbb | redos.py:205:24:205:28 | (a+)+ | 0 | +| redos.py:205:24:205:32 | (a+)+bbbb | redos.py:205:29:205:29 | b | 1 | +| redos.py:205:24:205:32 | (a+)+bbbb | redos.py:205:30:205:30 | b | 2 | +| redos.py:205:24:205:32 | (a+)+bbbb | redos.py:205:31:205:31 | b | 3 | +| redos.py:205:24:205:32 | (a+)+bbbb | redos.py:205:32:205:32 | b | 4 | +| redos.py:208:25:208:37 | (a+)+aaaaa*a+ | redos.py:208:25:208:29 | (a+)+ | 0 | +| redos.py:208:25:208:37 | (a+)+aaaaa*a+ | redos.py:208:30:208:30 | a | 1 | +| redos.py:208:25:208:37 | (a+)+aaaaa*a+ | redos.py:208:31:208:31 | a | 2 | +| redos.py:208:25:208:37 | (a+)+aaaaa*a+ | redos.py:208:32:208:32 | a | 3 | +| redos.py:208:25:208:37 | (a+)+aaaaa*a+ | redos.py:208:33:208:33 | a | 4 | +| redos.py:208:25:208:37 | (a+)+aaaaa*a+ | redos.py:208:34:208:35 | a* | 5 | +| redos.py:208:25:208:37 | (a+)+aaaaa*a+ | redos.py:208:36:208:37 | a+ | 6 | +| redos.py:211:24:211:34 | (a+)+aaaaa$ | redos.py:211:24:211:28 | (a+)+ | 0 | +| redos.py:211:24:211:34 | (a+)+aaaaa$ | redos.py:211:29:211:29 | a | 1 | +| redos.py:211:24:211:34 | (a+)+aaaaa$ | redos.py:211:30:211:30 | a | 2 | +| redos.py:211:24:211:34 | (a+)+aaaaa$ | redos.py:211:31:211:31 | a | 3 | +| redos.py:211:24:211:34 | (a+)+aaaaa$ | redos.py:211:32:211:32 | a | 4 | +| redos.py:211:24:211:34 | (a+)+aaaaa$ | redos.py:211:33:211:33 | a | 5 | +| redos.py:211:24:211:34 | (a+)+aaaaa$ | redos.py:211:34:211:34 | $ | 6 | +| redos.py:214:25:214:34 | (\\n+)+\\n\\n | redos.py:214:25:214:30 | (\\n+)+ | 0 | +| redos.py:214:25:214:34 | (\\n+)+\\n\\n | redos.py:214:31:214:32 | \\n | 1 | +| redos.py:214:25:214:34 | (\\n+)+\\n\\n | redos.py:214:33:214:34 | \\n | 2 | +| redos.py:217:24:217:34 | (\\n+)+\\n\\n$ | redos.py:217:24:217:29 | (\\n+)+ | 0 | +| redos.py:217:24:217:34 | (\\n+)+\\n\\n$ | redos.py:217:30:217:31 | \\n | 1 | +| redos.py:217:24:217:34 | (\\n+)+\\n\\n$ | redos.py:217:32:217:33 | \\n | 2 | +| redos.py:217:24:217:34 | (\\n+)+\\n\\n$ | redos.py:217:34:217:34 | $ | 3 | +| redos.py:220:24:220:32 | ([^X]+)*$ | redos.py:220:24:220:31 | ([^X]+)* | 0 | +| redos.py:220:24:220:32 | ([^X]+)*$ | redos.py:220:32:220:32 | $ | 1 | +| redos.py:223:24:223:35 | (([^X]b)+)*$ | redos.py:223:24:223:34 | (([^X]b)+)* | 0 | +| redos.py:223:24:223:35 | (([^X]b)+)*$ | redos.py:223:35:223:35 | $ | 1 | +| redos.py:223:26:223:30 | [^X]b | redos.py:223:26:223:29 | [^X] | 0 | +| redos.py:223:26:223:30 | [^X]b | redos.py:223:30:223:30 | b | 1 | +| redos.py:226:25:226:44 | (([^X]b)+)*($\|[^X]b) | redos.py:226:25:226:35 | (([^X]b)+)* | 0 | +| redos.py:226:25:226:44 | (([^X]b)+)*($\|[^X]b) | redos.py:226:36:226:44 | ($\|[^X]b) | 1 | +| redos.py:226:27:226:31 | [^X]b | redos.py:226:27:226:30 | [^X] | 0 | +| redos.py:226:27:226:31 | [^X]b | redos.py:226:31:226:31 | b | 1 | +| redos.py:226:39:226:43 | [^X]b | redos.py:226:39:226:42 | [^X] | 0 | +| redos.py:226:39:226:43 | [^X]b | redos.py:226:43:226:43 | b | 1 | +| redos.py:229:24:229:43 | (([^X]b)+)*($\|[^X]c) | redos.py:229:24:229:34 | (([^X]b)+)* | 0 | +| redos.py:229:24:229:43 | (([^X]b)+)*($\|[^X]c) | redos.py:229:35:229:43 | ($\|[^X]c) | 1 | +| redos.py:229:26:229:30 | [^X]b | redos.py:229:26:229:29 | [^X] | 0 | +| redos.py:229:26:229:30 | [^X]b | redos.py:229:30:229:30 | b | 1 | +| redos.py:229:38:229:42 | [^X]c | redos.py:229:38:229:41 | [^X] | 0 | +| redos.py:229:38:229:42 | [^X]c | redos.py:229:42:229:42 | c | 1 | +| redos.py:232:25:232:38 | ((ab)+)*ababab | redos.py:232:25:232:32 | ((ab)+)* | 0 | +| redos.py:232:25:232:38 | ((ab)+)*ababab | redos.py:232:33:232:33 | a | 1 | +| redos.py:232:25:232:38 | ((ab)+)*ababab | redos.py:232:34:232:34 | b | 2 | +| redos.py:232:25:232:38 | ((ab)+)*ababab | redos.py:232:35:232:35 | a | 3 | +| redos.py:232:25:232:38 | ((ab)+)*ababab | redos.py:232:36:232:36 | b | 4 | +| redos.py:232:25:232:38 | ((ab)+)*ababab | redos.py:232:37:232:37 | a | 5 | +| redos.py:232:25:232:38 | ((ab)+)*ababab | redos.py:232:38:232:38 | b | 6 | +| redos.py:232:27:232:28 | ab | redos.py:232:27:232:27 | a | 0 | +| redos.py:232:27:232:28 | ab | redos.py:232:28:232:28 | b | 1 | +| redos.py:235:25:235:46 | ((ab)+)*abab(ab)*(ab)+ | redos.py:235:25:235:32 | ((ab)+)* | 0 | +| redos.py:235:25:235:46 | ((ab)+)*abab(ab)*(ab)+ | redos.py:235:33:235:33 | a | 1 | +| redos.py:235:25:235:46 | ((ab)+)*abab(ab)*(ab)+ | redos.py:235:34:235:34 | b | 2 | +| redos.py:235:25:235:46 | ((ab)+)*abab(ab)*(ab)+ | redos.py:235:35:235:35 | a | 3 | +| redos.py:235:25:235:46 | ((ab)+)*abab(ab)*(ab)+ | redos.py:235:36:235:36 | b | 4 | +| redos.py:235:25:235:46 | ((ab)+)*abab(ab)*(ab)+ | redos.py:235:37:235:41 | (ab)* | 5 | +| redos.py:235:25:235:46 | ((ab)+)*abab(ab)*(ab)+ | redos.py:235:42:235:46 | (ab)+ | 6 | +| redos.py:235:27:235:28 | ab | redos.py:235:27:235:27 | a | 0 | +| redos.py:235:27:235:28 | ab | redos.py:235:28:235:28 | b | 1 | +| redos.py:235:38:235:39 | ab | redos.py:235:38:235:38 | a | 0 | +| redos.py:235:38:235:39 | ab | redos.py:235:39:235:39 | b | 1 | +| redos.py:235:43:235:44 | ab | redos.py:235:43:235:43 | a | 0 | +| redos.py:235:43:235:44 | ab | redos.py:235:44:235:44 | b | 1 | +| redos.py:238:27:238:28 | ab | redos.py:238:27:238:27 | a | 0 | +| redos.py:238:27:238:28 | ab | redos.py:238:28:238:28 | b | 1 | +| redos.py:241:24:241:32 | ((ab)+)*$ | redos.py:241:24:241:31 | ((ab)+)* | 0 | +| redos.py:241:24:241:32 | ((ab)+)*$ | redos.py:241:32:241:32 | $ | 1 | +| redos.py:241:26:241:27 | ab | redos.py:241:26:241:26 | a | 0 | +| redos.py:241:26:241:27 | ab | redos.py:241:27:241:27 | b | 1 | +| redos.py:244:25:244:56 | ((ab)+)*[a1][b1][a2][b2][a3][b3] | redos.py:244:25:244:32 | ((ab)+)* | 0 | +| redos.py:244:25:244:56 | ((ab)+)*[a1][b1][a2][b2][a3][b3] | redos.py:244:33:244:36 | [a1] | 1 | +| redos.py:244:25:244:56 | ((ab)+)*[a1][b1][a2][b2][a3][b3] | redos.py:244:37:244:40 | [b1] | 2 | +| redos.py:244:25:244:56 | ((ab)+)*[a1][b1][a2][b2][a3][b3] | redos.py:244:41:244:44 | [a2] | 3 | +| redos.py:244:25:244:56 | ((ab)+)*[a1][b1][a2][b2][a3][b3] | redos.py:244:45:244:48 | [b2] | 4 | +| redos.py:244:25:244:56 | ((ab)+)*[a1][b1][a2][b2][a3][b3] | redos.py:244:49:244:52 | [a3] | 5 | +| redos.py:244:25:244:56 | ((ab)+)*[a1][b1][a2][b2][a3][b3] | redos.py:244:53:244:56 | [b3] | 6 | +| redos.py:244:27:244:28 | ab | redos.py:244:27:244:27 | a | 0 | +| redos.py:244:27:244:28 | ab | redos.py:244:28:244:28 | b | 1 | +| redos.py:247:24:247:36 | ([\\n\\s]+)*(.) | redos.py:247:24:247:33 | ([\\n\\s]+)* | 0 | +| redos.py:247:24:247:36 | ([\\n\\s]+)*(.) | redos.py:247:34:247:36 | (.) | 1 | +| redos.py:250:26:250:30 | A*A*X | redos.py:250:26:250:27 | A* | 0 | +| redos.py:250:26:250:30 | A*A*X | redos.py:250:28:250:29 | A* | 1 | +| redos.py:250:26:250:30 | A*A*X | redos.py:250:30:250:30 | X | 2 | +| redos.py:256:24:256:102 | (\\w*foobarbaz\\w*foobarbaz\\w*foobarbaz\\w*foobarbaz\\s*foobarbaz\\d*foobarbaz\\w*)+- | redos.py:256:24:256:101 | (\\w*foobarbaz\\w*foobarbaz\\w*foobarbaz\\w*foobarbaz\\s*foobarbaz\\d*foobarbaz\\w*)+ | 0 | +| redos.py:256:24:256:102 | (\\w*foobarbaz\\w*foobarbaz\\w*foobarbaz\\w*foobarbaz\\s*foobarbaz\\d*foobarbaz\\w*)+- | redos.py:256:102:256:102 | - | 1 | +| redos.py:256:25:256:99 | \\w*foobarbaz\\w*foobarbaz\\w*foobarbaz\\w*foobarbaz\\s*foobarbaz\\d*foobarbaz\\w* | redos.py:256:25:256:27 | \\w* | 0 | +| redos.py:256:25:256:99 | \\w*foobarbaz\\w*foobarbaz\\w*foobarbaz\\w*foobarbaz\\s*foobarbaz\\d*foobarbaz\\w* | redos.py:256:28:256:28 | f | 1 | +| redos.py:256:25:256:99 | \\w*foobarbaz\\w*foobarbaz\\w*foobarbaz\\w*foobarbaz\\s*foobarbaz\\d*foobarbaz\\w* | redos.py:256:29:256:29 | o | 2 | +| redos.py:256:25:256:99 | \\w*foobarbaz\\w*foobarbaz\\w*foobarbaz\\w*foobarbaz\\s*foobarbaz\\d*foobarbaz\\w* | redos.py:256:30:256:30 | o | 3 | +| redos.py:256:25:256:99 | \\w*foobarbaz\\w*foobarbaz\\w*foobarbaz\\w*foobarbaz\\s*foobarbaz\\d*foobarbaz\\w* | redos.py:256:31:256:31 | b | 4 | +| redos.py:256:25:256:99 | \\w*foobarbaz\\w*foobarbaz\\w*foobarbaz\\w*foobarbaz\\s*foobarbaz\\d*foobarbaz\\w* | redos.py:256:32:256:32 | a | 5 | +| redos.py:256:25:256:99 | \\w*foobarbaz\\w*foobarbaz\\w*foobarbaz\\w*foobarbaz\\s*foobarbaz\\d*foobarbaz\\w* | redos.py:256:33:256:33 | r | 6 | +| redos.py:256:25:256:99 | \\w*foobarbaz\\w*foobarbaz\\w*foobarbaz\\w*foobarbaz\\s*foobarbaz\\d*foobarbaz\\w* | redos.py:256:34:256:34 | b | 7 | +| redos.py:256:25:256:99 | \\w*foobarbaz\\w*foobarbaz\\w*foobarbaz\\w*foobarbaz\\s*foobarbaz\\d*foobarbaz\\w* | redos.py:256:35:256:35 | a | 8 | +| redos.py:256:25:256:99 | \\w*foobarbaz\\w*foobarbaz\\w*foobarbaz\\w*foobarbaz\\s*foobarbaz\\d*foobarbaz\\w* | redos.py:256:36:256:36 | z | 9 | +| redos.py:256:25:256:99 | \\w*foobarbaz\\w*foobarbaz\\w*foobarbaz\\w*foobarbaz\\s*foobarbaz\\d*foobarbaz\\w* | redos.py:256:37:256:39 | \\w* | 10 | +| redos.py:256:25:256:99 | \\w*foobarbaz\\w*foobarbaz\\w*foobarbaz\\w*foobarbaz\\s*foobarbaz\\d*foobarbaz\\w* | redos.py:256:40:256:40 | f | 11 | +| redos.py:256:25:256:99 | \\w*foobarbaz\\w*foobarbaz\\w*foobarbaz\\w*foobarbaz\\s*foobarbaz\\d*foobarbaz\\w* | redos.py:256:41:256:41 | o | 12 | +| redos.py:256:25:256:99 | \\w*foobarbaz\\w*foobarbaz\\w*foobarbaz\\w*foobarbaz\\s*foobarbaz\\d*foobarbaz\\w* | redos.py:256:42:256:42 | o | 13 | +| redos.py:256:25:256:99 | \\w*foobarbaz\\w*foobarbaz\\w*foobarbaz\\w*foobarbaz\\s*foobarbaz\\d*foobarbaz\\w* | redos.py:256:43:256:43 | b | 14 | +| redos.py:256:25:256:99 | \\w*foobarbaz\\w*foobarbaz\\w*foobarbaz\\w*foobarbaz\\s*foobarbaz\\d*foobarbaz\\w* | redos.py:256:44:256:44 | a | 15 | +| redos.py:256:25:256:99 | \\w*foobarbaz\\w*foobarbaz\\w*foobarbaz\\w*foobarbaz\\s*foobarbaz\\d*foobarbaz\\w* | redos.py:256:45:256:45 | r | 16 | +| redos.py:256:25:256:99 | \\w*foobarbaz\\w*foobarbaz\\w*foobarbaz\\w*foobarbaz\\s*foobarbaz\\d*foobarbaz\\w* | redos.py:256:46:256:46 | b | 17 | +| redos.py:256:25:256:99 | \\w*foobarbaz\\w*foobarbaz\\w*foobarbaz\\w*foobarbaz\\s*foobarbaz\\d*foobarbaz\\w* | redos.py:256:47:256:47 | a | 18 | +| redos.py:256:25:256:99 | \\w*foobarbaz\\w*foobarbaz\\w*foobarbaz\\w*foobarbaz\\s*foobarbaz\\d*foobarbaz\\w* | redos.py:256:48:256:48 | z | 19 | +| redos.py:256:25:256:99 | \\w*foobarbaz\\w*foobarbaz\\w*foobarbaz\\w*foobarbaz\\s*foobarbaz\\d*foobarbaz\\w* | redos.py:256:49:256:51 | \\w* | 20 | +| redos.py:256:25:256:99 | \\w*foobarbaz\\w*foobarbaz\\w*foobarbaz\\w*foobarbaz\\s*foobarbaz\\d*foobarbaz\\w* | redos.py:256:52:256:52 | f | 21 | +| redos.py:256:25:256:99 | \\w*foobarbaz\\w*foobarbaz\\w*foobarbaz\\w*foobarbaz\\s*foobarbaz\\d*foobarbaz\\w* | redos.py:256:53:256:53 | o | 22 | +| redos.py:256:25:256:99 | \\w*foobarbaz\\w*foobarbaz\\w*foobarbaz\\w*foobarbaz\\s*foobarbaz\\d*foobarbaz\\w* | redos.py:256:54:256:54 | o | 23 | +| redos.py:256:25:256:99 | \\w*foobarbaz\\w*foobarbaz\\w*foobarbaz\\w*foobarbaz\\s*foobarbaz\\d*foobarbaz\\w* | redos.py:256:55:256:55 | b | 24 | +| redos.py:256:25:256:99 | \\w*foobarbaz\\w*foobarbaz\\w*foobarbaz\\w*foobarbaz\\s*foobarbaz\\d*foobarbaz\\w* | redos.py:256:56:256:56 | a | 25 | +| redos.py:256:25:256:99 | \\w*foobarbaz\\w*foobarbaz\\w*foobarbaz\\w*foobarbaz\\s*foobarbaz\\d*foobarbaz\\w* | redos.py:256:57:256:57 | r | 26 | +| redos.py:256:25:256:99 | \\w*foobarbaz\\w*foobarbaz\\w*foobarbaz\\w*foobarbaz\\s*foobarbaz\\d*foobarbaz\\w* | redos.py:256:58:256:58 | b | 27 | +| redos.py:256:25:256:99 | \\w*foobarbaz\\w*foobarbaz\\w*foobarbaz\\w*foobarbaz\\s*foobarbaz\\d*foobarbaz\\w* | redos.py:256:59:256:59 | a | 28 | +| redos.py:256:25:256:99 | \\w*foobarbaz\\w*foobarbaz\\w*foobarbaz\\w*foobarbaz\\s*foobarbaz\\d*foobarbaz\\w* | redos.py:256:60:256:60 | z | 29 | +| redos.py:256:25:256:99 | \\w*foobarbaz\\w*foobarbaz\\w*foobarbaz\\w*foobarbaz\\s*foobarbaz\\d*foobarbaz\\w* | redos.py:256:61:256:63 | \\w* | 30 | +| redos.py:256:25:256:99 | \\w*foobarbaz\\w*foobarbaz\\w*foobarbaz\\w*foobarbaz\\s*foobarbaz\\d*foobarbaz\\w* | redos.py:256:64:256:64 | f | 31 | +| redos.py:256:25:256:99 | \\w*foobarbaz\\w*foobarbaz\\w*foobarbaz\\w*foobarbaz\\s*foobarbaz\\d*foobarbaz\\w* | redos.py:256:65:256:65 | o | 32 | +| redos.py:256:25:256:99 | \\w*foobarbaz\\w*foobarbaz\\w*foobarbaz\\w*foobarbaz\\s*foobarbaz\\d*foobarbaz\\w* | redos.py:256:66:256:66 | o | 33 | +| redos.py:256:25:256:99 | \\w*foobarbaz\\w*foobarbaz\\w*foobarbaz\\w*foobarbaz\\s*foobarbaz\\d*foobarbaz\\w* | redos.py:256:67:256:67 | b | 34 | +| redos.py:256:25:256:99 | \\w*foobarbaz\\w*foobarbaz\\w*foobarbaz\\w*foobarbaz\\s*foobarbaz\\d*foobarbaz\\w* | redos.py:256:68:256:68 | a | 35 | +| redos.py:256:25:256:99 | \\w*foobarbaz\\w*foobarbaz\\w*foobarbaz\\w*foobarbaz\\s*foobarbaz\\d*foobarbaz\\w* | redos.py:256:69:256:69 | r | 36 | +| redos.py:256:25:256:99 | \\w*foobarbaz\\w*foobarbaz\\w*foobarbaz\\w*foobarbaz\\s*foobarbaz\\d*foobarbaz\\w* | redos.py:256:70:256:70 | b | 37 | +| redos.py:256:25:256:99 | \\w*foobarbaz\\w*foobarbaz\\w*foobarbaz\\w*foobarbaz\\s*foobarbaz\\d*foobarbaz\\w* | redos.py:256:71:256:71 | a | 38 | +| redos.py:256:25:256:99 | \\w*foobarbaz\\w*foobarbaz\\w*foobarbaz\\w*foobarbaz\\s*foobarbaz\\d*foobarbaz\\w* | redos.py:256:72:256:72 | z | 39 | +| redos.py:256:25:256:99 | \\w*foobarbaz\\w*foobarbaz\\w*foobarbaz\\w*foobarbaz\\s*foobarbaz\\d*foobarbaz\\w* | redos.py:256:73:256:75 | \\s* | 40 | +| redos.py:256:25:256:99 | \\w*foobarbaz\\w*foobarbaz\\w*foobarbaz\\w*foobarbaz\\s*foobarbaz\\d*foobarbaz\\w* | redos.py:256:76:256:76 | f | 41 | +| redos.py:256:25:256:99 | \\w*foobarbaz\\w*foobarbaz\\w*foobarbaz\\w*foobarbaz\\s*foobarbaz\\d*foobarbaz\\w* | redos.py:256:77:256:77 | o | 42 | +| redos.py:256:25:256:99 | \\w*foobarbaz\\w*foobarbaz\\w*foobarbaz\\w*foobarbaz\\s*foobarbaz\\d*foobarbaz\\w* | redos.py:256:78:256:78 | o | 43 | +| redos.py:256:25:256:99 | \\w*foobarbaz\\w*foobarbaz\\w*foobarbaz\\w*foobarbaz\\s*foobarbaz\\d*foobarbaz\\w* | redos.py:256:79:256:79 | b | 44 | +| redos.py:256:25:256:99 | \\w*foobarbaz\\w*foobarbaz\\w*foobarbaz\\w*foobarbaz\\s*foobarbaz\\d*foobarbaz\\w* | redos.py:256:80:256:80 | a | 45 | +| redos.py:256:25:256:99 | \\w*foobarbaz\\w*foobarbaz\\w*foobarbaz\\w*foobarbaz\\s*foobarbaz\\d*foobarbaz\\w* | redos.py:256:81:256:81 | r | 46 | +| redos.py:256:25:256:99 | \\w*foobarbaz\\w*foobarbaz\\w*foobarbaz\\w*foobarbaz\\s*foobarbaz\\d*foobarbaz\\w* | redos.py:256:82:256:82 | b | 47 | +| redos.py:256:25:256:99 | \\w*foobarbaz\\w*foobarbaz\\w*foobarbaz\\w*foobarbaz\\s*foobarbaz\\d*foobarbaz\\w* | redos.py:256:83:256:83 | a | 48 | +| redos.py:256:25:256:99 | \\w*foobarbaz\\w*foobarbaz\\w*foobarbaz\\w*foobarbaz\\s*foobarbaz\\d*foobarbaz\\w* | redos.py:256:84:256:84 | z | 49 | +| redos.py:256:25:256:99 | \\w*foobarbaz\\w*foobarbaz\\w*foobarbaz\\w*foobarbaz\\s*foobarbaz\\d*foobarbaz\\w* | redos.py:256:85:256:87 | \\d* | 50 | +| redos.py:256:25:256:99 | \\w*foobarbaz\\w*foobarbaz\\w*foobarbaz\\w*foobarbaz\\s*foobarbaz\\d*foobarbaz\\w* | redos.py:256:88:256:88 | f | 51 | +| redos.py:256:25:256:99 | \\w*foobarbaz\\w*foobarbaz\\w*foobarbaz\\w*foobarbaz\\s*foobarbaz\\d*foobarbaz\\w* | redos.py:256:89:256:89 | o | 52 | +| redos.py:256:25:256:99 | \\w*foobarbaz\\w*foobarbaz\\w*foobarbaz\\w*foobarbaz\\s*foobarbaz\\d*foobarbaz\\w* | redos.py:256:90:256:90 | o | 53 | +| redos.py:256:25:256:99 | \\w*foobarbaz\\w*foobarbaz\\w*foobarbaz\\w*foobarbaz\\s*foobarbaz\\d*foobarbaz\\w* | redos.py:256:91:256:91 | b | 54 | +| redos.py:256:25:256:99 | \\w*foobarbaz\\w*foobarbaz\\w*foobarbaz\\w*foobarbaz\\s*foobarbaz\\d*foobarbaz\\w* | redos.py:256:92:256:92 | a | 55 | +| redos.py:256:25:256:99 | \\w*foobarbaz\\w*foobarbaz\\w*foobarbaz\\w*foobarbaz\\s*foobarbaz\\d*foobarbaz\\w* | redos.py:256:93:256:93 | r | 56 | +| redos.py:256:25:256:99 | \\w*foobarbaz\\w*foobarbaz\\w*foobarbaz\\w*foobarbaz\\s*foobarbaz\\d*foobarbaz\\w* | redos.py:256:94:256:94 | b | 57 | +| redos.py:256:25:256:99 | \\w*foobarbaz\\w*foobarbaz\\w*foobarbaz\\w*foobarbaz\\s*foobarbaz\\d*foobarbaz\\w* | redos.py:256:95:256:95 | a | 58 | +| redos.py:256:25:256:99 | \\w*foobarbaz\\w*foobarbaz\\w*foobarbaz\\w*foobarbaz\\s*foobarbaz\\d*foobarbaz\\w* | redos.py:256:96:256:96 | z | 59 | +| redos.py:256:25:256:99 | \\w*foobarbaz\\w*foobarbaz\\w*foobarbaz\\w*foobarbaz\\s*foobarbaz\\d*foobarbaz\\w* | redos.py:256:97:256:99 | \\w* | 60 | +| redos.py:259:24:259:127 | (.thisisagoddamnlongstringforstresstestingthequery\|\\sthisisagoddamnlongstringforstresstestingthequery)*- | redos.py:259:24:259:126 | (.thisisagoddamnlongstringforstresstestingthequery\|\\sthisisagoddamnlongstringforstresstestingthequery)* | 0 | +| redos.py:259:24:259:127 | (.thisisagoddamnlongstringforstresstestingthequery\|\\sthisisagoddamnlongstringforstresstestingthequery)*- | redos.py:259:127:259:127 | - | 1 | +| redos.py:259:25:259:73 | .thisisagoddamnlongstringforstresstestingthequery | redos.py:259:25:259:25 | . | 0 | +| redos.py:259:25:259:73 | .thisisagoddamnlongstringforstresstestingthequery | redos.py:259:26:259:26 | t | 1 | +| redos.py:259:25:259:73 | .thisisagoddamnlongstringforstresstestingthequery | redos.py:259:27:259:27 | h | 2 | +| redos.py:259:25:259:73 | .thisisagoddamnlongstringforstresstestingthequery | redos.py:259:28:259:28 | i | 3 | +| redos.py:259:25:259:73 | .thisisagoddamnlongstringforstresstestingthequery | redos.py:259:29:259:29 | s | 4 | +| redos.py:259:25:259:73 | .thisisagoddamnlongstringforstresstestingthequery | redos.py:259:30:259:30 | i | 5 | +| redos.py:259:25:259:73 | .thisisagoddamnlongstringforstresstestingthequery | redos.py:259:31:259:31 | s | 6 | +| redos.py:259:25:259:73 | .thisisagoddamnlongstringforstresstestingthequery | redos.py:259:32:259:32 | a | 7 | +| redos.py:259:25:259:73 | .thisisagoddamnlongstringforstresstestingthequery | redos.py:259:33:259:33 | g | 8 | +| redos.py:259:25:259:73 | .thisisagoddamnlongstringforstresstestingthequery | redos.py:259:34:259:34 | o | 9 | +| redos.py:259:25:259:73 | .thisisagoddamnlongstringforstresstestingthequery | redos.py:259:35:259:35 | d | 10 | +| redos.py:259:25:259:73 | .thisisagoddamnlongstringforstresstestingthequery | redos.py:259:36:259:36 | d | 11 | +| redos.py:259:25:259:73 | .thisisagoddamnlongstringforstresstestingthequery | redos.py:259:37:259:37 | a | 12 | +| redos.py:259:25:259:73 | .thisisagoddamnlongstringforstresstestingthequery | redos.py:259:38:259:38 | m | 13 | +| redos.py:259:25:259:73 | .thisisagoddamnlongstringforstresstestingthequery | redos.py:259:39:259:39 | n | 14 | +| redos.py:259:25:259:73 | .thisisagoddamnlongstringforstresstestingthequery | redos.py:259:40:259:40 | l | 15 | +| redos.py:259:25:259:73 | .thisisagoddamnlongstringforstresstestingthequery | redos.py:259:41:259:41 | o | 16 | +| redos.py:259:25:259:73 | .thisisagoddamnlongstringforstresstestingthequery | redos.py:259:42:259:42 | n | 17 | +| redos.py:259:25:259:73 | .thisisagoddamnlongstringforstresstestingthequery | redos.py:259:43:259:43 | g | 18 | +| redos.py:259:25:259:73 | .thisisagoddamnlongstringforstresstestingthequery | redos.py:259:44:259:44 | s | 19 | +| redos.py:259:25:259:73 | .thisisagoddamnlongstringforstresstestingthequery | redos.py:259:45:259:45 | t | 20 | +| redos.py:259:25:259:73 | .thisisagoddamnlongstringforstresstestingthequery | redos.py:259:46:259:46 | r | 21 | +| redos.py:259:25:259:73 | .thisisagoddamnlongstringforstresstestingthequery | redos.py:259:47:259:47 | i | 22 | +| redos.py:259:25:259:73 | .thisisagoddamnlongstringforstresstestingthequery | redos.py:259:48:259:48 | n | 23 | +| redos.py:259:25:259:73 | .thisisagoddamnlongstringforstresstestingthequery | redos.py:259:49:259:49 | g | 24 | +| redos.py:259:25:259:73 | .thisisagoddamnlongstringforstresstestingthequery | redos.py:259:50:259:50 | f | 25 | +| redos.py:259:25:259:73 | .thisisagoddamnlongstringforstresstestingthequery | redos.py:259:51:259:51 | o | 26 | +| redos.py:259:25:259:73 | .thisisagoddamnlongstringforstresstestingthequery | redos.py:259:52:259:52 | r | 27 | +| redos.py:259:25:259:73 | .thisisagoddamnlongstringforstresstestingthequery | redos.py:259:53:259:53 | s | 28 | +| redos.py:259:25:259:73 | .thisisagoddamnlongstringforstresstestingthequery | redos.py:259:54:259:54 | t | 29 | +| redos.py:259:25:259:73 | .thisisagoddamnlongstringforstresstestingthequery | redos.py:259:55:259:55 | r | 30 | +| redos.py:259:25:259:73 | .thisisagoddamnlongstringforstresstestingthequery | redos.py:259:56:259:56 | e | 31 | +| redos.py:259:25:259:73 | .thisisagoddamnlongstringforstresstestingthequery | redos.py:259:57:259:57 | s | 32 | +| redos.py:259:25:259:73 | .thisisagoddamnlongstringforstresstestingthequery | redos.py:259:58:259:58 | s | 33 | +| redos.py:259:25:259:73 | .thisisagoddamnlongstringforstresstestingthequery | redos.py:259:59:259:59 | t | 34 | +| redos.py:259:25:259:73 | .thisisagoddamnlongstringforstresstestingthequery | redos.py:259:60:259:60 | e | 35 | +| redos.py:259:25:259:73 | .thisisagoddamnlongstringforstresstestingthequery | redos.py:259:61:259:61 | s | 36 | +| redos.py:259:25:259:73 | .thisisagoddamnlongstringforstresstestingthequery | redos.py:259:62:259:62 | t | 37 | +| redos.py:259:25:259:73 | .thisisagoddamnlongstringforstresstestingthequery | redos.py:259:63:259:63 | i | 38 | +| redos.py:259:25:259:73 | .thisisagoddamnlongstringforstresstestingthequery | redos.py:259:64:259:64 | n | 39 | +| redos.py:259:25:259:73 | .thisisagoddamnlongstringforstresstestingthequery | redos.py:259:65:259:65 | g | 40 | +| redos.py:259:25:259:73 | .thisisagoddamnlongstringforstresstestingthequery | redos.py:259:66:259:66 | t | 41 | +| redos.py:259:25:259:73 | .thisisagoddamnlongstringforstresstestingthequery | redos.py:259:67:259:67 | h | 42 | +| redos.py:259:25:259:73 | .thisisagoddamnlongstringforstresstestingthequery | redos.py:259:68:259:68 | e | 43 | +| redos.py:259:25:259:73 | .thisisagoddamnlongstringforstresstestingthequery | redos.py:259:69:259:69 | q | 44 | +| redos.py:259:25:259:73 | .thisisagoddamnlongstringforstresstestingthequery | redos.py:259:70:259:70 | u | 45 | +| redos.py:259:25:259:73 | .thisisagoddamnlongstringforstresstestingthequery | redos.py:259:71:259:71 | e | 46 | +| redos.py:259:25:259:73 | .thisisagoddamnlongstringforstresstestingthequery | redos.py:259:72:259:72 | r | 47 | +| redos.py:259:25:259:73 | .thisisagoddamnlongstringforstresstestingthequery | redos.py:259:73:259:73 | y | 48 | +| redos.py:259:75:259:124 | \\sthisisagoddamnlongstringforstresstestingthequery | redos.py:259:75:259:76 | \\s | 0 | +| redos.py:259:75:259:124 | \\sthisisagoddamnlongstringforstresstestingthequery | redos.py:259:77:259:77 | t | 1 | +| redos.py:259:75:259:124 | \\sthisisagoddamnlongstringforstresstestingthequery | redos.py:259:78:259:78 | h | 2 | +| redos.py:259:75:259:124 | \\sthisisagoddamnlongstringforstresstestingthequery | redos.py:259:79:259:79 | i | 3 | +| redos.py:259:75:259:124 | \\sthisisagoddamnlongstringforstresstestingthequery | redos.py:259:80:259:80 | s | 4 | +| redos.py:259:75:259:124 | \\sthisisagoddamnlongstringforstresstestingthequery | redos.py:259:81:259:81 | i | 5 | +| redos.py:259:75:259:124 | \\sthisisagoddamnlongstringforstresstestingthequery | redos.py:259:82:259:82 | s | 6 | +| redos.py:259:75:259:124 | \\sthisisagoddamnlongstringforstresstestingthequery | redos.py:259:83:259:83 | a | 7 | +| redos.py:259:75:259:124 | \\sthisisagoddamnlongstringforstresstestingthequery | redos.py:259:84:259:84 | g | 8 | +| redos.py:259:75:259:124 | \\sthisisagoddamnlongstringforstresstestingthequery | redos.py:259:85:259:85 | o | 9 | +| redos.py:259:75:259:124 | \\sthisisagoddamnlongstringforstresstestingthequery | redos.py:259:86:259:86 | d | 10 | +| redos.py:259:75:259:124 | \\sthisisagoddamnlongstringforstresstestingthequery | redos.py:259:87:259:87 | d | 11 | +| redos.py:259:75:259:124 | \\sthisisagoddamnlongstringforstresstestingthequery | redos.py:259:88:259:88 | a | 12 | +| redos.py:259:75:259:124 | \\sthisisagoddamnlongstringforstresstestingthequery | redos.py:259:89:259:89 | m | 13 | +| redos.py:259:75:259:124 | \\sthisisagoddamnlongstringforstresstestingthequery | redos.py:259:90:259:90 | n | 14 | +| redos.py:259:75:259:124 | \\sthisisagoddamnlongstringforstresstestingthequery | redos.py:259:91:259:91 | l | 15 | +| redos.py:259:75:259:124 | \\sthisisagoddamnlongstringforstresstestingthequery | redos.py:259:92:259:92 | o | 16 | +| redos.py:259:75:259:124 | \\sthisisagoddamnlongstringforstresstestingthequery | redos.py:259:93:259:93 | n | 17 | +| redos.py:259:75:259:124 | \\sthisisagoddamnlongstringforstresstestingthequery | redos.py:259:94:259:94 | g | 18 | +| redos.py:259:75:259:124 | \\sthisisagoddamnlongstringforstresstestingthequery | redos.py:259:95:259:95 | s | 19 | +| redos.py:259:75:259:124 | \\sthisisagoddamnlongstringforstresstestingthequery | redos.py:259:96:259:96 | t | 20 | +| redos.py:259:75:259:124 | \\sthisisagoddamnlongstringforstresstestingthequery | redos.py:259:97:259:97 | r | 21 | +| redos.py:259:75:259:124 | \\sthisisagoddamnlongstringforstresstestingthequery | redos.py:259:98:259:98 | i | 22 | +| redos.py:259:75:259:124 | \\sthisisagoddamnlongstringforstresstestingthequery | redos.py:259:99:259:99 | n | 23 | +| redos.py:259:75:259:124 | \\sthisisagoddamnlongstringforstresstestingthequery | redos.py:259:100:259:100 | g | 24 | +| redos.py:259:75:259:124 | \\sthisisagoddamnlongstringforstresstestingthequery | redos.py:259:101:259:101 | f | 25 | +| redos.py:259:75:259:124 | \\sthisisagoddamnlongstringforstresstestingthequery | redos.py:259:102:259:102 | o | 26 | +| redos.py:259:75:259:124 | \\sthisisagoddamnlongstringforstresstestingthequery | redos.py:259:103:259:103 | r | 27 | +| redos.py:259:75:259:124 | \\sthisisagoddamnlongstringforstresstestingthequery | redos.py:259:104:259:104 | s | 28 | +| redos.py:259:75:259:124 | \\sthisisagoddamnlongstringforstresstestingthequery | redos.py:259:105:259:105 | t | 29 | +| redos.py:259:75:259:124 | \\sthisisagoddamnlongstringforstresstestingthequery | redos.py:259:106:259:106 | r | 30 | +| redos.py:259:75:259:124 | \\sthisisagoddamnlongstringforstresstestingthequery | redos.py:259:107:259:107 | e | 31 | +| redos.py:259:75:259:124 | \\sthisisagoddamnlongstringforstresstestingthequery | redos.py:259:108:259:108 | s | 32 | +| redos.py:259:75:259:124 | \\sthisisagoddamnlongstringforstresstestingthequery | redos.py:259:109:259:109 | s | 33 | +| redos.py:259:75:259:124 | \\sthisisagoddamnlongstringforstresstestingthequery | redos.py:259:110:259:110 | t | 34 | +| redos.py:259:75:259:124 | \\sthisisagoddamnlongstringforstresstestingthequery | redos.py:259:111:259:111 | e | 35 | +| redos.py:259:75:259:124 | \\sthisisagoddamnlongstringforstresstestingthequery | redos.py:259:112:259:112 | s | 36 | +| redos.py:259:75:259:124 | \\sthisisagoddamnlongstringforstresstestingthequery | redos.py:259:113:259:113 | t | 37 | +| redos.py:259:75:259:124 | \\sthisisagoddamnlongstringforstresstestingthequery | redos.py:259:114:259:114 | i | 38 | +| redos.py:259:75:259:124 | \\sthisisagoddamnlongstringforstresstestingthequery | redos.py:259:115:259:115 | n | 39 | +| redos.py:259:75:259:124 | \\sthisisagoddamnlongstringforstresstestingthequery | redos.py:259:116:259:116 | g | 40 | +| redos.py:259:75:259:124 | \\sthisisagoddamnlongstringforstresstestingthequery | redos.py:259:117:259:117 | t | 41 | +| redos.py:259:75:259:124 | \\sthisisagoddamnlongstringforstresstestingthequery | redos.py:259:118:259:118 | h | 42 | +| redos.py:259:75:259:124 | \\sthisisagoddamnlongstringforstresstestingthequery | redos.py:259:119:259:119 | e | 43 | +| redos.py:259:75:259:124 | \\sthisisagoddamnlongstringforstresstestingthequery | redos.py:259:120:259:120 | q | 44 | +| redos.py:259:75:259:124 | \\sthisisagoddamnlongstringforstresstestingthequery | redos.py:259:121:259:121 | u | 45 | +| redos.py:259:75:259:124 | \\sthisisagoddamnlongstringforstresstestingthequery | redos.py:259:122:259:122 | e | 46 | +| redos.py:259:75:259:124 | \\sthisisagoddamnlongstringforstresstestingthequery | redos.py:259:123:259:123 | r | 47 | +| redos.py:259:75:259:124 | \\sthisisagoddamnlongstringforstresstestingthequery | redos.py:259:124:259:124 | y | 48 | +| redos.py:262:24:262:88 | (thisisagoddamnlongstringforstresstestingthequery\|this\\w+query)*- | redos.py:262:24:262:87 | (thisisagoddamnlongstringforstresstestingthequery\|this\\w+query)* | 0 | +| redos.py:262:24:262:88 | (thisisagoddamnlongstringforstresstestingthequery\|this\\w+query)*- | redos.py:262:88:262:88 | - | 1 | +| redos.py:262:25:262:72 | thisisagoddamnlongstringforstresstestingthequery | redos.py:262:25:262:25 | t | 0 | +| redos.py:262:25:262:72 | thisisagoddamnlongstringforstresstestingthequery | redos.py:262:26:262:26 | h | 1 | +| redos.py:262:25:262:72 | thisisagoddamnlongstringforstresstestingthequery | redos.py:262:27:262:27 | i | 2 | +| redos.py:262:25:262:72 | thisisagoddamnlongstringforstresstestingthequery | redos.py:262:28:262:28 | s | 3 | +| redos.py:262:25:262:72 | thisisagoddamnlongstringforstresstestingthequery | redos.py:262:29:262:29 | i | 4 | +| redos.py:262:25:262:72 | thisisagoddamnlongstringforstresstestingthequery | redos.py:262:30:262:30 | s | 5 | +| redos.py:262:25:262:72 | thisisagoddamnlongstringforstresstestingthequery | redos.py:262:31:262:31 | a | 6 | +| redos.py:262:25:262:72 | thisisagoddamnlongstringforstresstestingthequery | redos.py:262:32:262:32 | g | 7 | +| redos.py:262:25:262:72 | thisisagoddamnlongstringforstresstestingthequery | redos.py:262:33:262:33 | o | 8 | +| redos.py:262:25:262:72 | thisisagoddamnlongstringforstresstestingthequery | redos.py:262:34:262:34 | d | 9 | +| redos.py:262:25:262:72 | thisisagoddamnlongstringforstresstestingthequery | redos.py:262:35:262:35 | d | 10 | +| redos.py:262:25:262:72 | thisisagoddamnlongstringforstresstestingthequery | redos.py:262:36:262:36 | a | 11 | +| redos.py:262:25:262:72 | thisisagoddamnlongstringforstresstestingthequery | redos.py:262:37:262:37 | m | 12 | +| redos.py:262:25:262:72 | thisisagoddamnlongstringforstresstestingthequery | redos.py:262:38:262:38 | n | 13 | +| redos.py:262:25:262:72 | thisisagoddamnlongstringforstresstestingthequery | redos.py:262:39:262:39 | l | 14 | +| redos.py:262:25:262:72 | thisisagoddamnlongstringforstresstestingthequery | redos.py:262:40:262:40 | o | 15 | +| redos.py:262:25:262:72 | thisisagoddamnlongstringforstresstestingthequery | redos.py:262:41:262:41 | n | 16 | +| redos.py:262:25:262:72 | thisisagoddamnlongstringforstresstestingthequery | redos.py:262:42:262:42 | g | 17 | +| redos.py:262:25:262:72 | thisisagoddamnlongstringforstresstestingthequery | redos.py:262:43:262:43 | s | 18 | +| redos.py:262:25:262:72 | thisisagoddamnlongstringforstresstestingthequery | redos.py:262:44:262:44 | t | 19 | +| redos.py:262:25:262:72 | thisisagoddamnlongstringforstresstestingthequery | redos.py:262:45:262:45 | r | 20 | +| redos.py:262:25:262:72 | thisisagoddamnlongstringforstresstestingthequery | redos.py:262:46:262:46 | i | 21 | +| redos.py:262:25:262:72 | thisisagoddamnlongstringforstresstestingthequery | redos.py:262:47:262:47 | n | 22 | +| redos.py:262:25:262:72 | thisisagoddamnlongstringforstresstestingthequery | redos.py:262:48:262:48 | g | 23 | +| redos.py:262:25:262:72 | thisisagoddamnlongstringforstresstestingthequery | redos.py:262:49:262:49 | f | 24 | +| redos.py:262:25:262:72 | thisisagoddamnlongstringforstresstestingthequery | redos.py:262:50:262:50 | o | 25 | +| redos.py:262:25:262:72 | thisisagoddamnlongstringforstresstestingthequery | redos.py:262:51:262:51 | r | 26 | +| redos.py:262:25:262:72 | thisisagoddamnlongstringforstresstestingthequery | redos.py:262:52:262:52 | s | 27 | +| redos.py:262:25:262:72 | thisisagoddamnlongstringforstresstestingthequery | redos.py:262:53:262:53 | t | 28 | +| redos.py:262:25:262:72 | thisisagoddamnlongstringforstresstestingthequery | redos.py:262:54:262:54 | r | 29 | +| redos.py:262:25:262:72 | thisisagoddamnlongstringforstresstestingthequery | redos.py:262:55:262:55 | e | 30 | +| redos.py:262:25:262:72 | thisisagoddamnlongstringforstresstestingthequery | redos.py:262:56:262:56 | s | 31 | +| redos.py:262:25:262:72 | thisisagoddamnlongstringforstresstestingthequery | redos.py:262:57:262:57 | s | 32 | +| redos.py:262:25:262:72 | thisisagoddamnlongstringforstresstestingthequery | redos.py:262:58:262:58 | t | 33 | +| redos.py:262:25:262:72 | thisisagoddamnlongstringforstresstestingthequery | redos.py:262:59:262:59 | e | 34 | +| redos.py:262:25:262:72 | thisisagoddamnlongstringforstresstestingthequery | redos.py:262:60:262:60 | s | 35 | +| redos.py:262:25:262:72 | thisisagoddamnlongstringforstresstestingthequery | redos.py:262:61:262:61 | t | 36 | +| redos.py:262:25:262:72 | thisisagoddamnlongstringforstresstestingthequery | redos.py:262:62:262:62 | i | 37 | +| redos.py:262:25:262:72 | thisisagoddamnlongstringforstresstestingthequery | redos.py:262:63:262:63 | n | 38 | +| redos.py:262:25:262:72 | thisisagoddamnlongstringforstresstestingthequery | redos.py:262:64:262:64 | g | 39 | +| redos.py:262:25:262:72 | thisisagoddamnlongstringforstresstestingthequery | redos.py:262:65:262:65 | t | 40 | +| redos.py:262:25:262:72 | thisisagoddamnlongstringforstresstestingthequery | redos.py:262:66:262:66 | h | 41 | +| redos.py:262:25:262:72 | thisisagoddamnlongstringforstresstestingthequery | redos.py:262:67:262:67 | e | 42 | +| redos.py:262:25:262:72 | thisisagoddamnlongstringforstresstestingthequery | redos.py:262:68:262:68 | q | 43 | +| redos.py:262:25:262:72 | thisisagoddamnlongstringforstresstestingthequery | redos.py:262:69:262:69 | u | 44 | +| redos.py:262:25:262:72 | thisisagoddamnlongstringforstresstestingthequery | redos.py:262:70:262:70 | e | 45 | +| redos.py:262:25:262:72 | thisisagoddamnlongstringforstresstestingthequery | redos.py:262:71:262:71 | r | 46 | +| redos.py:262:25:262:72 | thisisagoddamnlongstringforstresstestingthequery | redos.py:262:72:262:72 | y | 47 | +| redos.py:262:74:262:85 | this\\w+query | redos.py:262:74:262:74 | t | 0 | +| redos.py:262:74:262:85 | this\\w+query | redos.py:262:75:262:75 | h | 1 | +| redos.py:262:74:262:85 | this\\w+query | redos.py:262:76:262:76 | i | 2 | +| redos.py:262:74:262:85 | this\\w+query | redos.py:262:77:262:77 | s | 3 | +| redos.py:262:74:262:85 | this\\w+query | redos.py:262:78:262:80 | \\w+ | 4 | +| redos.py:262:74:262:85 | this\\w+query | redos.py:262:81:262:81 | q | 5 | +| redos.py:262:74:262:85 | this\\w+query | redos.py:262:82:262:82 | u | 6 | +| redos.py:262:74:262:85 | this\\w+query | redos.py:262:83:262:83 | e | 7 | +| redos.py:262:74:262:85 | this\\w+query | redos.py:262:84:262:84 | r | 8 | +| redos.py:262:74:262:85 | this\\w+query | redos.py:262:85:262:85 | y | 9 | +| redos.py:265:25:265:128 | (thisisagoddamnlongstringforstresstestingthequery\|imanotherbutunrelatedstringcomparedtotheotherstring)*- | redos.py:265:25:265:127 | (thisisagoddamnlongstringforstresstestingthequery\|imanotherbutunrelatedstringcomparedtotheotherstring)* | 0 | +| redos.py:265:25:265:128 | (thisisagoddamnlongstringforstresstestingthequery\|imanotherbutunrelatedstringcomparedtotheotherstring)*- | redos.py:265:128:265:128 | - | 1 | +| redos.py:265:26:265:73 | thisisagoddamnlongstringforstresstestingthequery | redos.py:265:26:265:26 | t | 0 | +| redos.py:265:26:265:73 | thisisagoddamnlongstringforstresstestingthequery | redos.py:265:27:265:27 | h | 1 | +| redos.py:265:26:265:73 | thisisagoddamnlongstringforstresstestingthequery | redos.py:265:28:265:28 | i | 2 | +| redos.py:265:26:265:73 | thisisagoddamnlongstringforstresstestingthequery | redos.py:265:29:265:29 | s | 3 | +| redos.py:265:26:265:73 | thisisagoddamnlongstringforstresstestingthequery | redos.py:265:30:265:30 | i | 4 | +| redos.py:265:26:265:73 | thisisagoddamnlongstringforstresstestingthequery | redos.py:265:31:265:31 | s | 5 | +| redos.py:265:26:265:73 | thisisagoddamnlongstringforstresstestingthequery | redos.py:265:32:265:32 | a | 6 | +| redos.py:265:26:265:73 | thisisagoddamnlongstringforstresstestingthequery | redos.py:265:33:265:33 | g | 7 | +| redos.py:265:26:265:73 | thisisagoddamnlongstringforstresstestingthequery | redos.py:265:34:265:34 | o | 8 | +| redos.py:265:26:265:73 | thisisagoddamnlongstringforstresstestingthequery | redos.py:265:35:265:35 | d | 9 | +| redos.py:265:26:265:73 | thisisagoddamnlongstringforstresstestingthequery | redos.py:265:36:265:36 | d | 10 | +| redos.py:265:26:265:73 | thisisagoddamnlongstringforstresstestingthequery | redos.py:265:37:265:37 | a | 11 | +| redos.py:265:26:265:73 | thisisagoddamnlongstringforstresstestingthequery | redos.py:265:38:265:38 | m | 12 | +| redos.py:265:26:265:73 | thisisagoddamnlongstringforstresstestingthequery | redos.py:265:39:265:39 | n | 13 | +| redos.py:265:26:265:73 | thisisagoddamnlongstringforstresstestingthequery | redos.py:265:40:265:40 | l | 14 | +| redos.py:265:26:265:73 | thisisagoddamnlongstringforstresstestingthequery | redos.py:265:41:265:41 | o | 15 | +| redos.py:265:26:265:73 | thisisagoddamnlongstringforstresstestingthequery | redos.py:265:42:265:42 | n | 16 | +| redos.py:265:26:265:73 | thisisagoddamnlongstringforstresstestingthequery | redos.py:265:43:265:43 | g | 17 | +| redos.py:265:26:265:73 | thisisagoddamnlongstringforstresstestingthequery | redos.py:265:44:265:44 | s | 18 | +| redos.py:265:26:265:73 | thisisagoddamnlongstringforstresstestingthequery | redos.py:265:45:265:45 | t | 19 | +| redos.py:265:26:265:73 | thisisagoddamnlongstringforstresstestingthequery | redos.py:265:46:265:46 | r | 20 | +| redos.py:265:26:265:73 | thisisagoddamnlongstringforstresstestingthequery | redos.py:265:47:265:47 | i | 21 | +| redos.py:265:26:265:73 | thisisagoddamnlongstringforstresstestingthequery | redos.py:265:48:265:48 | n | 22 | +| redos.py:265:26:265:73 | thisisagoddamnlongstringforstresstestingthequery | redos.py:265:49:265:49 | g | 23 | +| redos.py:265:26:265:73 | thisisagoddamnlongstringforstresstestingthequery | redos.py:265:50:265:50 | f | 24 | +| redos.py:265:26:265:73 | thisisagoddamnlongstringforstresstestingthequery | redos.py:265:51:265:51 | o | 25 | +| redos.py:265:26:265:73 | thisisagoddamnlongstringforstresstestingthequery | redos.py:265:52:265:52 | r | 26 | +| redos.py:265:26:265:73 | thisisagoddamnlongstringforstresstestingthequery | redos.py:265:53:265:53 | s | 27 | +| redos.py:265:26:265:73 | thisisagoddamnlongstringforstresstestingthequery | redos.py:265:54:265:54 | t | 28 | +| redos.py:265:26:265:73 | thisisagoddamnlongstringforstresstestingthequery | redos.py:265:55:265:55 | r | 29 | +| redos.py:265:26:265:73 | thisisagoddamnlongstringforstresstestingthequery | redos.py:265:56:265:56 | e | 30 | +| redos.py:265:26:265:73 | thisisagoddamnlongstringforstresstestingthequery | redos.py:265:57:265:57 | s | 31 | +| redos.py:265:26:265:73 | thisisagoddamnlongstringforstresstestingthequery | redos.py:265:58:265:58 | s | 32 | +| redos.py:265:26:265:73 | thisisagoddamnlongstringforstresstestingthequery | redos.py:265:59:265:59 | t | 33 | +| redos.py:265:26:265:73 | thisisagoddamnlongstringforstresstestingthequery | redos.py:265:60:265:60 | e | 34 | +| redos.py:265:26:265:73 | thisisagoddamnlongstringforstresstestingthequery | redos.py:265:61:265:61 | s | 35 | +| redos.py:265:26:265:73 | thisisagoddamnlongstringforstresstestingthequery | redos.py:265:62:265:62 | t | 36 | +| redos.py:265:26:265:73 | thisisagoddamnlongstringforstresstestingthequery | redos.py:265:63:265:63 | i | 37 | +| redos.py:265:26:265:73 | thisisagoddamnlongstringforstresstestingthequery | redos.py:265:64:265:64 | n | 38 | +| redos.py:265:26:265:73 | thisisagoddamnlongstringforstresstestingthequery | redos.py:265:65:265:65 | g | 39 | +| redos.py:265:26:265:73 | thisisagoddamnlongstringforstresstestingthequery | redos.py:265:66:265:66 | t | 40 | +| redos.py:265:26:265:73 | thisisagoddamnlongstringforstresstestingthequery | redos.py:265:67:265:67 | h | 41 | +| redos.py:265:26:265:73 | thisisagoddamnlongstringforstresstestingthequery | redos.py:265:68:265:68 | e | 42 | +| redos.py:265:26:265:73 | thisisagoddamnlongstringforstresstestingthequery | redos.py:265:69:265:69 | q | 43 | +| redos.py:265:26:265:73 | thisisagoddamnlongstringforstresstestingthequery | redos.py:265:70:265:70 | u | 44 | +| redos.py:265:26:265:73 | thisisagoddamnlongstringforstresstestingthequery | redos.py:265:71:265:71 | e | 45 | +| redos.py:265:26:265:73 | thisisagoddamnlongstringforstresstestingthequery | redos.py:265:72:265:72 | r | 46 | +| redos.py:265:26:265:73 | thisisagoddamnlongstringforstresstestingthequery | redos.py:265:73:265:73 | y | 47 | +| redos.py:265:75:265:125 | imanotherbutunrelatedstringcomparedtotheotherstring | redos.py:265:75:265:75 | i | 0 | +| redos.py:265:75:265:125 | imanotherbutunrelatedstringcomparedtotheotherstring | redos.py:265:76:265:76 | m | 1 | +| redos.py:265:75:265:125 | imanotherbutunrelatedstringcomparedtotheotherstring | redos.py:265:77:265:77 | a | 2 | +| redos.py:265:75:265:125 | imanotherbutunrelatedstringcomparedtotheotherstring | redos.py:265:78:265:78 | n | 3 | +| redos.py:265:75:265:125 | imanotherbutunrelatedstringcomparedtotheotherstring | redos.py:265:79:265:79 | o | 4 | +| redos.py:265:75:265:125 | imanotherbutunrelatedstringcomparedtotheotherstring | redos.py:265:80:265:80 | t | 5 | +| redos.py:265:75:265:125 | imanotherbutunrelatedstringcomparedtotheotherstring | redos.py:265:81:265:81 | h | 6 | +| redos.py:265:75:265:125 | imanotherbutunrelatedstringcomparedtotheotherstring | redos.py:265:82:265:82 | e | 7 | +| redos.py:265:75:265:125 | imanotherbutunrelatedstringcomparedtotheotherstring | redos.py:265:83:265:83 | r | 8 | +| redos.py:265:75:265:125 | imanotherbutunrelatedstringcomparedtotheotherstring | redos.py:265:84:265:84 | b | 9 | +| redos.py:265:75:265:125 | imanotherbutunrelatedstringcomparedtotheotherstring | redos.py:265:85:265:85 | u | 10 | +| redos.py:265:75:265:125 | imanotherbutunrelatedstringcomparedtotheotherstring | redos.py:265:86:265:86 | t | 11 | +| redos.py:265:75:265:125 | imanotherbutunrelatedstringcomparedtotheotherstring | redos.py:265:87:265:87 | u | 12 | +| redos.py:265:75:265:125 | imanotherbutunrelatedstringcomparedtotheotherstring | redos.py:265:88:265:88 | n | 13 | +| redos.py:265:75:265:125 | imanotherbutunrelatedstringcomparedtotheotherstring | redos.py:265:89:265:89 | r | 14 | +| redos.py:265:75:265:125 | imanotherbutunrelatedstringcomparedtotheotherstring | redos.py:265:90:265:90 | e | 15 | +| redos.py:265:75:265:125 | imanotherbutunrelatedstringcomparedtotheotherstring | redos.py:265:91:265:91 | l | 16 | +| redos.py:265:75:265:125 | imanotherbutunrelatedstringcomparedtotheotherstring | redos.py:265:92:265:92 | a | 17 | +| redos.py:265:75:265:125 | imanotherbutunrelatedstringcomparedtotheotherstring | redos.py:265:93:265:93 | t | 18 | +| redos.py:265:75:265:125 | imanotherbutunrelatedstringcomparedtotheotherstring | redos.py:265:94:265:94 | e | 19 | +| redos.py:265:75:265:125 | imanotherbutunrelatedstringcomparedtotheotherstring | redos.py:265:95:265:95 | d | 20 | +| redos.py:265:75:265:125 | imanotherbutunrelatedstringcomparedtotheotherstring | redos.py:265:96:265:96 | s | 21 | +| redos.py:265:75:265:125 | imanotherbutunrelatedstringcomparedtotheotherstring | redos.py:265:97:265:97 | t | 22 | +| redos.py:265:75:265:125 | imanotherbutunrelatedstringcomparedtotheotherstring | redos.py:265:98:265:98 | r | 23 | +| redos.py:265:75:265:125 | imanotherbutunrelatedstringcomparedtotheotherstring | redos.py:265:99:265:99 | i | 24 | +| redos.py:265:75:265:125 | imanotherbutunrelatedstringcomparedtotheotherstring | redos.py:265:100:265:100 | n | 25 | +| redos.py:265:75:265:125 | imanotherbutunrelatedstringcomparedtotheotherstring | redos.py:265:101:265:101 | g | 26 | +| redos.py:265:75:265:125 | imanotherbutunrelatedstringcomparedtotheotherstring | redos.py:265:102:265:102 | c | 27 | +| redos.py:265:75:265:125 | imanotherbutunrelatedstringcomparedtotheotherstring | redos.py:265:103:265:103 | o | 28 | +| redos.py:265:75:265:125 | imanotherbutunrelatedstringcomparedtotheotherstring | redos.py:265:104:265:104 | m | 29 | +| redos.py:265:75:265:125 | imanotherbutunrelatedstringcomparedtotheotherstring | redos.py:265:105:265:105 | p | 30 | +| redos.py:265:75:265:125 | imanotherbutunrelatedstringcomparedtotheotherstring | redos.py:265:106:265:106 | a | 31 | +| redos.py:265:75:265:125 | imanotherbutunrelatedstringcomparedtotheotherstring | redos.py:265:107:265:107 | r | 32 | +| redos.py:265:75:265:125 | imanotherbutunrelatedstringcomparedtotheotherstring | redos.py:265:108:265:108 | e | 33 | +| redos.py:265:75:265:125 | imanotherbutunrelatedstringcomparedtotheotherstring | redos.py:265:109:265:109 | d | 34 | +| redos.py:265:75:265:125 | imanotherbutunrelatedstringcomparedtotheotherstring | redos.py:265:110:265:110 | t | 35 | +| redos.py:265:75:265:125 | imanotherbutunrelatedstringcomparedtotheotherstring | redos.py:265:111:265:111 | o | 36 | +| redos.py:265:75:265:125 | imanotherbutunrelatedstringcomparedtotheotherstring | redos.py:265:112:265:112 | t | 37 | +| redos.py:265:75:265:125 | imanotherbutunrelatedstringcomparedtotheotherstring | redos.py:265:113:265:113 | h | 38 | +| redos.py:265:75:265:125 | imanotherbutunrelatedstringcomparedtotheotherstring | redos.py:265:114:265:114 | e | 39 | +| redos.py:265:75:265:125 | imanotherbutunrelatedstringcomparedtotheotherstring | redos.py:265:115:265:115 | o | 40 | +| redos.py:265:75:265:125 | imanotherbutunrelatedstringcomparedtotheotherstring | redos.py:265:116:265:116 | t | 41 | +| redos.py:265:75:265:125 | imanotherbutunrelatedstringcomparedtotheotherstring | redos.py:265:117:265:117 | h | 42 | +| redos.py:265:75:265:125 | imanotherbutunrelatedstringcomparedtotheotherstring | redos.py:265:118:265:118 | e | 43 | +| redos.py:265:75:265:125 | imanotherbutunrelatedstringcomparedtotheotherstring | redos.py:265:119:265:119 | r | 44 | +| redos.py:265:75:265:125 | imanotherbutunrelatedstringcomparedtotheotherstring | redos.py:265:120:265:120 | s | 45 | +| redos.py:265:75:265:125 | imanotherbutunrelatedstringcomparedtotheotherstring | redos.py:265:121:265:121 | t | 46 | +| redos.py:265:75:265:125 | imanotherbutunrelatedstringcomparedtotheotherstring | redos.py:265:122:265:122 | r | 47 | +| redos.py:265:75:265:125 | imanotherbutunrelatedstringcomparedtotheotherstring | redos.py:265:123:265:123 | i | 48 | +| redos.py:265:75:265:125 | imanotherbutunrelatedstringcomparedtotheotherstring | redos.py:265:124:265:124 | n | 49 | +| redos.py:265:75:265:125 | imanotherbutunrelatedstringcomparedtotheotherstring | redos.py:265:125:265:125 | g | 50 | +| redos.py:268:25:268:62 | foo([\\uDC66\\uDC67]\|[\\uDC68\\uDC69])*foo | redos.py:268:25:268:25 | f | 0 | +| redos.py:268:25:268:62 | foo([\\uDC66\\uDC67]\|[\\uDC68\\uDC69])*foo | redos.py:268:26:268:26 | o | 1 | +| redos.py:268:25:268:62 | foo([\\uDC66\\uDC67]\|[\\uDC68\\uDC69])*foo | redos.py:268:27:268:27 | o | 2 | +| redos.py:268:25:268:62 | foo([\\uDC66\\uDC67]\|[\\uDC68\\uDC69])*foo | redos.py:268:28:268:59 | ([\\uDC66\\uDC67]\|[\\uDC68\\uDC69])* | 3 | +| redos.py:268:25:268:62 | foo([\\uDC66\\uDC67]\|[\\uDC68\\uDC69])*foo | redos.py:268:60:268:60 | f | 4 | +| redos.py:268:25:268:62 | foo([\\uDC66\\uDC67]\|[\\uDC68\\uDC69])*foo | redos.py:268:61:268:61 | o | 5 | +| redos.py:268:25:268:62 | foo([\\uDC66\\uDC67]\|[\\uDC68\\uDC69])*foo | redos.py:268:62:268:62 | o | 6 | +| redos.py:271:25:271:64 | foo((\\uDC66\|\\uDC67)\|(\\uDC68\|\\uDC69))*foo | redos.py:271:25:271:25 | f | 0 | +| redos.py:271:25:271:64 | foo((\\uDC66\|\\uDC67)\|(\\uDC68\|\\uDC69))*foo | redos.py:271:26:271:26 | o | 1 | +| redos.py:271:25:271:64 | foo((\\uDC66\|\\uDC67)\|(\\uDC68\|\\uDC69))*foo | redos.py:271:27:271:27 | o | 2 | +| redos.py:271:25:271:64 | foo((\\uDC66\|\\uDC67)\|(\\uDC68\|\\uDC69))*foo | redos.py:271:28:271:61 | ((\\uDC66\|\\uDC67)\|(\\uDC68\|\\uDC69))* | 3 | +| redos.py:271:25:271:64 | foo((\\uDC66\|\\uDC67)\|(\\uDC68\|\\uDC69))*foo | redos.py:271:62:271:62 | f | 4 | +| redos.py:271:25:271:64 | foo((\\uDC66\|\\uDC67)\|(\\uDC68\|\\uDC69))*foo | redos.py:271:63:271:63 | o | 5 | +| redos.py:271:25:271:64 | foo((\\uDC66\|\\uDC67)\|(\\uDC68\|\\uDC69))*foo | redos.py:271:64:271:64 | o | 6 | +| redos.py:274:24:274:35 | a{2,3}(b+)+X | redos.py:274:24:274:29 | a{2,3} | 0 | +| redos.py:274:24:274:35 | a{2,3}(b+)+X | redos.py:274:30:274:34 | (b+)+ | 1 | +| redos.py:274:24:274:35 | a{2,3}(b+)+X | redos.py:274:35:274:35 | X | 2 | +| redos.py:277:24:277:99 | ^<(\\w+)((?:\\s+\\w+(?:\\s*=\\s*(?:(?:"[^"]*")\|(?:'[^']*')\|[^>\\s]+))?)*)\\s*(\\/?)> | redos.py:277:24:277:24 | ^ | 0 | +| redos.py:277:24:277:99 | ^<(\\w+)((?:\\s+\\w+(?:\\s*=\\s*(?:(?:"[^"]*")\|(?:'[^']*')\|[^>\\s]+))?)*)\\s*(\\/?)> | redos.py:277:25:277:25 | < | 1 | +| redos.py:277:24:277:99 | ^<(\\w+)((?:\\s+\\w+(?:\\s*=\\s*(?:(?:"[^"]*")\|(?:'[^']*')\|[^>\\s]+))?)*)\\s*(\\/?)> | redos.py:277:26:277:30 | (\\w+) | 2 | +| redos.py:277:24:277:99 | ^<(\\w+)((?:\\s+\\w+(?:\\s*=\\s*(?:(?:"[^"]*")\|(?:'[^']*')\|[^>\\s]+))?)*)\\s*(\\/?)> | redos.py:277:31:277:90 | ((?:\\s+\\w+(?:\\s*=\\s*(?:(?:"[^"]*")\|(?:'[^']*')\|[^>\\s]+))?)*) | 3 | +| redos.py:277:24:277:99 | ^<(\\w+)((?:\\s+\\w+(?:\\s*=\\s*(?:(?:"[^"]*")\|(?:'[^']*')\|[^>\\s]+))?)*)\\s*(\\/?)> | redos.py:277:91:277:93 | \\s* | 4 | +| redos.py:277:24:277:99 | ^<(\\w+)((?:\\s+\\w+(?:\\s*=\\s*(?:(?:"[^"]*")\|(?:'[^']*')\|[^>\\s]+))?)*)\\s*(\\/?)> | redos.py:277:94:277:98 | (\\/?) | 5 | +| redos.py:277:24:277:99 | ^<(\\w+)((?:\\s+\\w+(?:\\s*=\\s*(?:(?:"[^"]*")\|(?:'[^']*')\|[^>\\s]+))?)*)\\s*(\\/?)> | redos.py:277:99:277:99 | > | 6 | +| redos.py:277:35:277:87 | \\s+\\w+(?:\\s*=\\s*(?:(?:"[^"]*")\|(?:'[^']*')\|[^>\\s]+))? | redos.py:277:35:277:37 | \\s+ | 0 | +| redos.py:277:35:277:87 | \\s+\\w+(?:\\s*=\\s*(?:(?:"[^"]*")\|(?:'[^']*')\|[^>\\s]+))? | redos.py:277:38:277:40 | \\w+ | 1 | +| redos.py:277:35:277:87 | \\s+\\w+(?:\\s*=\\s*(?:(?:"[^"]*")\|(?:'[^']*')\|[^>\\s]+))? | redos.py:277:41:277:87 | (?:\\s*=\\s*(?:(?:"[^"]*")\|(?:'[^']*')\|[^>\\s]+))? | 2 | +| redos.py:277:44:277:85 | \\s*=\\s*(?:(?:"[^"]*")\|(?:'[^']*')\|[^>\\s]+) | redos.py:277:44:277:46 | \\s* | 0 | +| redos.py:277:44:277:85 | \\s*=\\s*(?:(?:"[^"]*")\|(?:'[^']*')\|[^>\\s]+) | redos.py:277:47:277:47 | = | 1 | +| redos.py:277:44:277:85 | \\s*=\\s*(?:(?:"[^"]*")\|(?:'[^']*')\|[^>\\s]+) | redos.py:277:48:277:50 | \\s* | 2 | +| redos.py:277:44:277:85 | \\s*=\\s*(?:(?:"[^"]*")\|(?:'[^']*')\|[^>\\s]+) | redos.py:277:51:277:85 | (?:(?:"[^"]*")\|(?:'[^']*')\|[^>\\s]+) | 3 | +| redos.py:277:57:277:63 | "[^"]*" | redos.py:277:57:277:57 | " | 0 | +| redos.py:277:57:277:63 | "[^"]*" | redos.py:277:58:277:62 | [^"]* | 1 | +| redos.py:277:57:277:63 | "[^"]*" | redos.py:277:63:277:63 | " | 2 | +| redos.py:277:69:277:75 | '[^']*' | redos.py:277:69:277:69 | ' | 0 | +| redos.py:277:69:277:75 | '[^']*' | redos.py:277:70:277:74 | [^']* | 1 | +| redos.py:277:69:277:75 | '[^']*' | redos.py:277:75:277:75 | ' | 2 | +| redos.py:280:25:280:48 | (a+)*[\\s\\S][\\s\\S][\\s\\S]? | redos.py:280:25:280:29 | (a+)* | 0 | +| redos.py:280:25:280:48 | (a+)*[\\s\\S][\\s\\S][\\s\\S]? | redos.py:280:30:280:35 | [\\s\\S] | 1 | +| redos.py:280:25:280:48 | (a+)*[\\s\\S][\\s\\S][\\s\\S]? | redos.py:280:36:280:41 | [\\s\\S] | 2 | +| redos.py:280:25:280:48 | (a+)*[\\s\\S][\\s\\S][\\s\\S]? | redos.py:280:42:280:48 | [\\s\\S]? | 3 | +| redos.py:283:25:283:40 | (a+)*[\\s\\S]{2,3} | redos.py:283:25:283:29 | (a+)* | 0 | +| redos.py:283:25:283:40 | (a+)*[\\s\\S]{2,3} | redos.py:283:30:283:40 | [\\s\\S]{2,3} | 1 | +| redos.py:286:25:286:44 | (a+)*([\\s\\S]{2,}\|X)$ | redos.py:286:25:286:29 | (a+)* | 0 | +| redos.py:286:25:286:44 | (a+)*([\\s\\S]{2,}\|X)$ | redos.py:286:30:286:43 | ([\\s\\S]{2,}\|X) | 1 | +| redos.py:286:25:286:44 | (a+)*([\\s\\S]{2,}\|X)$ | redos.py:286:44:286:44 | $ | 2 | +| redos.py:289:25:289:41 | (a+)*([\\s\\S]*\|X)$ | redos.py:289:25:289:29 | (a+)* | 0 | +| redos.py:289:25:289:41 | (a+)*([\\s\\S]*\|X)$ | redos.py:289:30:289:40 | ([\\s\\S]*\|X) | 1 | +| redos.py:289:25:289:41 | (a+)*([\\s\\S]*\|X)$ | redos.py:289:41:289:41 | $ | 2 | +| redos.py:292:25:292:30 | (a+)*$ | redos.py:292:25:292:29 | (a+)* | 0 | +| redos.py:292:25:292:30 | (a+)*$ | redos.py:292:30:292:30 | $ | 1 | +| redos.py:295:34:295:39 | (a+)*$ | redos.py:295:34:295:38 | (a+)* | 0 | +| redos.py:295:34:295:39 | (a+)*$ | redos.py:295:39:295:39 | $ | 1 | +| redos.py:298:25:298:35 | ((;\|^)a+)+$ | redos.py:298:25:298:34 | ((;\|^)a+)+ | 0 | +| redos.py:298:25:298:35 | ((;\|^)a+)+$ | redos.py:298:35:298:35 | $ | 1 | +| redos.py:298:26:298:32 | (;\|^)a+ | redos.py:298:26:298:30 | (;\|^) | 0 | +| redos.py:298:26:298:32 | (;\|^)a+ | redos.py:298:31:298:32 | a+ | 1 | +| redos.py:301:24:301:104 | (^\|;)(0\|1)(0\|1)(0\|1)(0\|1)(0\|1)(0\|1)(0\|1)(0\|1)(0\|1)(0\|1)(0\|1)(0\|1)(0\|1)(0\|1)(e+)+f | redos.py:301:24:301:28 | (^\|;) | 0 | +| redos.py:301:24:301:104 | (^\|;)(0\|1)(0\|1)(0\|1)(0\|1)(0\|1)(0\|1)(0\|1)(0\|1)(0\|1)(0\|1)(0\|1)(0\|1)(0\|1)(0\|1)(e+)+f | redos.py:301:29:301:33 | (0\|1) | 1 | +| redos.py:301:24:301:104 | (^\|;)(0\|1)(0\|1)(0\|1)(0\|1)(0\|1)(0\|1)(0\|1)(0\|1)(0\|1)(0\|1)(0\|1)(0\|1)(0\|1)(0\|1)(e+)+f | redos.py:301:34:301:38 | (0\|1) | 2 | +| redos.py:301:24:301:104 | (^\|;)(0\|1)(0\|1)(0\|1)(0\|1)(0\|1)(0\|1)(0\|1)(0\|1)(0\|1)(0\|1)(0\|1)(0\|1)(0\|1)(0\|1)(e+)+f | redos.py:301:39:301:43 | (0\|1) | 3 | +| redos.py:301:24:301:104 | (^\|;)(0\|1)(0\|1)(0\|1)(0\|1)(0\|1)(0\|1)(0\|1)(0\|1)(0\|1)(0\|1)(0\|1)(0\|1)(0\|1)(0\|1)(e+)+f | redos.py:301:44:301:48 | (0\|1) | 4 | +| redos.py:301:24:301:104 | (^\|;)(0\|1)(0\|1)(0\|1)(0\|1)(0\|1)(0\|1)(0\|1)(0\|1)(0\|1)(0\|1)(0\|1)(0\|1)(0\|1)(0\|1)(e+)+f | redos.py:301:49:301:53 | (0\|1) | 5 | +| redos.py:301:24:301:104 | (^\|;)(0\|1)(0\|1)(0\|1)(0\|1)(0\|1)(0\|1)(0\|1)(0\|1)(0\|1)(0\|1)(0\|1)(0\|1)(0\|1)(0\|1)(e+)+f | redos.py:301:54:301:58 | (0\|1) | 6 | +| redos.py:301:24:301:104 | (^\|;)(0\|1)(0\|1)(0\|1)(0\|1)(0\|1)(0\|1)(0\|1)(0\|1)(0\|1)(0\|1)(0\|1)(0\|1)(0\|1)(0\|1)(e+)+f | redos.py:301:59:301:63 | (0\|1) | 7 | +| redos.py:301:24:301:104 | (^\|;)(0\|1)(0\|1)(0\|1)(0\|1)(0\|1)(0\|1)(0\|1)(0\|1)(0\|1)(0\|1)(0\|1)(0\|1)(0\|1)(0\|1)(e+)+f | redos.py:301:64:301:68 | (0\|1) | 8 | +| redos.py:301:24:301:104 | (^\|;)(0\|1)(0\|1)(0\|1)(0\|1)(0\|1)(0\|1)(0\|1)(0\|1)(0\|1)(0\|1)(0\|1)(0\|1)(0\|1)(0\|1)(e+)+f | redos.py:301:69:301:73 | (0\|1) | 9 | +| redos.py:301:24:301:104 | (^\|;)(0\|1)(0\|1)(0\|1)(0\|1)(0\|1)(0\|1)(0\|1)(0\|1)(0\|1)(0\|1)(0\|1)(0\|1)(0\|1)(0\|1)(e+)+f | redos.py:301:74:301:78 | (0\|1) | 10 | +| redos.py:301:24:301:104 | (^\|;)(0\|1)(0\|1)(0\|1)(0\|1)(0\|1)(0\|1)(0\|1)(0\|1)(0\|1)(0\|1)(0\|1)(0\|1)(0\|1)(0\|1)(e+)+f | redos.py:301:79:301:83 | (0\|1) | 11 | +| redos.py:301:24:301:104 | (^\|;)(0\|1)(0\|1)(0\|1)(0\|1)(0\|1)(0\|1)(0\|1)(0\|1)(0\|1)(0\|1)(0\|1)(0\|1)(0\|1)(0\|1)(e+)+f | redos.py:301:84:301:88 | (0\|1) | 12 | +| redos.py:301:24:301:104 | (^\|;)(0\|1)(0\|1)(0\|1)(0\|1)(0\|1)(0\|1)(0\|1)(0\|1)(0\|1)(0\|1)(0\|1)(0\|1)(0\|1)(0\|1)(e+)+f | redos.py:301:89:301:93 | (0\|1) | 13 | +| redos.py:301:24:301:104 | (^\|;)(0\|1)(0\|1)(0\|1)(0\|1)(0\|1)(0\|1)(0\|1)(0\|1)(0\|1)(0\|1)(0\|1)(0\|1)(0\|1)(0\|1)(e+)+f | redos.py:301:94:301:98 | (0\|1) | 14 | +| redos.py:301:24:301:104 | (^\|;)(0\|1)(0\|1)(0\|1)(0\|1)(0\|1)(0\|1)(0\|1)(0\|1)(0\|1)(0\|1)(0\|1)(0\|1)(0\|1)(0\|1)(e+)+f | redos.py:301:99:301:103 | (e+)+ | 15 | +| redos.py:301:24:301:104 | (^\|;)(0\|1)(0\|1)(0\|1)(0\|1)(0\|1)(0\|1)(0\|1)(0\|1)(0\|1)(0\|1)(0\|1)(0\|1)(0\|1)(0\|1)(e+)+f | redos.py:301:104:301:104 | f | 16 | +| redos.py:304:24:304:32 | ^ab(c+)+$ | redos.py:304:24:304:24 | ^ | 0 | +| redos.py:304:24:304:32 | ^ab(c+)+$ | redos.py:304:25:304:25 | a | 1 | +| redos.py:304:24:304:32 | ^ab(c+)+$ | redos.py:304:26:304:26 | b | 2 | +| redos.py:304:24:304:32 | ^ab(c+)+$ | redos.py:304:27:304:31 | (c+)+ | 3 | +| redos.py:304:24:304:32 | ^ab(c+)+$ | redos.py:304:32:304:32 | $ | 4 | +| redos.py:307:25:307:32 | \\d(\\s+)* | redos.py:307:25:307:26 | \\d | 0 | +| redos.py:307:25:307:32 | \\d(\\s+)* | redos.py:307:27:307:32 | (\\s+)* | 1 | +| redos.py:310:25:310:48 | (([^/]\|X)+)(\\/[\\s\\S]*)*$ | redos.py:310:25:310:35 | (([^/]\|X)+) | 0 | +| redos.py:310:25:310:48 | (([^/]\|X)+)(\\/[\\s\\S]*)*$ | redos.py:310:36:310:47 | (\\/[\\s\\S]*)* | 1 | +| redos.py:310:25:310:48 | (([^/]\|X)+)(\\/[\\s\\S]*)*$ | redos.py:310:48:310:48 | $ | 2 | +| redos.py:310:37:310:45 | \\/[\\s\\S]* | redos.py:310:37:310:38 | \\/ | 0 | +| redos.py:310:37:310:45 | \\/[\\s\\S]* | redos.py:310:39:310:45 | [\\s\\S]* | 1 | +| redos.py:313:25:313:44 | ^((x([^Y]+)?)*(Y\|$)) | redos.py:313:25:313:25 | ^ | 0 | +| redos.py:313:25:313:44 | ^((x([^Y]+)?)*(Y\|$)) | redos.py:313:26:313:44 | ((x([^Y]+)?)*(Y\|$)) | 1 | +| redos.py:313:27:313:43 | (x([^Y]+)?)*(Y\|$) | redos.py:313:27:313:38 | (x([^Y]+)?)* | 0 | +| redos.py:313:27:313:43 | (x([^Y]+)?)*(Y\|$) | redos.py:313:39:313:43 | (Y\|$) | 1 | +| redos.py:313:28:313:36 | x([^Y]+)? | redos.py:313:28:313:28 | x | 0 | +| redos.py:313:28:313:36 | x([^Y]+)? | redos.py:313:29:313:36 | ([^Y]+)? | 1 | +| redos.py:316:24:316:29 | (a*)+b | redos.py:316:24:316:28 | (a*)+ | 0 | +| redos.py:316:24:316:29 | (a*)+b | redos.py:316:29:316:29 | b | 1 | +| redos.py:319:24:319:38 | foo([\\w-]*)+bar | redos.py:319:24:319:24 | f | 0 | +| redos.py:319:24:319:38 | foo([\\w-]*)+bar | redos.py:319:25:319:25 | o | 1 | +| redos.py:319:24:319:38 | foo([\\w-]*)+bar | redos.py:319:26:319:26 | o | 2 | +| redos.py:319:24:319:38 | foo([\\w-]*)+bar | redos.py:319:27:319:35 | ([\\w-]*)+ | 3 | +| redos.py:319:24:319:38 | foo([\\w-]*)+bar | redos.py:319:36:319:36 | b | 4 | +| redos.py:319:24:319:38 | foo([\\w-]*)+bar | redos.py:319:37:319:37 | a | 5 | +| redos.py:319:24:319:38 | foo([\\w-]*)+bar | redos.py:319:38:319:38 | r | 6 | +| redos.py:322:24:322:32 | ((ab)*)+c | redos.py:322:24:322:31 | ((ab)*)+ | 0 | +| redos.py:322:24:322:32 | ((ab)*)+c | redos.py:322:32:322:32 | c | 1 | +| redos.py:322:26:322:27 | ab | redos.py:322:26:322:26 | a | 0 | +| redos.py:322:26:322:27 | ab | redos.py:322:27:322:27 | b | 1 | +| redos.py:325:24:325:31 | (a?a?)*b | redos.py:325:24:325:30 | (a?a?)* | 0 | +| redos.py:325:24:325:31 | (a?a?)*b | redos.py:325:31:325:31 | b | 1 | +| redos.py:325:25:325:28 | a?a? | redos.py:325:25:325:26 | a? | 0 | +| redos.py:325:25:325:28 | a?a? | redos.py:325:27:325:28 | a? | 1 | +| redos.py:328:25:328:30 | (a?)*b | redos.py:328:25:328:29 | (a?)* | 0 | +| redos.py:328:25:328:30 | (a?)*b | redos.py:328:30:328:30 | b | 1 | +| redos.py:331:24:331:31 | (c?a?)*b | redos.py:331:24:331:30 | (c?a?)* | 0 | +| redos.py:331:24:331:31 | (c?a?)*b | redos.py:331:31:331:31 | b | 1 | +| redos.py:331:25:331:28 | c?a? | redos.py:331:25:331:26 | c? | 0 | +| redos.py:331:25:331:28 | c?a? | redos.py:331:27:331:28 | a? | 1 | +| redos.py:334:24:334:33 | (?:a\|a?)+b | redos.py:334:24:334:32 | (?:a\|a?)+ | 0 | +| redos.py:334:24:334:33 | (?:a\|a?)+b | redos.py:334:33:334:33 | b | 1 | +| redos.py:337:24:337:31 | (a?b?)*$ | redos.py:337:24:337:30 | (a?b?)* | 0 | +| redos.py:337:24:337:31 | (a?b?)*$ | redos.py:337:31:337:31 | $ | 1 | +| redos.py:337:25:337:28 | a?b? | redos.py:337:25:337:26 | a? | 0 | +| redos.py:337:25:337:28 | a?b? | redos.py:337:27:337:28 | b? | 1 | +| redos.py:340:24:340:69 | PRE(([a-c]\|[c-d])T(e?e?e?e?\|X))+(cTcT\|cTXcTX$) | redos.py:340:24:340:24 | P | 0 | +| redos.py:340:24:340:69 | PRE(([a-c]\|[c-d])T(e?e?e?e?\|X))+(cTcT\|cTXcTX$) | redos.py:340:25:340:25 | R | 1 | +| redos.py:340:24:340:69 | PRE(([a-c]\|[c-d])T(e?e?e?e?\|X))+(cTcT\|cTXcTX$) | redos.py:340:26:340:26 | E | 2 | +| redos.py:340:24:340:69 | PRE(([a-c]\|[c-d])T(e?e?e?e?\|X))+(cTcT\|cTXcTX$) | redos.py:340:27:340:55 | (([a-c]\|[c-d])T(e?e?e?e?\|X))+ | 3 | +| redos.py:340:24:340:69 | PRE(([a-c]\|[c-d])T(e?e?e?e?\|X))+(cTcT\|cTXcTX$) | redos.py:340:56:340:69 | (cTcT\|cTXcTX$) | 4 | +| redos.py:340:28:340:53 | ([a-c]\|[c-d])T(e?e?e?e?\|X) | redos.py:340:28:340:40 | ([a-c]\|[c-d]) | 0 | +| redos.py:340:28:340:53 | ([a-c]\|[c-d])T(e?e?e?e?\|X) | redos.py:340:41:340:41 | T | 1 | +| redos.py:340:28:340:53 | ([a-c]\|[c-d])T(e?e?e?e?\|X) | redos.py:340:42:340:53 | (e?e?e?e?\|X) | 2 | +| redos.py:340:43:340:50 | e?e?e?e? | redos.py:340:43:340:44 | e? | 0 | +| redos.py:340:43:340:50 | e?e?e?e? | redos.py:340:45:340:46 | e? | 1 | +| redos.py:340:43:340:50 | e?e?e?e? | redos.py:340:47:340:48 | e? | 2 | +| redos.py:340:43:340:50 | e?e?e?e? | redos.py:340:49:340:50 | e? | 3 | +| redos.py:340:57:340:60 | cTcT | redos.py:340:57:340:57 | c | 0 | +| redos.py:340:57:340:60 | cTcT | redos.py:340:58:340:58 | T | 1 | +| redos.py:340:57:340:60 | cTcT | redos.py:340:59:340:59 | c | 2 | +| redos.py:340:57:340:60 | cTcT | redos.py:340:60:340:60 | T | 3 | +| redos.py:340:62:340:68 | cTXcTX$ | redos.py:340:62:340:62 | c | 0 | +| redos.py:340:62:340:68 | cTXcTX$ | redos.py:340:63:340:63 | T | 1 | +| redos.py:340:62:340:68 | cTXcTX$ | redos.py:340:64:340:64 | X | 2 | +| redos.py:340:62:340:68 | cTXcTX$ | redos.py:340:65:340:65 | c | 3 | +| redos.py:340:62:340:68 | cTXcTX$ | redos.py:340:66:340:66 | T | 4 | +| redos.py:340:62:340:68 | cTXcTX$ | redos.py:340:67:340:67 | X | 5 | +| redos.py:340:62:340:68 | cTXcTX$ | redos.py:340:68:340:68 | $ | 6 | +| redos.py:343:24:343:34 | ^((a)+\\w)+$ | redos.py:343:24:343:24 | ^ | 0 | +| redos.py:343:24:343:34 | ^((a)+\\w)+$ | redos.py:343:25:343:33 | ((a)+\\w)+ | 1 | +| redos.py:343:24:343:34 | ^((a)+\\w)+$ | redos.py:343:34:343:34 | $ | 2 | +| redos.py:343:26:343:31 | (a)+\\w | redos.py:343:26:343:29 | (a)+ | 0 | +| redos.py:343:26:343:31 | (a)+\\w | redos.py:343:30:343:31 | \\w | 1 | +| redos.py:346:24:346:31 | ^(b+.)+$ | redos.py:346:24:346:24 | ^ | 0 | +| redos.py:346:24:346:31 | ^(b+.)+$ | redos.py:346:25:346:30 | (b+.)+ | 1 | +| redos.py:346:24:346:31 | ^(b+.)+$ | redos.py:346:31:346:31 | $ | 2 | +| redos.py:346:26:346:28 | b+. | redos.py:346:26:346:27 | b+ | 0 | +| redos.py:346:26:346:28 | b+. | redos.py:346:28:346:28 | . | 1 | +| redos.py:349:25:349:27 | a*b | redos.py:349:25:349:26 | a* | 0 | +| redos.py:349:25:349:27 | a*b | redos.py:349:27:349:27 | b | 1 | +| redos.py:352:24:352:29 | (a*)*b | redos.py:352:24:352:28 | (a*)* | 0 | +| redos.py:352:24:352:29 | (a*)*b | redos.py:352:29:352:29 | b | 1 | +| redos.py:353:24:353:29 | (a+)*b | redos.py:353:24:353:28 | (a+)* | 0 | +| redos.py:353:24:353:29 | (a+)*b | redos.py:353:29:353:29 | b | 1 | +| redos.py:354:24:354:29 | (a*)+b | redos.py:354:24:354:28 | (a*)+ | 0 | +| redos.py:354:24:354:29 | (a*)+b | redos.py:354:29:354:29 | b | 1 | +| redos.py:355:24:355:29 | (a+)+b | redos.py:355:24:355:28 | (a+)+ | 0 | +| redos.py:355:24:355:29 | (a+)+b | redos.py:355:29:355:29 | b | 1 | +| redos.py:359:51:359:60 | :(?![/\\\\]) | redos.py:359:51:359:51 | : | 0 | +| redos.py:359:51:359:60 | :(?![/\\\\]) | redos.py:359:52:359:60 | (?![/\\\\]) | 1 | +| redos.py:362:24:362:42 | ^((?:a{\|-)\|\\w\\{)+X$ | redos.py:362:24:362:24 | ^ | 0 | +| redos.py:362:24:362:42 | ^((?:a{\|-)\|\\w\\{)+X$ | redos.py:362:25:362:40 | ((?:a{\|-)\|\\w\\{)+ | 1 | +| redos.py:362:24:362:42 | ^((?:a{\|-)\|\\w\\{)+X$ | redos.py:362:41:362:41 | X | 2 | +| redos.py:362:24:362:42 | ^((?:a{\|-)\|\\w\\{)+X$ | redos.py:362:42:362:42 | $ | 3 | +| redos.py:362:29:362:30 | a{ | redos.py:362:29:362:29 | a | 0 | +| redos.py:362:29:362:30 | a{ | redos.py:362:30:362:30 | { | 1 | +| redos.py:362:35:362:38 | \\w\\{ | redos.py:362:35:362:36 | \\w | 0 | +| redos.py:362:35:362:38 | \\w\\{ | redos.py:362:37:362:38 | \\{ | 1 | +| redos.py:363:24:363:45 | ^((?:a{0\|-)\|\\w\\{\\d)+X$ | redos.py:363:24:363:24 | ^ | 0 | +| redos.py:363:24:363:45 | ^((?:a{0\|-)\|\\w\\{\\d)+X$ | redos.py:363:25:363:43 | ((?:a{0\|-)\|\\w\\{\\d)+ | 1 | +| redos.py:363:24:363:45 | ^((?:a{0\|-)\|\\w\\{\\d)+X$ | redos.py:363:44:363:44 | X | 2 | +| redos.py:363:24:363:45 | ^((?:a{0\|-)\|\\w\\{\\d)+X$ | redos.py:363:45:363:45 | $ | 3 | +| redos.py:363:29:363:31 | a{0 | redos.py:363:29:363:29 | a | 0 | +| redos.py:363:29:363:31 | a{0 | redos.py:363:30:363:30 | { | 1 | +| redos.py:363:29:363:31 | a{0 | redos.py:363:31:363:31 | 0 | 2 | +| redos.py:363:36:363:41 | \\w\\{\\d | redos.py:363:36:363:37 | \\w | 0 | +| redos.py:363:36:363:41 | \\w\\{\\d | redos.py:363:38:363:39 | \\{ | 1 | +| redos.py:363:36:363:41 | \\w\\{\\d | redos.py:363:40:363:41 | \\d | 2 | +| redos.py:364:24:364:47 | ^((?:a{0,\|-)\|\\w\\{\\d,)+X$ | redos.py:364:24:364:24 | ^ | 0 | +| redos.py:364:24:364:47 | ^((?:a{0,\|-)\|\\w\\{\\d,)+X$ | redos.py:364:25:364:45 | ((?:a{0,\|-)\|\\w\\{\\d,)+ | 1 | +| redos.py:364:24:364:47 | ^((?:a{0,\|-)\|\\w\\{\\d,)+X$ | redos.py:364:46:364:46 | X | 2 | +| redos.py:364:24:364:47 | ^((?:a{0,\|-)\|\\w\\{\\d,)+X$ | redos.py:364:47:364:47 | $ | 3 | +| redos.py:364:29:364:32 | a{0, | redos.py:364:29:364:29 | a | 0 | +| redos.py:364:29:364:32 | a{0, | redos.py:364:30:364:30 | { | 1 | +| redos.py:364:29:364:32 | a{0, | redos.py:364:31:364:31 | 0 | 2 | +| redos.py:364:29:364:32 | a{0, | redos.py:364:32:364:32 | , | 3 | +| redos.py:364:37:364:43 | \\w\\{\\d, | redos.py:364:37:364:38 | \\w | 0 | +| redos.py:364:37:364:43 | \\w\\{\\d, | redos.py:364:39:364:40 | \\{ | 1 | +| redos.py:364:37:364:43 | \\w\\{\\d, | redos.py:364:41:364:42 | \\d | 2 | +| redos.py:364:37:364:43 | \\w\\{\\d, | redos.py:364:43:364:43 | , | 3 | +| redos.py:365:24:365:50 | ^((?:a{0,2\|-)\|\\w\\{\\d,\\d)+X$ | redos.py:365:24:365:24 | ^ | 0 | +| redos.py:365:24:365:50 | ^((?:a{0,2\|-)\|\\w\\{\\d,\\d)+X$ | redos.py:365:25:365:48 | ((?:a{0,2\|-)\|\\w\\{\\d,\\d)+ | 1 | +| redos.py:365:24:365:50 | ^((?:a{0,2\|-)\|\\w\\{\\d,\\d)+X$ | redos.py:365:49:365:49 | X | 2 | +| redos.py:365:24:365:50 | ^((?:a{0,2\|-)\|\\w\\{\\d,\\d)+X$ | redos.py:365:50:365:50 | $ | 3 | +| redos.py:365:29:365:33 | a{0,2 | redos.py:365:29:365:29 | a | 0 | +| redos.py:365:29:365:33 | a{0,2 | redos.py:365:30:365:30 | { | 1 | +| redos.py:365:29:365:33 | a{0,2 | redos.py:365:31:365:31 | 0 | 2 | +| redos.py:365:29:365:33 | a{0,2 | redos.py:365:32:365:32 | , | 3 | +| redos.py:365:29:365:33 | a{0,2 | redos.py:365:33:365:33 | 2 | 4 | +| redos.py:365:38:365:46 | \\w\\{\\d,\\d | redos.py:365:38:365:39 | \\w | 0 | +| redos.py:365:38:365:46 | \\w\\{\\d,\\d | redos.py:365:40:365:41 | \\{ | 1 | +| redos.py:365:38:365:46 | \\w\\{\\d,\\d | redos.py:365:42:365:43 | \\d | 2 | +| redos.py:365:38:365:46 | \\w\\{\\d,\\d | redos.py:365:44:365:44 | , | 3 | +| redos.py:365:38:365:46 | \\w\\{\\d,\\d | redos.py:365:45:365:46 | \\d | 4 | +| redos.py:368:25:368:54 | ^((?:a{0,2}\|-)\|\\w\\{\\d,\\d\\})+X$ | redos.py:368:25:368:25 | ^ | 0 | +| redos.py:368:25:368:54 | ^((?:a{0,2}\|-)\|\\w\\{\\d,\\d\\})+X$ | redos.py:368:26:368:52 | ((?:a{0,2}\|-)\|\\w\\{\\d,\\d\\})+ | 1 | +| redos.py:368:25:368:54 | ^((?:a{0,2}\|-)\|\\w\\{\\d,\\d\\})+X$ | redos.py:368:53:368:53 | X | 2 | +| redos.py:368:25:368:54 | ^((?:a{0,2}\|-)\|\\w\\{\\d,\\d\\})+X$ | redos.py:368:54:368:54 | $ | 3 | +| redos.py:368:40:368:50 | \\w\\{\\d,\\d\\} | redos.py:368:40:368:41 | \\w | 0 | +| redos.py:368:40:368:50 | \\w\\{\\d,\\d\\} | redos.py:368:42:368:43 | \\{ | 1 | +| redos.py:368:40:368:50 | \\w\\{\\d,\\d\\} | redos.py:368:44:368:45 | \\d | 2 | +| redos.py:368:40:368:50 | \\w\\{\\d,\\d\\} | redos.py:368:46:368:46 | , | 3 | +| redos.py:368:40:368:50 | \\w\\{\\d,\\d\\} | redos.py:368:47:368:48 | \\d | 4 | +| redos.py:368:40:368:50 | \\w\\{\\d,\\d\\} | redos.py:368:49:368:50 | \\} | 5 | +| unittests.py:4:16:4:28 | X([^\\.]\|\\.)*$ | unittests.py:4:16:4:16 | X | 0 | +| unittests.py:4:16:4:28 | X([^\\.]\|\\.)*$ | unittests.py:4:17:4:27 | ([^\\.]\|\\.)* | 1 | +| unittests.py:4:16:4:28 | X([^\\.]\|\\.)*$ | unittests.py:4:28:4:28 | $ | 2 | +| unittests.py:5:16:5:24 | X(\u00c6\|\\\u00c6)+$ | unittests.py:5:16:5:16 | X | 0 | +| unittests.py:5:16:5:24 | X(\u00c6\|\\\u00c6)+$ | unittests.py:5:17:5:23 | (\u00c6\|\\\u00c6)+ | 1 | +| unittests.py:5:16:5:24 | X(\u00c6\|\\\u00c6)+$ | unittests.py:5:24:5:24 | $ | 2 | +| unittests.py:8:16:8:25 | (?:.\|\\n)*b | unittests.py:8:16:8:24 | (?:.\|\\n)* | 0 | +| unittests.py:8:16:8:25 | (?:.\|\\n)*b | unittests.py:8:25:8:25 | b | 1 | +| unittests.py:9:16:9:25 | (?:.\|\\n)*b | unittests.py:9:16:9:24 | (?:.\|\\n)* | 0 | +| unittests.py:9:16:9:25 | (?:.\|\\n)*b | unittests.py:9:25:9:25 | b | 1 | +orChild +| KnownCVEs.py:16:25:16:28 | .\|\\n | KnownCVEs.py:16:25:16:25 | . | 0 | +| KnownCVEs.py:16:25:16:28 | .\|\\n | KnownCVEs.py:16:27:16:28 | \\n | 1 | +| KnownCVEs.py:22:21:22:34 | evoque\|overlay | KnownCVEs.py:22:21:22:26 | evoque | 0 | +| KnownCVEs.py:22:21:22:34 | evoque\|overlay | KnownCVEs.py:22:28:22:34 | overlay | 1 | +| KnownCVEs.py:35:19:35:79 | [-/:,#%.'"\\s!\\w]\|\\w-\\w\|'[\\s\\w]+'\\s*\|"[\\s\\w]+"\|\\([\\d,%\\.\\s]+\\) | KnownCVEs.py:35:19:35:34 | [-/:,#%.'"\\s!\\w] | 0 | +| KnownCVEs.py:35:19:35:79 | [-/:,#%.'"\\s!\\w]\|\\w-\\w\|'[\\s\\w]+'\\s*\|"[\\s\\w]+"\|\\([\\d,%\\.\\s]+\\) | KnownCVEs.py:35:36:35:40 | \\w-\\w | 1 | +| KnownCVEs.py:35:19:35:79 | [-/:,#%.'"\\s!\\w]\|\\w-\\w\|'[\\s\\w]+'\\s*\|"[\\s\\w]+"\|\\([\\d,%\\.\\s]+\\) | KnownCVEs.py:35:42:35:53 | '[\\s\\w]+'\\s* | 2 | +| KnownCVEs.py:35:19:35:79 | [-/:,#%.'"\\s!\\w]\|\\w-\\w\|'[\\s\\w]+'\\s*\|"[\\s\\w]+"\|\\([\\d,%\\.\\s]+\\) | KnownCVEs.py:35:55:35:63 | "[\\s\\w]+" | 3 | +| KnownCVEs.py:35:19:35:79 | [-/:,#%.'"\\s!\\w]\|\\w-\\w\|'[\\s\\w]+'\\s*\|"[\\s\\w]+"\|\\([\\d,%\\.\\s]+\\) | KnownCVEs.py:35:65:35:79 | \\([\\d,%\\.\\s]+\\) | 4 | +| KnownCVEs.py:80:14:83:139 | ([a-zA-Z]{1})\|([a-zA-Z]{1}[a-zA-Z]{1})\|([a-zA-Z]{1}[0-9]{1})\|([0-9]{1}[a-zA-Z]{1})\|([a-zA-Z0-9][-_a-zA-Z0-9]{0,61}[a-zA-Z0-9]) | KnownCVEs.py:80:14:83:26 | ([a-zA-Z]{1}) | 0 | +| KnownCVEs.py:80:14:83:139 | ([a-zA-Z]{1})\|([a-zA-Z]{1}[a-zA-Z]{1})\|([a-zA-Z]{1}[0-9]{1})\|([0-9]{1}[a-zA-Z]{1})\|([a-zA-Z0-9][-_a-zA-Z0-9]{0,61}[a-zA-Z0-9]) | KnownCVEs.py:80:28:83:51 | ([a-zA-Z]{1}[a-zA-Z]{1}) | 1 | +| KnownCVEs.py:80:14:83:139 | ([a-zA-Z]{1})\|([a-zA-Z]{1}[a-zA-Z]{1})\|([a-zA-Z]{1}[0-9]{1})\|([0-9]{1}[a-zA-Z]{1})\|([a-zA-Z0-9][-_a-zA-Z0-9]{0,61}[a-zA-Z0-9]) | KnownCVEs.py:80:53:83:73 | ([a-zA-Z]{1}[0-9]{1}) | 2 | +| KnownCVEs.py:80:14:83:139 | ([a-zA-Z]{1})\|([a-zA-Z]{1}[a-zA-Z]{1})\|([a-zA-Z]{1}[0-9]{1})\|([0-9]{1}[a-zA-Z]{1})\|([a-zA-Z0-9][-_a-zA-Z0-9]{0,61}[a-zA-Z0-9]) | KnownCVEs.py:80:75:83:95 | ([0-9]{1}[a-zA-Z]{1}) | 3 | +| KnownCVEs.py:80:14:83:139 | ([a-zA-Z]{1})\|([a-zA-Z]{1}[a-zA-Z]{1})\|([a-zA-Z]{1}[0-9]{1})\|([0-9]{1}[a-zA-Z]{1})\|([a-zA-Z0-9][-_a-zA-Z0-9]{0,61}[a-zA-Z0-9]) | KnownCVEs.py:80:97:83:139 | ([a-zA-Z0-9][-_a-zA-Z0-9]{0,61}[a-zA-Z0-9]) | 4 | +| KnownCVEs.py:80:146:83:183 | [a-zA-Z]{2,13}\|(xn--[a-zA-Z0-9]{2,30}) | KnownCVEs.py:80:146:83:159 | [a-zA-Z]{2,13} | 0 | +| KnownCVEs.py:80:146:83:183 | [a-zA-Z]{2,13}\|(xn--[a-zA-Z0-9]{2,30}) | KnownCVEs.py:80:161:83:183 | (xn--[a-zA-Z0-9]{2,30}) | 1 | +| redos.py:6:23:6:77 | ^\\b_((?:__\|[\\s\\S])+?)_\\b\|^\\*((?:\\*\\*\|[\\s\\S])+?)\\*(?!\\*) | redos.py:6:23:6:46 | ^\\b_((?:__\|[\\s\\S])+?)_\\b | 0 | +| redos.py:6:23:6:77 | ^\\b_((?:__\|[\\s\\S])+?)_\\b\|^\\*((?:\\*\\*\|[\\s\\S])+?)\\*(?!\\*) | redos.py:6:48:6:77 | ^\\*((?:\\*\\*\|[\\s\\S])+?)\\*(?!\\*) | 1 | +| redos.py:6:31:6:39 | __\|[\\s\\S] | redos.py:6:31:6:32 | __ | 0 | +| redos.py:6:31:6:39 | __\|[\\s\\S] | redos.py:6:34:6:39 | [\\s\\S] | 1 | +| redos.py:6:55:6:65 | \\*\\*\|[\\s\\S] | redos.py:6:55:6:58 | \\*\\* | 0 | +| redos.py:6:55:6:65 | \\*\\*\|[\\s\\S] | redos.py:6:60:6:65 | [\\s\\S] | 1 | +| redos.py:11:24:11:74 | ^\\b_((?:__\|[^_])+?)_\\b\|^\\*((?:\\*\\*\|[^*])+?)\\*(?!\\*) | redos.py:11:24:11:45 | ^\\b_((?:__\|[^_])+?)_\\b | 0 | +| redos.py:11:24:11:74 | ^\\b_((?:__\|[^_])+?)_\\b\|^\\*((?:\\*\\*\|[^*])+?)\\*(?!\\*) | redos.py:11:47:11:74 | ^\\*((?:\\*\\*\|[^*])+?)\\*(?!\\*) | 1 | +| redos.py:11:32:11:38 | __\|[^_] | redos.py:11:32:11:33 | __ | 0 | +| redos.py:11:32:11:38 | __\|[^_] | redos.py:11:35:11:38 | [^_] | 1 | +| redos.py:11:54:11:62 | \\*\\*\|[^*] | redos.py:11:54:11:57 | \\*\\* | 0 | +| redos.py:11:54:11:62 | \\*\\*\|[^*] | redos.py:11:59:11:62 | [^*] | 1 | +| redos.py:21:33:21:102 | "(?:[^"\\\\]\|\\\\\\\\\|\\\\.)+"\|'(?:[^'\\\\]\|\\\\\\\\\|\\\\.)+'\|\\((?:[^)\\\\]\|\\\\\\\\\|\\\\.)+\\) | redos.py:21:33:21:54 | "(?:[^"\\\\]\|\\\\\\\\\|\\\\.)+" | 0 | +| redos.py:21:33:21:102 | "(?:[^"\\\\]\|\\\\\\\\\|\\\\.)+"\|'(?:[^'\\\\]\|\\\\\\\\\|\\\\.)+'\|\\((?:[^)\\\\]\|\\\\\\\\\|\\\\.)+\\) | redos.py:21:56:21:77 | '(?:[^'\\\\]\|\\\\\\\\\|\\\\.)+' | 1 | +| redos.py:21:33:21:102 | "(?:[^"\\\\]\|\\\\\\\\\|\\\\.)+"\|'(?:[^'\\\\]\|\\\\\\\\\|\\\\.)+'\|\\((?:[^)\\\\]\|\\\\\\\\\|\\\\.)+\\) | redos.py:21:79:21:102 | \\((?:[^)\\\\]\|\\\\\\\\\|\\\\.)+\\) | 2 | +| redos.py:21:37:21:51 | [^"\\\\]\|\\\\\\\\\|\\\\. | redos.py:21:37:21:42 | [^"\\\\] | 0 | +| redos.py:21:37:21:51 | [^"\\\\]\|\\\\\\\\\|\\\\. | redos.py:21:44:21:47 | \\\\\\\\ | 1 | +| redos.py:21:37:21:51 | [^"\\\\]\|\\\\\\\\\|\\\\. | redos.py:21:49:21:51 | \\\\. | 2 | +| redos.py:21:60:21:74 | [^'\\\\]\|\\\\\\\\\|\\\\. | redos.py:21:60:21:65 | [^'\\\\] | 0 | +| redos.py:21:60:21:74 | [^'\\\\]\|\\\\\\\\\|\\\\. | redos.py:21:67:21:70 | \\\\\\\\ | 1 | +| redos.py:21:60:21:74 | [^'\\\\]\|\\\\\\\\\|\\\\. | redos.py:21:72:21:74 | \\\\. | 2 | +| redos.py:21:84:21:98 | [^)\\\\]\|\\\\\\\\\|\\\\. | redos.py:21:84:21:89 | [^)\\\\] | 0 | +| redos.py:21:84:21:98 | [^)\\\\]\|\\\\\\\\\|\\\\. | redos.py:21:91:21:94 | \\\\\\\\ | 1 | +| redos.py:21:84:21:98 | [^)\\\\]\|\\\\\\\\\|\\\\. | redos.py:21:96:21:98 | \\\\. | 2 | +| redos.py:30:74:30:77 | \\n\|$ | redos.py:30:74:30:75 | \\n | 0 | +| redos.py:30:74:30:77 | \\n\|$ | redos.py:30:77:30:77 | $ | 1 | +| redos.py:33:73:33:76 | \\n\|$ | redos.py:33:73:33:74 | \\n | 0 | +| redos.py:33:73:33:76 | \\n\|$ | redos.py:33:76:33:76 | $ | 1 | +| redos.py:38:34:38:39 | \\\\\\/\|. | redos.py:38:34:38:37 | \\\\\\/ | 0 | +| redos.py:38:34:38:39 | \\\\\\/\|. | redos.py:38:39:38:39 | . | 1 | +| redos.py:38:54:38:57 | \\W\|$ | redos.py:38:54:38:55 | \\W | 0 | +| redos.py:38:54:38:57 | \\W\|$ | redos.py:38:57:38:57 | $ | 1 | +| redos.py:43:25:43:38 | [\\s\\[\\{\\(]\|#.* | redos.py:43:25:43:34 | [\\s\\[\\{\\(] | 0 | +| redos.py:43:25:43:38 | [\\s\\[\\{\\(]\|#.* | redos.py:43:36:43:38 | #.* | 1 | +| redos.py:46:25:46:34 | \\r\\n\|\\r\|\\n | redos.py:46:25:46:28 | \\r\\n | 0 | +| redos.py:46:25:46:34 | \\r\\n\|\\r\|\\n | redos.py:46:30:46:31 | \\r | 1 | +| redos.py:46:25:46:34 | \\r\\n\|\\r\|\\n | redos.py:46:33:46:34 | \\n | 2 | +| redos.py:49:34:49:50 | [^"']\|".*?"\|'.*?' | redos.py:49:34:49:38 | [^"'] | 0 | +| redos.py:49:34:49:50 | [^"']\|".*?"\|'.*?' | redos.py:49:40:49:44 | ".*?" | 1 | +| redos.py:49:34:49:50 | [^"']\|".*?"\|'.*?' | redos.py:49:46:49:50 | '.*?' | 2 | +| redos.py:49:56:49:62 | [(,)]\|$ | redos.py:49:56:49:60 | [(,)] | 0 | +| redos.py:49:56:49:62 | [(,)]\|$ | redos.py:49:62:49:62 | $ | 1 | +| redos.py:57:25:57:27 | a\|. | redos.py:57:25:57:25 | a | 0 | +| redos.py:57:25:57:27 | a\|. | redos.py:57:27:57:27 | . | 1 | +| redos.py:62:39:62:49 | [\\\\-.]\|[_]+ | redos.py:62:39:62:44 | [\\\\-.] | 0 | +| redos.py:62:39:62:49 | [\\\\-.]\|[_]+ | redos.py:62:46:62:49 | [_]+ | 1 | +| redos.py:62:90:62:130 | ([a-z]{2,3})\|([a-z]{2,3}[.]{1}[a-z]{2,3}) | redos.py:62:90:62:101 | ([a-z]{2,3}) | 0 | +| redos.py:62:90:62:130 | ([a-z]{2,3})\|([a-z]{2,3}[.]{1}[a-z]{2,3}) | redos.py:62:103:62:130 | ([a-z]{2,3}[.]{1}[a-z]{2,3}) | 1 | +| redos.py:68:26:68:52 | [\\w#:.~>+()\\s-]+\|\\*\|\\[.*?\\] | redos.py:68:26:68:41 | [\\w#:.~>+()\\s-]+ | 0 | +| redos.py:68:26:68:52 | [\\w#:.~>+()\\s-]+\|\\*\|\\[.*?\\] | redos.py:68:43:68:44 | \\* | 1 | +| redos.py:68:26:68:52 | [\\w#:.~>+()\\s-]+\|\\*\|\\[.*?\\] | redos.py:68:46:68:52 | \\[.*?\\] | 2 | +| redos.py:68:60:68:62 | ,\|$ | redos.py:68:60:68:60 | , | 0 | +| redos.py:68:60:68:62 | ,\|$ | redos.py:68:62:68:62 | $ | 1 | +| redos.py:73:25:73:27 | "\|' | redos.py:73:25:73:25 | " | 0 | +| redos.py:73:25:73:27 | "\|' | redos.py:73:27:73:27 | ' | 1 | +| redos.py:76:25:76:29 | b\|a?b | redos.py:76:25:76:25 | b | 0 | +| redos.py:76:25:76:29 | b\|a?b | redos.py:76:27:76:29 | a?b | 1 | +| redos.py:79:25:79:29 | a\|aa? | redos.py:79:25:79:25 | a | 0 | +| redos.py:79:25:79:29 | a\|aa? | redos.py:79:27:79:29 | aa? | 1 | +| redos.py:82:25:82:28 | .\|\\n | redos.py:82:25:82:25 | . | 0 | +| redos.py:82:25:82:28 | .\|\\n | redos.py:82:27:82:28 | \\n | 1 | +| redos.py:85:25:85:28 | .\|\\n | redos.py:85:25:85:25 | . | 0 | +| redos.py:85:25:85:28 | .\|\\n | redos.py:85:27:85:28 | \\n | 1 | +| redos.py:91:25:91:29 | a\|aa? | redos.py:91:25:91:25 | a | 0 | +| redos.py:91:25:91:29 | a\|aa? | redos.py:91:27:91:29 | aa? | 1 | +| redos.py:97:26:97:36 | [\\s\\S]\|[^a] | redos.py:97:26:97:31 | [\\s\\S] | 0 | +| redos.py:97:26:97:36 | [\\s\\S]\|[^a] | redos.py:97:33:97:36 | [^a] | 1 | +| redos.py:103:26:103:31 | .\|[^a] | redos.py:103:26:103:26 | . | 0 | +| redos.py:103:26:103:31 | .\|[^a] | redos.py:103:28:103:31 | [^a] | 1 | +| redos.py:106:27:106:32 | a\|[^a] | redos.py:106:27:106:27 | a | 0 | +| redos.py:106:27:106:32 | a\|[^a] | redos.py:106:29:106:32 | [^a] | 1 | +| redos.py:109:26:109:31 | b\|[^a] | redos.py:109:26:109:26 | b | 0 | +| redos.py:109:26:109:31 | b\|[^a] | redos.py:109:28:109:31 | [^a] | 1 | +| redos.py:112:26:112:31 | G\|[^a] | redos.py:112:26:112:26 | G | 0 | +| redos.py:112:26:112:31 | G\|[^a] | redos.py:112:28:112:31 | [^a] | 1 | +| redos.py:115:26:115:35 | [0-9]\|[^a] | redos.py:115:26:115:30 | [0-9] | 0 | +| redos.py:115:26:115:35 | [0-9]\|[^a] | redos.py:115:32:115:35 | [^a] | 1 | +| redos.py:118:31:118:116 | ([!#\\$%&'\\*\\+\\-\\.\\^_`\\\|~0-9A-Za-z]+)\|"((?:\\\\[\\x00-\\x7f]\|[^\\x00-\\x08\\x0a-\\x1f\\x7f"])*)" | redos.py:118:31:118:66 | ([!#\\$%&'\\*\\+\\-\\.\\^_`\\\|~0-9A-Za-z]+) | 0 | +| redos.py:118:31:118:116 | ([!#\\$%&'\\*\\+\\-\\.\\^_`\\\|~0-9A-Za-z]+)\|"((?:\\\\[\\x00-\\x7f]\|[^\\x00-\\x08\\x0a-\\x1f\\x7f"])*)" | redos.py:118:68:118:116 | "((?:\\\\[\\x00-\\x7f]\|[^\\x00-\\x08\\x0a-\\x1f\\x7f"])*)" | 1 | +| redos.py:118:73:118:112 | \\\\[\\x00-\\x7f]\|[^\\x00-\\x08\\x0a-\\x1f\\x7f"] | redos.py:118:73:118:85 | \\\\[\\x00-\\x7f] | 0 | +| redos.py:118:73:118:112 | \\\\[\\x00-\\x7f]\|[^\\x00-\\x08\\x0a-\\x1f\\x7f"] | redos.py:118:87:118:112 | [^\\x00-\\x08\\x0a-\\x1f\\x7f"] | 1 | +| redos.py:121:29:121:68 | \\\\[\\x00-\\x7f]\|[^\\x00-\\x08\\x0a-\\x1f\\x7f"] | redos.py:121:29:121:41 | \\\\[\\x00-\\x7f] | 0 | +| redos.py:121:29:121:68 | \\\\[\\x00-\\x7f]\|[^\\x00-\\x08\\x0a-\\x1f\\x7f"] | redos.py:121:43:121:68 | [^\\x00-\\x08\\x0a-\\x1f\\x7f"] | 1 | +| redos.py:124:29:124:70 | \\\\[\\x00-\\x7f]\|[^\\x00-\\x08\\x0a-\\x1f\\x7f"\\\\] | redos.py:124:29:124:41 | \\\\[\\x00-\\x7f] | 0 | +| redos.py:124:29:124:70 | \\\\[\\x00-\\x7f]\|[^\\x00-\\x08\\x0a-\\x1f\\x7f"\\\\] | redos.py:124:43:124:70 | [^\\x00-\\x08\\x0a-\\x1f\\x7f"\\\\] | 1 | +| redos.py:127:26:127:36 | [a-z]\|[d-h] | redos.py:127:26:127:30 | [a-z] | 0 | +| redos.py:127:26:127:36 | [a-z]\|[d-h] | redos.py:127:32:127:36 | [d-h] | 1 | +| redos.py:130:26:130:38 | [^a-z]\|[^0-9] | redos.py:130:26:130:31 | [^a-z] | 0 | +| redos.py:130:26:130:38 | [^a-z]\|[^0-9] | redos.py:130:33:130:38 | [^0-9] | 1 | +| redos.py:133:26:133:33 | \\d\|[0-9] | redos.py:133:26:133:27 | \\d | 0 | +| redos.py:133:26:133:33 | \\d\|[0-9] | redos.py:133:29:133:33 | [0-9] | 1 | +| redos.py:136:26:136:30 | \\s\|\\s | redos.py:136:26:136:27 | \\s | 0 | +| redos.py:136:26:136:30 | \\s\|\\s | redos.py:136:29:136:30 | \\s | 1 | +| redos.py:139:26:139:29 | \\w\|G | redos.py:139:26:139:27 | \\w | 0 | +| redos.py:139:26:139:29 | \\w\|G | redos.py:139:29:139:29 | G | 1 | +| redos.py:142:27:142:31 | \\s\|\\d | redos.py:142:27:142:28 | \\s | 0 | +| redos.py:142:27:142:31 | \\s\|\\d | redos.py:142:30:142:31 | \\d | 1 | +| redos.py:145:26:145:30 | \\d\|\\w | redos.py:145:26:145:27 | \\d | 0 | +| redos.py:145:26:145:30 | \\d\|\\w | redos.py:145:29:145:30 | \\w | 1 | +| redos.py:148:26:148:29 | \\d\|5 | redos.py:148:26:148:27 | \\d | 0 | +| redos.py:148:26:148:29 | \\d\|5 | redos.py:148:29:148:29 | 5 | 1 | +| redos.py:151:26:151:32 | \\s\|[\\f] | redos.py:151:26:151:27 | \\s | 0 | +| redos.py:151:26:151:32 | \\s\|[\\f] | redos.py:151:29:151:32 | [\\f] | 1 | +| redos.py:154:26:154:36 | \\s\|[\\v]\|\\\\v | redos.py:154:26:154:27 | \\s | 0 | +| redos.py:154:26:154:36 | \\s\|[\\v]\|\\\\v | redos.py:154:29:154:32 | [\\v] | 1 | +| redos.py:154:26:154:36 | \\s\|[\\v]\|\\\\v | redos.py:154:34:154:36 | \\\\v | 2 | +| redos.py:157:26:157:32 | \\f\|[\\f] | redos.py:157:26:157:27 | \\f | 0 | +| redos.py:157:26:157:32 | \\f\|[\\f] | redos.py:157:29:157:32 | [\\f] | 1 | +| redos.py:160:26:160:30 | \\W\|\\D | redos.py:160:26:160:27 | \\W | 0 | +| redos.py:160:26:160:30 | \\W\|\\D | redos.py:160:29:160:30 | \\D | 1 | +| redos.py:163:26:163:30 | \\S\|\\w | redos.py:163:26:163:27 | \\S | 0 | +| redos.py:163:26:163:30 | \\S\|\\w | redos.py:163:29:163:30 | \\w | 1 | +| redos.py:166:26:166:32 | \\S\|[\\w] | redos.py:166:26:166:27 | \\S | 0 | +| redos.py:166:26:166:32 | \\S\|[\\w] | redos.py:166:29:166:32 | [\\w] | 1 | +| redos.py:169:26:169:35 | 1s\|[\\da-z] | redos.py:169:26:169:27 | 1s | 0 | +| redos.py:169:26:169:35 | 1s\|[\\da-z] | redos.py:169:29:169:35 | [\\da-z] | 1 | +| redos.py:172:26:172:31 | 0\|[\\d] | redos.py:172:26:172:26 | 0 | 0 | +| redos.py:172:26:172:31 | 0\|[\\d] | redos.py:172:28:172:31 | [\\d] | 1 | +| redos.py:184:35:184:37 | >\|$ | redos.py:184:35:184:35 | > | 0 | +| redos.py:184:35:184:37 | >\|$ | redos.py:184:37:184:37 | $ | 1 | +| redos.py:187:35:187:37 | >\|$ | redos.py:187:35:187:35 | > | 0 | +| redos.py:187:35:187:37 | >\|$ | redos.py:187:37:187:37 | $ | 1 | +| redos.py:193:28:193:47 | \\s+\|#.*\|\\(\\?#[^)]*\\) | redos.py:193:28:193:30 | \\s+ | 0 | +| redos.py:193:28:193:47 | \\s+\|#.*\|\\(\\?#[^)]*\\) | redos.py:193:32:193:34 | #.* | 1 | +| redos.py:193:28:193:47 | \\s+\|#.*\|\\(\\?#[^)]*\\) | redos.py:193:36:193:47 | \\(\\?#[^)]*\\) | 2 | +| redos.py:193:53:193:72 | [?*+]\|{\\d+(?:,\\d*)?} | redos.py:193:53:193:57 | [?*+] | 0 | +| redos.py:193:53:193:72 | [?*+]\|{\\d+(?:,\\d*)?} | redos.py:193:59:193:72 | {\\d+(?:,\\d*)?} | 1 | +| redos.py:199:25:199:32 | a+\|b+\|c+ | redos.py:199:25:199:26 | a+ | 0 | +| redos.py:199:25:199:32 | a+\|b+\|c+ | redos.py:199:28:199:29 | b+ | 1 | +| redos.py:199:25:199:32 | a+\|b+\|c+ | redos.py:199:31:199:32 | c+ | 2 | +| redos.py:226:37:226:43 | $\|[^X]b | redos.py:226:37:226:37 | $ | 0 | +| redos.py:226:37:226:43 | $\|[^X]b | redos.py:226:39:226:43 | [^X]b | 1 | +| redos.py:229:36:229:42 | $\|[^X]c | redos.py:229:36:229:36 | $ | 0 | +| redos.py:229:36:229:42 | $\|[^X]c | redos.py:229:38:229:42 | [^X]c | 1 | +| redos.py:259:25:259:124 | .thisisagoddamnlongstringforstresstestingthequery\|\\sthisisagoddamnlongstringforstresstestingthequery | redos.py:259:25:259:73 | .thisisagoddamnlongstringforstresstestingthequery | 0 | +| redos.py:259:25:259:124 | .thisisagoddamnlongstringforstresstestingthequery\|\\sthisisagoddamnlongstringforstresstestingthequery | redos.py:259:75:259:124 | \\sthisisagoddamnlongstringforstresstestingthequery | 1 | +| redos.py:262:25:262:85 | thisisagoddamnlongstringforstresstestingthequery\|this\\w+query | redos.py:262:25:262:72 | thisisagoddamnlongstringforstresstestingthequery | 0 | +| redos.py:262:25:262:85 | thisisagoddamnlongstringforstresstestingthequery\|this\\w+query | redos.py:262:74:262:85 | this\\w+query | 1 | +| redos.py:265:26:265:125 | thisisagoddamnlongstringforstresstestingthequery\|imanotherbutunrelatedstringcomparedtotheotherstring | redos.py:265:26:265:73 | thisisagoddamnlongstringforstresstestingthequery | 0 | +| redos.py:265:26:265:125 | thisisagoddamnlongstringforstresstestingthequery\|imanotherbutunrelatedstringcomparedtotheotherstring | redos.py:265:75:265:125 | imanotherbutunrelatedstringcomparedtotheotherstring | 1 | +| redos.py:268:29:268:57 | [\\uDC66\\uDC67]\|[\\uDC68\\uDC69] | redos.py:268:29:268:42 | [\\uDC66\\uDC67] | 0 | +| redos.py:268:29:268:57 | [\\uDC66\\uDC67]\|[\\uDC68\\uDC69] | redos.py:268:44:268:57 | [\\uDC68\\uDC69] | 1 | +| redos.py:271:29:271:59 | (\\uDC66\|\\uDC67)\|(\\uDC68\|\\uDC69) | redos.py:271:29:271:43 | (\\uDC66\|\\uDC67) | 0 | +| redos.py:271:29:271:59 | (\\uDC66\|\\uDC67)\|(\\uDC68\|\\uDC69) | redos.py:271:45:271:59 | (\\uDC68\|\\uDC69) | 1 | +| redos.py:271:30:271:42 | \\uDC66\|\\uDC67 | redos.py:271:30:271:35 | \\uDC66 | 0 | +| redos.py:271:30:271:42 | \\uDC66\|\\uDC67 | redos.py:271:37:271:42 | \\uDC67 | 1 | +| redos.py:271:46:271:58 | \\uDC68\|\\uDC69 | redos.py:271:46:271:51 | \\uDC68 | 0 | +| redos.py:271:46:271:58 | \\uDC68\|\\uDC69 | redos.py:271:53:271:58 | \\uDC69 | 1 | +| redos.py:277:54:277:84 | (?:"[^"]*")\|(?:'[^']*')\|[^>\\s]+ | redos.py:277:54:277:64 | (?:"[^"]*") | 0 | +| redos.py:277:54:277:84 | (?:"[^"]*")\|(?:'[^']*')\|[^>\\s]+ | redos.py:277:66:277:76 | (?:'[^']*') | 1 | +| redos.py:277:54:277:84 | (?:"[^"]*")\|(?:'[^']*')\|[^>\\s]+ | redos.py:277:78:277:84 | [^>\\s]+ | 2 | +| redos.py:286:31:286:42 | [\\s\\S]{2,}\|X | redos.py:286:31:286:40 | [\\s\\S]{2,} | 0 | +| redos.py:286:31:286:42 | [\\s\\S]{2,}\|X | redos.py:286:42:286:42 | X | 1 | +| redos.py:289:31:289:39 | [\\s\\S]*\|X | redos.py:289:31:289:37 | [\\s\\S]* | 0 | +| redos.py:289:31:289:39 | [\\s\\S]*\|X | redos.py:289:39:289:39 | X | 1 | +| redos.py:292:25:292:38 | (a+)*$\|[\\s\\S]+ | redos.py:292:25:292:30 | (a+)*$ | 0 | +| redos.py:292:25:292:38 | (a+)*$\|[\\s\\S]+ | redos.py:292:32:292:38 | [\\s\\S]+ | 1 | +| redos.py:295:26:295:39 | [\\s\\S]+\|(a+)*$ | redos.py:295:26:295:32 | [\\s\\S]+ | 0 | +| redos.py:295:26:295:39 | [\\s\\S]+\|(a+)*$ | redos.py:295:34:295:39 | (a+)*$ | 1 | +| redos.py:298:27:298:29 | ;\|^ | redos.py:298:27:298:27 | ; | 0 | +| redos.py:298:27:298:29 | ;\|^ | redos.py:298:29:298:29 | ^ | 1 | +| redos.py:301:25:301:27 | ^\|; | redos.py:301:25:301:25 | ^ | 0 | +| redos.py:301:25:301:27 | ^\|; | redos.py:301:27:301:27 | ; | 1 | +| redos.py:301:30:301:32 | 0\|1 | redos.py:301:30:301:30 | 0 | 0 | +| redos.py:301:30:301:32 | 0\|1 | redos.py:301:32:301:32 | 1 | 1 | +| redos.py:301:35:301:37 | 0\|1 | redos.py:301:35:301:35 | 0 | 0 | +| redos.py:301:35:301:37 | 0\|1 | redos.py:301:37:301:37 | 1 | 1 | +| redos.py:301:40:301:42 | 0\|1 | redos.py:301:40:301:40 | 0 | 0 | +| redos.py:301:40:301:42 | 0\|1 | redos.py:301:42:301:42 | 1 | 1 | +| redos.py:301:45:301:47 | 0\|1 | redos.py:301:45:301:45 | 0 | 0 | +| redos.py:301:45:301:47 | 0\|1 | redos.py:301:47:301:47 | 1 | 1 | +| redos.py:301:50:301:52 | 0\|1 | redos.py:301:50:301:50 | 0 | 0 | +| redos.py:301:50:301:52 | 0\|1 | redos.py:301:52:301:52 | 1 | 1 | +| redos.py:301:55:301:57 | 0\|1 | redos.py:301:55:301:55 | 0 | 0 | +| redos.py:301:55:301:57 | 0\|1 | redos.py:301:57:301:57 | 1 | 1 | +| redos.py:301:60:301:62 | 0\|1 | redos.py:301:60:301:60 | 0 | 0 | +| redos.py:301:60:301:62 | 0\|1 | redos.py:301:62:301:62 | 1 | 1 | +| redos.py:301:65:301:67 | 0\|1 | redos.py:301:65:301:65 | 0 | 0 | +| redos.py:301:65:301:67 | 0\|1 | redos.py:301:67:301:67 | 1 | 1 | +| redos.py:301:70:301:72 | 0\|1 | redos.py:301:70:301:70 | 0 | 0 | +| redos.py:301:70:301:72 | 0\|1 | redos.py:301:72:301:72 | 1 | 1 | +| redos.py:301:75:301:77 | 0\|1 | redos.py:301:75:301:75 | 0 | 0 | +| redos.py:301:75:301:77 | 0\|1 | redos.py:301:77:301:77 | 1 | 1 | +| redos.py:301:80:301:82 | 0\|1 | redos.py:301:80:301:80 | 0 | 0 | +| redos.py:301:80:301:82 | 0\|1 | redos.py:301:82:301:82 | 1 | 1 | +| redos.py:301:85:301:87 | 0\|1 | redos.py:301:85:301:85 | 0 | 0 | +| redos.py:301:85:301:87 | 0\|1 | redos.py:301:87:301:87 | 1 | 1 | +| redos.py:301:90:301:92 | 0\|1 | redos.py:301:90:301:90 | 0 | 0 | +| redos.py:301:90:301:92 | 0\|1 | redos.py:301:92:301:92 | 1 | 1 | +| redos.py:301:95:301:97 | 0\|1 | redos.py:301:95:301:95 | 0 | 0 | +| redos.py:301:95:301:97 | 0\|1 | redos.py:301:97:301:97 | 1 | 1 | +| redos.py:310:27:310:32 | [^/]\|X | redos.py:310:27:310:30 | [^/] | 0 | +| redos.py:310:27:310:32 | [^/]\|X | redos.py:310:32:310:32 | X | 1 | +| redos.py:313:40:313:42 | Y\|$ | redos.py:313:40:313:40 | Y | 0 | +| redos.py:313:40:313:42 | Y\|$ | redos.py:313:42:313:42 | $ | 1 | +| redos.py:334:27:334:30 | a\|a? | redos.py:334:27:334:27 | a | 0 | +| redos.py:334:27:334:30 | a\|a? | redos.py:334:29:334:30 | a? | 1 | +| redos.py:340:29:340:39 | [a-c]\|[c-d] | redos.py:340:29:340:33 | [a-c] | 0 | +| redos.py:340:29:340:39 | [a-c]\|[c-d] | redos.py:340:35:340:39 | [c-d] | 1 | +| redos.py:340:43:340:52 | e?e?e?e?\|X | redos.py:340:43:340:50 | e?e?e?e? | 0 | +| redos.py:340:43:340:52 | e?e?e?e?\|X | redos.py:340:52:340:52 | X | 1 | +| redos.py:340:57:340:68 | cTcT\|cTXcTX$ | redos.py:340:57:340:60 | cTcT | 0 | +| redos.py:340:57:340:68 | cTcT\|cTXcTX$ | redos.py:340:62:340:68 | cTXcTX$ | 1 | +| redos.py:358:26:358:28 | a\|b | redos.py:358:26:358:26 | a | 0 | +| redos.py:358:26:358:28 | a\|b | redos.py:358:28:358:28 | b | 1 | +| redos.py:359:28:359:60 | [\\s;,"'<>(){}\|[\\]@=+*]\|:(?![/\\\\]) | redos.py:359:28:359:49 | [\\s;,"'<>(){}\|[\\]@=+*] | 0 | +| redos.py:359:28:359:60 | [\\s;,"'<>(){}\|[\\]@=+*]\|:(?![/\\\\]) | redos.py:359:51:359:60 | :(?![/\\\\]) | 1 | +| redos.py:362:26:362:38 | (?:a{\|-)\|\\w\\{ | redos.py:362:26:362:33 | (?:a{\|-) | 0 | +| redos.py:362:26:362:38 | (?:a{\|-)\|\\w\\{ | redos.py:362:35:362:38 | \\w\\{ | 1 | +| redos.py:362:29:362:32 | a{\|- | redos.py:362:29:362:30 | a{ | 0 | +| redos.py:362:29:362:32 | a{\|- | redos.py:362:32:362:32 | - | 1 | +| redos.py:363:26:363:41 | (?:a{0\|-)\|\\w\\{\\d | redos.py:363:26:363:34 | (?:a{0\|-) | 0 | +| redos.py:363:26:363:41 | (?:a{0\|-)\|\\w\\{\\d | redos.py:363:36:363:41 | \\w\\{\\d | 1 | +| redos.py:363:29:363:33 | a{0\|- | redos.py:363:29:363:31 | a{0 | 0 | +| redos.py:363:29:363:33 | a{0\|- | redos.py:363:33:363:33 | - | 1 | +| redos.py:364:26:364:43 | (?:a{0,\|-)\|\\w\\{\\d, | redos.py:364:26:364:35 | (?:a{0,\|-) | 0 | +| redos.py:364:26:364:43 | (?:a{0,\|-)\|\\w\\{\\d, | redos.py:364:37:364:43 | \\w\\{\\d, | 1 | +| redos.py:364:29:364:34 | a{0,\|- | redos.py:364:29:364:32 | a{0, | 0 | +| redos.py:364:29:364:34 | a{0,\|- | redos.py:364:34:364:34 | - | 1 | +| redos.py:365:26:365:46 | (?:a{0,2\|-)\|\\w\\{\\d,\\d | redos.py:365:26:365:36 | (?:a{0,2\|-) | 0 | +| redos.py:365:26:365:46 | (?:a{0,2\|-)\|\\w\\{\\d,\\d | redos.py:365:38:365:46 | \\w\\{\\d,\\d | 1 | +| redos.py:365:29:365:35 | a{0,2\|- | redos.py:365:29:365:33 | a{0,2 | 0 | +| redos.py:365:29:365:35 | a{0,2\|- | redos.py:365:35:365:35 | - | 1 | +| redos.py:368:27:368:50 | (?:a{0,2}\|-)\|\\w\\{\\d,\\d\\} | redos.py:368:27:368:38 | (?:a{0,2}\|-) | 0 | +| redos.py:368:27:368:50 | (?:a{0,2}\|-)\|\\w\\{\\d,\\d\\} | redos.py:368:40:368:50 | \\w\\{\\d,\\d\\} | 1 | +| redos.py:368:30:368:37 | a{0,2}\|- | redos.py:368:30:368:35 | a{0,2} | 0 | +| redos.py:368:30:368:37 | a{0,2}\|- | redos.py:368:37:368:37 | - | 1 | +| unittests.py:4:18:4:25 | [^\\.]\|\\. | unittests.py:4:18:4:22 | [^\\.] | 0 | +| unittests.py:4:18:4:25 | [^\\.]\|\\. | unittests.py:4:24:4:25 | \\. | 1 | +| unittests.py:5:18:5:21 | \u00c6\|\\\u00c6 | unittests.py:5:18:5:18 | \u00c6 | 0 | +| unittests.py:5:18:5:21 | \u00c6\|\\\u00c6 | unittests.py:5:20:5:21 | \\\u00c6 | 1 | +| unittests.py:8:19:8:22 | .\|\\n | unittests.py:8:19:8:19 | . | 0 | +| unittests.py:8:19:8:22 | .\|\\n | unittests.py:8:21:8:22 | \\n | 1 | +| unittests.py:9:19:9:22 | .\|\\n | unittests.py:9:19:9:19 | . | 0 | +| unittests.py:9:19:9:22 | .\|\\n | unittests.py:9:21:9:22 | \\n | 1 | +quantifierChild +| KnownCVEs.py:5:28:5:30 | \\s* | KnownCVEs.py:5:28:5:29 | \\s | 0 | +| KnownCVEs.py:5:32:5:34 | \\d+ | KnownCVEs.py:5:32:5:33 | \\d | 0 | +| KnownCVEs.py:5:36:5:38 | \\s* | KnownCVEs.py:5:36:5:37 | \\s | 0 | +| KnownCVEs.py:5:40:5:42 | \\S+ | KnownCVEs.py:5:40:5:41 | \\S | 0 | +| KnownCVEs.py:5:46:5:47 | .* | KnownCVEs.py:5:46:5:46 | . | 0 | +| KnownCVEs.py:15:16:15:20 | [+-]? | KnownCVEs.py:15:16:15:19 | [+-] | 0 | +| KnownCVEs.py:15:21:15:26 | (\\d+)* | KnownCVEs.py:15:21:15:25 | (\\d+) | 0 | +| KnownCVEs.py:15:22:15:24 | \\d+ | KnownCVEs.py:15:22:15:23 | \\d | 0 | +| KnownCVEs.py:15:29:15:31 | \\d+ | KnownCVEs.py:15:29:15:30 | \\d | 0 | +| KnownCVEs.py:15:32:15:33 | %? | KnownCVEs.py:15:32:15:32 | % | 0 | +| KnownCVEs.py:16:19:16:21 | \\s+ | KnownCVEs.py:16:19:16:20 | \\s | 0 | +| KnownCVEs.py:16:22:16:31 | (?:.\|\\n)*? | KnownCVEs.py:16:22:16:29 | (?:.\|\\n) | 0 | +| KnownCVEs.py:16:32:16:34 | \\s+ | KnownCVEs.py:16:32:16:33 | \\s | 0 | +| KnownCVEs.py:17:19:17:21 | \\s+ | KnownCVEs.py:17:19:17:20 | \\s | 0 | +| KnownCVEs.py:17:24:17:26 | \\S+ | KnownCVEs.py:17:24:17:25 | \\S | 0 | +| KnownCVEs.py:17:29:17:31 | \\s+ | KnownCVEs.py:17:29:17:30 | \\s | 0 | +| KnownCVEs.py:17:32:17:36 | [^}]+ | KnownCVEs.py:17:32:17:35 | [^}] | 0 | +| KnownCVEs.py:17:37:17:39 | \\s+ | KnownCVEs.py:17:37:17:38 | \\s | 0 | +| KnownCVEs.py:18:17:18:18 | .* | KnownCVEs.py:18:17:18:17 | . | 0 | +| KnownCVEs.py:18:21:18:22 | .* | KnownCVEs.py:18:21:18:21 | . | 0 | +| KnownCVEs.py:18:25:18:26 | .* | KnownCVEs.py:18:25:18:25 | . | 0 | +| KnownCVEs.py:19:17:19:19 | \\s* | KnownCVEs.py:19:17:19:18 | \\s | 0 | +| KnownCVEs.py:19:21:19:42 | (?:(.+)(\\s*)(=)(\\s*))? | KnownCVEs.py:19:21:19:41 | (?:(.+)(\\s*)(=)(\\s*)) | 0 | +| KnownCVEs.py:19:25:19:26 | .+ | KnownCVEs.py:19:25:19:25 | . | 0 | +| KnownCVEs.py:19:29:19:31 | \\s* | KnownCVEs.py:19:29:19:30 | \\s | 0 | +| KnownCVEs.py:19:37:19:39 | \\s* | KnownCVEs.py:19:37:19:38 | \\s | 0 | +| KnownCVEs.py:19:44:19:45 | .+ | KnownCVEs.py:19:44:19:44 | . | 0 | +| KnownCVEs.py:19:52:19:53 | .* | KnownCVEs.py:19:52:19:52 | . | 0 | +| KnownCVEs.py:19:60:19:62 | \\s* | KnownCVEs.py:19:60:19:61 | \\s | 0 | +| KnownCVEs.py:20:26:20:28 | \\s* | KnownCVEs.py:20:26:20:27 | \\s | 0 | +| KnownCVEs.py:20:31:20:33 | \\s* | KnownCVEs.py:20:31:20:32 | \\s | 0 | +| KnownCVEs.py:20:36:20:38 | \\w+ | KnownCVEs.py:20:36:20:37 | \\w | 0 | +| KnownCVEs.py:20:41:20:43 | \\s* | KnownCVEs.py:20:41:20:42 | \\s | 0 | +| KnownCVEs.py:20:45:20:47 | \\s* | KnownCVEs.py:20:45:20:46 | \\s | 0 | +| KnownCVEs.py:20:50:20:52 | .*? | KnownCVEs.py:20:50:20:50 | . | 0 | +| KnownCVEs.py:20:55:20:57 | \\s* | KnownCVEs.py:20:55:20:56 | \\s | 0 | +| KnownCVEs.py:20:60:20:62 | \\s* | KnownCVEs.py:20:60:20:61 | \\s | 0 | +| KnownCVEs.py:21:23:21:25 | \\s* | KnownCVEs.py:21:23:21:24 | \\s | 0 | +| KnownCVEs.py:21:32:21:34 | \\s* | KnownCVEs.py:21:32:21:33 | \\s | 0 | +| KnownCVEs.py:21:35:21:37 | .*? | KnownCVEs.py:21:35:21:35 | . | 0 | +| KnownCVEs.py:21:38:21:40 | \\s* | KnownCVEs.py:21:38:21:39 | \\s | 0 | +| KnownCVEs.py:22:39:22:42 | (%)? | KnownCVEs.py:22:39:22:41 | (%) | 0 | +| KnownCVEs.py:22:44:22:70 | (\\s*[#\\w\\-"\\'.]+[^=,%}]+?)? | KnownCVEs.py:22:44:22:69 | (\\s*[#\\w\\-"\\'.]+[^=,%}]+?) | 0 | +| KnownCVEs.py:22:45:22:47 | \\s* | KnownCVEs.py:22:45:22:46 | \\s | 0 | +| KnownCVEs.py:22:48:22:59 | [#\\w\\-"\\'.]+ | KnownCVEs.py:22:48:22:58 | [#\\w\\-"\\'.] | 0 | +| KnownCVEs.py:22:60:22:68 | [^=,%}]+? | KnownCVEs.py:22:60:22:66 | [^=,%}] | 0 | +| KnownCVEs.py:23:19:23:21 | \\w+ | KnownCVEs.py:23:19:23:20 | \\w | 0 | +| KnownCVEs.py:23:26:23:28 | \\s* | KnownCVEs.py:23:26:23:27 | \\s | 0 | +| KnownCVEs.py:23:30:23:32 | \\s* | KnownCVEs.py:23:30:23:31 | \\s | 0 | +| KnownCVEs.py:23:35:23:39 | [^;]* | KnownCVEs.py:23:35:23:38 | [^;] | 0 | +| KnownCVEs.py:23:42:23:44 | \\s* | KnownCVEs.py:23:42:23:43 | \\s | 0 | +| KnownCVEs.py:27:35:27:37 | \\S+ | KnownCVEs.py:27:35:27:36 | \\S | 0 | +| KnownCVEs.py:27:39:27:53 | [a-zA-Z0-9._-]+ | KnownCVEs.py:27:39:27:52 | [a-zA-Z0-9._-] | 0 | +| KnownCVEs.py:27:56:27:70 | [a-zA-Z0-9._-]+ | KnownCVEs.py:27:56:27:69 | [a-zA-Z0-9._-] | 0 | +| KnownCVEs.py:30:21:31:28 | (?:.*,)* | KnownCVEs.py:30:21:31:27 | (?:.*,) | 0 | +| KnownCVEs.py:30:24:31:25 | .* | KnownCVEs.py:30:24:31:24 | . | 0 | +| KnownCVEs.py:30:29:31:33 | [ \t]* | KnownCVEs.py:30:29:31:32 | [ \t] | 0 | +| KnownCVEs.py:30:35:31:40 | [^ \t]+ | KnownCVEs.py:30:35:31:39 | [^ \t] | 0 | +| KnownCVEs.py:30:42:31:46 | [ \t]+ | KnownCVEs.py:30:42:31:45 | [ \t] | 0 | +| KnownCVEs.py:30:54:31:58 | ["']? | KnownCVEs.py:30:54:31:57 | ["'] | 0 | +| KnownCVEs.py:30:61:31:66 | [^"']* | KnownCVEs.py:30:61:31:65 | [^"'] | 0 | +| KnownCVEs.py:35:18:35:81 | ([-/:,#%.'"\\s!\\w]\|\\w-\\w\|'[\\s\\w]+'\\s*\|"[\\s\\w]+"\|\\([\\d,%\\.\\s]+\\))* | KnownCVEs.py:35:18:35:80 | ([-/:,#%.'"\\s!\\w]\|\\w-\\w\|'[\\s\\w]+'\\s*\|"[\\s\\w]+"\|\\([\\d,%\\.\\s]+\\)) | 0 | +| KnownCVEs.py:35:43:35:49 | [\\s\\w]+ | KnownCVEs.py:35:43:35:48 | [\\s\\w] | 0 | +| KnownCVEs.py:35:51:35:53 | \\s* | KnownCVEs.py:35:51:35:52 | \\s | 0 | +| KnownCVEs.py:35:56:35:62 | [\\s\\w]+ | KnownCVEs.py:35:56:35:61 | [\\s\\w] | 0 | +| KnownCVEs.py:35:67:35:77 | [\\d,%\\.\\s]+ | KnownCVEs.py:35:67:35:76 | [\\d,%\\.\\s] | 0 | +| KnownCVEs.py:80:10:83:144 | (:?(([a-zA-Z]{1})\|([a-zA-Z]{1}[a-zA-Z]{1})\|([a-zA-Z]{1}[0-9]{1})\|([0-9]{1}[a-zA-Z]{1})\|([a-zA-Z0-9][-_a-zA-Z0-9]{0,61}[a-zA-Z0-9]))\\.)+ | KnownCVEs.py:80:10:83:143 | (:?(([a-zA-Z]{1})\|([a-zA-Z]{1}[a-zA-Z]{1})\|([a-zA-Z]{1}[0-9]{1})\|([0-9]{1}[a-zA-Z]{1})\|([a-zA-Z0-9][-_a-zA-Z0-9]{0,61}[a-zA-Z0-9]))\\.) | 0 | +| KnownCVEs.py:80:11:83:12 | :? | KnownCVEs.py:80:11:83:11 | : | 0 | +| KnownCVEs.py:80:15:83:25 | [a-zA-Z]{1} | KnownCVEs.py:80:15:83:22 | [a-zA-Z] | 0 | +| KnownCVEs.py:80:29:83:39 | [a-zA-Z]{1} | KnownCVEs.py:80:29:83:36 | [a-zA-Z] | 0 | +| KnownCVEs.py:80:40:83:50 | [a-zA-Z]{1} | KnownCVEs.py:80:40:83:47 | [a-zA-Z] | 0 | +| KnownCVEs.py:80:54:83:64 | [a-zA-Z]{1} | KnownCVEs.py:80:54:83:61 | [a-zA-Z] | 0 | +| KnownCVEs.py:80:65:83:72 | [0-9]{1} | KnownCVEs.py:80:65:83:69 | [0-9] | 0 | +| KnownCVEs.py:80:76:83:83 | [0-9]{1} | KnownCVEs.py:80:76:83:80 | [0-9] | 0 | +| KnownCVEs.py:80:84:83:94 | [a-zA-Z]{1} | KnownCVEs.py:80:84:83:91 | [a-zA-Z] | 0 | +| KnownCVEs.py:80:109:83:127 | [-_a-zA-Z0-9]{0,61} | KnownCVEs.py:80:109:83:121 | [-_a-zA-Z0-9] | 0 | +| KnownCVEs.py:80:146:83:159 | [a-zA-Z]{2,13} | KnownCVEs.py:80:146:83:153 | [a-zA-Z] | 0 | +| KnownCVEs.py:80:166:83:182 | [a-zA-Z0-9]{2,30} | KnownCVEs.py:80:166:83:176 | [a-zA-Z0-9] | 0 | +| redos.py:6:28:6:42 | (?:__\|[\\s\\S])+? | redos.py:6:28:6:40 | (?:__\|[\\s\\S]) | 0 | +| redos.py:6:52:6:68 | (?:\\*\\*\|[\\s\\S])+? | redos.py:6:52:6:66 | (?:\\*\\*\|[\\s\\S]) | 0 | +| redos.py:11:29:11:41 | (?:__\|[^_])+? | redos.py:11:29:11:39 | (?:__\|[^_]) | 0 | +| redos.py:11:51:11:65 | (?:\\*\\*\|[^*])+? | redos.py:11:51:11:63 | (?:\\*\\*\|[^*]) | 0 | +| redos.py:16:24:16:29 | (.*,)+ | redos.py:16:24:16:28 | (.*,) | 0 | +| redos.py:16:25:16:26 | .* | redos.py:16:25:16:25 | . | 0 | +| redos.py:16:30:16:31 | .+ | redos.py:16:30:16:30 | . | 0 | +| redos.py:21:24:21:105 | (?:\\s+(?:"(?:[^"\\\\]\|\\\\\\\\\|\\\\.)+"\|'(?:[^'\\\\]\|\\\\\\\\\|\\\\.)+'\|\\((?:[^)\\\\]\|\\\\\\\\\|\\\\.)+\\)))? | redos.py:21:24:21:104 | (?:\\s+(?:"(?:[^"\\\\]\|\\\\\\\\\|\\\\.)+"\|'(?:[^'\\\\]\|\\\\\\\\\|\\\\.)+'\|\\((?:[^)\\\\]\|\\\\\\\\\|\\\\.)+\\))) | 0 | +| redos.py:21:27:21:29 | \\s+ | redos.py:21:27:21:28 | \\s | 0 | +| redos.py:21:34:21:53 | (?:[^"\\\\]\|\\\\\\\\\|\\\\.)+ | redos.py:21:34:21:52 | (?:[^"\\\\]\|\\\\\\\\\|\\\\.) | 0 | +| redos.py:21:57:21:76 | (?:[^'\\\\]\|\\\\\\\\\|\\\\.)+ | redos.py:21:57:21:75 | (?:[^'\\\\]\|\\\\\\\\\|\\\\.) | 0 | +| redos.py:21:81:21:100 | (?:[^)\\\\]\|\\\\\\\\\|\\\\.)+ | redos.py:21:81:21:99 | (?:[^)\\\\]\|\\\\\\\\\|\\\\.) | 0 | +| redos.py:25:28:25:56 | (?:[\\s\\S]*?\\(\\*[\\s\\S]*?\\*\\))* | redos.py:25:28:25:55 | (?:[\\s\\S]*?\\(\\*[\\s\\S]*?\\*\\)) | 0 | +| redos.py:25:31:25:38 | [\\s\\S]*? | redos.py:25:31:25:36 | [\\s\\S] | 0 | +| redos.py:25:43:25:50 | [\\s\\S]*? | redos.py:25:43:25:48 | [\\s\\S] | 0 | +| redos.py:25:57:25:64 | [\\s\\S]*? | redos.py:25:57:25:62 | [\\s\\S] | 0 | +| redos.py:30:25:30:26 | * | redos.py:30:25:30:25 | | 0 | +| redos.py:30:30:30:31 | .* | redos.py:30:30:30:30 | . | 0 | +| redos.py:30:34:30:35 | .* | redos.py:30:34:30:34 | . | 0 | +| redos.py:30:39:30:40 | * | redos.py:30:39:30:39 | | 0 | +| redos.py:30:42:30:46 | [-:]+ | redos.py:30:42:30:45 | [-:] | 0 | +| redos.py:30:47:30:48 | * | redos.py:30:47:30:47 | | 0 | +| redos.py:30:51:30:57 | [-\| :]* | redos.py:30:51:30:56 | [-\| :] | 0 | +| redos.py:30:62:30:80 | (?:.*\\\|.*(?:\\n\|$))* | redos.py:30:62:30:79 | (?:.*\\\|.*(?:\\n\|$)) | 0 | +| redos.py:30:65:30:66 | .* | redos.py:30:65:30:65 | . | 0 | +| redos.py:30:69:30:70 | .* | redos.py:30:69:30:69 | . | 0 | +| redos.py:30:82:30:84 | \\n* | redos.py:30:82:30:83 | \\n | 0 | +| redos.py:33:24:33:25 | * | redos.py:33:24:33:24 | | 0 | +| redos.py:33:29:33:30 | .* | redos.py:33:29:33:29 | . | 0 | +| redos.py:33:33:33:34 | .* | redos.py:33:33:33:33 | . | 0 | +| redos.py:33:38:33:39 | * | redos.py:33:38:33:38 | | 0 | +| redos.py:33:41:33:45 | [-:]+ | redos.py:33:41:33:44 | [-:] | 0 | +| redos.py:33:46:33:47 | * | redos.py:33:46:33:46 | | 0 | +| redos.py:33:50:33:56 | [-\| :]* | redos.py:33:50:33:55 | [-\| :] | 0 | +| redos.py:33:61:33:79 | (?:.*\\\|.*(?:\\n\|$))* | redos.py:33:61:33:78 | (?:.*\\\|.*(?:\\n\|$)) | 0 | +| redos.py:33:64:33:65 | .* | redos.py:33:64:33:64 | . | 0 | +| redos.py:33:68:33:69 | .* | redos.py:33:68:33:68 | . | 0 | +| redos.py:38:33:38:42 | (\\\\\\/\|.)*? | redos.py:38:33:38:40 | (\\\\\\/\|.) | 0 | +| redos.py:38:45:38:50 | [gim]* | redos.py:38:45:38:49 | [gim] | 0 | +| redos.py:43:24:43:40 | ([\\s\\[\\{\\(]\|#.*)* | redos.py:43:24:43:39 | ([\\s\\[\\{\\(]\|#.*) | 0 | +| redos.py:43:37:43:38 | .* | redos.py:43:37:43:37 | . | 0 | +| redos.py:46:24:46:36 | (\\r\\n\|\\r\|\\n)+ | redos.py:46:24:46:35 | (\\r\\n\|\\r\|\\n) | 0 | +| redos.py:49:31:49:53 | (?:[^"']\|".*?"\|'.*?')*? | redos.py:49:31:49:51 | (?:[^"']\|".*?"\|'.*?') | 0 | +| redos.py:49:41:49:43 | .*? | redos.py:49:41:49:41 | . | 0 | +| redos.py:49:47:49:49 | .*? | redos.py:49:47:49:47 | . | 0 | +| redos.py:54:32:54:43 | [\\_$a-z0-9]* | redos.py:54:32:54:42 | [\\_$a-z0-9] | 0 | +| redos.py:54:44:54:53 | (\\[.*?\\])* | redos.py:54:44:54:52 | (\\[.*?\\]) | 0 | +| redos.py:54:47:54:49 | .*? | redos.py:54:47:54:47 | . | 0 | +| redos.py:54:54:54:88 | (\\.[\\_$a-z][\\_$a-z0-9]*(\\[.*?\\])*)* | redos.py:54:54:54:87 | (\\.[\\_$a-z][\\_$a-z0-9]*(\\[.*?\\])*) | 0 | +| redos.py:54:65:54:76 | [\\_$a-z0-9]* | redos.py:54:65:54:75 | [\\_$a-z0-9] | 0 | +| redos.py:54:77:54:86 | (\\[.*?\\])* | redos.py:54:77:54:85 | (\\[.*?\\]) | 0 | +| redos.py:54:80:54:82 | .*? | redos.py:54:80:54:80 | . | 0 | +| redos.py:57:24:57:29 | (a\|.)* | redos.py:57:24:57:28 | (a\|.) | 0 | +| redos.py:60:24:60:32 | ([a-z]+)+ | redos.py:60:24:60:31 | ([a-z]+) | 0 | +| redos.py:60:25:60:30 | [a-z]+ | redos.py:60:25:60:29 | [a-z] | 0 | +| redos.py:61:24:61:32 | ([a-z]*)* | redos.py:61:24:61:31 | ([a-z]*) | 0 | +| redos.py:61:25:61:30 | [a-z]* | redos.py:61:25:61:29 | [a-z] | 0 | +| redos.py:62:37:62:67 | (([\\\\-.]\|[_]+)?([a-zA-Z0-9]+))* | redos.py:62:37:62:66 | (([\\\\-.]\|[_]+)?([a-zA-Z0-9]+)) | 0 | +| redos.py:62:38:62:51 | ([\\\\-.]\|[_]+)? | redos.py:62:38:62:50 | ([\\\\-.]\|[_]+) | 0 | +| redos.py:62:46:62:49 | [_]+ | redos.py:62:46:62:48 | [_] | 0 | +| redos.py:62:53:62:64 | [a-zA-Z0-9]+ | redos.py:62:53:62:63 | [a-zA-Z0-9] | 0 | +| redos.py:62:68:62:73 | (@){1} | redos.py:62:68:62:70 | (@) | 0 | +| redos.py:62:74:62:82 | [a-z0-9]+ | redos.py:62:74:62:81 | [a-z0-9] | 0 | +| redos.py:62:83:62:88 | [.]{1} | redos.py:62:83:62:85 | [.] | 0 | +| redos.py:62:91:62:100 | [a-z]{2,3} | redos.py:62:91:62:95 | [a-z] | 0 | +| redos.py:62:104:62:113 | [a-z]{2,3} | redos.py:62:104:62:108 | [a-z] | 0 | +| redos.py:62:114:62:119 | [.]{1} | redos.py:62:114:62:116 | [.] | 0 | +| redos.py:62:120:62:129 | [a-z]{2,3} | redos.py:62:120:62:124 | [a-z] | 0 | +| redos.py:63:25:63:36 | (([a-z])+.)+ | redos.py:63:25:63:35 | (([a-z])+.) | 0 | +| redos.py:63:26:63:33 | ([a-z])+ | redos.py:63:26:63:32 | ([a-z]) | 0 | +| redos.py:63:42:63:49 | ([a-z])+ | redos.py:63:42:63:48 | ([a-z]) | 0 | +| redos.py:68:25:68:54 | ([\\w#:.~>+()\\s-]+\|\\*\|\\[.*?\\])+ | redos.py:68:25:68:53 | ([\\w#:.~>+()\\s-]+\|\\*\|\\[.*?\\]) | 0 | +| redos.py:68:26:68:41 | [\\w#:.~>+()\\s-]+ | redos.py:68:26:68:40 | [\\w#:.~>+()\\s-] | 0 | +| redos.py:68:48:68:50 | .*? | redos.py:68:48:68:48 | . | 0 | +| redos.py:68:56:68:58 | \\s* | redos.py:68:56:68:57 | \\s | 0 | +| redos.py:73:29:73:36 | (\\\\?.)*? | redos.py:73:29:73:34 | (\\\\?.) | 0 | +| redos.py:73:30:73:32 | \\\\? | redos.py:73:30:73:31 | \\\\ | 0 | +| redos.py:76:24:76:31 | (b\|a?b)* | redos.py:76:24:76:30 | (b\|a?b) | 0 | +| redos.py:76:27:76:28 | a? | redos.py:76:27:76:27 | a | 0 | +| redos.py:79:24:79:31 | (a\|aa?)* | redos.py:79:24:79:30 | (a\|aa?) | 0 | +| redos.py:79:28:79:29 | a? | redos.py:79:28:79:28 | a | 0 | +| redos.py:82:24:82:30 | (.\|\\n)* | redos.py:82:24:82:29 | (.\|\\n) | 0 | +| redos.py:85:24:85:30 | (.\|\\n)* | redos.py:85:24:85:29 | (.\|\\n) | 0 | +| redos.py:88:24:88:32 | ([\\w.]+)* | redos.py:88:24:88:31 | ([\\w.]+) | 0 | +| redos.py:88:25:88:30 | [\\w.]+ | redos.py:88:25:88:29 | [\\w.] | 0 | +| redos.py:91:24:91:31 | (a\|aa?)* | redos.py:91:24:91:30 | (a\|aa?) | 0 | +| redos.py:91:28:91:29 | a? | redos.py:91:28:91:28 | a | 0 | +| redos.py:97:25:97:38 | ([\\s\\S]\|[^a])* | redos.py:97:25:97:37 | ([\\s\\S]\|[^a]) | 0 | +| redos.py:100:25:100:33 | ([^"']+)* | redos.py:100:25:100:32 | ([^"']+) | 0 | +| redos.py:100:26:100:31 | [^"']+ | redos.py:100:26:100:30 | [^"'] | 0 | +| redos.py:103:25:103:33 | (.\|[^a])* | redos.py:103:25:103:32 | (.\|[^a]) | 0 | +| redos.py:106:26:106:34 | (a\|[^a])* | redos.py:106:26:106:33 | (a\|[^a]) | 0 | +| redos.py:109:25:109:33 | (b\|[^a])* | redos.py:109:25:109:32 | (b\|[^a]) | 0 | +| redos.py:112:25:112:33 | (G\|[^a])* | redos.py:112:25:112:32 | (G\|[^a]) | 0 | +| redos.py:115:25:115:37 | ([0-9]\|[^a])* | redos.py:115:25:115:36 | ([0-9]\|[^a]) | 0 | +| redos.py:118:24:118:119 | (?:=(?:([!#\\$%&'\\*\\+\\-\\.\\^_`\\\|~0-9A-Za-z]+)\|"((?:\\\\[\\x00-\\x7f]\|[^\\x00-\\x08\\x0a-\\x1f\\x7f"])*)"))? | redos.py:118:24:118:118 | (?:=(?:([!#\\$%&'\\*\\+\\-\\.\\^_`\\\|~0-9A-Za-z]+)\|"((?:\\\\[\\x00-\\x7f]\|[^\\x00-\\x08\\x0a-\\x1f\\x7f"])*)")) | 0 | +| redos.py:118:32:118:65 | [!#\\$%&'\\*\\+\\-\\.\\^_`\\\|~0-9A-Za-z]+ | redos.py:118:32:118:64 | [!#\\$%&'\\*\\+\\-\\.\\^_`\\\|~0-9A-Za-z] | 0 | +| redos.py:118:70:118:114 | (?:\\\\[\\x00-\\x7f]\|[^\\x00-\\x08\\x0a-\\x1f\\x7f"])* | redos.py:118:70:118:113 | (?:\\\\[\\x00-\\x7f]\|[^\\x00-\\x08\\x0a-\\x1f\\x7f"]) | 0 | +| redos.py:121:26:121:70 | (?:\\\\[\\x00-\\x7f]\|[^\\x00-\\x08\\x0a-\\x1f\\x7f"])* | redos.py:121:26:121:69 | (?:\\\\[\\x00-\\x7f]\|[^\\x00-\\x08\\x0a-\\x1f\\x7f"]) | 0 | +| redos.py:124:26:124:72 | (?:\\\\[\\x00-\\x7f]\|[^\\x00-\\x08\\x0a-\\x1f\\x7f"\\\\])* | redos.py:124:26:124:71 | (?:\\\\[\\x00-\\x7f]\|[^\\x00-\\x08\\x0a-\\x1f\\x7f"\\\\]) | 0 | +| redos.py:127:25:127:38 | ([a-z]\|[d-h])* | redos.py:127:25:127:37 | ([a-z]\|[d-h]) | 0 | +| redos.py:130:25:130:40 | ([^a-z]\|[^0-9])* | redos.py:130:25:130:39 | ([^a-z]\|[^0-9]) | 0 | +| redos.py:133:25:133:35 | (\\d\|[0-9])* | redos.py:133:25:133:34 | (\\d\|[0-9]) | 0 | +| redos.py:136:25:136:32 | (\\s\|\\s)* | redos.py:136:25:136:31 | (\\s\|\\s) | 0 | +| redos.py:139:25:139:31 | (\\w\|G)* | redos.py:139:25:139:30 | (\\w\|G) | 0 | +| redos.py:142:26:142:33 | (\\s\|\\d)* | redos.py:142:26:142:32 | (\\s\|\\d) | 0 | +| redos.py:145:25:145:32 | (\\d\|\\w)* | redos.py:145:25:145:31 | (\\d\|\\w) | 0 | +| redos.py:148:25:148:31 | (\\d\|5)* | redos.py:148:25:148:30 | (\\d\|5) | 0 | +| redos.py:151:25:151:34 | (\\s\|[\\f])* | redos.py:151:25:151:33 | (\\s\|[\\f]) | 0 | +| redos.py:154:25:154:38 | (\\s\|[\\v]\|\\\\v)* | redos.py:154:25:154:37 | (\\s\|[\\v]\|\\\\v) | 0 | +| redos.py:157:25:157:34 | (\\f\|[\\f])* | redos.py:157:25:157:33 | (\\f\|[\\f]) | 0 | +| redos.py:160:25:160:32 | (\\W\|\\D)* | redos.py:160:25:160:31 | (\\W\|\\D) | 0 | +| redos.py:163:25:163:32 | (\\S\|\\w)* | redos.py:163:25:163:31 | (\\S\|\\w) | 0 | +| redos.py:166:25:166:34 | (\\S\|[\\w])* | redos.py:166:25:166:33 | (\\S\|[\\w]) | 0 | +| redos.py:169:25:169:37 | (1s\|[\\da-z])* | redos.py:169:25:169:36 | (1s\|[\\da-z]) | 0 | +| redos.py:172:25:172:33 | (0\|[\\d])* | redos.py:172:25:172:32 | (0\|[\\d]) | 0 | +| redos.py:175:25:175:32 | ([\\d]+)* | redos.py:175:25:175:31 | ([\\d]+) | 0 | +| redos.py:175:26:175:30 | [\\d]+ | redos.py:175:26:175:29 | [\\d] | 0 | +| redos.py:178:25:178:37 | (\\d+(X\\d+)?)+ | redos.py:178:25:178:36 | (\\d+(X\\d+)?) | 0 | +| redos.py:178:26:178:28 | \\d+ | redos.py:178:26:178:27 | \\d | 0 | +| redos.py:178:29:178:35 | (X\\d+)? | redos.py:178:29:178:34 | (X\\d+) | 0 | +| redos.py:178:31:178:33 | \\d+ | redos.py:178:31:178:32 | \\d | 0 | +| redos.py:181:25:181:43 | ([0-9]+(X[0-9]*)?)* | redos.py:181:25:181:42 | ([0-9]+(X[0-9]*)?) | 0 | +| redos.py:181:26:181:31 | [0-9]+ | redos.py:181:26:181:30 | [0-9] | 0 | +| redos.py:181:32:181:41 | (X[0-9]*)? | redos.py:181:32:181:40 | (X[0-9]*) | 0 | +| redos.py:181:34:181:39 | [0-9]* | redos.py:181:34:181:38 | [0-9] | 0 | +| redos.py:184:26:184:33 | ([^>]+)* | redos.py:184:26:184:32 | ([^>]+) | 0 | +| redos.py:184:27:184:31 | [^>]+ | redos.py:184:27:184:30 | [^>] | 0 | +| redos.py:187:25:187:33 | ([^>a]+)* | redos.py:187:25:187:32 | ([^>a]+) | 0 | +| redos.py:187:26:187:31 | [^>a]+ | redos.py:187:26:187:30 | [^>a] | 0 | +| redos.py:190:24:190:31 | (\\n\\s*)+ | redos.py:190:24:190:30 | (\\n\\s*) | 0 | +| redos.py:190:27:190:29 | \\s* | redos.py:190:27:190:28 | \\s | 0 | +| redos.py:193:25:193:49 | (?:\\s+\|#.*\|\\(\\?#[^)]*\\))* | redos.py:193:25:193:48 | (?:\\s+\|#.*\|\\(\\?#[^)]*\\)) | 0 | +| redos.py:193:28:193:30 | \\s+ | redos.py:193:28:193:29 | \\s | 0 | +| redos.py:193:33:193:34 | .* | redos.py:193:33:193:33 | . | 0 | +| redos.py:193:41:193:45 | [^)]* | redos.py:193:41:193:44 | [^)] | 0 | +| redos.py:193:60:193:62 | \\d+ | redos.py:193:60:193:61 | \\d | 0 | +| redos.py:193:63:193:71 | (?:,\\d*)? | redos.py:193:63:193:70 | (?:,\\d*) | 0 | +| redos.py:193:67:193:69 | \\d* | redos.py:193:67:193:68 | \\d | 0 | +| redos.py:196:28:196:30 | \\s* | redos.py:196:28:196:29 | \\s | 0 | +| redos.py:196:32:196:40 | [a-zA-Z]+ | redos.py:196:32:196:39 | [a-zA-Z] | 0 | +| redos.py:196:45:196:53 | [a-zA-Z]+ | redos.py:196:45:196:52 | [a-zA-Z] | 0 | +| redos.py:196:57:196:96 | ((\\s*([a-zA-Z]+)\\: ?([ a-zA-Z{}]+),?)+)* | redos.py:196:57:196:95 | ((\\s*([a-zA-Z]+)\\: ?([ a-zA-Z{}]+),?)+) | 0 | +| redos.py:196:58:196:94 | (\\s*([a-zA-Z]+)\\: ?([ a-zA-Z{}]+),?)+ | redos.py:196:58:196:93 | (\\s*([a-zA-Z]+)\\: ?([ a-zA-Z{}]+),?) | 0 | +| redos.py:196:59:196:61 | \\s* | redos.py:196:59:196:60 | \\s | 0 | +| redos.py:196:63:196:71 | [a-zA-Z]+ | redos.py:196:63:196:70 | [a-zA-Z] | 0 | +| redos.py:196:75:196:76 | ? | redos.py:196:75:196:75 | | 0 | +| redos.py:196:78:196:89 | [ a-zA-Z{}]+ | redos.py:196:78:196:88 | [ a-zA-Z{}] | 0 | +| redos.py:196:91:196:92 | ,? | redos.py:196:91:196:91 | , | 0 | +| redos.py:196:97:196:99 | \\s* | redos.py:196:97:196:98 | \\s | 0 | +| redos.py:199:24:199:34 | (a+\|b+\|c+)* | redos.py:199:24:199:33 | (a+\|b+\|c+) | 0 | +| redos.py:199:25:199:26 | a+ | redos.py:199:25:199:25 | a | 0 | +| redos.py:199:28:199:29 | b+ | redos.py:199:28:199:28 | b | 0 | +| redos.py:199:31:199:32 | c+ | redos.py:199:31:199:31 | c | 0 | +| redos.py:202:25:202:34 | ((a+a?)*)+ | redos.py:202:25:202:33 | ((a+a?)*) | 0 | +| redos.py:202:26:202:32 | (a+a?)* | redos.py:202:26:202:31 | (a+a?) | 0 | +| redos.py:202:27:202:28 | a+ | redos.py:202:27:202:27 | a | 0 | +| redos.py:202:29:202:30 | a? | redos.py:202:29:202:29 | a | 0 | +| redos.py:202:35:202:36 | b+ | redos.py:202:35:202:35 | b | 0 | +| redos.py:205:24:205:28 | (a+)+ | redos.py:205:24:205:27 | (a+) | 0 | +| redos.py:205:25:205:26 | a+ | redos.py:205:25:205:25 | a | 0 | +| redos.py:208:25:208:29 | (a+)+ | redos.py:208:25:208:28 | (a+) | 0 | +| redos.py:208:26:208:27 | a+ | redos.py:208:26:208:26 | a | 0 | +| redos.py:208:34:208:35 | a* | redos.py:208:34:208:34 | a | 0 | +| redos.py:208:36:208:37 | a+ | redos.py:208:36:208:36 | a | 0 | +| redos.py:211:24:211:28 | (a+)+ | redos.py:211:24:211:27 | (a+) | 0 | +| redos.py:211:25:211:26 | a+ | redos.py:211:25:211:25 | a | 0 | +| redos.py:214:25:214:30 | (\\n+)+ | redos.py:214:25:214:29 | (\\n+) | 0 | +| redos.py:214:26:214:28 | \\n+ | redos.py:214:26:214:27 | \\n | 0 | +| redos.py:217:24:217:29 | (\\n+)+ | redos.py:217:24:217:28 | (\\n+) | 0 | +| redos.py:217:25:217:27 | \\n+ | redos.py:217:25:217:26 | \\n | 0 | +| redos.py:220:24:220:31 | ([^X]+)* | redos.py:220:24:220:30 | ([^X]+) | 0 | +| redos.py:220:25:220:29 | [^X]+ | redos.py:220:25:220:28 | [^X] | 0 | +| redos.py:223:24:223:34 | (([^X]b)+)* | redos.py:223:24:223:33 | (([^X]b)+) | 0 | +| redos.py:223:25:223:32 | ([^X]b)+ | redos.py:223:25:223:31 | ([^X]b) | 0 | +| redos.py:226:25:226:35 | (([^X]b)+)* | redos.py:226:25:226:34 | (([^X]b)+) | 0 | +| redos.py:226:26:226:33 | ([^X]b)+ | redos.py:226:26:226:32 | ([^X]b) | 0 | +| redos.py:229:24:229:34 | (([^X]b)+)* | redos.py:229:24:229:33 | (([^X]b)+) | 0 | +| redos.py:229:25:229:32 | ([^X]b)+ | redos.py:229:25:229:31 | ([^X]b) | 0 | +| redos.py:232:25:232:32 | ((ab)+)* | redos.py:232:25:232:31 | ((ab)+) | 0 | +| redos.py:232:26:232:30 | (ab)+ | redos.py:232:26:232:29 | (ab) | 0 | +| redos.py:235:25:235:32 | ((ab)+)* | redos.py:235:25:235:31 | ((ab)+) | 0 | +| redos.py:235:26:235:30 | (ab)+ | redos.py:235:26:235:29 | (ab) | 0 | +| redos.py:235:37:235:41 | (ab)* | redos.py:235:37:235:40 | (ab) | 0 | +| redos.py:235:42:235:46 | (ab)+ | redos.py:235:42:235:45 | (ab) | 0 | +| redos.py:238:25:238:32 | ((ab)+)* | redos.py:238:25:238:31 | ((ab)+) | 0 | +| redos.py:238:26:238:30 | (ab)+ | redos.py:238:26:238:29 | (ab) | 0 | +| redos.py:241:24:241:31 | ((ab)+)* | redos.py:241:24:241:30 | ((ab)+) | 0 | +| redos.py:241:25:241:29 | (ab)+ | redos.py:241:25:241:28 | (ab) | 0 | +| redos.py:244:25:244:32 | ((ab)+)* | redos.py:244:25:244:31 | ((ab)+) | 0 | +| redos.py:244:26:244:30 | (ab)+ | redos.py:244:26:244:29 | (ab) | 0 | +| redos.py:247:24:247:33 | ([\\n\\s]+)* | redos.py:247:24:247:32 | ([\\n\\s]+) | 0 | +| redos.py:247:25:247:31 | [\\n\\s]+ | redos.py:247:25:247:30 | [\\n\\s] | 0 | +| redos.py:250:25:250:32 | (A*A*X)* | redos.py:250:25:250:31 | (A*A*X) | 0 | +| redos.py:250:26:250:27 | A* | redos.py:250:26:250:26 | A | 0 | +| redos.py:250:28:250:29 | A* | redos.py:250:28:250:28 | A | 0 | +| redos.py:253:25:253:35 | ([^\\\\\\]]+)* | redos.py:253:25:253:34 | ([^\\\\\\]]+) | 0 | +| redos.py:253:26:253:33 | [^\\\\\\]]+ | redos.py:253:26:253:32 | [^\\\\\\]] | 0 | +| redos.py:256:24:256:101 | (\\w*foobarbaz\\w*foobarbaz\\w*foobarbaz\\w*foobarbaz\\s*foobarbaz\\d*foobarbaz\\w*)+ | redos.py:256:24:256:100 | (\\w*foobarbaz\\w*foobarbaz\\w*foobarbaz\\w*foobarbaz\\s*foobarbaz\\d*foobarbaz\\w*) | 0 | +| redos.py:256:25:256:27 | \\w* | redos.py:256:25:256:26 | \\w | 0 | +| redos.py:256:37:256:39 | \\w* | redos.py:256:37:256:38 | \\w | 0 | +| redos.py:256:49:256:51 | \\w* | redos.py:256:49:256:50 | \\w | 0 | +| redos.py:256:61:256:63 | \\w* | redos.py:256:61:256:62 | \\w | 0 | +| redos.py:256:73:256:75 | \\s* | redos.py:256:73:256:74 | \\s | 0 | +| redos.py:256:85:256:87 | \\d* | redos.py:256:85:256:86 | \\d | 0 | +| redos.py:256:97:256:99 | \\w* | redos.py:256:97:256:98 | \\w | 0 | +| redos.py:259:24:259:126 | (.thisisagoddamnlongstringforstresstestingthequery\|\\sthisisagoddamnlongstringforstresstestingthequery)* | redos.py:259:24:259:125 | (.thisisagoddamnlongstringforstresstestingthequery\|\\sthisisagoddamnlongstringforstresstestingthequery) | 0 | +| redos.py:262:24:262:87 | (thisisagoddamnlongstringforstresstestingthequery\|this\\w+query)* | redos.py:262:24:262:86 | (thisisagoddamnlongstringforstresstestingthequery\|this\\w+query) | 0 | +| redos.py:262:78:262:80 | \\w+ | redos.py:262:78:262:79 | \\w | 0 | +| redos.py:265:25:265:127 | (thisisagoddamnlongstringforstresstestingthequery\|imanotherbutunrelatedstringcomparedtotheotherstring)* | redos.py:265:25:265:126 | (thisisagoddamnlongstringforstresstestingthequery\|imanotherbutunrelatedstringcomparedtotheotherstring) | 0 | +| redos.py:268:28:268:59 | ([\\uDC66\\uDC67]\|[\\uDC68\\uDC69])* | redos.py:268:28:268:58 | ([\\uDC66\\uDC67]\|[\\uDC68\\uDC69]) | 0 | +| redos.py:271:28:271:61 | ((\\uDC66\|\\uDC67)\|(\\uDC68\|\\uDC69))* | redos.py:271:28:271:60 | ((\\uDC66\|\\uDC67)\|(\\uDC68\|\\uDC69)) | 0 | +| redos.py:274:24:274:29 | a{2,3} | redos.py:274:24:274:24 | a | 0 | +| redos.py:274:30:274:34 | (b+)+ | redos.py:274:30:274:33 | (b+) | 0 | +| redos.py:274:31:274:32 | b+ | redos.py:274:31:274:31 | b | 0 | +| redos.py:277:27:277:29 | \\w+ | redos.py:277:27:277:28 | \\w | 0 | +| redos.py:277:32:277:89 | (?:\\s+\\w+(?:\\s*=\\s*(?:(?:"[^"]*")\|(?:'[^']*')\|[^>\\s]+))?)* | redos.py:277:32:277:88 | (?:\\s+\\w+(?:\\s*=\\s*(?:(?:"[^"]*")\|(?:'[^']*')\|[^>\\s]+))?) | 0 | +| redos.py:277:35:277:37 | \\s+ | redos.py:277:35:277:36 | \\s | 0 | +| redos.py:277:38:277:40 | \\w+ | redos.py:277:38:277:39 | \\w | 0 | +| redos.py:277:41:277:87 | (?:\\s*=\\s*(?:(?:"[^"]*")\|(?:'[^']*')\|[^>\\s]+))? | redos.py:277:41:277:86 | (?:\\s*=\\s*(?:(?:"[^"]*")\|(?:'[^']*')\|[^>\\s]+)) | 0 | +| redos.py:277:44:277:46 | \\s* | redos.py:277:44:277:45 | \\s | 0 | +| redos.py:277:48:277:50 | \\s* | redos.py:277:48:277:49 | \\s | 0 | +| redos.py:277:58:277:62 | [^"]* | redos.py:277:58:277:61 | [^"] | 0 | +| redos.py:277:70:277:74 | [^']* | redos.py:277:70:277:73 | [^'] | 0 | +| redos.py:277:78:277:84 | [^>\\s]+ | redos.py:277:78:277:83 | [^>\\s] | 0 | +| redos.py:277:91:277:93 | \\s* | redos.py:277:91:277:92 | \\s | 0 | +| redos.py:277:95:277:97 | \\/? | redos.py:277:95:277:96 | \\/ | 0 | +| redos.py:280:25:280:29 | (a+)* | redos.py:280:25:280:28 | (a+) | 0 | +| redos.py:280:26:280:27 | a+ | redos.py:280:26:280:26 | a | 0 | +| redos.py:280:42:280:48 | [\\s\\S]? | redos.py:280:42:280:47 | [\\s\\S] | 0 | +| redos.py:283:25:283:29 | (a+)* | redos.py:283:25:283:28 | (a+) | 0 | +| redos.py:283:26:283:27 | a+ | redos.py:283:26:283:26 | a | 0 | +| redos.py:283:30:283:40 | [\\s\\S]{2,3} | redos.py:283:30:283:35 | [\\s\\S] | 0 | +| redos.py:286:25:286:29 | (a+)* | redos.py:286:25:286:28 | (a+) | 0 | +| redos.py:286:26:286:27 | a+ | redos.py:286:26:286:26 | a | 0 | +| redos.py:286:31:286:40 | [\\s\\S]{2,} | redos.py:286:31:286:36 | [\\s\\S] | 0 | +| redos.py:289:25:289:29 | (a+)* | redos.py:289:25:289:28 | (a+) | 0 | +| redos.py:289:26:289:27 | a+ | redos.py:289:26:289:26 | a | 0 | +| redos.py:289:31:289:37 | [\\s\\S]* | redos.py:289:31:289:36 | [\\s\\S] | 0 | +| redos.py:292:25:292:29 | (a+)* | redos.py:292:25:292:28 | (a+) | 0 | +| redos.py:292:26:292:27 | a+ | redos.py:292:26:292:26 | a | 0 | +| redos.py:292:32:292:38 | [\\s\\S]+ | redos.py:292:32:292:37 | [\\s\\S] | 0 | +| redos.py:295:26:295:32 | [\\s\\S]+ | redos.py:295:26:295:31 | [\\s\\S] | 0 | +| redos.py:295:34:295:38 | (a+)* | redos.py:295:34:295:37 | (a+) | 0 | +| redos.py:295:35:295:36 | a+ | redos.py:295:35:295:35 | a | 0 | +| redos.py:298:25:298:34 | ((;\|^)a+)+ | redos.py:298:25:298:33 | ((;\|^)a+) | 0 | +| redos.py:298:31:298:32 | a+ | redos.py:298:31:298:31 | a | 0 | +| redos.py:301:99:301:103 | (e+)+ | redos.py:301:99:301:102 | (e+) | 0 | +| redos.py:301:100:301:101 | e+ | redos.py:301:100:301:100 | e | 0 | +| redos.py:304:27:304:31 | (c+)+ | redos.py:304:27:304:30 | (c+) | 0 | +| redos.py:304:28:304:29 | c+ | redos.py:304:28:304:28 | c | 0 | +| redos.py:307:24:307:37 | (\\d(\\s+)*){20} | redos.py:307:24:307:33 | (\\d(\\s+)*) | 0 | +| redos.py:307:27:307:32 | (\\s+)* | redos.py:307:27:307:31 | (\\s+) | 0 | +| redos.py:307:28:307:30 | \\s+ | redos.py:307:28:307:29 | \\s | 0 | +| redos.py:310:26:310:34 | ([^/]\|X)+ | redos.py:310:26:310:33 | ([^/]\|X) | 0 | +| redos.py:310:36:310:47 | (\\/[\\s\\S]*)* | redos.py:310:36:310:46 | (\\/[\\s\\S]*) | 0 | +| redos.py:310:39:310:45 | [\\s\\S]* | redos.py:310:39:310:44 | [\\s\\S] | 0 | +| redos.py:313:27:313:38 | (x([^Y]+)?)* | redos.py:313:27:313:37 | (x([^Y]+)?) | 0 | +| redos.py:313:29:313:36 | ([^Y]+)? | redos.py:313:29:313:35 | ([^Y]+) | 0 | +| redos.py:313:30:313:34 | [^Y]+ | redos.py:313:30:313:33 | [^Y] | 0 | +| redos.py:316:24:316:28 | (a*)+ | redos.py:316:24:316:27 | (a*) | 0 | +| redos.py:316:25:316:26 | a* | redos.py:316:25:316:25 | a | 0 | +| redos.py:319:27:319:35 | ([\\w-]*)+ | redos.py:319:27:319:34 | ([\\w-]*) | 0 | +| redos.py:319:28:319:33 | [\\w-]* | redos.py:319:28:319:32 | [\\w-] | 0 | +| redos.py:322:24:322:31 | ((ab)*)+ | redos.py:322:24:322:30 | ((ab)*) | 0 | +| redos.py:322:25:322:29 | (ab)* | redos.py:322:25:322:28 | (ab) | 0 | +| redos.py:325:24:325:30 | (a?a?)* | redos.py:325:24:325:29 | (a?a?) | 0 | +| redos.py:325:25:325:26 | a? | redos.py:325:25:325:25 | a | 0 | +| redos.py:325:27:325:28 | a? | redos.py:325:27:325:27 | a | 0 | +| redos.py:328:25:328:29 | (a?)* | redos.py:328:25:328:28 | (a?) | 0 | +| redos.py:328:26:328:27 | a? | redos.py:328:26:328:26 | a | 0 | +| redos.py:331:24:331:30 | (c?a?)* | redos.py:331:24:331:29 | (c?a?) | 0 | +| redos.py:331:25:331:26 | c? | redos.py:331:25:331:25 | c | 0 | +| redos.py:331:27:331:28 | a? | redos.py:331:27:331:27 | a | 0 | +| redos.py:334:24:334:32 | (?:a\|a?)+ | redos.py:334:24:334:31 | (?:a\|a?) | 0 | +| redos.py:334:29:334:30 | a? | redos.py:334:29:334:29 | a | 0 | +| redos.py:337:24:337:30 | (a?b?)* | redos.py:337:24:337:29 | (a?b?) | 0 | +| redos.py:337:25:337:26 | a? | redos.py:337:25:337:25 | a | 0 | +| redos.py:337:27:337:28 | b? | redos.py:337:27:337:27 | b | 0 | +| redos.py:340:27:340:55 | (([a-c]\|[c-d])T(e?e?e?e?\|X))+ | redos.py:340:27:340:54 | (([a-c]\|[c-d])T(e?e?e?e?\|X)) | 0 | +| redos.py:340:43:340:44 | e? | redos.py:340:43:340:43 | e | 0 | +| redos.py:340:45:340:46 | e? | redos.py:340:45:340:45 | e | 0 | +| redos.py:340:47:340:48 | e? | redos.py:340:47:340:47 | e | 0 | +| redos.py:340:49:340:50 | e? | redos.py:340:49:340:49 | e | 0 | +| redos.py:343:25:343:33 | ((a)+\\w)+ | redos.py:343:25:343:32 | ((a)+\\w) | 0 | +| redos.py:343:26:343:29 | (a)+ | redos.py:343:26:343:28 | (a) | 0 | +| redos.py:346:25:346:30 | (b+.)+ | redos.py:346:25:346:29 | (b+.) | 0 | +| redos.py:346:26:346:27 | b+ | redos.py:346:26:346:26 | b | 0 | +| redos.py:349:25:349:26 | a* | redos.py:349:25:349:25 | a | 0 | +| redos.py:352:24:352:28 | (a*)* | redos.py:352:24:352:27 | (a*) | 0 | +| redos.py:352:25:352:26 | a* | redos.py:352:25:352:25 | a | 0 | +| redos.py:353:24:353:28 | (a+)* | redos.py:353:24:353:27 | (a+) | 0 | +| redos.py:353:25:353:26 | a+ | redos.py:353:25:353:25 | a | 0 | +| redos.py:354:24:354:28 | (a*)+ | redos.py:354:24:354:27 | (a*) | 0 | +| redos.py:354:25:354:26 | a* | redos.py:354:25:354:25 | a | 0 | +| redos.py:355:24:355:28 | (a+)+ | redos.py:355:24:355:27 | (a+) | 0 | +| redos.py:355:25:355:26 | a+ | redos.py:355:25:355:25 | a | 0 | +| redos.py:358:25:358:30 | (a\|b)+ | redos.py:358:25:358:29 | (a\|b) | 0 | +| redos.py:359:25:359:62 | (?:[\\s;,"'<>(){}\|[\\]@=+*]\|:(?![/\\\\]))+ | redos.py:359:25:359:61 | (?:[\\s;,"'<>(){}\|[\\]@=+*]\|:(?![/\\\\])) | 0 | +| redos.py:362:25:362:40 | ((?:a{\|-)\|\\w\\{)+ | redos.py:362:25:362:39 | ((?:a{\|-)\|\\w\\{) | 0 | +| redos.py:363:25:363:43 | ((?:a{0\|-)\|\\w\\{\\d)+ | redos.py:363:25:363:42 | ((?:a{0\|-)\|\\w\\{\\d) | 0 | +| redos.py:364:25:364:45 | ((?:a{0,\|-)\|\\w\\{\\d,)+ | redos.py:364:25:364:44 | ((?:a{0,\|-)\|\\w\\{\\d,) | 0 | +| redos.py:365:25:365:48 | ((?:a{0,2\|-)\|\\w\\{\\d,\\d)+ | redos.py:365:25:365:47 | ((?:a{0,2\|-)\|\\w\\{\\d,\\d) | 0 | +| redos.py:368:26:368:52 | ((?:a{0,2}\|-)\|\\w\\{\\d,\\d\\})+ | redos.py:368:26:368:51 | ((?:a{0,2}\|-)\|\\w\\{\\d,\\d\\}) | 0 | +| redos.py:368:30:368:35 | a{0,2} | redos.py:368:30:368:30 | a | 0 | +| unittests.py:4:17:4:27 | ([^\\.]\|\\.)* | unittests.py:4:17:4:26 | ([^\\.]\|\\.) | 0 | +| unittests.py:5:17:5:23 | (\u00c6\|\\\u00c6)+ | unittests.py:5:17:5:22 | (\u00c6\|\\\u00c6) | 0 | +| unittests.py:8:16:8:24 | (?:.\|\\n)* | unittests.py:8:16:8:23 | (?:.\|\\n) | 0 | +| unittests.py:9:16:9:24 | (?:.\|\\n)* | unittests.py:9:16:9:23 | (?:.\|\\n) | 0 | +classChild +| KnownCVEs.py:15:16:15:19 | [+-] | KnownCVEs.py:15:17:15:17 | + | 0 | false | false | +| KnownCVEs.py:15:16:15:19 | [+-] | KnownCVEs.py:15:18:15:18 | - | 1 | false | false | +| KnownCVEs.py:17:32:17:35 | [^}] | KnownCVEs.py:17:34:17:34 | } | 0 | true | false | +| KnownCVEs.py:22:48:22:58 | [#\\w\\-"\\'.] | KnownCVEs.py:22:49:22:49 | # | 0 | false | false | +| KnownCVEs.py:22:48:22:58 | [#\\w\\-"\\'.] | KnownCVEs.py:22:50:22:51 | \\w | 1 | false | false | +| KnownCVEs.py:22:48:22:58 | [#\\w\\-"\\'.] | KnownCVEs.py:22:52:22:53 | \\- | 2 | false | false | +| KnownCVEs.py:22:48:22:58 | [#\\w\\-"\\'.] | KnownCVEs.py:22:54:22:54 | " | 3 | false | false | +| KnownCVEs.py:22:48:22:58 | [#\\w\\-"\\'.] | KnownCVEs.py:22:55:22:56 | \\' | 4 | false | false | +| KnownCVEs.py:22:48:22:58 | [#\\w\\-"\\'.] | KnownCVEs.py:22:57:22:57 | . | 5 | false | false | +| KnownCVEs.py:22:60:22:66 | [^=,%}] | KnownCVEs.py:22:62:22:62 | = | 0 | true | false | +| KnownCVEs.py:22:60:22:66 | [^=,%}] | KnownCVEs.py:22:63:22:63 | , | 1 | true | false | +| KnownCVEs.py:22:60:22:66 | [^=,%}] | KnownCVEs.py:22:64:22:64 | % | 2 | true | false | +| KnownCVEs.py:22:60:22:66 | [^=,%}] | KnownCVEs.py:22:65:22:65 | } | 3 | true | false | +| KnownCVEs.py:23:35:23:38 | [^;] | KnownCVEs.py:23:37:23:37 | ; | 0 | true | false | +| KnownCVEs.py:27:39:27:52 | [a-zA-Z0-9._-] | KnownCVEs.py:27:40:27:42 | a-z | 0 | false | false | +| KnownCVEs.py:27:39:27:52 | [a-zA-Z0-9._-] | KnownCVEs.py:27:43:27:45 | A-Z | 1 | false | false | +| KnownCVEs.py:27:39:27:52 | [a-zA-Z0-9._-] | KnownCVEs.py:27:46:27:48 | 0-9 | 2 | false | false | +| KnownCVEs.py:27:39:27:52 | [a-zA-Z0-9._-] | KnownCVEs.py:27:49:27:49 | . | 3 | false | false | +| KnownCVEs.py:27:39:27:52 | [a-zA-Z0-9._-] | KnownCVEs.py:27:50:27:50 | _ | 4 | false | false | +| KnownCVEs.py:27:39:27:52 | [a-zA-Z0-9._-] | KnownCVEs.py:27:51:27:51 | - | 5 | false | false | +| KnownCVEs.py:27:56:27:69 | [a-zA-Z0-9._-] | KnownCVEs.py:27:57:27:59 | a-z | 0 | false | false | +| KnownCVEs.py:27:56:27:69 | [a-zA-Z0-9._-] | KnownCVEs.py:27:60:27:62 | A-Z | 1 | false | false | +| KnownCVEs.py:27:56:27:69 | [a-zA-Z0-9._-] | KnownCVEs.py:27:63:27:65 | 0-9 | 2 | false | false | +| KnownCVEs.py:27:56:27:69 | [a-zA-Z0-9._-] | KnownCVEs.py:27:66:27:66 | . | 3 | false | false | +| KnownCVEs.py:27:56:27:69 | [a-zA-Z0-9._-] | KnownCVEs.py:27:67:27:67 | _ | 4 | false | false | +| KnownCVEs.py:27:56:27:69 | [a-zA-Z0-9._-] | KnownCVEs.py:27:68:27:68 | - | 5 | false | false | +| KnownCVEs.py:30:29:31:32 | [ \t] | KnownCVEs.py:30:30:31:30 | | 0 | false | false | +| KnownCVEs.py:30:29:31:32 | [ \t] | KnownCVEs.py:30:31:31:31 | \t | 1 | false | false | +| KnownCVEs.py:30:35:31:39 | [^ \t] | KnownCVEs.py:30:37:31:37 | | 0 | true | false | +| KnownCVEs.py:30:35:31:39 | [^ \t] | KnownCVEs.py:30:38:31:38 | \t | 1 | true | false | +| KnownCVEs.py:30:42:31:45 | [ \t] | KnownCVEs.py:30:43:31:43 | | 0 | false | false | +| KnownCVEs.py:30:42:31:45 | [ \t] | KnownCVEs.py:30:44:31:44 | \t | 1 | false | false | +| KnownCVEs.py:30:54:31:57 | ["'] | KnownCVEs.py:30:55:31:55 | " | 0 | false | false | +| KnownCVEs.py:30:54:31:57 | ["'] | KnownCVEs.py:30:56:31:56 | ' | 1 | false | false | +| KnownCVEs.py:30:61:31:65 | [^"'] | KnownCVEs.py:30:63:31:63 | " | 0 | true | false | +| KnownCVEs.py:30:61:31:65 | [^"'] | KnownCVEs.py:30:64:31:64 | ' | 1 | true | false | +| KnownCVEs.py:35:19:35:34 | [-/:,#%.'"\\s!\\w] | KnownCVEs.py:35:20:35:20 | - | 0 | false | false | +| KnownCVEs.py:35:19:35:34 | [-/:,#%.'"\\s!\\w] | KnownCVEs.py:35:21:35:21 | / | 1 | false | false | +| KnownCVEs.py:35:19:35:34 | [-/:,#%.'"\\s!\\w] | KnownCVEs.py:35:22:35:22 | : | 2 | false | false | +| KnownCVEs.py:35:19:35:34 | [-/:,#%.'"\\s!\\w] | KnownCVEs.py:35:23:35:23 | , | 3 | false | false | +| KnownCVEs.py:35:19:35:34 | [-/:,#%.'"\\s!\\w] | KnownCVEs.py:35:24:35:24 | # | 4 | false | false | +| KnownCVEs.py:35:19:35:34 | [-/:,#%.'"\\s!\\w] | KnownCVEs.py:35:25:35:25 | % | 5 | false | false | +| KnownCVEs.py:35:19:35:34 | [-/:,#%.'"\\s!\\w] | KnownCVEs.py:35:26:35:26 | . | 6 | false | false | +| KnownCVEs.py:35:19:35:34 | [-/:,#%.'"\\s!\\w] | KnownCVEs.py:35:27:35:27 | ' | 7 | false | false | +| KnownCVEs.py:35:19:35:34 | [-/:,#%.'"\\s!\\w] | KnownCVEs.py:35:28:35:28 | " | 8 | false | false | +| KnownCVEs.py:35:19:35:34 | [-/:,#%.'"\\s!\\w] | KnownCVEs.py:35:29:35:30 | \\s | 9 | false | false | +| KnownCVEs.py:35:19:35:34 | [-/:,#%.'"\\s!\\w] | KnownCVEs.py:35:31:35:31 | ! | 10 | false | false | +| KnownCVEs.py:35:19:35:34 | [-/:,#%.'"\\s!\\w] | KnownCVEs.py:35:32:35:33 | \\w | 11 | false | false | +| KnownCVEs.py:35:43:35:48 | [\\s\\w] | KnownCVEs.py:35:44:35:45 | \\s | 0 | false | false | +| KnownCVEs.py:35:43:35:48 | [\\s\\w] | KnownCVEs.py:35:46:35:47 | \\w | 1 | false | false | +| KnownCVEs.py:35:56:35:61 | [\\s\\w] | KnownCVEs.py:35:57:35:58 | \\s | 0 | false | false | +| KnownCVEs.py:35:56:35:61 | [\\s\\w] | KnownCVEs.py:35:59:35:60 | \\w | 1 | false | false | +| KnownCVEs.py:35:67:35:76 | [\\d,%\\.\\s] | KnownCVEs.py:35:68:35:69 | \\d | 0 | false | false | +| KnownCVEs.py:35:67:35:76 | [\\d,%\\.\\s] | KnownCVEs.py:35:70:35:70 | , | 1 | false | false | +| KnownCVEs.py:35:67:35:76 | [\\d,%\\.\\s] | KnownCVEs.py:35:71:35:71 | % | 2 | false | false | +| KnownCVEs.py:35:67:35:76 | [\\d,%\\.\\s] | KnownCVEs.py:35:72:35:73 | \\. | 3 | false | false | +| KnownCVEs.py:35:67:35:76 | [\\d,%\\.\\s] | KnownCVEs.py:35:74:35:75 | \\s | 4 | false | false | +| KnownCVEs.py:80:15:83:22 | [a-zA-Z] | KnownCVEs.py:80:16:83:18 | a-z | 0 | false | false | +| KnownCVEs.py:80:15:83:22 | [a-zA-Z] | KnownCVEs.py:80:19:83:21 | A-Z | 1 | false | false | +| KnownCVEs.py:80:29:83:36 | [a-zA-Z] | KnownCVEs.py:80:30:83:32 | a-z | 0 | false | false | +| KnownCVEs.py:80:29:83:36 | [a-zA-Z] | KnownCVEs.py:80:33:83:35 | A-Z | 1 | false | false | +| KnownCVEs.py:80:40:83:47 | [a-zA-Z] | KnownCVEs.py:80:41:83:43 | a-z | 0 | false | false | +| KnownCVEs.py:80:40:83:47 | [a-zA-Z] | KnownCVEs.py:80:44:83:46 | A-Z | 1 | false | false | +| KnownCVEs.py:80:54:83:61 | [a-zA-Z] | KnownCVEs.py:80:55:83:57 | a-z | 0 | false | false | +| KnownCVEs.py:80:54:83:61 | [a-zA-Z] | KnownCVEs.py:80:58:83:60 | A-Z | 1 | false | false | +| KnownCVEs.py:80:65:83:69 | [0-9] | KnownCVEs.py:80:66:83:68 | 0-9 | 0 | false | false | +| KnownCVEs.py:80:76:83:80 | [0-9] | KnownCVEs.py:80:77:83:79 | 0-9 | 0 | false | false | +| KnownCVEs.py:80:84:83:91 | [a-zA-Z] | KnownCVEs.py:80:85:83:87 | a-z | 0 | false | false | +| KnownCVEs.py:80:84:83:91 | [a-zA-Z] | KnownCVEs.py:80:88:83:90 | A-Z | 1 | false | false | +| KnownCVEs.py:80:98:83:108 | [a-zA-Z0-9] | KnownCVEs.py:80:99:83:101 | a-z | 0 | false | false | +| KnownCVEs.py:80:98:83:108 | [a-zA-Z0-9] | KnownCVEs.py:80:102:83:104 | A-Z | 1 | false | false | +| KnownCVEs.py:80:98:83:108 | [a-zA-Z0-9] | KnownCVEs.py:80:105:83:107 | 0-9 | 2 | false | false | +| KnownCVEs.py:80:109:83:121 | [-_a-zA-Z0-9] | KnownCVEs.py:80:110:83:110 | - | 0 | false | false | +| KnownCVEs.py:80:109:83:121 | [-_a-zA-Z0-9] | KnownCVEs.py:80:111:83:111 | _ | 1 | false | false | +| KnownCVEs.py:80:109:83:121 | [-_a-zA-Z0-9] | KnownCVEs.py:80:112:83:114 | a-z | 2 | false | false | +| KnownCVEs.py:80:109:83:121 | [-_a-zA-Z0-9] | KnownCVEs.py:80:115:83:117 | A-Z | 3 | false | false | +| KnownCVEs.py:80:109:83:121 | [-_a-zA-Z0-9] | KnownCVEs.py:80:118:83:120 | 0-9 | 4 | false | false | +| KnownCVEs.py:80:128:83:138 | [a-zA-Z0-9] | KnownCVEs.py:80:129:83:131 | a-z | 0 | false | false | +| KnownCVEs.py:80:128:83:138 | [a-zA-Z0-9] | KnownCVEs.py:80:132:83:134 | A-Z | 1 | false | false | +| KnownCVEs.py:80:128:83:138 | [a-zA-Z0-9] | KnownCVEs.py:80:135:83:137 | 0-9 | 2 | false | false | +| KnownCVEs.py:80:146:83:153 | [a-zA-Z] | KnownCVEs.py:80:147:83:149 | a-z | 0 | false | false | +| KnownCVEs.py:80:146:83:153 | [a-zA-Z] | KnownCVEs.py:80:150:83:152 | A-Z | 1 | false | false | +| KnownCVEs.py:80:166:83:176 | [a-zA-Z0-9] | KnownCVEs.py:80:167:83:169 | a-z | 0 | false | false | +| KnownCVEs.py:80:166:83:176 | [a-zA-Z0-9] | KnownCVEs.py:80:170:83:172 | A-Z | 1 | false | false | +| KnownCVEs.py:80:166:83:176 | [a-zA-Z0-9] | KnownCVEs.py:80:173:83:175 | 0-9 | 2 | false | false | +| redos.py:6:34:6:39 | [\\s\\S] | redos.py:6:35:6:36 | \\s | 0 | false | true | +| redos.py:6:34:6:39 | [\\s\\S] | redos.py:6:37:6:38 | \\S | 1 | false | true | +| redos.py:6:60:6:65 | [\\s\\S] | redos.py:6:61:6:62 | \\s | 0 | false | true | +| redos.py:6:60:6:65 | [\\s\\S] | redos.py:6:63:6:64 | \\S | 1 | false | true | +| redos.py:11:35:11:38 | [^_] | redos.py:11:37:11:37 | _ | 0 | true | false | +| redos.py:11:59:11:62 | [^*] | redos.py:11:61:11:61 | * | 0 | true | false | +| redos.py:21:37:21:42 | [^"\\\\] | redos.py:21:39:21:39 | " | 0 | true | false | +| redos.py:21:37:21:42 | [^"\\\\] | redos.py:21:40:21:41 | \\\\ | 1 | true | false | +| redos.py:21:60:21:65 | [^'\\\\] | redos.py:21:62:21:62 | ' | 0 | true | false | +| redos.py:21:60:21:65 | [^'\\\\] | redos.py:21:63:21:64 | \\\\ | 1 | true | false | +| redos.py:21:84:21:89 | [^)\\\\] | redos.py:21:86:21:86 | ) | 0 | true | false | +| redos.py:21:84:21:89 | [^)\\\\] | redos.py:21:87:21:88 | \\\\ | 1 | true | false | +| redos.py:25:31:25:36 | [\\s\\S] | redos.py:25:32:25:33 | \\s | 0 | false | true | +| redos.py:25:31:25:36 | [\\s\\S] | redos.py:25:34:25:35 | \\S | 1 | false | true | +| redos.py:25:43:25:48 | [\\s\\S] | redos.py:25:44:25:45 | \\s | 0 | false | true | +| redos.py:25:43:25:48 | [\\s\\S] | redos.py:25:46:25:47 | \\S | 1 | false | true | +| redos.py:25:57:25:62 | [\\s\\S] | redos.py:25:58:25:59 | \\s | 0 | false | true | +| redos.py:25:57:25:62 | [\\s\\S] | redos.py:25:60:25:61 | \\S | 1 | false | true | +| redos.py:30:42:30:45 | [-:] | redos.py:30:43:30:43 | - | 0 | false | false | +| redos.py:30:42:30:45 | [-:] | redos.py:30:44:30:44 | : | 1 | false | false | +| redos.py:30:51:30:56 | [-\| :] | redos.py:30:52:30:52 | - | 0 | false | false | +| redos.py:30:51:30:56 | [-\| :] | redos.py:30:53:30:53 | \| | 1 | false | false | +| redos.py:30:51:30:56 | [-\| :] | redos.py:30:54:30:54 | | 2 | false | false | +| redos.py:30:51:30:56 | [-\| :] | redos.py:30:55:30:55 | : | 3 | false | false | +| redos.py:33:41:33:44 | [-:] | redos.py:33:42:33:42 | - | 0 | false | false | +| redos.py:33:41:33:44 | [-:] | redos.py:33:43:33:43 | : | 1 | false | false | +| redos.py:33:50:33:55 | [-\| :] | redos.py:33:51:33:51 | - | 0 | false | false | +| redos.py:33:50:33:55 | [-\| :] | redos.py:33:52:33:52 | \| | 1 | false | false | +| redos.py:33:50:33:55 | [-\| :] | redos.py:33:53:33:53 | | 2 | false | false | +| redos.py:33:50:33:55 | [-\| :] | redos.py:33:54:33:54 | : | 3 | false | false | +| redos.py:38:28:38:31 | [ *] | redos.py:38:29:38:29 | | 0 | false | false | +| redos.py:38:28:38:31 | [ *] | redos.py:38:30:38:30 | * | 1 | false | false | +| redos.py:38:45:38:49 | [gim] | redos.py:38:46:38:46 | g | 0 | false | false | +| redos.py:38:45:38:49 | [gim] | redos.py:38:47:38:47 | i | 1 | false | false | +| redos.py:38:45:38:49 | [gim] | redos.py:38:48:38:48 | m | 2 | false | false | +| redos.py:43:25:43:34 | [\\s\\[\\{\\(] | redos.py:43:26:43:27 | \\s | 0 | false | false | +| redos.py:43:25:43:34 | [\\s\\[\\{\\(] | redos.py:43:28:43:29 | \\[ | 1 | false | false | +| redos.py:43:25:43:34 | [\\s\\[\\{\\(] | redos.py:43:30:43:31 | \\{ | 2 | false | false | +| redos.py:43:25:43:34 | [\\s\\[\\{\\(] | redos.py:43:32:43:33 | \\( | 3 | false | false | +| redos.py:49:34:49:38 | [^"'] | redos.py:49:36:49:36 | " | 0 | true | false | +| redos.py:49:34:49:38 | [^"'] | redos.py:49:37:49:37 | ' | 1 | true | false | +| redos.py:49:56:49:60 | [(,)] | redos.py:49:57:49:57 | ( | 0 | false | false | +| redos.py:49:56:49:60 | [(,)] | redos.py:49:58:49:58 | , | 1 | false | false | +| redos.py:49:56:49:60 | [(,)] | redos.py:49:59:49:59 | ) | 2 | false | false | +| redos.py:54:24:54:31 | [\\_$a-z] | redos.py:54:25:54:26 | \\_ | 0 | false | false | +| redos.py:54:24:54:31 | [\\_$a-z] | redos.py:54:27:54:27 | $ | 1 | false | false | +| redos.py:54:24:54:31 | [\\_$a-z] | redos.py:54:28:54:30 | a-z | 2 | false | false | +| redos.py:54:32:54:42 | [\\_$a-z0-9] | redos.py:54:33:54:34 | \\_ | 0 | false | false | +| redos.py:54:32:54:42 | [\\_$a-z0-9] | redos.py:54:35:54:35 | $ | 1 | false | false | +| redos.py:54:32:54:42 | [\\_$a-z0-9] | redos.py:54:36:54:38 | a-z | 2 | false | false | +| redos.py:54:32:54:42 | [\\_$a-z0-9] | redos.py:54:39:54:41 | 0-9 | 3 | false | false | +| redos.py:54:57:54:64 | [\\_$a-z] | redos.py:54:58:54:59 | \\_ | 0 | false | false | +| redos.py:54:57:54:64 | [\\_$a-z] | redos.py:54:60:54:60 | $ | 1 | false | false | +| redos.py:54:57:54:64 | [\\_$a-z] | redos.py:54:61:54:63 | a-z | 2 | false | false | +| redos.py:54:65:54:75 | [\\_$a-z0-9] | redos.py:54:66:54:67 | \\_ | 0 | false | false | +| redos.py:54:65:54:75 | [\\_$a-z0-9] | redos.py:54:68:54:68 | $ | 1 | false | false | +| redos.py:54:65:54:75 | [\\_$a-z0-9] | redos.py:54:69:54:71 | a-z | 2 | false | false | +| redos.py:54:65:54:75 | [\\_$a-z0-9] | redos.py:54:72:54:74 | 0-9 | 3 | false | false | +| redos.py:60:25:60:29 | [a-z] | redos.py:60:26:60:28 | a-z | 0 | false | false | +| redos.py:61:25:61:29 | [a-z] | redos.py:61:26:61:28 | a-z | 0 | false | false | +| redos.py:62:25:62:35 | [a-zA-Z0-9] | redos.py:62:26:62:28 | a-z | 0 | false | false | +| redos.py:62:25:62:35 | [a-zA-Z0-9] | redos.py:62:29:62:31 | A-Z | 1 | false | false | +| redos.py:62:25:62:35 | [a-zA-Z0-9] | redos.py:62:32:62:34 | 0-9 | 2 | false | false | +| redos.py:62:39:62:44 | [\\\\-.] | redos.py:62:40:62:43 | \\\\-. | 0 | false | false | +| redos.py:62:46:62:48 | [_] | redos.py:62:47:62:47 | _ | 0 | false | false | +| redos.py:62:53:62:63 | [a-zA-Z0-9] | redos.py:62:54:62:56 | a-z | 0 | false | false | +| redos.py:62:53:62:63 | [a-zA-Z0-9] | redos.py:62:57:62:59 | A-Z | 1 | false | false | +| redos.py:62:53:62:63 | [a-zA-Z0-9] | redos.py:62:60:62:62 | 0-9 | 2 | false | false | +| redos.py:62:74:62:81 | [a-z0-9] | redos.py:62:75:62:77 | a-z | 0 | false | false | +| redos.py:62:74:62:81 | [a-z0-9] | redos.py:62:78:62:80 | 0-9 | 1 | false | false | +| redos.py:62:83:62:85 | [.] | redos.py:62:84:62:84 | . | 0 | false | false | +| redos.py:62:91:62:95 | [a-z] | redos.py:62:92:62:94 | a-z | 0 | false | false | +| redos.py:62:104:62:108 | [a-z] | redos.py:62:105:62:107 | a-z | 0 | false | false | +| redos.py:62:114:62:116 | [.] | redos.py:62:115:62:115 | . | 0 | false | false | +| redos.py:62:120:62:124 | [a-z] | redos.py:62:121:62:123 | a-z | 0 | false | false | +| redos.py:63:27:63:31 | [a-z] | redos.py:63:28:63:30 | a-z | 0 | false | false | +| redos.py:63:37:63:41 | [A-Z] | redos.py:63:38:63:40 | A-Z | 0 | false | false | +| redos.py:63:43:63:47 | [a-z] | redos.py:63:44:63:46 | a-z | 0 | false | false | +| redos.py:68:26:68:40 | [\\w#:.~>+()\\s-] | redos.py:68:27:68:28 | \\w | 0 | false | false | +| redos.py:68:26:68:40 | [\\w#:.~>+()\\s-] | redos.py:68:29:68:29 | # | 1 | false | false | +| redos.py:68:26:68:40 | [\\w#:.~>+()\\s-] | redos.py:68:30:68:30 | : | 2 | false | false | +| redos.py:68:26:68:40 | [\\w#:.~>+()\\s-] | redos.py:68:31:68:31 | . | 3 | false | false | +| redos.py:68:26:68:40 | [\\w#:.~>+()\\s-] | redos.py:68:32:68:32 | ~ | 4 | false | false | +| redos.py:68:26:68:40 | [\\w#:.~>+()\\s-] | redos.py:68:33:68:33 | > | 5 | false | false | +| redos.py:68:26:68:40 | [\\w#:.~>+()\\s-] | redos.py:68:34:68:34 | + | 6 | false | false | +| redos.py:68:26:68:40 | [\\w#:.~>+()\\s-] | redos.py:68:35:68:35 | ( | 7 | false | false | +| redos.py:68:26:68:40 | [\\w#:.~>+()\\s-] | redos.py:68:36:68:36 | ) | 8 | false | false | +| redos.py:68:26:68:40 | [\\w#:.~>+()\\s-] | redos.py:68:37:68:38 | \\s | 9 | false | false | +| redos.py:68:26:68:40 | [\\w#:.~>+()\\s-] | redos.py:68:39:68:39 | - | 10 | false | false | +| redos.py:88:25:88:29 | [\\w.] | redos.py:88:26:88:27 | \\w | 0 | false | false | +| redos.py:88:25:88:29 | [\\w.] | redos.py:88:28:88:28 | . | 1 | false | false | +| redos.py:97:26:97:31 | [\\s\\S] | redos.py:97:27:97:28 | \\s | 0 | false | true | +| redos.py:97:26:97:31 | [\\s\\S] | redos.py:97:29:97:30 | \\S | 1 | false | true | +| redos.py:97:33:97:36 | [^a] | redos.py:97:35:97:35 | a | 0 | true | false | +| redos.py:100:26:100:30 | [^"'] | redos.py:100:28:100:28 | " | 0 | true | false | +| redos.py:100:26:100:30 | [^"'] | redos.py:100:29:100:29 | ' | 1 | true | false | +| redos.py:103:28:103:31 | [^a] | redos.py:103:30:103:30 | a | 0 | true | false | +| redos.py:106:29:106:32 | [^a] | redos.py:106:31:106:31 | a | 0 | true | false | +| redos.py:109:28:109:31 | [^a] | redos.py:109:30:109:30 | a | 0 | true | false | +| redos.py:112:28:112:31 | [^a] | redos.py:112:30:112:30 | a | 0 | true | false | +| redos.py:115:26:115:30 | [0-9] | redos.py:115:27:115:29 | 0-9 | 0 | false | false | +| redos.py:115:32:115:35 | [^a] | redos.py:115:34:115:34 | a | 0 | true | false | +| redos.py:118:32:118:64 | [!#\\$%&'\\*\\+\\-\\.\\^_`\\\|~0-9A-Za-z] | redos.py:118:33:118:33 | ! | 0 | false | false | +| redos.py:118:32:118:64 | [!#\\$%&'\\*\\+\\-\\.\\^_`\\\|~0-9A-Za-z] | redos.py:118:34:118:34 | # | 1 | false | false | +| redos.py:118:32:118:64 | [!#\\$%&'\\*\\+\\-\\.\\^_`\\\|~0-9A-Za-z] | redos.py:118:35:118:36 | \\$ | 2 | false | false | +| redos.py:118:32:118:64 | [!#\\$%&'\\*\\+\\-\\.\\^_`\\\|~0-9A-Za-z] | redos.py:118:37:118:37 | % | 3 | false | false | +| redos.py:118:32:118:64 | [!#\\$%&'\\*\\+\\-\\.\\^_`\\\|~0-9A-Za-z] | redos.py:118:38:118:38 | & | 4 | false | false | +| redos.py:118:32:118:64 | [!#\\$%&'\\*\\+\\-\\.\\^_`\\\|~0-9A-Za-z] | redos.py:118:39:118:39 | ' | 5 | false | false | +| redos.py:118:32:118:64 | [!#\\$%&'\\*\\+\\-\\.\\^_`\\\|~0-9A-Za-z] | redos.py:118:40:118:41 | \\* | 6 | false | false | +| redos.py:118:32:118:64 | [!#\\$%&'\\*\\+\\-\\.\\^_`\\\|~0-9A-Za-z] | redos.py:118:42:118:43 | \\+ | 7 | false | false | +| redos.py:118:32:118:64 | [!#\\$%&'\\*\\+\\-\\.\\^_`\\\|~0-9A-Za-z] | redos.py:118:44:118:45 | \\- | 8 | false | false | +| redos.py:118:32:118:64 | [!#\\$%&'\\*\\+\\-\\.\\^_`\\\|~0-9A-Za-z] | redos.py:118:46:118:47 | \\. | 9 | false | false | +| redos.py:118:32:118:64 | [!#\\$%&'\\*\\+\\-\\.\\^_`\\\|~0-9A-Za-z] | redos.py:118:48:118:49 | \\^ | 10 | false | false | +| redos.py:118:32:118:64 | [!#\\$%&'\\*\\+\\-\\.\\^_`\\\|~0-9A-Za-z] | redos.py:118:50:118:50 | _ | 11 | false | false | +| redos.py:118:32:118:64 | [!#\\$%&'\\*\\+\\-\\.\\^_`\\\|~0-9A-Za-z] | redos.py:118:51:118:51 | ` | 12 | false | false | +| redos.py:118:32:118:64 | [!#\\$%&'\\*\\+\\-\\.\\^_`\\\|~0-9A-Za-z] | redos.py:118:52:118:53 | \\\| | 13 | false | false | +| redos.py:118:32:118:64 | [!#\\$%&'\\*\\+\\-\\.\\^_`\\\|~0-9A-Za-z] | redos.py:118:54:118:54 | ~ | 14 | false | false | +| redos.py:118:32:118:64 | [!#\\$%&'\\*\\+\\-\\.\\^_`\\\|~0-9A-Za-z] | redos.py:118:55:118:57 | 0-9 | 15 | false | false | +| redos.py:118:32:118:64 | [!#\\$%&'\\*\\+\\-\\.\\^_`\\\|~0-9A-Za-z] | redos.py:118:58:118:60 | A-Z | 16 | false | false | +| redos.py:118:32:118:64 | [!#\\$%&'\\*\\+\\-\\.\\^_`\\\|~0-9A-Za-z] | redos.py:118:61:118:63 | a-z | 17 | false | false | +| redos.py:118:75:118:85 | [\\x00-\\x7f] | redos.py:118:76:118:84 | \\x00-\\x7f | 0 | false | false | +| redos.py:118:87:118:112 | [^\\x00-\\x08\\x0a-\\x1f\\x7f"] | redos.py:118:89:118:97 | \\x00-\\x08 | 0 | true | false | +| redos.py:118:87:118:112 | [^\\x00-\\x08\\x0a-\\x1f\\x7f"] | redos.py:118:98:118:106 | \\x0a-\\x1f | 1 | true | false | +| redos.py:118:87:118:112 | [^\\x00-\\x08\\x0a-\\x1f\\x7f"] | redos.py:118:107:118:110 | \\x7f | 2 | true | false | +| redos.py:118:87:118:112 | [^\\x00-\\x08\\x0a-\\x1f\\x7f"] | redos.py:118:111:118:111 | " | 3 | true | false | +| redos.py:121:31:121:41 | [\\x00-\\x7f] | redos.py:121:32:121:40 | \\x00-\\x7f | 0 | false | false | +| redos.py:121:43:121:68 | [^\\x00-\\x08\\x0a-\\x1f\\x7f"] | redos.py:121:45:121:53 | \\x00-\\x08 | 0 | true | false | +| redos.py:121:43:121:68 | [^\\x00-\\x08\\x0a-\\x1f\\x7f"] | redos.py:121:54:121:62 | \\x0a-\\x1f | 1 | true | false | +| redos.py:121:43:121:68 | [^\\x00-\\x08\\x0a-\\x1f\\x7f"] | redos.py:121:63:121:66 | \\x7f | 2 | true | false | +| redos.py:121:43:121:68 | [^\\x00-\\x08\\x0a-\\x1f\\x7f"] | redos.py:121:67:121:67 | " | 3 | true | false | +| redos.py:124:31:124:41 | [\\x00-\\x7f] | redos.py:124:32:124:40 | \\x00-\\x7f | 0 | false | false | +| redos.py:124:43:124:70 | [^\\x00-\\x08\\x0a-\\x1f\\x7f"\\\\] | redos.py:124:45:124:53 | \\x00-\\x08 | 0 | true | false | +| redos.py:124:43:124:70 | [^\\x00-\\x08\\x0a-\\x1f\\x7f"\\\\] | redos.py:124:54:124:62 | \\x0a-\\x1f | 1 | true | false | +| redos.py:124:43:124:70 | [^\\x00-\\x08\\x0a-\\x1f\\x7f"\\\\] | redos.py:124:63:124:66 | \\x7f | 2 | true | false | +| redos.py:124:43:124:70 | [^\\x00-\\x08\\x0a-\\x1f\\x7f"\\\\] | redos.py:124:67:124:67 | " | 3 | true | false | +| redos.py:124:43:124:70 | [^\\x00-\\x08\\x0a-\\x1f\\x7f"\\\\] | redos.py:124:68:124:69 | \\\\ | 4 | true | false | +| redos.py:127:26:127:30 | [a-z] | redos.py:127:27:127:29 | a-z | 0 | false | false | +| redos.py:127:32:127:36 | [d-h] | redos.py:127:33:127:35 | d-h | 0 | false | false | +| redos.py:130:26:130:31 | [^a-z] | redos.py:130:28:130:30 | a-z | 0 | true | false | +| redos.py:130:33:130:38 | [^0-9] | redos.py:130:35:130:37 | 0-9 | 0 | true | false | +| redos.py:133:29:133:33 | [0-9] | redos.py:133:30:133:32 | 0-9 | 0 | false | false | +| redos.py:151:29:151:32 | [\\f] | redos.py:151:30:151:31 | \\f | 0 | false | false | +| redos.py:154:29:154:32 | [\\v] | redos.py:154:30:154:31 | \\v | 0 | false | false | +| redos.py:157:29:157:32 | [\\f] | redos.py:157:30:157:31 | \\f | 0 | false | false | +| redos.py:166:29:166:32 | [\\w] | redos.py:166:30:166:31 | \\w | 0 | false | false | +| redos.py:169:29:169:35 | [\\da-z] | redos.py:169:30:169:31 | \\d | 0 | false | false | +| redos.py:169:29:169:35 | [\\da-z] | redos.py:169:32:169:34 | a-z | 1 | false | false | +| redos.py:172:28:172:31 | [\\d] | redos.py:172:29:172:30 | \\d | 0 | false | false | +| redos.py:175:26:175:29 | [\\d] | redos.py:175:27:175:28 | \\d | 0 | false | false | +| redos.py:181:26:181:30 | [0-9] | redos.py:181:27:181:29 | 0-9 | 0 | false | false | +| redos.py:181:34:181:38 | [0-9] | redos.py:181:35:181:37 | 0-9 | 0 | false | false | +| redos.py:184:27:184:30 | [^>] | redos.py:184:29:184:29 | > | 0 | true | false | +| redos.py:187:26:187:30 | [^>a] | redos.py:187:28:187:28 | > | 0 | true | false | +| redos.py:187:26:187:30 | [^>a] | redos.py:187:29:187:29 | a | 1 | true | false | +| redos.py:193:41:193:44 | [^)] | redos.py:193:43:193:43 | ) | 0 | true | false | +| redos.py:193:53:193:57 | [?*+] | redos.py:193:54:193:54 | ? | 0 | false | false | +| redos.py:193:53:193:57 | [?*+] | redos.py:193:55:193:55 | * | 1 | false | false | +| redos.py:193:53:193:57 | [?*+] | redos.py:193:56:193:56 | + | 2 | false | false | +| redos.py:196:32:196:39 | [a-zA-Z] | redos.py:196:33:196:35 | a-z | 0 | false | false | +| redos.py:196:32:196:39 | [a-zA-Z] | redos.py:196:36:196:38 | A-Z | 1 | false | false | +| redos.py:196:45:196:52 | [a-zA-Z] | redos.py:196:46:196:48 | a-z | 0 | false | false | +| redos.py:196:45:196:52 | [a-zA-Z] | redos.py:196:49:196:51 | A-Z | 1 | false | false | +| redos.py:196:63:196:70 | [a-zA-Z] | redos.py:196:64:196:66 | a-z | 0 | false | false | +| redos.py:196:63:196:70 | [a-zA-Z] | redos.py:196:67:196:69 | A-Z | 1 | false | false | +| redos.py:196:78:196:88 | [ a-zA-Z{}] | redos.py:196:79:196:79 | | 0 | false | false | +| redos.py:196:78:196:88 | [ a-zA-Z{}] | redos.py:196:80:196:82 | a-z | 1 | false | false | +| redos.py:196:78:196:88 | [ a-zA-Z{}] | redos.py:196:83:196:85 | A-Z | 2 | false | false | +| redos.py:196:78:196:88 | [ a-zA-Z{}] | redos.py:196:86:196:86 | { | 3 | false | false | +| redos.py:196:78:196:88 | [ a-zA-Z{}] | redos.py:196:87:196:87 | } | 4 | false | false | +| redos.py:220:25:220:28 | [^X] | redos.py:220:27:220:27 | X | 0 | true | false | +| redos.py:223:26:223:29 | [^X] | redos.py:223:28:223:28 | X | 0 | true | false | +| redos.py:226:27:226:30 | [^X] | redos.py:226:29:226:29 | X | 0 | true | false | +| redos.py:226:39:226:42 | [^X] | redos.py:226:41:226:41 | X | 0 | true | false | +| redos.py:229:26:229:29 | [^X] | redos.py:229:28:229:28 | X | 0 | true | false | +| redos.py:229:38:229:41 | [^X] | redos.py:229:40:229:40 | X | 0 | true | false | +| redos.py:244:33:244:36 | [a1] | redos.py:244:34:244:34 | a | 0 | false | false | +| redos.py:244:33:244:36 | [a1] | redos.py:244:35:244:35 | 1 | 1 | false | false | +| redos.py:244:37:244:40 | [b1] | redos.py:244:38:244:38 | b | 0 | false | false | +| redos.py:244:37:244:40 | [b1] | redos.py:244:39:244:39 | 1 | 1 | false | false | +| redos.py:244:41:244:44 | [a2] | redos.py:244:42:244:42 | a | 0 | false | false | +| redos.py:244:41:244:44 | [a2] | redos.py:244:43:244:43 | 2 | 1 | false | false | +| redos.py:244:45:244:48 | [b2] | redos.py:244:46:244:46 | b | 0 | false | false | +| redos.py:244:45:244:48 | [b2] | redos.py:244:47:244:47 | 2 | 1 | false | false | +| redos.py:244:49:244:52 | [a3] | redos.py:244:50:244:50 | a | 0 | false | false | +| redos.py:244:49:244:52 | [a3] | redos.py:244:51:244:51 | 3 | 1 | false | false | +| redos.py:244:53:244:56 | [b3] | redos.py:244:54:244:54 | b | 0 | false | false | +| redos.py:244:53:244:56 | [b3] | redos.py:244:55:244:55 | 3 | 1 | false | false | +| redos.py:247:25:247:30 | [\\n\\s] | redos.py:247:26:247:27 | \\n | 0 | false | false | +| redos.py:247:25:247:30 | [\\n\\s] | redos.py:247:28:247:29 | \\s | 1 | false | false | +| redos.py:253:26:253:32 | [^\\\\\\]] | redos.py:253:28:253:29 | \\\\ | 0 | true | false | +| redos.py:253:26:253:32 | [^\\\\\\]] | redos.py:253:30:253:31 | \\] | 1 | true | false | +| redos.py:268:29:268:42 | [\\uDC66\\uDC67] | redos.py:268:30:268:35 | \\uDC66 | 0 | false | false | +| redos.py:268:29:268:42 | [\\uDC66\\uDC67] | redos.py:268:36:268:41 | \\uDC67 | 1 | false | false | +| redos.py:268:44:268:57 | [\\uDC68\\uDC69] | redos.py:268:45:268:50 | \\uDC68 | 0 | false | false | +| redos.py:268:44:268:57 | [\\uDC68\\uDC69] | redos.py:268:51:268:56 | \\uDC69 | 1 | false | false | +| redos.py:277:58:277:61 | [^"] | redos.py:277:60:277:60 | " | 0 | true | false | +| redos.py:277:70:277:73 | [^'] | redos.py:277:72:277:72 | ' | 0 | true | false | +| redos.py:277:78:277:83 | [^>\\s] | redos.py:277:80:277:80 | > | 0 | true | false | +| redos.py:277:78:277:83 | [^>\\s] | redos.py:277:81:277:82 | \\s | 1 | true | false | +| redos.py:280:30:280:35 | [\\s\\S] | redos.py:280:31:280:32 | \\s | 0 | false | true | +| redos.py:280:30:280:35 | [\\s\\S] | redos.py:280:33:280:34 | \\S | 1 | false | true | +| redos.py:280:36:280:41 | [\\s\\S] | redos.py:280:37:280:38 | \\s | 0 | false | true | +| redos.py:280:36:280:41 | [\\s\\S] | redos.py:280:39:280:40 | \\S | 1 | false | true | +| redos.py:280:42:280:47 | [\\s\\S] | redos.py:280:43:280:44 | \\s | 0 | false | true | +| redos.py:280:42:280:47 | [\\s\\S] | redos.py:280:45:280:46 | \\S | 1 | false | true | +| redos.py:283:30:283:35 | [\\s\\S] | redos.py:283:31:283:32 | \\s | 0 | false | true | +| redos.py:283:30:283:35 | [\\s\\S] | redos.py:283:33:283:34 | \\S | 1 | false | true | +| redos.py:286:31:286:36 | [\\s\\S] | redos.py:286:32:286:33 | \\s | 0 | false | true | +| redos.py:286:31:286:36 | [\\s\\S] | redos.py:286:34:286:35 | \\S | 1 | false | true | +| redos.py:289:31:289:36 | [\\s\\S] | redos.py:289:32:289:33 | \\s | 0 | false | true | +| redos.py:289:31:289:36 | [\\s\\S] | redos.py:289:34:289:35 | \\S | 1 | false | true | +| redos.py:292:32:292:37 | [\\s\\S] | redos.py:292:33:292:34 | \\s | 0 | false | true | +| redos.py:292:32:292:37 | [\\s\\S] | redos.py:292:35:292:36 | \\S | 1 | false | true | +| redos.py:295:26:295:31 | [\\s\\S] | redos.py:295:27:295:28 | \\s | 0 | false | true | +| redos.py:295:26:295:31 | [\\s\\S] | redos.py:295:29:295:30 | \\S | 1 | false | true | +| redos.py:310:27:310:30 | [^/] | redos.py:310:29:310:29 | / | 0 | true | false | +| redos.py:310:39:310:44 | [\\s\\S] | redos.py:310:40:310:41 | \\s | 0 | false | true | +| redos.py:310:39:310:44 | [\\s\\S] | redos.py:310:42:310:43 | \\S | 1 | false | true | +| redos.py:313:30:313:33 | [^Y] | redos.py:313:32:313:32 | Y | 0 | true | false | +| redos.py:319:28:319:32 | [\\w-] | redos.py:319:29:319:30 | \\w | 0 | false | false | +| redos.py:319:28:319:32 | [\\w-] | redos.py:319:31:319:31 | - | 1 | false | false | +| redos.py:340:29:340:33 | [a-c] | redos.py:340:30:340:32 | a-c | 0 | false | false | +| redos.py:340:35:340:39 | [c-d] | redos.py:340:36:340:38 | c-d | 0 | false | false | +| redos.py:359:28:359:49 | [\\s;,"'<>(){}\|[\\]@=+*] | redos.py:359:29:359:30 | \\s | 0 | false | false | +| redos.py:359:28:359:49 | [\\s;,"'<>(){}\|[\\]@=+*] | redos.py:359:31:359:31 | ; | 1 | false | false | +| redos.py:359:28:359:49 | [\\s;,"'<>(){}\|[\\]@=+*] | redos.py:359:32:359:32 | , | 2 | false | false | +| redos.py:359:28:359:49 | [\\s;,"'<>(){}\|[\\]@=+*] | redos.py:359:33:359:33 | " | 3 | false | false | +| redos.py:359:28:359:49 | [\\s;,"'<>(){}\|[\\]@=+*] | redos.py:359:34:359:34 | ' | 4 | false | false | +| redos.py:359:28:359:49 | [\\s;,"'<>(){}\|[\\]@=+*] | redos.py:359:35:359:35 | < | 5 | false | false | +| redos.py:359:28:359:49 | [\\s;,"'<>(){}\|[\\]@=+*] | redos.py:359:36:359:36 | > | 6 | false | false | +| redos.py:359:28:359:49 | [\\s;,"'<>(){}\|[\\]@=+*] | redos.py:359:37:359:37 | ( | 7 | false | false | +| redos.py:359:28:359:49 | [\\s;,"'<>(){}\|[\\]@=+*] | redos.py:359:38:359:38 | ) | 8 | false | false | +| redos.py:359:28:359:49 | [\\s;,"'<>(){}\|[\\]@=+*] | redos.py:359:39:359:39 | { | 9 | false | false | +| redos.py:359:28:359:49 | [\\s;,"'<>(){}\|[\\]@=+*] | redos.py:359:40:359:40 | } | 10 | false | false | +| redos.py:359:28:359:49 | [\\s;,"'<>(){}\|[\\]@=+*] | redos.py:359:41:359:41 | \| | 11 | false | false | +| redos.py:359:28:359:49 | [\\s;,"'<>(){}\|[\\]@=+*] | redos.py:359:42:359:42 | [ | 12 | false | false | +| redos.py:359:28:359:49 | [\\s;,"'<>(){}\|[\\]@=+*] | redos.py:359:43:359:44 | \\] | 13 | false | false | +| redos.py:359:28:359:49 | [\\s;,"'<>(){}\|[\\]@=+*] | redos.py:359:45:359:45 | @ | 14 | false | false | +| redos.py:359:28:359:49 | [\\s;,"'<>(){}\|[\\]@=+*] | redos.py:359:46:359:46 | = | 15 | false | false | +| redos.py:359:28:359:49 | [\\s;,"'<>(){}\|[\\]@=+*] | redos.py:359:47:359:47 | + | 16 | false | false | +| redos.py:359:28:359:49 | [\\s;,"'<>(){}\|[\\]@=+*] | redos.py:359:48:359:48 | * | 17 | false | false | +| redos.py:359:55:359:59 | [/\\\\] | redos.py:359:56:359:56 | / | 0 | false | false | +| redos.py:359:55:359:59 | [/\\\\] | redos.py:359:57:359:58 | \\\\ | 1 | false | false | +| unittests.py:4:18:4:22 | [^\\.] | unittests.py:4:20:4:21 | \\. | 0 | true | false | diff --git a/python/ql/test/query-tests/Security/CWE-730/ReDosRegexHierarchy.ql b/python/ql/test/query-tests/Security/CWE-730/ReDosRegexHierarchy.ql new file mode 100644 index 000000000000..c3e8572fcb06 --- /dev/null +++ b/python/ql/test/query-tests/Security/CWE-730/ReDosRegexHierarchy.ql @@ -0,0 +1,16 @@ +import python +import semmle.python.RegexTreeView + +query predicate seqChild(RegExpSequence s, RegExpTerm r, int i) { r = s.getChild(i) } + +query predicate orChild(RegExpAlt s, RegExpTerm r, int i) { r = s.getChild(i) } + +query predicate quantifierChild(RegExpQuantifier s, RegExpTerm r, int i) { r = s.getChild(i) } + +query predicate classChild( + RegExpCharacterClass s, RegExpTerm r, int i, boolean inverted, boolean universal +) { + r = s.getChild(i) and + (if s.isInverted() then inverted = true else inverted = false) and + if s.isUniversalClass() then universal = true else universal = false +} diff --git a/python/ql/test/query-tests/Security/CWE-730/redos.py b/python/ql/test/query-tests/Security/CWE-730/redos.py new file mode 100644 index 000000000000..266cb210bd3b --- /dev/null +++ b/python/ql/test/query-tests/Security/CWE-730/redos.py @@ -0,0 +1,368 @@ +import re + +# NOT GOOD; attack: "_" + "__".repeat(100) +# Adapted from marked (https://github.com/markedjs/marked), which is licensed +# under the MIT license; see file marked-LICENSE. +bad1 = re.compile(r'''^\b_((?:__|[\s\S])+?)_\b|^\*((?:\*\*|[\s\S])+?)\*(?!\*)''') + +# GOOD +# Adapted from marked (https://github.com/markedjs/marked), which is licensed +# under the MIT license; see file marked-LICENSE. +good1 = re.compile(r'^\b_((?:__|[^_])+?)_\b|^\*((?:\*\*|[^*])+?)\*(?!\*)') + +# GOOD - there is no witness in the end that could cause the regexp to not match +# Adapted from brace-expansion (https://github.com/juliangruber/brace-expansion), +# which is licensed under the MIT license; see file brace-expansion-LICENSE. +good2 = re.compile(r'(.*,)+.+') + +# NOT GOOD; attack: " '" + "\\\\".repeat(100) +# Adapted from CodeMirror (https://github.com/codemirror/codemirror), +# which is licensed under the MIT license; see file CodeMirror-LICENSE. +bad2 = re.compile(r'''^(?:\s+(?:"(?:[^"\\]|\\\\|\\.)+"|'(?:[^'\\]|\\\\|\\.)+'|\((?:[^)\\]|\\\\|\\.)+\)))?''') + +# GOOD +# Adapted from lulucms2 (https://github.com/yiifans/lulucms2). +good2 = re.compile(r'''\(\*(?:[\s\S]*?\(\*[\s\S]*?\*\))*[\s\S]*?\*\)''') + +# GOOD +# Adapted from jest (https://github.com/facebook/jest), which is licensed +# under the MIT license; see file jest-LICENSE. +good3 = re.compile(r'''^ *(\S.*\|.*)\n *([-:]+ *\|[-| :]*)\n((?:.*\|.*(?:\n|$))*)\n*''') + +# NOT GOOD, variant of good3; attack: "a|\n:|\n" + "||\n".repeat(100) +bad4 = re.compile(r'''^ *(\S.*\|.*)\n *([-:]+ *\|[-| :]*)\n((?:.*\|.*(?:\n|$))*)a''') + +# NOT GOOD; attack: "/" + "\\/a".repeat(100) +# Adapted from ANodeBlog (https://github.com/gefangshuai/ANodeBlog), +# which is licensed under the Apache License 2.0; see file ANodeBlog-LICENSE. +bad5 = re.compile(r'''\/(?![ *])(\\\/|.)*?\/[gim]*(?=\W|$)''') + +# NOT GOOD; attack: "##".repeat(100) + "\na" +# Adapted from CodeMirror (https://github.com/codemirror/codemirror), +# which is licensed under the MIT license; see file CodeMirror-LICENSE. +bad6 = re.compile(r'''^([\s\[\{\(]|#.*)*$''') + +# GOOD +good4 = re.compile(r'''(\r\n|\r|\n)+''') + +# BAD - PoC: `node -e "/((?:[^\"\']|\".*?\"|\'.*?\')*?)([(,)]|$)/.test(\"'''''''''''''''''''''''''''''''''''''''''''''\\\"\");"`. It's complicated though, because the regexp still matches something, it just matches the empty-string after the attack string. +actuallyBad = re.compile(r'''((?:[^"']|".*?"|'.*?')*?)([(,)]|$)''') + +# NOT GOOD; attack: "a" + "[]".repeat(100) + ".b\n" +# Adapted from Knockout (https://github.com/knockout/knockout), which is +# licensed under the MIT license; see file knockout-LICENSE +bad6 = re.compile(r'''^[\_$a-z][\_$a-z0-9]*(\[.*?\])*(\.[\_$a-z][\_$a-z0-9]*(\[.*?\])*)*$''') + +# GOOD +good6 = re.compile(r'''(a|.)*''') + +# Testing the NFA - only some of the below are detected. +bad7 = re.compile(r'''^([a-z]+)+$''') +bad8 = re.compile(r'''^([a-z]*)*$''') +bad9 = re.compile(r'''^([a-zA-Z0-9])(([\\-.]|[_]+)?([a-zA-Z0-9]+))*(@){1}[a-z0-9]+[.]{1}(([a-z]{2,3})|([a-z]{2,3}[.]{1}[a-z]{2,3}))$''') +bad10 = re.compile(r'''^(([a-z])+.)+[A-Z]([a-z])+$''') + +# NOT GOOD; attack: "[" + "][".repeat(100) + "]!" +# Adapted from Prototype.js (https://github.com/prototypejs/prototype), which +# is licensed under the MIT license; see file Prototype.js-LICENSE. +bad11 = re.compile(r'''(([\w#:.~>+()\s-]+|\*|\[.*?\])+)\s*(,|$)''') + +# NOT GOOD; attack: "'" + "\\a".repeat(100) + '"' +# Adapted from Prism (https://github.com/PrismJS/prism), which is licensed +# under the MIT license; see file Prism-LICENSE. +bad12 = re.compile(r'''("|')(\\?.)*?\1''') + +# NOT GOOD +bad13 = re.compile(r'''(b|a?b)*c''') + +# NOT GOOD +bad15 = re.compile(r'''(a|aa?)*b''') + +# GOOD +good7 = re.compile(r'''(.|\n)*!''') + +# NOT GOOD; attack: "\n".repeat(100) + "." +bad16 = re.compile(r'''(.|\n)*!''') + +# GOOD +good8 = re.compile(r'''([\w.]+)*''') + +# NOT GOOD +bad17 = re.compile(r'''(a|aa?)*b''') + +# GOOD - not used as regexp +good9 = '(a|aa?)*b' + +# NOT GOOD +bad18 = re.compile(r'''(([\s\S]|[^a])*)"''') + +# GOOD - there is no witness in the end that could cause the regexp to not match +good10 = re.compile(r'''([^"']+)*''') + +# NOT GOOD +bad20 = re.compile(r'''((.|[^a])*)"''') + +# GOOD +good10 = re.compile(r'''((a|[^a])*)"''') + +# NOT GOOD +bad21 = re.compile(r'''((b|[^a])*)"''') + +# NOT GOOD +bad22 = re.compile(r'''((G|[^a])*)"''') + +# NOT GOOD +bad23 = re.compile(r'''(([0-9]|[^a])*)"''') + +# NOT GOOD +bad24 = re.compile(r'''(?:=(?:([!#\$%&'\*\+\-\.\^_`\|~0-9A-Za-z]+)|"((?:\\[\x00-\x7f]|[^\x00-\x08\x0a-\x1f\x7f"])*)"))?''') + +# NOT GOOD +bad25 = re.compile(r'''"((?:\\[\x00-\x7f]|[^\x00-\x08\x0a-\x1f\x7f"])*)"''') + +# GOOD +bad26 = re.compile(r'''"((?:\\[\x00-\x7f]|[^\x00-\x08\x0a-\x1f\x7f"\\])*)"''') + +# NOT GOOD +bad27 = re.compile(r'''(([a-z]|[d-h])*)"''') + +# NOT GOOD +bad27 = re.compile(r'''(([^a-z]|[^0-9])*)"''') + +# NOT GOOD +bad28 = re.compile(r'''((\d|[0-9])*)"''') + +# NOT GOOD +bad29 = re.compile(r'''((\s|\s)*)"''') + +# NOT GOOD +bad30 = re.compile(r'''((\w|G)*)"''') + +# GOOD +good11 = re.compile(r'''((\s|\d)*)"''') + +# NOT GOOD +bad31 = re.compile(r'''((\d|\w)*)"''') + +# NOT GOOD +bad32 = re.compile(r'''((\d|5)*)"''') + +# NOT GOOD +bad33 = re.compile(r'''((\s|[\f])*)"''') + +# NOT GOOD +bad34 = re.compile(r'''((\s|[\v]|\\v)*)"''') + +# NOT GOOD +bad35 = re.compile(r'''((\f|[\f])*)"''') + +# NOT GOOD +bad36 = re.compile(r'''((\W|\D)*)"''') + +# NOT GOOD +bad37 = re.compile(r'''((\S|\w)*)"''') + +# NOT GOOD +bad38 = re.compile(r'''((\S|[\w])*)"''') + +# NOT GOOD +bad39 = re.compile(r'''((1s|[\da-z])*)"''') + +# NOT GOOD +bad40 = re.compile(r'''((0|[\d])*)"''') + +# NOT GOOD +bad41 = re.compile(r'''(([\d]+)*)"''') + +# GOOD - there is no witness in the end that could cause the regexp to not match +good12 = re.compile(r'''(\d+(X\d+)?)+''') + +# GOOD - there is no witness in the end that could cause the regexp to not match +good13 = re.compile(r'''([0-9]+(X[0-9]*)?)*''') + +# GOOD +good15 = re.compile(r'''^([^>]+)*(>|$)''') + +# NOT GOOD +bad43 = re.compile(r'''^([^>a]+)*(>|$)''') + +# NOT GOOD +bad44 = re.compile(r'''(\n\s*)+$''') + +# NOT GOOD +bad45 = re.compile(r'''^(?:\s+|#.*|\(\?#[^)]*\))*(?:[?*+]|{\d+(?:,\d*)?})''') + +# NOT GOOD +bad46 = re.compile(r'''\{\[\s*([a-zA-Z]+)\(([a-zA-Z]+)\)((\s*([a-zA-Z]+)\: ?([ a-zA-Z{}]+),?)+)*\s*\]\}''') + +# NOT GOOD +bad47 = re.compile(r'''(a+|b+|c+)*c''') + +# NOT GOOD +bad48 = re.compile(r'''(((a+a?)*)+b+)''') + +# NOT GOOD +bad49 = re.compile(r'''(a+)+bbbb''') + +# GOOD +good16 = re.compile(r'''(a+)+aaaaa*a+''') + +# NOT GOOD +bad50 = re.compile(r'''(a+)+aaaaa$''') + +# GOOD +good17 = re.compile(r'''(\n+)+\n\n''') + +# NOT GOOD +bad51 = re.compile(r'''(\n+)+\n\n$''') + +# NOT GOOD +bad52 = re.compile(r'''([^X]+)*$''') + +# NOT GOOD +bad53 = re.compile(r'''(([^X]b)+)*$''') + +# GOOD +good18 = re.compile(r'''(([^X]b)+)*($|[^X]b)''') + +# NOT GOOD +bad54 = re.compile(r'''(([^X]b)+)*($|[^X]c)''') + +# GOOD +good20 = re.compile(r'''((ab)+)*ababab''') + +# GOOD +good21 = re.compile(r'''((ab)+)*abab(ab)*(ab)+''') + +# GOOD +good22 = re.compile(r'''((ab)+)*''') + +# NOT GOOD +bad55 = re.compile(r'''((ab)+)*$''') + +# GOOD +good23 = re.compile(r'''((ab)+)*[a1][b1][a2][b2][a3][b3]''') + +# NOT GOOD +bad56 = re.compile(r'''([\n\s]+)*(.)''') + +# GOOD - any witness passes through the accept state. +good24 = re.compile(r'''(A*A*X)*''') + +# GOOD +good26 = re.compile(r'''([^\\\]]+)*''') + +# NOT GOOD +bad59 = re.compile(r'''(\w*foobarbaz\w*foobarbaz\w*foobarbaz\w*foobarbaz\s*foobarbaz\d*foobarbaz\w*)+-''') + +# NOT GOOD +bad60 = re.compile(r'''(.thisisagoddamnlongstringforstresstestingthequery|\sthisisagoddamnlongstringforstresstestingthequery)*-''') + +# NOT GOOD +bad61 = re.compile(r'''(thisisagoddamnlongstringforstresstestingthequery|this\w+query)*-''') + +# GOOD +good27 = re.compile(r'''(thisisagoddamnlongstringforstresstestingthequery|imanotherbutunrelatedstringcomparedtotheotherstring)*-''') + +# GOOD +good28 = re.compile(r'''foo([\uDC66\uDC67]|[\uDC68\uDC69])*foo''') + +# GOOD +good29 = re.compile(r'''foo((\uDC66|\uDC67)|(\uDC68|\uDC69))*foo''') + +# NOT GOOD (but cannot currently construct a prefix) +bad62 = re.compile(r'''a{2,3}(b+)+X''') + +# NOT GOOD (and a good prefix test) +bad63 = re.compile(r'''^<(\w+)((?:\s+\w+(?:\s*=\s*(?:(?:"[^"]*")|(?:'[^']*')|[^>\s]+))?)*)\s*(\/?)>''') + +# GOOD +good30 = re.compile(r'''(a+)*[\s\S][\s\S][\s\S]?''') + +# GOOD - but we fail to see that repeating the attack string ends in the "accept any" state (due to not parsing the range `[\s\S]{2,3}`). +good31 = re.compile(r'''(a+)*[\s\S]{2,3}''') + +# GOOD - but we spuriously conclude that a rejecting suffix exists (due to not parsing the range `[\s\S]{2,}` when constructing the NFA). +good32 = re.compile(r'''(a+)*([\s\S]{2,}|X)$''') + +# GOOD +good33 = re.compile(r'''(a+)*([\s\S]*|X)$''') + +# NOT GOOD +bad64 = re.compile(r'''((a+)*$|[\s\S]+)''') + +# GOOD - but still flagged. The only change compared to the above is the order of alternatives, which we don't model. +good34 = re.compile(r'''([\s\S]+|(a+)*$)''') + +# GOOD +good35 = re.compile(r'''((;|^)a+)+$''') + +# NOT GOOD (a good prefix test) +bad65 = re.compile(r'''(^|;)(0|1)(0|1)(0|1)(0|1)(0|1)(0|1)(0|1)(0|1)(0|1)(0|1)(0|1)(0|1)(0|1)(0|1)(e+)+f''') + +# NOT GOOD +bad66 = re.compile(r'''^ab(c+)+$''') + +# NOT GOOD +bad67 = re.compile(r'''(\d(\s+)*){20}''') + +# GOOD - but we spuriously conclude that a rejecting suffix exists. +good36 = re.compile(r'''(([^/]|X)+)(\/[\s\S]*)*$''') + +# GOOD - but we spuriously conclude that a rejecting suffix exists. +good37 = re.compile(r'''^((x([^Y]+)?)*(Y|$))''') + +# NOT GOOD +bad68 = re.compile(r'''(a*)+b''') + +# NOT GOOD +bad69 = re.compile(r'''foo([\w-]*)+bar''') + +# NOT GOOD +bad70 = re.compile(r'''((ab)*)+c''') + +# NOT GOOD +bad71 = re.compile(r'''(a?a?)*b''') + +# GOOD +good38 = re.compile(r'''(a?)*b''') + +# NOT GOOD - but not detected +bad72 = re.compile(r'''(c?a?)*b''') + +# NOT GOOD +bad73 = re.compile(r'''(?:a|a?)+b''') + +# NOT GOOD - but not detected. +bad74 = re.compile(r'''(a?b?)*$''') + +# NOT GOOD +bad76 = re.compile(r'''PRE(([a-c]|[c-d])T(e?e?e?e?|X))+(cTcT|cTXcTX$)''') + +# NOT GOOD - but not detected +bad77 = re.compile(r'''^((a)+\w)+$''') + +# NOT GOOD +bad78 = re.compile(r'''^(b+.)+$''') + +# GOOD +good39 = re.compile(r'''a*b''') + +# All 4 bad combinations of nested * and + +bad79 = re.compile(r'''(a*)*b''') +bad80 = re.compile(r'''(a+)*b''') +bad81 = re.compile(r'''(a*)+b''') +bad82 = re.compile(r'''(a+)+b''') + +# GOOD +good40 = re.compile(r'''(a|b)+''') +good41 = re.compile(r'''(?:[\s;,"'<>(){}|[\]@=+*]|:(?![/\\]))+''') # parses wrongly, sees column 42 as a char set start + +# NOT GOOD +bad83 = re.compile(r'''^((?:a{|-)|\w\{)+X$''') +bad84 = re.compile(r'''^((?:a{0|-)|\w\{\d)+X$''') +bad85 = re.compile(r'''^((?:a{0,|-)|\w\{\d,)+X$''') +bad86 = re.compile(r'''^((?:a{0,2|-)|\w\{\d,\d)+X$''') + +# GOOD: +good42 = re.compile(r'''^((?:a{0,2}|-)|\w\{\d,\d\})+X$''') diff --git a/python/ql/test/query-tests/Security/CWE-730/unittests.py b/python/ql/test/query-tests/Security/CWE-730/unittests.py new file mode 100644 index 000000000000..7b69c10771fc --- /dev/null +++ b/python/ql/test/query-tests/Security/CWE-730/unittests.py @@ -0,0 +1,9 @@ +import re + +# Treatment of escapes +re.compile(r"X([^\.]|\.)*$") # No ReDoS. +re.compile(r"X(Æ|\Æ)+$") # Has ReDoS. + +# Treatment of line breaks +re.compile(r'(?:.|\n)*b') # No ReDoS. +re.compile(r'(?:.|\n)*b', re.DOTALL) # Has ReDoS.