{"record":{"id":"cddaa860c72597f0","repo":"flarum/framework","slug":"core-admin-appearance-custom-styles-cannot-use-less-features","errorCode":null,"errorMessage":"core.admin.appearance.custom_styles_cannot_use_less_features","messagePattern":"core\\.admin\\.appearance\\.custom_styles_cannot_use_less_features","errorType":"validation","errorClass":"ValidationException","httpStatus":null,"severity":"error","filePath":"framework/core/src/Forum/ValidateCustomLess.php","lineNumber":70,"sourceCode":"            isset($event->settings['custom_less']) ? ['custom_less'] : [],\n            array_intersect(\n                array_keys($event->settings),\n                array_column($this->customLessSettings, 'key')\n            )\n        );\n\n        foreach ($lessFeatureKeys as $key) {\n            // The file system is taken away from the compiler by\n            // LessCompiler::containImports(), which is what actually stops a\n            // custom-LESS file read. This check stays so the administrator is\n            // told at save time rather than silently getting a stylesheet with\n            // the import dropped. `@impor` is matched as well as `@import`,\n            // because less.php matches the directive as `@import?` and so\n            // parses both the same way.\n            if (is_string($event->settings[$key]) && preg_match('/@impor|data-uri\\s*\\(/i', $event->settings[$key])) {\n                $translator = $this->container->make(TranslatorInterface::class);\n\n                throw new ValidationException([\n                    $key => $translator->trans('core.admin.appearance.custom_styles_cannot_use_less_features')\n                ]);\n            }\n        }\n\n        // We haven't saved the settings yet, but we want to trial a full\n        // recompile of the CSS to see if this custom LESS will break\n        // anything. In order to do that, we will temporarily override the\n        // settings repository with the new settings so that the recompile\n        // is effective. We will also use a dummy filesystem so that nothing\n        // is actually written yet.\n\n        $settings = $this->container->make(SettingsRepositoryInterface::class);\n\n        $this->container->extend(\n            SettingsRepositoryInterface::class,\n            function ($settings) use ($event) {\n                return new OverrideSettingsRepository($settings, $event->settings);","sourceCodeStart":52,"sourceCodeEnd":88,"githubUrl":"https://github.com/flarum/framework/blob/4b939f685389bfe8a380e9e28ddf305a1c66950c/framework/core/src/Forum/ValidateCustomLess.php#L52-L88","documentation":"ValidateCustomLess::whenSettingsSaving throws ValidationException with the translated message core.admin.appearance.custom_styles_cannot_use_less_features when custom LESS/CSS settings contain constructs less.php cannot safely evaluate: @import directives (or the mistyped '@impor' which less.php parses the same way) or data-uri() calls. These features would let admins read arbitrary files at compile time, so they are blocked.","triggerScenarios":"Saving admin Appearance settings where custom_less or custom_header contains '@import', '@impor', or 'data-uri(' (case-insensitive), detected by the regex in whenSettingsSaving before assets are compiled.","commonSituations":"Admins pasting CSS/LESS copied from themes that use @import for fonts or external stylesheets; importing Google Fonts via @import url(...) in custom styles.","solutions":["Remove any @import statements and data-uri() calls from the custom LESS/CSS.","Load external stylesheets with <link> tags in custom HTML/header instead of CSS @import.","Inline the imported LESS content directly, or add fonts via the site's HTML head section rather than LESS imports."],"exampleFix":"// before (custom_less)\n@import url(\"https://fonts.googleapis.com/css?family=Open+Sans\");\n\n// after (custom header HTML instead)\n<link rel=\"stylesheet\" href=\"https://fonts.googleapis.com/css?family=Open+Sans\">","handlingStrategy":"validation","validationCode":"// before saving custom styles\nconst blocked = /@impor|data-uri\\s*\\(/i;\nif (typeof customLess === 'string' && blocked.test(customLess)) {\n  alert('Custom styles cannot use @import or data-uri()');\n}","typeGuard":null,"tryCatchPattern":"try {\n    $this->settings->save($payload);\n} catch (ValidationException $e) {\n    return response()->json(['errors' => $e->errors()], 422);\n}","preventionTips":["Load external CSS with <link> tags in the header, never @import.","Inline any needed LESS rather than importing files in custom styles.","Remember '@impor' typo variants are also blocked by the same regex."],"tags":["flarum","validation","less","security","custom-styles"],"backgroundTag":"unsupported-operation","analyzedSha":"4b939f685389bfe8a380e9e28ddf305a1c66950c","analyzedAt":"2026-09-15T18:09:20.879Z","contentChangedAt":"2026-09-15T18:09:20.879Z","schemaVersion":2},"datasetVersion":"2026-09-16T09:17:16.951Z"}