Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 // Copyright 2014 The Chromium Authors. All rights reserved. | 1 // Copyright 2014 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #include "config.h" | 5 #include "config.h" |
| 6 #include "core/frame/SubresourceIntegrity.h" | 6 #include "core/frame/SubresourceIntegrity.h" |
| 7 | 7 |
| 8 #include "core/HTMLNames.h" | 8 #include "core/HTMLNames.h" |
| 9 #include "core/dom/Document.h" | 9 #include "core/dom/Document.h" |
| 10 #include "core/dom/Element.h" | 10 #include "core/dom/Element.h" |
| (...skipping 22 matching lines...) Expand all Loading... | |
| 33 // Check if it's a base64 encoded value. We're pretty loose here, as there's | 33 // Check if it's a base64 encoded value. We're pretty loose here, as there's |
| 34 // not much risk in it, and it'll make it simpler for developers. | 34 // not much risk in it, and it'll make it simpler for developers. |
| 35 return isASCIIAlphanumeric(c) || c == '_' || c == '-' || c == '+' || c == '/ ' || c == '='; | 35 return isASCIIAlphanumeric(c) || c == '_' || c == '-' || c == '+' || c == '/ ' || c == '='; |
| 36 } | 36 } |
| 37 | 37 |
| 38 static bool isTypeCharacter(UChar c) | 38 static bool isTypeCharacter(UChar c) |
| 39 { | 39 { |
| 40 return isASCIIAlphanumeric(c) || c == '+' || c == '.' || c == '-'; | 40 return isASCIIAlphanumeric(c) || c == '+' || c == '.' || c == '-'; |
| 41 } | 41 } |
| 42 | 42 |
| 43 static bool isOptionNameCharacter(UChar c) | |
| 44 { | |
| 45 return isASCIIAlphanumeric(c) || c == '-'; | |
| 46 } | |
| 47 | |
| 48 static bool isOptionValueCharacter(UChar c) | |
| 49 { | |
| 50 return isASCIIAlphanumeric(c) || c == '!' || c == '#' || c == '$' || c == '% ' || c == '&' || c == '\'' || c == '*' || c == '+' || c == '-' || c == '.' || c == '^' || c == '_' || c == '`' || c == '|' || c == '~' || c == '/'; | |
| 51 } | |
| 52 | |
| 43 static void logErrorToConsole(const String& message, Document& document) | 53 static void logErrorToConsole(const String& message, Document& document) |
| 44 { | 54 { |
| 45 document.addConsoleMessage(ConsoleMessage::create(SecurityMessageSource, Err orMessageLevel, message)); | 55 document.addConsoleMessage(ConsoleMessage::create(SecurityMessageSource, Err orMessageLevel, message)); |
| 46 } | 56 } |
| 47 | 57 |
| 48 static bool DigestsEqual(const DigestValue& digest1, const DigestValue& digest2) | 58 static bool DigestsEqual(const DigestValue& digest1, const DigestValue& digest2) |
| 49 { | 59 { |
| 50 if (digest1.size() != digest2.size()) | 60 if (digest1.size() != digest2.size()) |
| 51 return false; | 61 return false; |
| 52 | 62 |
| (...skipping 143 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 196 if (position == begin || (position != end && *position != '?')) { | 206 if (position == begin || (position != end && *position != '?')) { |
| 197 digest = emptyString(); | 207 digest = emptyString(); |
| 198 return false; | 208 return false; |
| 199 } | 209 } |
| 200 | 210 |
| 201 // We accept base64url encoding, but normalize to "normal" base64 internally : | 211 // We accept base64url encoding, but normalize to "normal" base64 internally : |
| 202 digest = normalizeToBase64(String(begin, position - begin)); | 212 digest = normalizeToBase64(String(begin, position - begin)); |
| 203 return true; | 213 return true; |
| 204 } | 214 } |
| 205 | 215 |
| 216 bool SubresourceIntegrity::isValidMimeTypeValue(const String& value) | |
| 217 { | |
| 218 Vector<UChar> characters; | |
| 219 value.appendTo(characters); | |
| 220 const UChar* position = characters.data(); | |
| 221 const UChar* end = characters.end(); | |
| 206 | 222 |
| 207 // Before: | |
| 208 // | |
| 209 // [algorithm]-[hash] OR [algorithm]-[hash]?[options] | |
| 210 // ^ ^ ^ | |
| 211 // position/end position end | |
| 212 // | |
| 213 // After (if successful: if the method returns false, we make no promises and th e caller should exit early): | |
| 214 // | |
| 215 // [algorithm]-[hash] OR [algorithm]-[hash]?[options] | |
| 216 // ^ ^ | |
| 217 // position/end position/end | |
| 218 bool SubresourceIntegrity::parseMimeType(const UChar*& position, const UChar* en d, String& type) | |
| 219 { | |
| 220 type = emptyString(); | |
| 221 | |
| 222 if (position == end) | |
| 223 return true; | |
| 224 | |
| 225 if (!skipToken<UChar>(position, end, "?ct=")) | |
| 226 return false; | |
| 227 | |
| 228 const UChar* begin = position; | |
| 229 skipWhile<UChar, isASCIIAlpha>(position, end); | 223 skipWhile<UChar, isASCIIAlpha>(position, end); |
| 230 if (position == end) | 224 if (position == end) |
| 231 return false; | 225 return false; |
| 232 | 226 |
| 233 if (!skipExactly<UChar>(position, end, '/')) | 227 if (!skipExactly<UChar>(position, end, '/')) |
| 234 return false; | 228 return false; |
| 235 | 229 |
| 236 if (position == end) | |
| 237 return false; | |
| 238 | |
| 239 skipWhile<UChar, isTypeCharacter>(position, end); | 230 skipWhile<UChar, isTypeCharacter>(position, end); |
| 240 if (position != end) | 231 if (position != end) |
| 241 return false; | 232 return false; |
| 242 | 233 |
| 243 type = String(begin, position - begin); | |
| 244 return true; | 234 return true; |
| 245 } | 235 } |
| 246 | 236 |
| 237 | |
| 238 // Before: | |
| 239 // | |
| 240 // [algorithm]-[hash] OR [algorithm]-[hash]?[name]=[value] OR [algorithm]-[hash]?[name]=[value]?[rest of options] | |
| 241 // ^ ^ ^ ^ ^ | |
| 242 // position/end position end position end | |
| 243 // | |
| 244 // After (if successful: if the method returns false, we make no promises and th e caller should exit early): | |
| 245 // | |
| 246 // [algorithm]-[hash] OR [algorithm]-[hash]?[name]=[value] OR [algorithm]-[hash]?[name]=[value]?[rest of options] | |
|
Mike West
2015/05/11 03:25:28
This is such a weird syntax.
| |
| 247 // ^ ^ ^ ^ | |
| 248 // position/end position/end position end | |
| 249 bool SubresourceIntegrity::parseOption(const UChar*& position, const UChar* end, String& name, String& value) | |
| 250 { | |
| 251 name = emptyString(); | |
| 252 value = emptyString(); | |
| 253 | |
| 254 if (position == end) | |
| 255 return true; | |
| 256 | |
| 257 if (!skipExactly<UChar>(position, end, '?')) | |
| 258 return false; | |
| 259 | |
| 260 const UChar* begin = position; | |
| 261 skipWhile<UChar, isOptionNameCharacter>(position, end); | |
| 262 if (position == end) | |
| 263 return false; | |
| 264 | |
| 265 name = String(begin, position - begin); | |
| 266 | |
| 267 if (!skipExactly<UChar>(position, end, '=') || position == end) | |
| 268 return false; | |
| 269 | |
| 270 begin = position; | |
| 271 skipWhile<UChar, isOptionValueCharacter>(position, end); | |
| 272 | |
| 273 value = String(begin, position - begin); | |
| 274 | |
| 275 if (position == end || *position == '?') | |
|
Mike West
2015/05/11 03:25:28
Would you mind asserting that `position < end` bef
| |
| 276 return true; | |
| 277 | |
| 278 return false; | |
| 279 } | |
| 280 | |
| 247 SubresourceIntegrity::IntegrityParseResult SubresourceIntegrity::parseIntegrityA ttribute(const WTF::String& attribute, WTF::Vector<IntegrityMetadata>& metadataL ist, Document& document) | 281 SubresourceIntegrity::IntegrityParseResult SubresourceIntegrity::parseIntegrityA ttribute(const WTF::String& attribute, WTF::Vector<IntegrityMetadata>& metadataL ist, Document& document) |
| 248 { | 282 { |
| 249 Vector<UChar> characters; | 283 Vector<UChar> characters; |
| 250 attribute.stripWhiteSpace().appendTo(characters); | 284 attribute.stripWhiteSpace().appendTo(characters); |
| 251 const UChar* position = characters.data(); | 285 const UChar* position = characters.data(); |
| 252 const UChar* end = characters.end(); | 286 const UChar* end = characters.end(); |
| 253 const UChar* currentIntegrityEnd; | 287 const UChar* currentIntegrityEnd; |
| 254 | 288 |
| 255 metadataList.clear(); | 289 metadataList.clear(); |
| 256 bool error = false; | 290 bool error = false; |
| (...skipping 36 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 293 ASSERT(parseResult == AlgorithmValid); | 327 ASSERT(parseResult == AlgorithmValid); |
| 294 | 328 |
| 295 if (!parseDigest(position, currentIntegrityEnd, digest)) { | 329 if (!parseDigest(position, currentIntegrityEnd, digest)) { |
| 296 logErrorToConsole("Error parsing 'integrity' attribute ('" + attribu te + "'). The digest must be a valid, base64-encoded value.", document); | 330 logErrorToConsole("Error parsing 'integrity' attribute ('" + attribu te + "'). The digest must be a valid, base64-encoded value.", document); |
| 297 error = true; | 331 error = true; |
| 298 skipUntil<UChar, isASCIISpace>(position, end); | 332 skipUntil<UChar, isASCIISpace>(position, end); |
| 299 UseCounter::count(document, UseCounter::SRIElementWithUnparsableInte grityAttribute); | 333 UseCounter::count(document, UseCounter::SRIElementWithUnparsableInte grityAttribute); |
| 300 continue; | 334 continue; |
| 301 } | 335 } |
| 302 | 336 |
| 303 if (!parseMimeType(position, currentIntegrityEnd, type)) { | 337 bool skipToNextIntegrity = false; |
| 304 logErrorToConsole("Error parsing 'integrity' attribute ('" + attribu te + "'). The content type could not be parsed.", document); | 338 while (*position == '?') { |
| 305 error = true; | 339 String name, value; |
| 306 skipUntil<UChar, isASCIISpace>(position, end); | 340 if (!parseOption(position, currentIntegrityEnd, name, value)) { |
| 307 UseCounter::count(document, UseCounter::SRIElementWithUnparsableInte grityAttribute); | 341 logErrorToConsole("Error parsing 'integrity' attribute ('" + att ribute + "'). The options could not be parsed.", document); |
| 342 skipToNextIntegrity = true; | |
| 343 error = true; | |
| 344 skipUntil<UChar, isASCIISpace>(position, end); | |
| 345 UseCounter::count(document, UseCounter::SRIElementWithUnparsable IntegrityAttribute); | |
| 346 break; | |
| 347 } | |
| 348 | |
| 349 if (name == "ct") { | |
| 350 if (!isValidMimeTypeValue(value)) { | |
| 351 logErrorToConsole("Error parsing 'integrity' attribute ('" + attribute + "'). The content type could not be parsed.", document); | |
| 352 skipToNextIntegrity = true; | |
| 353 error = true; | |
| 354 skipUntil<UChar, isASCIISpace>(position, end); | |
| 355 UseCounter::count(document, UseCounter::SRIElementWithUnpars ableIntegrityAttribute); | |
| 356 break; | |
| 357 } | |
| 358 | |
| 359 type = value; | |
| 360 } else { | |
| 361 logErrorToConsole("Ignoring unrecogized 'integrity' attribute op tion '" + name + "' with value '" + value + "'.", document); | |
| 362 } | |
| 363 } | |
| 364 | |
| 365 if (skipToNextIntegrity) | |
| 308 continue; | 366 continue; |
| 309 } | |
| 310 | 367 |
| 311 IntegrityMetadata integrityMetadata = { | 368 IntegrityMetadata integrityMetadata = { |
| 312 digest, | 369 digest, |
| 313 algorithm, | 370 algorithm, |
| 314 type | 371 type |
| 315 }; | 372 }; |
| 316 metadataList.append(integrityMetadata); | 373 metadataList.append(integrityMetadata); |
| 317 } | 374 } |
| 318 | 375 |
| 319 if (metadataList.size() == 0 && error) | 376 if (metadataList.size() == 0 && error) |
| 320 return IntegrityParseNoValidResult; | 377 return IntegrityParseNoValidResult; |
| 321 | 378 |
| 322 return IntegrityParseValidResult; | 379 return IntegrityParseValidResult; |
| 323 } | 380 } |
| 324 | 381 |
| 325 } // namespace blink | 382 } // namespace blink |
| OLD | NEW |