Opened 5 months ago

Closed 4 months ago

#36348 closed Bug (fixed)

ManifestStaticFilesStorage breaks CSS containing data: URIs with multiple nested url()s

Reported by: Samuel Cormier-Iijima Owned by: Samuel Cormier-Iijima
Component: contrib.staticfiles Version: 5.2
Severity: Normal Keywords:
Cc: Adam Johnson, Claude Paroz, Adam Zapletal Triage Stage: Ready for checkin
Has patch: yes Needs documentation: no
Needs tests: no Patch needs improvement: no
Easy pickings: no UI/UX: no

Description

Consider a CSS file containing the following:

a {
  background-image: url("data:image/svg+xml;charset=utf-8,filter='url(%23b)' filter='url(%23c)'");
}

When this is processed by ManifestStaticFilesStorage, the output is this:

a {
  background-image: url("data:image/svg+xml;charset=utf-8,filter='url(%23b)' filter='url("#c")'");
}

Notice that the second url() has been changed and now contains a double quote, which is invalid. The entire data: URI should be left as-is, but the regex in the CSS replacement patterns does not account for this case and sees the closing paren of the first nested url() as the end.

Change History (12)

comment:1 by Samuel Cormier-Iijima, 5 months ago

Type: UncategorizedBug

comment:2 by Natalia Bidart, 5 months ago

Has patch: set

comment:4 by Natalia Bidart, 5 months ago

Resolution: invalid
Status: assignedclosed

Hello Samuel Cormier-Iijima, thank you for your ticket. I wasn't able to find documentation supporting the validity of the provided CSS. Based on what I've found:

  1. The data:image/svg+xml scheme allows embedding SVGs directly, but the correct format looks like this: background-image: url("data:image/svg+xml;charset=utf-8,<svg>...</svg>");. See MDN documentation for reference.
  2. It seems the CSS is attempting to apply multiple filters within the SVG via the filter attribute in the data URL. However, I don't believe this is valid SVG syntax. In SVG, filters are defined within the <defs> element and then applied using a single filter attribute on a specific SVG element. See filter attribute and filter element docs.

Could you provide a failing test using valid CSS? I'll close this ticket as invalid for now, as I’m not convinced this is a Django issue. Feel free to reopen it with more information or a reproducible example using valid syntax. Also I would advice reaching out to the Django Forum for getting further assistance and confirming this is indeed a Django issue.

comment:5 by Samuel Cormier-Iijima, 5 months ago

Sure, I had shortened the example to make the issue clearer, but this happens with valid SVGs. Here's a sample valid SVG file:

<svg width="100" height="100" xmlns="http://www.w3.org/2000/svg">
  <filter id="blur1">
    <feGaussianBlur stdDeviation="2" />
  </filter>
  <filter id="blur2">
    <feGaussianBlur stdDeviation="5" />
  </filter>
  <rect width="50" height="50" x="10" y="10" fill="red" filter="url(#blur1)" />
  <rect width="50" height="50" x="40" y="40" fill="blue" filter="url(#blur2)" />
</svg>

If this gets inlined into CSS with a data: URI, the resulting CSS file looks like this:

#example {
    background-image: url("data:image/svg+xml,%3Csvg width='100' height='100' xmlns='http://www.w3.org/2000/svg'%3E%3Cfilter id='blur1'%3E%3CfeGaussianBlur stdDeviation='2' /%3E%3C/filter%3E%3Cfilter id='blur2'%3E%3CfeGaussianBlur stdDeviation='5' /%3E%3C/filter%3E%3Crect width='50' height='50' x='10' y='10' fill='red' filter='url(%23blur1)' /%3E%3Crect width='50' height='50' x='40' y='40' fill='blue' filter='url(%23blur2)' /%3E%3C/svg%3E");
}

When this then gets processed by Django's collectstatic, the output is this:

#example {
    background-image: url("data:image/svg+xml,%3Csvg width='100' height='100' xmlns='http://www.w3.org/2000/svg'%3E%3Cfilter id='blur1'%3E%3CfeGaussianBlur stdDeviation='2' /%3E%3C/filter%3E%3Cfilter id='blur2'%3E%3CfeGaussianBlur stdDeviation='5' /%3E%3C/filter%3E%3Crect width='50' height='50' x='10' y='10' fill='red' filter='url(%23blur1)' /%3E%3Crect width='50' height='50' x='40' y='40' fill='blue' filter='url("#blur2")' /%3E%3C/svg%3E");
}

Notice that the second use of filter has been changed to url("#blur2") - because this has the double quotes this CSS is invalid.

comment:6 by Natalia Bidart, 5 months ago

Resolution: invalid
Status: closednew
Triage Stage: UnreviewedAccepted

Thank you Samuel, this helps. I have used your SVG and I have encoded it and try it with ManifestStaticFilesStorage. What I get is:

#example {
    background-image: url("data:image/svg+xml,%3Csvg width='100' height='100' xmlns='http://www.w3.org/2000/svg'%3E%3Cfilter id='blur1'%3E%3CfeGaussianBlur stdDeviation='2' /%3E%3C/filter%3E%3Cfilter id='blur2'%3E%3CfeGaussianBlur stdDeviation='5' /%3E%3C/filter%3E%3Crect width='50' height='50' x='10' y='10' fill='red' filter='url(%23blur1)' /%3E%3Crect width='50' height='50' x='40' y='40' fill='blue' filter='url("#blur2#blur2")' /%3E%3C/svg%3E");
}

I'm not sure why I get url("#blur2#blur2") instead of url("#blur2") but I see your point that is the result invalid. What I don't understand is why the SVG still seems to work: I tested in Firefox, is this a Firefox wizardry?

comment:7 by Natalia Bidart, 5 months ago

Cc: Adam Johnson Claude Paroz added

comment:8 by Natalia Bidart, 5 months ago

Needs tests: set
Patch needs improvement: set

comment:9 by Adam Zapletal, 5 months ago

Cc: Adam Zapletal added

comment:10 by Jacob Walls, 4 months ago

Needs tests: unset
Patch needs improvement: unset

comment:11 by Sarah Boyce, 4 months ago

Triage Stage: AcceptedReady for checkin

comment:12 by Sarah Boyce <42296566+sarahboyce@…>, 4 months ago

Resolution: fixed
Status: newclosed

In 1ba5fe19:

Fixed #36348 -- Fixed handling multiple nested url()s in ManifestStaticFilesStorage.

Signed-off-by: Samuel Cormier-Iijima <samuel@…>

Note: See TracTickets for help on using tickets.
Back to Top