diff --git a/CHANGELOG.md b/CHANGELOG.md index 3c6f8a5..f798625 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,13 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and this project follows [Semantic Versioning](https://semver.org/spec/v2.0.0.html) where applicable. +## 1.0.7 — 2026-10-16 + +- Require Symfony ExpressionLanguage 8.1.9 and YAML 8.1.6 or newer to exclude CVE-2026-109449 and CVE-2026-109413. +- Require SymPress Kernel 1.1.10 or newer to retain the corrected HttpFoundation 8.1.9 minimum in lowest-version installations. +- Require Symfony FrameworkBundle 8.1.8 or newer so the native development cache warmer is registered when the profiler is disabled. +- Verify dependency version boundaries and rejection of excessively nested expressions and inline YAML collections. + ## 1.0.6 — 2026-10-09 - Require maintained Symfony Routing and YAML 8.1 or newer, excluding the vulnerable 8.0 releases covered by the May 2026 advisories. diff --git a/composer.json b/composer.json index ef0f7d3..a75f13d 100644 --- a/composer.json +++ b/composer.json @@ -20,17 +20,18 @@ "symfony/console": "^8.1", "symfony/dependency-injection": "^8.1.8", "symfony/error-handler": "^8.1", - "symfony/expression-language": "^8.0", + "symfony/expression-language": "^8.1.9", "symfony/filesystem": "^8.0", - "symfony/framework-bundle": "^8.1", + "symfony/framework-bundle": "^8.1.8", "symfony/http-kernel": "^8.1", "symfony/routing": "^8.1", "symfony/service-contracts": "^3.6", - "symfony/yaml": "^8.1", - "sympress/kernel": "^1.1.1" + "symfony/yaml": "^8.1.6", + "sympress/kernel": "^1.1.10" }, "require-dev": { "brain/monkey": "^2.6", + "composer/semver": "^3.4", "sympress/qa": "^0.1.2" }, "suggest": { diff --git a/tests/Security/SymfonySecurityCompatibilityTest.php b/tests/Security/SymfonySecurityCompatibilityTest.php new file mode 100644 index 0000000..c42abff --- /dev/null +++ b/tests/Security/SymfonySecurityCompatibilityTest.php @@ -0,0 +1,77 @@ +} $manifest */ + $manifest = json_decode((string) file_get_contents(dirname(__DIR__, 2) . '/composer.json'), true, flags: JSON_THROW_ON_ERROR); + + self::assertFalse(Semver::satisfies($rejected, $manifest['require'][$package])); + self::assertTrue(Semver::satisfies($accepted, $manifest['require'][$package])); + } + + /** @return iterable */ + public static function dependencyBoundaries(): iterable + { + yield 'expression initial incomplete fix' => ['symfony/expression-language', '8.1.6', '8.1.9']; + yield 'expression last affected patch' => ['symfony/expression-language', '8.1.8', '8.1.9']; + yield 'yaml inline lexer' => ['symfony/yaml', '8.1.5', '8.1.6']; + yield 'kernel header parser floor' => ['sympress/kernel', '1.1.9', '1.1.10']; + yield 'native development cache warmer' => ['symfony/framework-bundle', '8.1.7', '8.1.8']; + } + + #[DataProvider('deepExpressions')] + public function testDependencyInjectionRejectsDeepExpressions(string $expression): void + { + try { + (new ExpressionLanguage())->lint($expression, []); + self::fail('An excessively nested expression was accepted.'); + } catch (SyntaxError $exception) { + self::assertStringContainsString('maximum nesting level is 256', $exception->getMessage()); + } + } + + /** @return iterable */ + public static function deepExpressions(): iterable + { + yield 'unary nesting' => [str_repeat('!', 300) . '1']; + + $expression = '1'; + for ($level = 0; $level < 60; ++$level) { + $expression = '(' . $expression . str_repeat(' + 1', 60) . ')'; + } + yield 'chained binary nesting missed by 8.1.6' => [$expression]; + } + + public function testOrdinaryDependencyInjectionExpressionStillWorks(): void + { + self::assertSame(3, (new ExpressionLanguage())->evaluate('1 + 2')); + } + + public function testInlineYamlLexerHonorsConfiguredNestingLimit(): void + { + $parser = new Parser(8); + self::assertSame(['root' => [[1]]], $parser->parse('root: [[1]]')); + + try { + $parser->parse('root: ' . str_repeat('[', 9)); + self::fail('The inline YAML lexer exceeded its configured nesting limit.'); + } catch (ParseException $exception) { + self::assertStringContainsString('Maximum nesting depth of 8 exceeded', $exception->getMessage()); + } + } +}