Apply import validation consistently across the CSS and SCSS filters - #53
Merged
Merged
Conversation
setImportValidator() was only consulted on the local-file branch. Targets carrying a scheme, and protocol-relative targets, were dispatched to the remote loader before the validator ran, so a caller that had confined imports to a set of roots still had those forms resolved unchecked. The validator now runs once, before the branch dispatch, and receives the import source in whichever form it takes: a filesystem path for local imports, or the URL for scheme-bearing and protocol-relative ones. Callers that set no validator are unaffected.
CssImportFilter lets a caller authorise each @import before it is inlined; ScssphpFilter had no equivalent, so consumers compiling SCSS they do not control had no way to bound where @import resolves. scssphp resolves targets against the configured import paths and against the importing file's own directory, both with `..` traversal allowed. This adds the same opt-in hook, implemented by filtering the result of Compiler::findImport() in a small subclass. A rejected import is reported as unresolved, so scssphp emits the original statement verbatim rather than inlining the file -- matching how CssImportFilter treats a rejected import. Callers that set no validator are unaffected.
LukeTowers
force-pushed
the
feature/import-validation
branch
from
September 15, 2026 06:08
03015ec to
915eb98
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Two related gaps in how
@importresolution can be confined by a consumer.CssImportFilter— the validator skipped two import formssetImportValidator()was only consulted on the local-file branch. An@importwhose target carries a scheme, or is protocol-relative, is dispatched to the remote loader earlier in the sameif/elseifchain, so those forms never reached the validator — a caller that had confined imports to a set of roots still had them resolved unchecked.The validator now runs once, before the branch dispatch, and receives the import source in whichever form it takes: a filesystem path for local imports, or the URL for scheme-bearing and protocol-relative ones. The docblock is updated to say so.
ScssphpFilter— no equivalent hook existedCssImportFilterlets a caller authorise each import;ScssphpFilterhad nothing comparable, so a consumer compiling SCSS it does not control had no way to bound where@importresolves. scssphp resolves targets against the configured import paths and against the importing file's own directory, both with..traversal allowed.This adds the same opt-in
setImportValidator(), implemented by filtering the result ofCompiler::findImport()in a smallValidatingCompilersubclass. All ofScssphpFilter's configuration is private, so the behaviour can't be layered on from outside the class. A rejected import is reported as unresolved, so scssphp emits the original statement verbatim instead of inlining the file — matching howCssImportFiltertreats a rejected import.Compatibility
Both hooks are opt-in and default to
null. Callers that set no validator get byte-identical behaviour; nothing in the existing suite changed.Tests
5 added:
CssImportFilterTest::testImportValidatorAuthorisesSchemeBearingImports— fails onmaster, passes here. Uses the reject path, so nothing is fetched or read.CssImportFilterTest::testImportsAreUnaffectedWhenNoValidatorIsSetScssphpFilterTest::{testImportValidatorCanRejectImports, testImportValidatorReceivesResolvedPathAndCanAllow, testImportsAreUnaffectedWhenNoValidatorIsSet}Full suite:
422 tests, 586 assertions, 0 failures(59 pre-existing skips for absent external binaries).phpcsclean against the repo ruleset.