cacheKey); EditorSetting::instance()->resetDefault(); \System\Behaviors\SettingsModel::clearInternalCache(); parent::tearDown(); } /** * Test that renderCss output does not contain script tags even when * malicious CSS using LESS escape syntax is stored in the database. */ public function testRenderCssStripsScriptTags() { $maliciousStyles = '.x { content: ~"', $renderedCss); } /** * Regression for GHSA-5cwr-5jxg-pcf6. renderCss() caches the raw compiler * output, so sanitizing only the cache-miss return leaves every later cache * hit unsanitized. The first render primes the cache; the second is the one * that used to emit active markup into the backend ', $renderedCss); } /** * A cache entry poisoned before GHSA-5cwr-5jxg-pcf6 was patched is not * cleared by upgrading, so it must still be sanitized when read back. */ public function testRenderCssStripsScriptTagsFromExistingCacheEntry() { \Illuminate\Support\Facades\Cache::forever( EditorSetting::instance()->cacheKey, '.fr-view .x{content:', $renderedCss); } /** * Test that normal CSS content is preserved through renderCss, on both the * cache miss and the cache hit that follows it. */ public function testRenderCssPreservesNormalCss() { $normalStyles = '.my-class { color: blue; font-weight: bold; }'; EditorSetting::set('html_custom_styles', $normalStyles); \System\Behaviors\SettingsModel::clearInternalCache(); \Illuminate\Support\Facades\Cache::forget(EditorSetting::instance()->cacheKey); $renderedCss = EditorSetting::renderCss(); $this->assertStringContainsString('color', $renderedCss); $this->assertStringContainsString('font-weight', $renderedCss); $this->assertDoesNotMatchRegularExpression('/<[a-z\/!]/', $renderedCss); // Sanitizing the cache hit must not alter legitimate CSS $this->assertEquals($renderedCss, EditorSetting::renderCss()); } /** * Regression for GHSA-58fp-mcx6-7qf9. A user-supplied `@import (inline)` * directive in `html_custom_styles` must not be able to disclose server files. */ public function testRenderCssBlocksImportAttack() { $tmpSecret = tempnam(sys_get_temp_dir(), 'editorsetting-leak-canary-'); file_put_contents($tmpSecret, "APP_KEY=do-not-leak-via-editorsetting\n"); try { EditorSetting::set('html_custom_styles', '@import (inline) "' . $tmpSecret . '";'); \System\Behaviors\SettingsModel::clearInternalCache(); \Illuminate\Support\Facades\Cache::forget(EditorSetting::instance()->cacheKey); $renderedCss = EditorSetting::renderCss(); $this->assertStringNotContainsString('APP_KEY', $renderedCss); $this->assertStringNotContainsString('do-not-leak-via-editorsetting', $renderedCss); } finally { @unlink($tmpSecret); } } }