| <!DOCTYPE html> |
| <title>CSS Values and Units Test: attr() security limitations</title> |
| <link rel="help" href="https://drafts.csswg.org/css-values-5/#attr-security"> |
| <script src="/resources/testharness.js"></script> |
| <script src="/resources/testharnessreport.js"></script> |
| |
| <style> |
| @property --some-string { |
| syntax: "<string>"; |
| inherits: false; |
| initial-value: "empty"; |
| } |
| @property --some-string-list { |
| syntax: "<string>+"; |
| inherits: false; |
| initial-value: "empty"; |
| } |
| div { |
| --condition-val: 3; |
| --str: text; |
| --true: true; |
| --some-string: attr(data-foo); |
| --some-string-list: "https://does-not-exist2.test/404.png" attr(data-foo); |
| --some-other-url: attr(data-foo); |
| --image-set-valid: url("https://does-not-exist.test/404.png") type(attr(data-foo)); |
| --image-set-invalid: attr(data-foo type(<url>)) 1x; |
| } |
| </style> |
| |
| <html> |
| <body> |
| <div id="attr"></div> |
| </body> |
| </html> |
| |
| <script> |
| function test_attr(property, attrString, attrValue, expectedValue) { |
| var elem = document.getElementById("attr"); |
| elem.setAttribute("data-foo", attrValue); |
| elem.style.setProperty(property, attrString); |
| |
| test(() => { |
| assert_equals(window.getComputedStyle(elem).getPropertyValue(property), |
| expectedValue); |
| }, `'${property}: ${attrString}' with data-foo="${attrValue}"`); |
| |
| elem.style.setProperty(property, null); |
| } |
| |
| function test_registered_custom_property(customPropertyName, customPropertySyntax, customPropertyInitialValue, |
| attrValue, expectedValue) { |
| window.CSS.registerProperty({ |
| name: customPropertyName, |
| syntax: customPropertySyntax, |
| inherits: false, |
| initialValue: customPropertyInitialValue, |
| }); |
| var elem = document.getElementById("attr"); |
| elem.setAttribute("data-foo", attrValue); |
| var attrString = "attr(data-foo type(" + customPropertySyntax + "))"; |
| elem.style.setProperty(customPropertyName, attrString); |
| test(() => { |
| assert_equals(window.getComputedStyle(elem).getPropertyValue(customPropertyName), |
| expectedValue); |
| }, `'${customPropertyName}: ${attrString}' with data-foo="${attrValue}"`); |
| elem.style.setProperty(customPropertyName, null); |
| } |
| |
| // Direct use. |
| test_attr('--x', |
| 'image-set(attr(data-foo))', |
| 'https://does-not-exist.test/404.png', |
| 'image-set("https://does-not-exist.test/404.png")'); |
| test_attr('background-image', |
| 'image-set(attr(data-foo))', |
| 'https://does-not-exist.test/404.png', |
| 'none'); |
| test_attr('background-image', |
| 'image-set("https://does-not-exist.test/404.png")', |
| 'https://does-not-exist.test/404.png', |
| 'image-set(url("https://does-not-exist.test/404.png") 1dppx)'); |
| |
| test_attr('--x', |
| 'src(attr(data-foo))', |
| 'https://does-not-exist.test/404.png', |
| 'src("https://does-not-exist.test/404.png")'); |
| test_attr('background-image', |
| 'src(attr(data-foo))', |
| 'https://does-not-exist.test/404.png', |
| 'none'); |
| test_attr('background-image', |
| 'src("https://does-not-exist.test/404.png")', |
| 'https://does-not-exist.test/404.png', |
| 'src(url("https://does-not-exist.test/404.png"))'); |
| |
| // The following string() function is under discussion in the working group and does not exist yet. |
| test_attr('--x', |
| 'src(string("https://does-not-exist.test" attr(data-foo)))', |
| '/404.png', |
| 'src(string("https://does-not-exist.test" "/404.png"))'); |
| test_attr('background-image', |
| 'src(string("https://does-not-exist.test" attr(data-foo)))', |
| '/404.png', |
| 'none'); |
| test_attr('background-image', |
| 'src(string("https://does-not-exist.test/""404.png"))', |
| '/404.png', |
| 'src(url("https://does-not-exist.test/404.png"))'); |
| |
| test_attr('--x', |
| 'attr(data-foo type(<url>))', |
| 'url(https://does-not-exist.test/404.png)', |
| 'url("https://does-not-exist.test/404.png")'); |
| test_attr('background-image', |
| 'attr(data-foo type(<url>))', |
| 'url(https://does-not-exist.test/404.png)', |
| 'none'); |
| test_attr('background-image', |
| 'url("https://does-not-exist.test/404.png")', |
| 'url(https://does-not-exist.test/404.png)', |
| 'url("https://does-not-exist.test/404.png")'); |
| |
| test_attr('--x', |
| 'image(attr(data-foo))', |
| 'https://does-not-exist.test/404.png', |
| 'image("https://does-not-exist.test/404.png")'); |
| test_attr('background-image', |
| 'image(attr(data-foo))', |
| 'https://does-not-exist.test/404.png', |
| 'none'); |
| test_attr('background-image', |
| 'image("https://does-not-exist.test/404.png")', |
| 'https://does-not-exist.test/404.png', |
| 'image(url("https://does-not-exist.test/404.png"))'); |
| |
| test_attr('background-image', |
| 'url(https://does-not-exist.test/404.png), attr(data-foo type(<image>))', |
| 'linear-gradient(#000000, #ffffff)', |
| 'url("https://does-not-exist.test/404.png"), linear-gradient(rgb(0, 0, 0), rgb(255, 255, 255))'); |
| |
| // The remaining tests use image-set(), but should be equivalent for image() etc. |
| |
| // Test in a fallback. |
| test_attr('--x', |
| 'image-set(var(--y, attr(data-foo)))', |
| 'https://does-not-exist.test/404.png', |
| 'image-set("https://does-not-exist.test/404.png")'); |
| test_attr('background-image', |
| 'image-set(var(--y, attr(data-foo)))', |
| 'https://does-not-exist.test/404.png', |
| 'none'); |
| |
| // Test via a registered custom property. |
| test_attr('--x', |
| 'image-set(var(--some-string))', |
| 'https://does-not-exist.test/404.png', |
| 'image-set("https://does-not-exist.test/404.png")'); |
| test_attr('background-image', |
| 'image-set(var(--some-string))', |
| 'https://does-not-exist.test/404.png', |
| 'none'); |
| |
| // Test via a registered custom property (list). |
| test_attr('--x', |
| 'image-set(var(--some-string-list))', |
| 'https://does-not-exist.test/404.png', |
| 'image-set("https://does-not-exist2.test/404.png" "https://does-not-exist.test/404.png")'); |
| test_attr('background-image', |
| 'image-set(var(--some-string-list))', |
| 'https://does-not-exist.test/404.png', |
| 'none'); |
| test_registered_custom_property('--registered-url', '<url>', 'url("https://does-not-exist.test/empty-url")', 'https://does-not-exist.test/404.png', 'url("https://does-not-exist.test/empty-url")'); |
| test_registered_custom_property('--registered-color', '<color>', 'red', 'blue', 'rgb(0, 0, 255)'); |
| |
| // Test via a non-registered custom property. |
| test_attr('--x', |
| 'image-set(var(--some-other-url))', |
| 'https://does-not-exist.test/404.png', |
| 'image-set("https://does-not-exist.test/404.png")'); |
| test_attr('background-image', |
| 'image-set(var(--some-other-url))', |
| 'https://does-not-exist.test/404.png', |
| 'none'); |
| |
| // Test multiple token substitution |
| test_attr('background-image', |
| 'attr(data-foo type(*))', |
| 'url(https://does-not-exist.test/404.png), linear-gradient(black, white)', |
| 'none'); |
| |
| // Test total attr()-tainting for substitution values |
| test_attr('background-image', |
| 'image-set(var(--image-set-valid))', |
| 'image/jpeg', |
| 'none'); |
| test_attr('background-image', |
| 'image-set(var(--image-set-invalid))', |
| 'https://does-not-exist.test/404.png', |
| 'none'); |
| |
| // Test attr-tainting carries through if() function. |
| test_attr('--x', |
| 'image-set(if(style(--true): attr(data-foo);))', |
| 'https://does-not-exist.test/404.png', |
| 'image-set("https://does-not-exist.test/404.png")'); |
| test_attr('background-image', |
| 'image-set(if(style(--true): attr(data-foo);))', |
| 'https://does-not-exist.test/404.png', |
| 'none'); |
| test_attr('background-image', |
| `image-set( |
| if(style(--true): url(https://does-not-exist-2.test/404.png); |
| else: attr(data-foo);))`, |
| 'https://does-not-exist-2.test/404.png', |
| 'image-set(url("https://does-not-exist-2.test/404.png") 1dppx)'); |
| test_attr('background-image', |
| `image-set( |
| if(style(--some-string): url(https://does-not-exist.test/404.png);))`, |
| 'https://does-not-exist.test/404.png', |
| 'none'); |
| test_attr('background-image', |
| `image-set( |
| if(style(--condition-val: attr(data-foo type(*))): url(https://does-not-exist.test/404.png);))`, |
| '3', |
| 'none'); |
| test_attr('background-image', |
| `image-set( |
| if(style(--condition-val: attr(data-foo type(*))): url(https://does-not-exist.test/404.png); |
| style(--true): url(https://does-not-exist.test/404.png); |
| else: url(https://does-not-exist.test/404.png);))`, |
| '1', |
| 'none'); |
| test_attr('background-image', |
| `image-set(if(style(--true): url(https://does-not-exist.test/404.png); |
| style(--condition-val): url(https://does-not-exist.test/404.png); |
| else: url(https://does-not-exist.test/404.png);))`, |
| 'attr(data-foo type(*))', |
| 'image-set(url("https://does-not-exist.test/404.png") 1dppx)'); |
| test_attr('background-image', |
| `image-set( |
| if(style(--condition-val: if(style(--true): attr(data-foo type(*));)): url(https://does-not-exist.test/404.png);))`, |
| '3', |
| 'none'); |
| test_attr('--x', |
| `image-set(if(style(--condition-val: if(style(--true): attr(data-foo type(*));)): url(https://does-not-exist.test/404.png);))`, |
| '3', |
| 'image-set(url(https://does-not-exist.test/404.png))'); |
| test_attr('--x', |
| `image-set(if(style(--condition-val >= attr(data-foo type(*))): url(https://does-not-exist.test/404.png);))`, |
| '3', |
| 'image-set(url(https://does-not-exist.test/404.png))'); |
| test_attr('background-image', |
| `image-set( |
| if(style(--condition-val >= attr(data-foo type(*))): url(https://does-not-exist.test/404.png);))`, |
| '3', |
| 'none'); |
| test_attr('background-image', |
| `image-set( |
| if(style(--condition-val < attr(data-foo type(*))): url(https://does-not-exist.test/404.png);))`, |
| '3', |
| 'none'); |
| test_attr('background-image', |
| `image-set( |
| if(style(--str < attr(data-foo type(*))): url(https://does-not-exist.test/404.png);))`, |
| '3', |
| 'none'); |
| test_attr('background-image', |
| `image-set( |
| if(style(--condition-val < attr(data-foo type(*))): url(https://does-not-exist.test/404.png);))`, |
| 'text', |
| 'none'); |
| </script> |