From 4f31542d077ada9cbb7cc5114071860efaeb761b Mon Sep 17 00:00:00 2001 From: Claude Date: Thu, 8 Oct 2026 10:01:38 +0000 Subject: [PATCH 1/2] fix(lexer): remove reference to deleted PATTERN_TABLE_SEPARATOR #44 (blockquote laziness) and #45 (table detection) were merged in that order. #44 used PATTERN_TABLE_SEPARATOR in quoteLineState(), #45 deleted the constant, so every blockquote now throws "Error: Undefined constant Lexer::PATTERN_TABLE_SEPARATOR" on main (40 test errors). The check added nothing to blockquote laziness; drop it. (cherry picked from commit 7e799b1, pushed to the #44 branch after #44 had already been merged) Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01CPcuaaxrCottkeL1ykoSWe --- src/Lexer/Lexer.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Lexer/Lexer.php b/src/Lexer/Lexer.php index 4600260..583fc47 100644 --- a/src/Lexer/Lexer.php +++ b/src/Lexer/Lexer.php @@ -651,7 +651,7 @@ private function quoteLineState(string $content, bool $paragraphOpen, bool $fenc } $isText = $this->isParagraphContinuation($content) || ($paragraphOpen && $content !== '' && !ctype_space($content) && !preg_match(self::PATTERN_HEADING, $content)); - return [$isText && !preg_match(self::PATTERN_TABLE_SEPARATOR, $content), false]; + return [$isText, false]; } private function matchLine(string $line): Token From c551026c9f2057801a1a914a65753c65320bf682 Mon Sep 17 00:00:00 2001 From: Claude Date: Thu, 8 Oct 2026 10:11:34 +0000 Subject: [PATCH 2/2] fix(parser): enforce maxBytes after NFC normalization; stop ignoring phpunit.xml - The size limit was checked before Unicode normalization, but NFC can grow the input (U+0958 has no composed form and doubles in size), so the parser could process more than maxBytes. The limit is now also checked on the normalized string. - .gitignore listed /phpunit.xml although it is the tracked PHPUnit config (an ignore rule has no effect on a tracked file and only misleads); the entries are removed. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01CPcuaaxrCottkeL1ykoSWe --- .gitignore | 4 ---- src/MarkdownParser.php | 11 ++++++++++- tests/Integration/MarkdownParserTest.php | 11 +++++++++++ 3 files changed, 21 insertions(+), 5 deletions(-) diff --git a/.gitignore b/.gitignore index 582c5e9..455cec8 100644 --- a/.gitignore +++ b/.gitignore @@ -9,10 +9,6 @@ .phpunit.result.cache .phpunit.cache -# PHPUnit -/app/phpunit.xml -/phpunit.xml - # Logs /logs/ *.log diff --git a/src/MarkdownParser.php b/src/MarkdownParser.php index 1efa5e9..fd1d73e 100644 --- a/src/MarkdownParser.php +++ b/src/MarkdownParser.php @@ -49,7 +49,8 @@ public function __construct( } /** - * @throws ParseException if input exceeds maxBytes or is not valid UTF-8. + * @throws ParseException if input exceeds maxBytes (before or after NFC normalization) + * or is not valid UTF-8. */ private function guard(string &$markdown): void { @@ -63,6 +64,14 @@ private function guard(string &$markdown): void } $normalized = $this->normalizer->normalize($markdown); $markdown = $normalized !== false ? $normalized : $markdown; + + // NFC can grow the input (e.g. U+0958 has no composed form and doubles in size): + // the limit applies to what the parser actually processes. + if (strlen($markdown) > $this->maxBytes) { + throw new ParseException( + sprintf('Input exceeds maximum allowed size of %d bytes after Unicode normalization.', $this->maxBytes), + ); + } } /** diff --git a/tests/Integration/MarkdownParserTest.php b/tests/Integration/MarkdownParserTest.php index 36a9654..28b5ce2 100644 --- a/tests/Integration/MarkdownParserTest.php +++ b/tests/Integration/MarkdownParserTest.php @@ -131,6 +131,17 @@ public function testMaxBytesAllowsInputAtExactByteLimit(): void $this->assertNotEmpty($html); } + public function testMaxBytesIsEnforcedAfterNormalization(): void + { + // U+0958 (3 bytes) has no composed form: NFC turns it into 2 code points (6 bytes). + // 10 × 3 = 30 bytes fits the limit before normalization, 60 bytes does not after. + $parser = new MarkdownParser(maxBytes: 40); + + $this->expectException(ParseException::class); + $this->expectExceptionMessage('after Unicode normalization'); + $parser->parse(str_repeat("\u{0958}", 10)); + } + // ── NFC normalisation ───────────────────────────────────────────────────── public function testNfcNormalisationConvertsNfdInput(): void