Skip to content

[2.x] fix: require a scheme when declining remote LESS imports - #5065

Merged
imorland merged 1 commit into
2.xfrom
im/less-inline-double-slash-read
Sep 21, 2026
Merged

imorland merged 1 commit into
2.xfrom
im/less-inline-double-slash-read

Conversation

@imorland

Copy link
Copy Markdown
Member

Fixes #0000

Changes proposed in this pull request:

LessCompiler::containImports() declines imports it treats as remote, so less.php emits them for the browser rather than trying to resolve them locally. The check matched #^(https?:)?//#i, where the optional scheme also matches a bare leading //. A path beginning // is a valid absolute path, so a local path could satisfy a check meant only for remote URLs — and declining doesn't end the import, so an inline import would then be read from that path.

This requires a real scheme:

- if (preg_match('#^(https?:)?//#i', $path) === 1) {
+ if (preg_match('#^https?://#i', $path) === 1) {

An @import url('https://…') webfont still works, which is what the guard is there for, and theme/extension imports (@import "mixins.less", resolved through Flarum's import directories) are unaffected.

Behaviour change: a protocol-relative import (@import url("//host/x")) is now refused at save. It can't be distinguished from a local path from the string alone — and the string is all the callback gets (less.php passes it the filename, not the import's inline flag) — so it has to go the safe way. Nothing in core or the bundled extensions uses one; write the scheme out in full instead. Worth a changelog line.

Reviewers should focus on:

Whether requiring a scheme is the right call versus keeping protocol-relative working. Distinguishing a remote host from a local path heuristically doesn't hold up — a hostname-shaped first segment is forgeable, and keying on whether the file exists makes behaviour depend on what's on disk — so the scheme is the one property decidable from the path alone.

Tests

The existing tests covered // with non-inline imports and (inline) with single-slash paths, but not both together, which was the gap. Added:

  • custom_less_cannot_read_a_file_via_a_double_slash_path (data provider: @import (inline), @impor (inline), three-slash) — fails on the old guard, passes on this one.
  • a_theme_colour_cannot_read_a_file_via_a_double_slash_path — a guard on the config-variable path (which already handled this, via its own check).
  • a_protocol_relative_import_is_refused — records the behaviour change above as intended.

A docblock on an earlier test claimed the callback wouldn't be consulted for an inline import. It is consulted for every import, so the claim was wrong and it's removed.

Screenshot

N/A — no visual change. Confirmed on a local install that an @import url('https://fonts.googleapis.com/…') webfont compiles into forum.css and the font loads.

Necessity

  • Has the problem that is being solved here been clearly explained?
  • If applicable, have various options for solving this problem been considered? (Scheme-required vs. hostname-shape vs. file-exists heuristics.)
  • For core PRs, does this need to be in core, or could it be in an extension? (LessCompiler is core.)
  • Are we willing to maintain this for years / potentially forever? (Tighter one-line guard, fewer edge cases than before.)

Confirmed

  • Frontend changes: tested on a local Flarum installation. (N/A — no frontend changes.)
  • Frontend changes: tests are green (run yarn test in js/). (N/A.)
  • Frontend changes: tests have been added, or are not appropriate here. (N/A.)
  • Backend changes: tests are green (run composer test).
  • Backend changes: tests have been added, or are not appropriate here.
  • Where applicable, changes are suitable for all supported database drivers (MySQL, MariaDB, PostgreSQL, SQLite). (No database interaction.)
  • Core developer confirmed locally this works as intended.
  • The description above is written by me and describes what this pull request actually does.

Required changes:

  • Related documentation PR: (Remove if irrelevant)

containImports() declines imports it treats as remote so less.php emits
them for the browser. The check matched `#^(https?:)?//#i`, whose
optional scheme also matches a bare leading `//` -- and a `//`-prefixed
string is a valid absolute path, so a local path satisfied a check meant
only for remote URLs. Declining does not end the import, so an inline
import would then be read from that path.

Require a real scheme. An `@import url('https://...')` webfont still
works, and theme/extension imports resolved through Flarum's import
directories are unaffected.

Protocol-relative imports (`@import url("//host/x")`) are now refused at
save: they cannot be told apart from a local path, and the callback only
receives the filename, not the import's inline flag, so the decision has
to be made from the path string alone.
@imorland
imorland requested a review from a team as a code owner September 21, 2026 06:49
@imorland imorland added this to the 2.0-pre milestone Sep 21, 2026
@imorland
imorland merged commit 63aa067 into 2.x Sep 21, 2026
30 checks passed
@imorland
imorland deleted the im/less-inline-double-slash-read branch September 21, 2026 07:05
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant