Status: FAIL | Duration: 83ms | Subtests: 0/1 | Open test on wpt.live | wpt.fyi
/css/css-values/attr-security.html
<!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 { --some-string: attr(data-foo); --some-string-list: "https://does-not-exist-2.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("--unregistered", attrString); let value = window.getComputedStyle(elem).getPropertyValue("--unregistered"); test(() => { // Skip tests that include unsupported functions, since they are not violation of attr() security. if (value == "" || CSS.supports(`${property} : ${value}`)) { elem.style.setProperty(property, attrString); assert_equals(window.getComputedStyle(elem).getPropertyValue(property), expectedValue); } }, `'${property}: ${attrString}' with data-foo="${attrValue}"`); elem.style.setProperty(property, null); } function test_attr_shorthand(shorthand, longhand, attrString, attrValue, expectedValue) { var elem = document.getElementById("attr"); elem.setAttribute("data-foo", attrValue); elem.style.setProperty(shorthand, attrString); test(() => { assert_equals(window.getComputedStyle(elem).getPropertyValue(longhand), expectedValue); }, `'${shorthand}: ${attrString}' with data-foo="${attrValue}"`); elem.style.setProperty(shorthand, 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); } const url = "https://does-not-exist.test/404.png"; const url2 = "https://does-not-exist-2.test/404.png"; // Direct use. test_attr('--x', `image-set(attr(data-foo))`, url, `image-set("${url}")`); test_attr('background-image', `image-set(attr(data-foo))`, url, 'none'); test_attr_shorthand('background', 'background-image', `image-set(attr(data-foo))`, url, 'none'); test_attr('background-image', `image-set("${url}")`, url, `image-set(url("${url}") 1dppx)`); test_attr('--x', `src(attr(data-foo))`, url, `src("${url}")`); test_attr('background-image', `src(attr(data-foo))`, url, `none`); test_attr('background-image', `src("${url}")`, url, `src(url("${url}"))`); // 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("${url}"))`); test_attr('--x', `attr(data-foo type(<url>))`, `url(${url})`, ``); test_attr('background-image', `attr(data-foo type(<url>))`, `url(${url})`, `none`); test_attr('background-image', `url("${url}")`, `url(${url})`, `url("${url}")`); test_attr('--x', `image(attr(data-foo))`, url, `image("${url}")`); test_attr('background-image', `image(attr(data-foo))`, url, 'none'); test_attr('background-image', `image("${url}")`, url, `image(url("${url}"))`); test_attr('background-image', `url(${url}), attr(data-foo type(<image>))`, `linear-gradient(#000000, #ffffff)`, `url("${url}"), 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)))`, url, `image-set("${url}")`); test_attr('background-image', `image-set(var(--y, attr(data-foo)))`, url, 'none'); // Test via a registered custom property. test_attr('--x', `image-set(var(--some-string))`, url, `image-set("${url}")`); test_attr('background-image', `image-set(var(--some-string))`, url, 'none'); // Test via a registered custom property (list). test_attr('--x', `image-set(var(--some-string-list))`, url, `image-set("${url2}" "${url}")`); test_attr('background-image', `image-set(var(--some-string-list))`, url, 'none'); test_registered_custom_property('--registered-url', '<url>', `url("${url2}")`, '${url}', `url("${url2}")`); 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))`, url, `image-set("${url}")`); test_attr('background-image', `image-set(var(--some-other-url))`, url, 'none'); // Test multiple token substitution test_attr('background-image', `attr(data-foo type(*))`, `url(${url}), 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))`, url, 'none'); </script>