(body)
| 59 | } |
| 60 | |
| 61 | function isSafeRegexPatternBody(body) { |
| 62 | if (!body || body.length > 256) return false; |
| 63 | for (const ch of body) { |
| 64 | if (!isAllowedSafeRegexCharacter(ch)) return false; |
| 65 | } |
| 66 | if ( |
| 67 | body.includes('(?=') |
| 68 | || body.includes('(?!') |
| 69 | || body.includes('(?<=') |
| 70 | || body.includes('(?<!') |
| 71 | || body.includes('(?<') |
| 72 | ) { |
| 73 | return false; |
| 74 | } |
| 75 | if (hasUnsafeRegexBackreference(body)) { |
| 76 | return false; |
| 77 | } |
| 78 | |
| 79 | const groupStack = []; |
| 80 | let escaped = false; |
| 81 | let inCharClass = false; |
| 82 | for (let index = 0; index < body.length; index += 1) { |
| 83 | const ch = body[index]; |
| 84 | if (escaped) { |
| 85 | escaped = false; |
| 86 | continue; |
| 87 | } |
| 88 | if (ch === '\\') { |
| 89 | const next = body[index + 1]; |
| 90 | if (!next) return false; |
| 91 | if (/[a-z]/i.test(next) && next !== 'd') { |
| 92 | return false; |
| 93 | } |
| 94 | escaped = true; |
| 95 | continue; |
| 96 | } |
| 97 | if (inCharClass) { |
| 98 | if (ch === ']') inCharClass = false; |
| 99 | continue; |
| 100 | } |
| 101 | if (ch === '[') { |
| 102 | inCharClass = true; |
| 103 | continue; |
| 104 | } |
| 105 | if (ch === '(') { |
| 106 | if (body[index + 1] === '?') { |
| 107 | return false; |
| 108 | } |
| 109 | groupStack.push({ hasInnerQuantifier: false, hasAlternation: false }); |
| 110 | continue; |
| 111 | } |
| 112 | if (ch === '|') { |
| 113 | if (groupStack.length > 0) { |
| 114 | groupStack[groupStack.length - 1].hasAlternation = true; |
| 115 | } |
| 116 | continue; |
| 117 | } |
| 118 | if (ch === ')') { |
no test coverage detected