attr-security.html (11672B)
1 <!DOCTYPE html> 2 <title>CSS Values and Units Test: attr() security limitations</title> 3 <link rel="help" href="https://drafts.csswg.org/css-values-5/#attr-security"> 4 <script src="/resources/testharness.js"></script> 5 <script src="/resources/testharnessreport.js"></script> 6 7 <style> 8 @property --some-string { 9 syntax: "<string>"; 10 inherits: false; 11 initial-value: "empty"; 12 } 13 @property --some-string-list { 14 syntax: "<string>+"; 15 inherits: false; 16 initial-value: "empty"; 17 } 18 div { 19 --condition-val: 3; 20 --str: text; 21 --true: true; 22 --some-string: attr(data-foo); 23 --some-string-list: "https://does-not-exist2.test/404.png" attr(data-foo); 24 --some-other-url: attr(data-foo); 25 --image-set-valid: url("https://does-not-exist.test/404.png") type(attr(data-foo)); 26 --image-set-invalid: attr(data-foo type(<url>)) 1x; 27 } 28 </style> 29 30 <html> 31 <body> 32 <div id="attr"></div> 33 </body> 34 </html> 35 36 <script> 37 function test_attr(property, attrString, attrValue, expectedValue) { 38 var elem = document.getElementById("attr"); 39 elem.setAttribute("data-foo", attrValue); 40 elem.style.setProperty(property, attrString); 41 42 test(() => { 43 assert_equals(window.getComputedStyle(elem).getPropertyValue(property), 44 expectedValue); 45 }, `'${property}: ${attrString}' with data-foo="${attrValue}"`); 46 47 elem.style.setProperty(property, null); 48 } 49 50 function test_registered_custom_property(customPropertyName, customPropertySyntax, customPropertyInitialValue, 51 attrValue, expectedValue) { 52 window.CSS.registerProperty({ 53 name: customPropertyName, 54 syntax: customPropertySyntax, 55 inherits: false, 56 initialValue: customPropertyInitialValue, 57 }); 58 var elem = document.getElementById("attr"); 59 elem.setAttribute("data-foo", attrValue); 60 var attrString = "attr(data-foo type(" + customPropertySyntax + "))"; 61 elem.style.setProperty(customPropertyName, attrString); 62 test(() => { 63 assert_equals(window.getComputedStyle(elem).getPropertyValue(customPropertyName), 64 expectedValue); 65 }, `'${customPropertyName}: ${attrString}' with data-foo="${attrValue}"`); 66 elem.style.setProperty(customPropertyName, null); 67 } 68 69 // Direct use. 70 test_attr('--x', 71 'image-set(attr(data-foo))', 72 'https://does-not-exist.test/404.png', 73 'image-set("https://does-not-exist.test/404.png")'); 74 test_attr('background-image', 75 'image-set(attr(data-foo))', 76 'https://does-not-exist.test/404.png', 77 'none'); 78 test_attr('background-image', 79 'image-set("https://does-not-exist.test/404.png")', 80 'https://does-not-exist.test/404.png', 81 'image-set(url("https://does-not-exist.test/404.png") 1dppx)'); 82 83 test_attr('--x', 84 'src(attr(data-foo))', 85 'https://does-not-exist.test/404.png', 86 'src("https://does-not-exist.test/404.png")'); 87 test_attr('background-image', 88 'src(attr(data-foo))', 89 'https://does-not-exist.test/404.png', 90 'none'); 91 test_attr('background-image', 92 'src("https://does-not-exist.test/404.png")', 93 'https://does-not-exist.test/404.png', 94 'src(url("https://does-not-exist.test/404.png"))'); 95 96 // The following string() function is under discussion in the working group and does not exist yet. 97 test_attr('--x', 98 'src(string("https://does-not-exist.test" attr(data-foo)))', 99 '/404.png', 100 'src(string("https://does-not-exist.test" "/404.png"))'); 101 test_attr('background-image', 102 'src(string("https://does-not-exist.test" attr(data-foo)))', 103 '/404.png', 104 'none'); 105 test_attr('background-image', 106 'src(string("https://does-not-exist.test/""404.png"))', 107 '/404.png', 108 'src(url("https://does-not-exist.test/404.png"))'); 109 110 test_attr('--x', 111 'attr(data-foo type(<url>))', 112 'url(https://does-not-exist.test/404.png)', 113 'url("https://does-not-exist.test/404.png")'); 114 test_attr('background-image', 115 'attr(data-foo type(<url>))', 116 'url(https://does-not-exist.test/404.png)', 117 'none'); 118 test_attr('background-image', 119 'url("https://does-not-exist.test/404.png")', 120 'url(https://does-not-exist.test/404.png)', 121 'url("https://does-not-exist.test/404.png")'); 122 123 test_attr('--x', 124 'image(attr(data-foo))', 125 'https://does-not-exist.test/404.png', 126 'image("https://does-not-exist.test/404.png")'); 127 test_attr('background-image', 128 'image(attr(data-foo))', 129 'https://does-not-exist.test/404.png', 130 'none'); 131 test_attr('background-image', 132 'image("https://does-not-exist.test/404.png")', 133 'https://does-not-exist.test/404.png', 134 'image(url("https://does-not-exist.test/404.png"))'); 135 136 test_attr('background-image', 137 'url(https://does-not-exist.test/404.png), attr(data-foo type(<image>))', 138 'linear-gradient(#000000, #ffffff)', 139 'url("https://does-not-exist.test/404.png"), linear-gradient(rgb(0, 0, 0), rgb(255, 255, 255))'); 140 141 // The remaining tests use image-set(), but should be equivalent for image() etc. 142 143 // Test in a fallback. 144 test_attr('--x', 145 'image-set(var(--y, attr(data-foo)))', 146 'https://does-not-exist.test/404.png', 147 'image-set("https://does-not-exist.test/404.png")'); 148 test_attr('background-image', 149 'image-set(var(--y, attr(data-foo)))', 150 'https://does-not-exist.test/404.png', 151 'none'); 152 153 // Test via a registered custom property. 154 test_attr('--x', 155 'image-set(var(--some-string))', 156 'https://does-not-exist.test/404.png', 157 'image-set("https://does-not-exist.test/404.png")'); 158 test_attr('background-image', 159 'image-set(var(--some-string))', 160 'https://does-not-exist.test/404.png', 161 'none'); 162 163 // Test via a registered custom property (list). 164 test_attr('--x', 165 'image-set(var(--some-string-list))', 166 'https://does-not-exist.test/404.png', 167 'image-set("https://does-not-exist2.test/404.png" "https://does-not-exist.test/404.png")'); 168 test_attr('background-image', 169 'image-set(var(--some-string-list))', 170 'https://does-not-exist.test/404.png', 171 'none'); 172 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")'); 173 test_registered_custom_property('--registered-color', '<color>', 'red', 'blue', 'rgb(0, 0, 255)'); 174 175 // Test via a non-registered custom property. 176 test_attr('--x', 177 'image-set(var(--some-other-url))', 178 'https://does-not-exist.test/404.png', 179 'image-set("https://does-not-exist.test/404.png")'); 180 test_attr('background-image', 181 'image-set(var(--some-other-url))', 182 'https://does-not-exist.test/404.png', 183 'none'); 184 185 // Test multiple token substitution 186 test_attr('background-image', 187 'attr(data-foo type(*))', 188 'url(https://does-not-exist.test/404.png), linear-gradient(black, white)', 189 'none'); 190 191 // Test total attr()-tainting for substitution values 192 test_attr('background-image', 193 'image-set(var(--image-set-valid))', 194 'image/jpeg', 195 'none'); 196 test_attr('background-image', 197 'image-set(var(--image-set-invalid))', 198 'https://does-not-exist.test/404.png', 199 'none'); 200 201 // Test attr-tainting carries through if() function. 202 test_attr('--x', 203 'image-set(if(style(--true): attr(data-foo);))', 204 'https://does-not-exist.test/404.png', 205 'image-set("https://does-not-exist.test/404.png")'); 206 test_attr('background-image', 207 'image-set(if(style(--true): attr(data-foo);))', 208 'https://does-not-exist.test/404.png', 209 'none'); 210 test_attr('background-image', 211 `image-set( 212 if(style(--true): url(https://does-not-exist-2.test/404.png); 213 else: attr(data-foo);))`, 214 'https://does-not-exist-2.test/404.png', 215 'image-set(url("https://does-not-exist-2.test/404.png") 1dppx)'); 216 test_attr('background-image', 217 `image-set( 218 if(style(--some-string): url(https://does-not-exist.test/404.png);))`, 219 'https://does-not-exist.test/404.png', 220 'none'); 221 test_attr('background-image', 222 `image-set( 223 if(style(--condition-val: attr(data-foo type(*))): url(https://does-not-exist.test/404.png);))`, 224 '3', 225 'none'); 226 test_attr('background-image', 227 `image-set( 228 if(style(--condition-val: attr(data-foo type(*))): url(https://does-not-exist.test/404.png); 229 style(--true): url(https://does-not-exist.test/404.png); 230 else: url(https://does-not-exist.test/404.png);))`, 231 '1', 232 'none'); 233 test_attr('background-image', 234 `image-set(if(style(--true): url(https://does-not-exist.test/404.png); 235 style(--condition-val): url(https://does-not-exist.test/404.png); 236 else: url(https://does-not-exist.test/404.png);))`, 237 'attr(data-foo type(*))', 238 'image-set(url("https://does-not-exist.test/404.png") 1dppx)'); 239 test_attr('background-image', 240 `image-set( 241 if(style(--condition-val: if(style(--true): attr(data-foo type(*));)): url(https://does-not-exist.test/404.png);))`, 242 '3', 243 'none'); 244 test_attr('--x', 245 `image-set(if(style(--condition-val: if(style(--true): attr(data-foo type(*));)): url(https://does-not-exist.test/404.png);))`, 246 '3', 247 'image-set(url(https://does-not-exist.test/404.png))'); 248 test_attr('--x', 249 `image-set(if(style(--condition-val >= attr(data-foo type(*))): url(https://does-not-exist.test/404.png);))`, 250 '3', 251 'image-set(url(https://does-not-exist.test/404.png))'); 252 test_attr('background-image', 253 `image-set( 254 if(style(--condition-val >= attr(data-foo type(*))): url(https://does-not-exist.test/404.png);))`, 255 '3', 256 'none'); 257 test_attr('background-image', 258 `image-set( 259 if(style(--condition-val < attr(data-foo type(*))): url(https://does-not-exist.test/404.png);))`, 260 '3', 261 'none'); 262 test_attr('background-image', 263 `image-set( 264 if(style(--str < attr(data-foo type(*))): url(https://does-not-exist.test/404.png);))`, 265 '3', 266 'none'); 267 test_attr('background-image', 268 `image-set( 269 if(style(--condition-val < attr(data-foo type(*))): url(https://does-not-exist.test/404.png);))`, 270 'text', 271 'none'); 272 </script>