Chromium Code Reviews| Index: Source/core/frame/SubresourceIntegrity.cpp |
| diff --git a/Source/core/frame/SubresourceIntegrity.cpp b/Source/core/frame/SubresourceIntegrity.cpp |
| index 0832fccf0233a8642aa933f89679ab5b4ef65fde..a911b1fe6f5de2b48b4d2083c8ae45d902f341da 100644 |
| --- a/Source/core/frame/SubresourceIntegrity.cpp |
| +++ b/Source/core/frame/SubresourceIntegrity.cpp |
| @@ -40,6 +40,16 @@ static bool isTypeCharacter(UChar c) |
| return isASCIIAlphanumeric(c) || c == '+' || c == '.' || c == '-'; |
| } |
| +static bool isOptionNameCharacter(UChar c) |
| +{ |
| + return isASCIIAlphanumeric(c) || c == '-'; |
| +} |
| + |
| +static bool isOptionValueCharacter(UChar c) |
| +{ |
| + return isASCIIAlphanumeric(c) || c == '!' || c == '#' || c == '$' || c == '%' || c == '&' || c == '\'' || c == '*' || c == '+' || c == '-' || c == '.' || c == '^' || c == '_' || c == '`' || c == '|' || c == '~' || c == '/'; |
| +} |
| + |
| static void logErrorToConsole(const String& message, Document& document) |
| { |
| document.addConsoleMessage(ConsoleMessage::create(SecurityMessageSource, ErrorMessageLevel, message)); |
| @@ -203,45 +213,69 @@ bool SubresourceIntegrity::parseDigest(const UChar*& position, const UChar* end, |
| return true; |
| } |
| +bool SubresourceIntegrity::isValidMimeTypeValue(const String& value) |
| +{ |
| + Vector<UChar> characters; |
| + value.appendTo(characters); |
| + const UChar* position = characters.data(); |
| + const UChar* end = characters.end(); |
| + |
| + skipWhile<UChar, isASCIIAlpha>(position, end); |
| + if (position == end) |
| + return false; |
| + |
| + if (!skipExactly<UChar>(position, end, '/')) |
| + return false; |
| + |
| + skipWhile<UChar, isTypeCharacter>(position, end); |
| + if (position != end) |
| + return false; |
| + |
| + return true; |
| +} |
| + |
| // Before: |
| // |
| -// [algorithm]-[hash] OR [algorithm]-[hash]?[options] |
| -// ^ ^ ^ |
| -// position/end position end |
| +// [algorithm]-[hash] OR [algorithm]-[hash]?[name]=[value] OR [algorithm]-[hash]?[name]=[value]?[rest of options] |
| +// ^ ^ ^ ^ ^ |
| +// position/end position end position end |
| // |
| // After (if successful: if the method returns false, we make no promises and the caller should exit early): |
| // |
| -// [algorithm]-[hash] OR [algorithm]-[hash]?[options] |
| -// ^ ^ |
| -// position/end position/end |
| -bool SubresourceIntegrity::parseMimeType(const UChar*& position, const UChar* end, String& type) |
| +// [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.
|
| +// ^ ^ ^ ^ |
| +// position/end position/end position end |
| +bool SubresourceIntegrity::parseOption(const UChar*& position, const UChar* end, String& name, String& value) |
| { |
| - type = emptyString(); |
| + name = emptyString(); |
| + value = emptyString(); |
| if (position == end) |
| return true; |
| - if (!skipToken<UChar>(position, end, "?ct=")) |
| + if (!skipExactly<UChar>(position, end, '?')) |
| return false; |
| const UChar* begin = position; |
| - skipWhile<UChar, isASCIIAlpha>(position, end); |
| + skipWhile<UChar, isOptionNameCharacter>(position, end); |
| if (position == end) |
| return false; |
| - if (!skipExactly<UChar>(position, end, '/')) |
| - return false; |
| + name = String(begin, position - begin); |
| - if (position == end) |
| + if (!skipExactly<UChar>(position, end, '=') || position == end) |
| return false; |
| - skipWhile<UChar, isTypeCharacter>(position, end); |
| - if (position != end) |
| - return false; |
| + begin = position; |
| + skipWhile<UChar, isOptionValueCharacter>(position, end); |
| - type = String(begin, position - begin); |
| - return true; |
| + value = String(begin, position - begin); |
| + |
| + if (position == end || *position == '?') |
|
Mike West
2015/05/11 03:25:28
Would you mind asserting that `position < end` bef
|
| + return true; |
| + |
| + return false; |
| } |
| SubresourceIntegrity::IntegrityParseResult SubresourceIntegrity::parseIntegrityAttribute(const WTF::String& attribute, WTF::Vector<IntegrityMetadata>& metadataList, Document& document) |
| @@ -300,14 +334,37 @@ SubresourceIntegrity::IntegrityParseResult SubresourceIntegrity::parseIntegrityA |
| continue; |
| } |
| - if (!parseMimeType(position, currentIntegrityEnd, type)) { |
| - logErrorToConsole("Error parsing 'integrity' attribute ('" + attribute + "'). The content type could not be parsed.", document); |
| - error = true; |
| - skipUntil<UChar, isASCIISpace>(position, end); |
| - UseCounter::count(document, UseCounter::SRIElementWithUnparsableIntegrityAttribute); |
| - continue; |
| + bool skipToNextIntegrity = false; |
| + while (*position == '?') { |
| + String name, value; |
| + if (!parseOption(position, currentIntegrityEnd, name, value)) { |
| + logErrorToConsole("Error parsing 'integrity' attribute ('" + attribute + "'). The options could not be parsed.", document); |
| + skipToNextIntegrity = true; |
| + error = true; |
| + skipUntil<UChar, isASCIISpace>(position, end); |
| + UseCounter::count(document, UseCounter::SRIElementWithUnparsableIntegrityAttribute); |
| + break; |
| + } |
| + |
| + if (name == "ct") { |
| + if (!isValidMimeTypeValue(value)) { |
| + logErrorToConsole("Error parsing 'integrity' attribute ('" + attribute + "'). The content type could not be parsed.", document); |
| + skipToNextIntegrity = true; |
| + error = true; |
| + skipUntil<UChar, isASCIISpace>(position, end); |
| + UseCounter::count(document, UseCounter::SRIElementWithUnparsableIntegrityAttribute); |
| + break; |
| + } |
| + |
| + type = value; |
| + } else { |
| + logErrorToConsole("Ignoring unrecogized 'integrity' attribute option '" + name + "' with value '" + value + "'.", document); |
| + } |
| } |
| + if (skipToNextIntegrity) |
| + continue; |
| + |
| IntegrityMetadata integrityMetadata = { |
| digest, |
| algorithm, |