(body: string | null)
| 29 | }; |
| 30 | |
| 31 | export const findNoteInPRBody = (body: string | null) => { |
| 32 | if (!body) return null; |
| 33 | |
| 34 | const onelineMatch = /(?:(?:\r?\n)|^)Notes: (.+?)(?:(?:\r?\n)|$)/gi.exec(body); |
| 35 | const multilineMatch = /(?:(?:\r?\n)Notes:(?:\r?\n+)((?:\*.+(?:(?:\r?\n)|$))+))/gi.exec(body); |
| 36 | |
| 37 | let notes: string | null = null; |
| 38 | if (onelineMatch?.[1]) { |
| 39 | notes = onelineMatch[1]; |
| 40 | } else if (multilineMatch?.[1]) { |
| 41 | notes = multilineMatch[1]; |
| 42 | } |
| 43 | |
| 44 | // Remove the default PR template if it exists. Bound the lazy scan for the |
| 45 | // same reason as in updatePRBodyForNoNotes: `notes` is derived from the |
| 46 | // attacker-controlled PR body, and an unbounded `.*?` under the global flag |
| 47 | // is O(n^2) when the input contains many `<!--` prefixes with no closing |
| 48 | // `-->`. Capping the span keeps this linear in the input length. |
| 49 | notes = notes ? notes.replace(/<!--.{0,1000}?-->/g, '') : null; |
| 50 | |
| 51 | if (notes) { |
| 52 | debug(`Found Notes: ${JSON.stringify(notes.trim())}`); |
| 53 | |
| 54 | const sanitizeMap = new Map([ |
| 55 | ['<', '<'], |
| 56 | ['>', '>'], |
| 57 | ]); |
| 58 | for (const [oldVal, newVal] of sanitizeMap.entries()) { |
| 59 | notes = notes.replaceAll(oldVal, newVal); |
| 60 | } |
| 61 | } |
| 62 | |
| 63 | return notes ? notes.trim() : notes; |
| 64 | }; |
| 65 | |
| 66 | const OMIT_FROM_RELEASE_NOTES_KEYS = [ |
| 67 | /^blank.?$/i, |
no outgoing calls
no test coverage detected