Page MenuHomePhabricator

Support CSS variable fallbacks in template styles
Closed, ResolvedPublic1 Estimated Story Points

Description

Background

  • Add one sentence summary on why were are doing this ticket with relevant context. Ensure you add context on how the outcome of this ticket will affect the end user.

User story

As a template editor I need to use CSS variables in a way that supports all Wikimedia deployed skins.

Requirements

It should be possible to edit https://en.wikipedia.beta.wmflabs.org/wiki/Template:Tracked/styles.css so that it uses fallbacks like follows and save without error:

.tracked {
	float: right;
	clear: right;
	margin: 0 0 1em 1em;
	width: 12em;
	border: 1px solid var( --border-color-interactive, #72777d );
	border-radius: 2px;
	background-color: var( --background-color-neutral, #eaecf0 );
	font-size: 85%;
	text-align: center;
	padding: 0.5em;
}

Communication criteria - does this need an announcement or discussion?

  • We will run a user-notice once the feature is in production.

Event Timeline

Jdlrobson renamed this task from Web Task Creation Form to Support CSS variable fallbacks in template styles.Apr 5 2024, 12:03 PM
Jdlrobson updated the task description. (Show Details)

Change #1017080 had a related patch set uploaded (by Jdlrobson; author: Mabualruz):

[css-sanitizer@master] feat(css-sanitizer): Improve color parsing var func fallback values

https://gerrit.wikimedia.org/r/1017080

Why do we need fallback values? On project, the custom properties should always be defined by codex, etc, and I'd think we'd actually want to hard fail with an error if an unknown property is referenced, rather than fallback.

Custom properties are a signficant potential security issue, and I think we'd want to keep our support as simple as possible. The more weird cases we introduce, the more likely it is that we open up a hole where someone can bypass the sanitizer by combining properties in unusual ways. Significantly, "The allowed syntax for custom properties is extremely permissive. The <declaration-value> production matches any sequence of one or more tokens..." so I think we want to error on the side of sharply limiting what custom properties we allow where, rather than move in the direction of allowing custom properties in their full flexibility.

The idea of skin-specific custom properties arose, but I think that can be done without adding additional var() syntax like so:

background-color: #fa11bac;
background-color: var(--skin-specific-custom-property);

As I understand it, the second declaration is "guaranteed invalid" if the skin specific custom property isn't defined, so this will use the first property in this case.

EDIT: actually I think I misunderstood the meaning of "guaranteed invalid" in the spec (https://www.w3.org/TR/css-variables-1/#invalid-variables) and the inheritance won't actually work the way I expected it would. Can someone else double-check my logic and figure out whether this can be done in another way without requiring more custom-property syntax?

EDIT: yes, I misunderstood. See below for other concerns.

Mabualruz lowered the priority of this task from High to Medium.Apr 5 2024, 1:07 PM
Mabualruz added subscribers: stjn, Mabualruz.

Based on the latest feedback and discussion here and on T360562, I think Medium is more fitting for now.

And I also recommended adding the current support examples for fallback, somewhere for the community to check on.

I want still to understand the security risks of var() and attr() functions in CSS in our projects, maybe we can do proper secure support if possible.

I also think @stjn have some different concerns that he left on https://phabricator.wikimedia.org/T360562#9692634

Just FYI I will be on vacation starting the end of day for 2 weeks, just in case I do not reply or being slow to do so

Change #1017080 had a related patch set uploaded (by Mabualruz; author: Mabualruz):

[css-sanitizer@master] feat(css-sanitizer): Improve color parsing var func fallback values

https://gerrit.wikimedia.org/r/1017080

EDIT: actually I think I misunderstood the meaning of "guaranteed invalid" in the spec (https://www.w3.org/TR/css-variables-1/#invalid-variables) and the inheritance won't actually work the way I expected it would. Can someone else double-check my logic and figure out whether this can be done in another way without requiring more custom-property syntax?

Copying the comment: unless you hard-code something to switch it to var( --skin-specific, #fff ), this example would lead to override of #fff to an empty value in all the browsers supporting var() in case --skin-specific is undefined.

I'm already worried about the token-gluing possible with:

border: var(--foo) var(--bar) var(--bat);

which is allowed by the current patch. I guess adding default values doesn't make this *worse* but the semantics of var are already very worrying from a security standpoint. As long as users can't define their own custom properties we're probably okaaay, but we're relying on Codex and Skin authors not to inadvertently define custom properties that can be abused by concatenating them in unusual and unexpected ways.

We could consider locking this down by allowing var *only* in places where repetition (UnorderedGroup::someOf in css-sanitizer) isn't allowed, like allowing in background-color but not background, and in border-*-color but not border-color (which allows 4 vars to be concatenated) or border.

EDIT: UnorderedGroup::someOf actually prohibits repetition. It's only Quantifier which would be problematic, and even there Quantifier::optional is fine and Quantifier::hash requires the values to be comma-separated, which would probably complicate any concatenation-based attack. With all that in mind, border-color seems the only problematic property, which allows up to four var properties to be concatenated:

* { border-color: var(--foo) var(--bar) var(--bat) var(--baz); }

As another mitigation, apparently url() is treated as a single token, and var must evaluate to a series of tokens. So you /probably/ can't abuse this to prepend javascript: to a URL, for example.

But I still think it may be worthwhile to restrict var in border-color to be safe, and I might consider restricting it in the comma-separated cases, box-shadow and text-shadow, although in both of those cases there are also required numeric elements as well complicating the concatenation.

Change #1017299 had a related patch set uploaded (by C. Scott Ananian; author: C. Scott Ananian):

[css-sanitizer@master] Harden security by disallowing custom properties for `border-color`

https://gerrit.wikimedia.org/r/1017299

Change #1017299 merged by jenkins-bot:

[css-sanitizer@master] Harden security by disallowing custom properties for `border-color`

https://gerrit.wikimedia.org/r/1017299

Change #1017080 merged by jenkins-bot:

[css-sanitizer@master] feat(css-sanitizer): Improve color parsing var func fallback values

https://gerrit.wikimedia.org/r/1017080

I discussed this with C Scott yesterday and we agreed to push ahead with a release to make sure the solution we've landed on is complete. This may be delayed as @cscott is attending an event this week.

Once that's done I'll follow up with the security team on T361956.

Change #1021564 had a related patch set uploaded (by Jdlrobson; author: Jdlrobson):

[mediawiki/extensions/TemplateStyles@master] Update wikimedia/css-sanitizer to 5.3.0

https://gerrit.wikimedia.org/r/1021564

Change #1021567 had a related patch set uploaded (by Jdlrobson; author: Jdlrobson):

[mediawiki/vendor@master] Update CSS sanitizer to 5.3.0

https://gerrit.wikimedia.org/r/1021567

Jdlrobson set the point value for this task to 1.Apr 23 2024, 4:47 PM

Change #1021564 merged by jenkins-bot:

[mediawiki/extensions/TemplateStyles@master] Allow wikimedia/css-sanitizer at 5.3.0

https://gerrit.wikimedia.org/r/1021564

Change #1021567 merged by jenkins-bot:

[mediawiki/vendor@master] Update CSS sanitizer to 5.3.0

https://gerrit.wikimedia.org/r/1021567

I have confirmed on beta cluster the requirements have been met.

This will be available in production from 2nd May.

Before I start using this onwiki: is there a risk of this later being revoked following the security review?

Hi @hgzh I checked in with the security team and we feel like it's unlikely we'd completely revoke the functionality, particularly for color properties. The worst that could probably happen is further sanitizing or limiting certain css fallback values and that would need to consider at least temporary backwards compatibility with on-wiki usages. I personally I would feel comfortable recommending using the following:

.foo {
  color: #202122; /* define this in case next line gets stripped (very unlikely in this particular case) , this also supports really old browsers*/
  color: var( --color-base, #202122 ); /* make sure to use the fallback if important for when color-base is not defined by the skin - true for Monobook for example */
}

Hope this helps!

I just happened to run into this border-color mantrap.

It does not need witchcraft nor help of the devil to set up regular expressions which permit valid assignments for any but will bounce back dangerous assignments. Even branching of special cases like this will allways work.

You cannot document nor explain nor justify any exception for border-color in any manual, based on a statement that MW has not been able to write regular expressions etc.

Even worse, WMF is enforcing the superfluous inverting dark mode. All border lines connecting and separating things will vanish in the dark since it is not permitted to invert border colours.

It does not need witchcraft nor help of the devil to set up regular expressions which permit valid assignments for any but will bounce back dangerous assignments. Even branching of special cases like this will allways work.

I don't think you understand how CSS variables work. These replacements are made by the browser when it evaluates the CSS stylesheet, not by MediaWiki. Once you allow the use of CSS variables in stylesheets, you have very little control over what gets substituted. (We could maybe use @property for type-safe variables but browser support for that is limited.)

I think border properties could be allowed, per the reasoning in T361956#9742379, but there will be properties where we need to selectively disallow the use of variables for security/privacy reasons.

We are going to adopt templates to follow WMF properties for inverting dark mode, and we follow the WMF definitions and try to inherit some of those.

Actually one approach is to define colour schemes which are currently e.g. dark green, black, white, light grey for common appearance to assign variables which coukd get other values in black-white exchange mode. This is much more efficient to have one CSS style structure with one common specification which elements get which line colour. When inverting, just the indicating class could assign other colours for those variables, which could be evaluated in many places.

Otherwise every CSS block would need to be duplicated for inverted theme class, and assigned other constant values now. Every change would need to be maintained twice.