Source code
Revision control
Copy as Markdown
Other Tools
Test Info:
- This WPT test may be referenced by the following Test IDs:
- /css/css-values/attr-security.html - WPT Dashboard Interop Dashboard
<!DOCTYPE html>
<title>CSS Values and Units Test: attr() security limitations</title>
<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-other-url: 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_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))`,
url,
`image-set("${url}")`);
test_attr('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',
'/404.png',
test_attr('background-image',
'/404.png',
'none');
test_attr('background-image',
'/404.png',
`src(url("${url}"))`);
test_attr('--x',
`attr(data-foo type(<url>))`,
`url(${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>