Confine asset combiner imports to allowed roots (GHSA-2223-f22x-24cq) - #1504
Confine asset combiner imports to allowed roots (GHSA-2223-f22x-24cq)#1504LukeTowers wants to merge 2 commits into
Conversation
A backend user with `cms.manage_assets` could upload a `.js` asset containing `=include ../../../.env` and have the combiner inline arbitrary server-readable files (APP_KEY, DB credentials, etc.) into output that `SystemController@combine` serves unauthenticated. This is the JavaScript sibling of the LESS import flaw (GHSA-58fp-mcx6-7qf9); that fix left the JS and CSS importers exploitable. - Configure the JS importer with the same allowed import roots already used for LESS (themes/plugins/modules), now that `JavascriptImporter` enforces path confinement (winter/storm). - Confine Assetic's `CssImportFilter` via its new opt-in import validator, rejecting `@import` targets that escape the allowed roots. - Define the allowed roots once and share them across the JS, CSS, and LESS filters. Also flag `cms.manage_assets` and `cms.manage_content` as trusted-user permissions in the backend UI. Both are classed as administrative in the security policy's scoring guidance but, unlike the other CMS permissions, carried no warning comment. Adds regression coverage in CombineAssetsTest for `=include` traversal, the `.js`-only extension gate, and legitimate same-tree includes. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
WalkthroughCMS backend permissions now expose descriptive comments for content and asset management, with corresponding English translation strings. Combined asset processing now restricts JavaScript, CSS, and LESS imports to approved roots, validates CSS paths through Estimated code review effort: 3 (Moderate) | ~20 minutes 🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
Follows the storm-side removal of ImportGuard: the combiner's CssImportFilter validator now calls the generic PathResolver::withinAny() instead. No behaviour change. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@modules/system/tests/classes/CombineAssetsTest.php`:
- Around line 193-195: Move temporary theme-directory cleanup from the three
finally blocks in modules/system/tests/classes/CombineAssetsTest.php at lines
193-195, 216-218, and 235-236 into the test class tearDown() method. At each
site, register $themeDir for teardown instead of calling
\File::deleteDirectory(), while retaining secret-file cleanup; ensure tearDown()
deletes all registered theme directories with \File::deleteDirectory().
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro Plus
Run ID: 1f4f1a85-7a27-4c85-b5a9-5832fbea677c
📒 Files selected for processing (4)
modules/cms/ServiceProvider.phpmodules/cms/lang/en/lang.phpmodules/system/classes/CombineAssets.phpmodules/system/tests/classes/CombineAssetsTest.php
| } finally { | ||
| @unlink($secretPath); | ||
| \File::deleteDirectory($themeDir); |
There was a problem hiding this comment.
📐 Maintainability & Code Quality | 🟠 Major | ⚡ Quick win
Move temporary theme-directory cleanup to tearDown().
modules/system/tests/classes/CombineAssetsTest.php#L193-L195: register$themeDirfor teardown instead of deleting it in thisfinally.modules/system/tests/classes/CombineAssetsTest.php#L216-L218: register$themeDirfor teardown instead of deleting it in thisfinally.modules/system/tests/classes/CombineAssetsTest.php#L235-L236: register$themeDirfor teardown instead of deleting it in thisfinally.
As per coding guidelines, “Fixtures that flow through Assetic\Asset\FileAsset ... must live under base_path() ... and be cleaned up with \File::deleteDirectory() in tearDown().”
Proposed cleanup pattern
+protected $temporaryThemeDirs = [];
+
+protected function tearDown(): void
+{
+ foreach ($this->temporaryThemeDirs as $themeDir) {
+ \File::deleteDirectory($themeDir);
+ }
+
+ $this->temporaryThemeDirs = [];
+ parent::tearDown();
+}
+
protected function makeTempThemeDir(): string
{
$themeDir = base_path('storage/framework/cache/security-tests/theme-' . bin2hex(random_bytes(4)));
mkdir($themeDir . '/assets/less', 0777, true);
+ $this->temporaryThemeDirs[] = $themeDir;
+
return $themeDir;
}🧰 Tools
🪛 ast-grep (0.44.1)
[info] 193-193: Avoid unsafe call to unlink
Context: unlink($secretPath)
Note: [CWE-73] External Control of File Name or Path.
(avoid-unlink)
📍 Affects 1 file
modules/system/tests/classes/CombineAssetsTest.php#L193-L195(this comment)modules/system/tests/classes/CombineAssetsTest.php#L216-L218modules/system/tests/classes/CombineAssetsTest.php#L235-L236
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@modules/system/tests/classes/CombineAssetsTest.php` around lines 193 - 195,
Move temporary theme-directory cleanup from the three finally blocks in
modules/system/tests/classes/CombineAssetsTest.php at lines 193-195, 216-218,
and 235-236 into the test class tearDown() method. At each site, register
$themeDir for teardown instead of calling \File::deleteDirectory(), while
retaining secret-file cleanup; ensure tearDown() deletes all registered theme
directories with \File::deleteDirectory().
Source: Coding guidelines
Summary
Fixes GHSA-2223-f22x-24cq. A backend user with
cms.manage_assetscould upload a.jsasset containing=include ../../../.envand have the combiner inline arbitrary server-readable files (APP_KEY, DB credentials, etc.) into output thatSystemController@combineserves unauthenticated. This is the JavaScript sibling of the LESS import flaw (GHSA-58fp-mcx6-7qf9); that fix left the JS and CSS importers exploitable.The core hardening lives in winter/storm (
JavascriptImporterpath confinement +.jsextension gate, sharedImportGuard). This PR wires it into the combiner and closes the matching CSS gap.Changes
CombineAssetsdefines the allowed import roots once (themes/plugins/modules) and shares them across:setAllowedImportRoots(),PathResolver+ImportGuard),cms.manage_assetsandcms.manage_contentare now flagged as trusted-user permissions in the backend UI. Both are classed as administrative in the security policy scoring guidance but, unlike the other CMS permissions, carried no warning comment.Tests
New
CombineAssetsTestcases for=includetraversal blocking, the.js-only extension gate, and legitimate same-tree includes. 12/12 pass. Verified end-to-end: the.envPoC no longer leaks, while the real backend JS (winter.js, cross-tree../../../system/...includes) and LESS bundles still combine with zero blocked imports.Dependencies / merge order
Requires (release order):
CssImportFilter::setImportValidator()(the CSS gate is inert until this ships and the constraint is bumped)wip/ghsa-2223-js-importer-lfi—JavascriptImporterconfinement +ImportGuard🤖 Generated with Claude Code
Summary by CodeRabbit
Security
Permissions