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 , 5 months ago
Type: | Uncategorized → Bug |
---|
comment:2 by , 5 months ago
Has patch: | set |
---|
comment:3 by , 5 months ago
comment:4 by , 5 months ago
Resolution: | → invalid |
---|---|
Status: | assigned → closed |
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:
- 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. - 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 , 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 , 5 months ago
Resolution: | invalid |
---|---|
Status: | closed → new |
Triage Stage: | Unreviewed → Accepted |
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 , 5 months ago
Cc: | added |
---|
comment:8 by , 5 months ago
Needs tests: | set |
---|---|
Patch needs improvement: | set |
comment:9 by , 5 months ago
Cc: | added |
---|
comment:10 by , 4 months ago
Needs tests: | unset |
---|---|
Patch needs improvement: | unset |
comment:11 by , 4 months ago
Triage Stage: | Accepted → Ready for checkin |
---|
https://github.com/django/django/pull/19408