@recaptime-dev's working patches + fork for Phorge, a community fork of Phabricator. (Upstream dev and stable branches are at upstream/main and upstream/stable respectively.) hq.recaptime.dev/wiki/Phorge
phorge phabricator
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

Fix an issue where the "%%%" parser could match too many lines in unterminated blocks

Summary: Fixes T13530. The block parser could match too many lines in an unterminated "%%%" literal block. Adjust the logic to stop doing this (and hopefully be a little easier to read).

Test Plan: Added a failing test, made it pass.

Maniphest Tasks: T13530

Differential Revision: https://secure.phabricator.com/D21208

+53 -29
+47 -29
src/infrastructure/markup/blockrule/PhutilRemarkupLiteralBlockRule.php
··· 22 22 // output text and assists automated escaping of blocks coming into the 23 23 // system. 24 24 25 - $num_lines = 0; 26 - while (preg_match('/^\s*%%%/', $lines[$cursor])) { 27 - $num_lines++; 25 + $start_pattern = '(^\s*%%%)'; 26 + $end_pattern = '(%%%\s*$)'; 27 + $trivial_pattern = '(^\s*%%%\s*$)'; 28 + 29 + if (!preg_match($start_pattern, $lines[$cursor])) { 30 + return 0; 31 + } 32 + 33 + $start_cursor = $cursor; 28 34 29 - // If the line has ONLY "%%%", the block opener doesn't get to double 30 - // up as a block terminator. 31 - if (preg_match('/^\s*%%%\s*\z/', $lines[$cursor])) { 32 - $num_lines++; 33 - $cursor++; 35 + $found_empty = false; 36 + $block_start = null; 37 + while (true) { 38 + if (!isset($lines[$cursor])) { 39 + break; 34 40 } 35 41 36 - while (isset($lines[$cursor])) { 37 - if (!preg_match('/%%%\s*$/', $lines[$cursor])) { 38 - $num_lines++; 39 - $cursor++; 40 - continue; 42 + $line = $lines[$cursor]; 43 + 44 + if ($block_start === null) { 45 + $is_start = preg_match($start_pattern, $line); 46 + 47 + // If we've matched a block and then consumed one or more empty lines 48 + // after it, stop merging more blocks into the match. 49 + if ($found_empty) { 50 + break; 41 51 } 42 - break; 52 + 53 + if ($is_start) { 54 + $block_start = $cursor; 55 + } 43 56 } 44 57 45 - $cursor++; 58 + if ($block_start !== null) { 59 + $is_end = preg_match($end_pattern, $line); 60 + 61 + // If a line contains only "%%%", it will match both the start and 62 + // end patterns, but it only counts as a block start. 63 + if ($is_end && ($cursor === $block_start)) { 64 + $is_trivial = preg_match($trivial_pattern, $line); 65 + if ($is_trivial) { 66 + $is_end = false; 67 + } 68 + } 46 69 47 - $found_empty = false; 48 - while (isset($lines[$cursor])) { 49 - if (!strlen(trim($lines[$cursor]))) { 50 - $num_lines++; 70 + if ($is_end) { 71 + $block_start = null; 51 72 $cursor++; 52 - $found_empty = true; 53 73 continue; 54 74 } 55 - break; 56 75 } 57 76 58 - if ($found_empty) { 59 - // If there's an empty line after the block, stop merging blocks. 60 - break; 77 + if ($block_start === null) { 78 + if (strlen(trim($line))) { 79 + break; 80 + } 81 + $found_empty = true; 61 82 } 62 83 63 - if (!isset($lines[$cursor])) { 64 - // If we're at the end of the input, stop looking for more lines. 65 - break; 66 - } 84 + $cursor++; 67 85 } 68 86 69 - return $num_lines; 87 + return ($cursor - $start_cursor); 70 88 } 71 89 72 90 public function markupText($text, $children) {
+1
src/infrastructure/markup/remarkup/PhutilRemarkupEngine.php
··· 204 204 } 205 205 206 206 $cursor += $num_lines; 207 + 207 208 break; 208 209 } 209 210 }
+5
src/infrastructure/markup/remarkup/__tests__/remarkup/percent-block-unterminated.txt
··· 1 + %%%xyz 2 + ~~~~~~~~~~ 3 + <p class="remarkup-literal">xyz</p> 4 + ~~~~~~~~~~ 5 + xyz