Index: Source/core/html/parser/XSSAuditor.cpp |
diff --git a/Source/core/html/parser/XSSAuditor.cpp b/Source/core/html/parser/XSSAuditor.cpp |
index 77912acd33767a4976ba3af27f196ca9fd1d7c02..bef0b2043468ac4f10f94cc2af7824f77a1884d8 100644 |
--- a/Source/core/html/parser/XSSAuditor.cpp |
+++ b/Source/core/html/parser/XSSAuditor.cpp |
@@ -249,15 +249,16 @@ static bool isSemicolonSeparatedAttribute(const HTMLToken::Attribute& attribute) |
return threadSafeMatch(attribute.name, SVGNames::valuesAttr); |
} |
-static bool semicolonSeparatedValueContainsJavaScriptURL(const String& value) |
+static String semicolonSeparatedValueContainingJavaScriptURL(const String& value) |
{ |
Vector<String> valueList; |
value.split(';', valueList); |
for (size_t i = 0; i < valueList.size(); ++i) { |
- if (protocolIsJavaScript(valueList[i])) |
- return true; |
+ String stripped = stripLeadingAndTrailingHTMLSpaces(valueList[i]); |
+ if (protocolIsJavaScript(stripped)) |
+ return stripped; |
} |
- return false; |
+ return emptyString(); |
} |
XSSAuditor::XSSAuditor() |
@@ -600,14 +601,24 @@ bool XSSAuditor::eraseDangerousAttributesIfInjected(const FilterTokenRequest& re |
bool didBlockScript = false; |
for (size_t i = 0; i < request.token.attributes().size(); ++i) { |
+ bool eraseAttribute = false; |
+ bool valueContainsJavaScriptURL = false; |
const HTMLToken::Attribute& attribute = request.token.attributes().at(i); |
- bool isInlineEventHandler = isNameOfInlineEventHandler(attribute.name); |
- // FIXME: It would be better if we didn't create a new String for every attribute in the document. |
- String strippedValue = stripLeadingAndTrailingHTMLSpaces(String(attribute.value)); |
- bool valueContainsJavaScriptURL = (!isInlineEventHandler && protocolIsJavaScript(strippedValue)) || (isSemicolonSeparatedAttribute(attribute) && semicolonSeparatedValueContainsJavaScriptURL(strippedValue)); |
- if (!isInlineEventHandler && !valueContainsJavaScriptURL) |
- continue; |
- if (!isContainedInRequest(canonicalize(snippetFromAttribute(request, attribute), ScriptLikeAttributeTruncation))) |
+ // FIXME: Don't create a new String for every attribute.value in the document. |
+ if (isNameOfInlineEventHandler(attribute.name)) { |
+ eraseAttribute = isContainedInRequest(canonicalize(snippetFromAttribute(request, attribute), ScriptLikeAttributeTruncation)); |
+ } else if (protocolIsJavaScript(stripLeadingAndTrailingHTMLSpaces(String(attribute.value)))) { |
+ valueContainsJavaScriptURL = true; |
+ eraseAttribute = isContainedInRequest(canonicalize(snippetFromAttribute(request, attribute), ScriptLikeAttributeTruncation)); |
+ } else if (isSemicolonSeparatedAttribute(attribute)) { |
+ String subValue = semicolonSeparatedValueContainingJavaScriptURL(String(attribute.value)); |
+ if (!subValue.isEmpty()) { |
+ valueContainsJavaScriptURL = true; |
+ eraseAttribute = isContainedInRequest(canonicalize(nameFromAttribute(request, attribute), NoTruncation)) |
+ && isContainedInRequest(canonicalize(subValue, ScriptLikeAttributeTruncation)); |
+ } |
+ } |
+ if (!eraseAttribute) |
continue; |
request.token.eraseValueOfAttribute(i); |
if (valueContainsJavaScriptURL) |
@@ -648,9 +659,18 @@ String XSSAuditor::canonicalizedSnippetForTagName(const FilterTokenRequest& requ |
return canonicalize(request.sourceTracker.sourceForToken(request.token).substring(0, request.token.name().size() + 1), NoTruncation); |
} |
+String XSSAuditor::nameFromAttribute(const FilterTokenRequest& request, const HTMLToken::Attribute& attribute) |
+{ |
+ // The range inlcudes the character which terminates the name. So, |
+ // for an input of |name="value"|, the snippet is |name=|. |
+ int start = attribute.nameRange.start - request.token.startIndex(); |
+ int end = attribute.valueRange.start - request.token.startIndex(); |
+ return request.sourceTracker.sourceForToken(request.token).substring(start, end - start); |
+} |
+ |
String XSSAuditor::snippetFromAttribute(const FilterTokenRequest& request, const HTMLToken::Attribute& attribute) |
{ |
- // The range doesn't inlcude the character which terminates the value. So, |
+ // The range doesn't include the character which terminates the value. So, |
// for an input of |name="value"|, the snippet is |name="value|. For an |
// unquoted input of |name=value |, the snippet is |name=value|. |
// FIXME: We should grab one character before the name also. |