| OLD | NEW |
| 1 /* | 1 /* |
| 2 * Copyright (C) 2011 Adam Barth. All Rights Reserved. | 2 * Copyright (C) 2011 Adam Barth. All Rights Reserved. |
| 3 * Copyright (C) 2011 Daniel Bates (dbates@intudata.com). | 3 * Copyright (C) 2011 Daniel Bates (dbates@intudata.com). |
| 4 * | 4 * |
| 5 * Redistribution and use in source and binary forms, with or without | 5 * Redistribution and use in source and binary forms, with or without |
| 6 * modification, are permitted provided that the following conditions | 6 * modification, are permitted provided that the following conditions |
| 7 * are met: | 7 * are met: |
| 8 * 1. Redistributions of source code must retain the above copyright | 8 * 1. Redistributions of source code must retain the above copyright |
| 9 * notice, this list of conditions and the following disclaimer. | 9 * notice, this list of conditions and the following disclaimer. |
| 10 * 2. Redistributions in binary form must reproduce the above copyright | 10 * 2. Redistributions in binary form must reproduce the above copyright |
| (...skipping 231 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 242 return FilterReflectedXSS; | 242 return FilterReflectedXSS; |
| 243 | 243 |
| 244 return result; | 244 return result; |
| 245 } | 245 } |
| 246 | 246 |
| 247 static bool isSemicolonSeparatedAttribute(const HTMLToken::Attribute& attribute) | 247 static bool isSemicolonSeparatedAttribute(const HTMLToken::Attribute& attribute) |
| 248 { | 248 { |
| 249 return threadSafeMatch(attribute.name, SVGNames::valuesAttr); | 249 return threadSafeMatch(attribute.name, SVGNames::valuesAttr); |
| 250 } | 250 } |
| 251 | 251 |
| 252 static bool semicolonSeparatedValueContainsJavaScriptURL(const String& value) | 252 static String semicolonSeparatedValueContainingJavaScriptURL(const String& value
) |
| 253 { | 253 { |
| 254 Vector<String> valueList; | 254 Vector<String> valueList; |
| 255 value.split(';', valueList); | 255 value.split(';', valueList); |
| 256 for (size_t i = 0; i < valueList.size(); ++i) { | 256 for (size_t i = 0; i < valueList.size(); ++i) { |
| 257 if (protocolIsJavaScript(valueList[i])) | 257 String stripped = stripLeadingAndTrailingHTMLSpaces(valueList[i]); |
| 258 return true; | 258 if (protocolIsJavaScript(stripped)) |
| 259 return stripped; |
| 259 } | 260 } |
| 260 return false; | 261 return emptyString(); |
| 261 } | 262 } |
| 262 | 263 |
| 263 XSSAuditor::XSSAuditor() | 264 XSSAuditor::XSSAuditor() |
| 264 : m_isEnabled(false) | 265 : m_isEnabled(false) |
| 265 , m_xssProtection(FilterReflectedXSS) | 266 , m_xssProtection(FilterReflectedXSS) |
| 266 , m_didSendValidCSPHeader(false) | 267 , m_didSendValidCSPHeader(false) |
| 267 , m_didSendValidXSSProtectionHeader(false) | 268 , m_didSendValidXSSProtectionHeader(false) |
| 268 , m_state(Uninitialized) | 269 , m_state(Uninitialized) |
| 269 , m_scriptTagFoundInRequest(false) | 270 , m_scriptTagFoundInRequest(false) |
| 270 , m_scriptTagNestingLevel(0) | 271 , m_scriptTagNestingLevel(0) |
| (...skipping 322 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 593 | 594 |
| 594 return eraseAttributeIfInjected(request, formactionAttr, kURLWithUniqueOrigi
n, SrcLikeAttributeTruncation); | 595 return eraseAttributeIfInjected(request, formactionAttr, kURLWithUniqueOrigi
n, SrcLikeAttributeTruncation); |
| 595 } | 596 } |
| 596 | 597 |
| 597 bool XSSAuditor::eraseDangerousAttributesIfInjected(const FilterTokenRequest& re
quest) | 598 bool XSSAuditor::eraseDangerousAttributesIfInjected(const FilterTokenRequest& re
quest) |
| 598 { | 599 { |
| 599 DEFINE_STATIC_LOCAL(String, safeJavaScriptURL, ("javascript:void(0)")); | 600 DEFINE_STATIC_LOCAL(String, safeJavaScriptURL, ("javascript:void(0)")); |
| 600 | 601 |
| 601 bool didBlockScript = false; | 602 bool didBlockScript = false; |
| 602 for (size_t i = 0; i < request.token.attributes().size(); ++i) { | 603 for (size_t i = 0; i < request.token.attributes().size(); ++i) { |
| 604 bool eraseAttribute = false; |
| 605 bool valueContainsJavaScriptURL = false; |
| 603 const HTMLToken::Attribute& attribute = request.token.attributes().at(i)
; | 606 const HTMLToken::Attribute& attribute = request.token.attributes().at(i)
; |
| 604 bool isInlineEventHandler = isNameOfInlineEventHandler(attribute.name); | 607 // FIXME: Don't create a new String for every attribute.value in the doc
ument. |
| 605 // FIXME: It would be better if we didn't create a new String for every
attribute in the document. | 608 if (isNameOfInlineEventHandler(attribute.name)) { |
| 606 String strippedValue = stripLeadingAndTrailingHTMLSpaces(String(attribut
e.value)); | 609 eraseAttribute = isContainedInRequest(canonicalize(snippetFromAttrib
ute(request, attribute), ScriptLikeAttributeTruncation)); |
| 607 bool valueContainsJavaScriptURL = (!isInlineEventHandler && protocolIsJa
vaScript(strippedValue)) || (isSemicolonSeparatedAttribute(attribute) && semicol
onSeparatedValueContainsJavaScriptURL(strippedValue)); | 610 } else if (protocolIsJavaScript(stripLeadingAndTrailingHTMLSpaces(String
(attribute.value)))) { |
| 608 if (!isInlineEventHandler && !valueContainsJavaScriptURL) | 611 valueContainsJavaScriptURL = true; |
| 609 continue; | 612 eraseAttribute = isContainedInRequest(canonicalize(snippetFromAttrib
ute(request, attribute), ScriptLikeAttributeTruncation)); |
| 610 if (!isContainedInRequest(canonicalize(snippetFromAttribute(request, att
ribute), ScriptLikeAttributeTruncation))) | 613 } else if (isSemicolonSeparatedAttribute(attribute)) { |
| 614 String subValue = semicolonSeparatedValueContainingJavaScriptURL(Str
ing(attribute.value)); |
| 615 if (!subValue.isEmpty()) { |
| 616 valueContainsJavaScriptURL = true; |
| 617 eraseAttribute = isContainedInRequest(canonicalize(nameFromAttri
bute(request, attribute), NoTruncation)) |
| 618 && isContainedInRequest(canonicalize(subValue, ScriptLikeAtt
ributeTruncation)); |
| 619 } |
| 620 } |
| 621 if (!eraseAttribute) |
| 611 continue; | 622 continue; |
| 612 request.token.eraseValueOfAttribute(i); | 623 request.token.eraseValueOfAttribute(i); |
| 613 if (valueContainsJavaScriptURL) | 624 if (valueContainsJavaScriptURL) |
| 614 request.token.appendToAttributeValue(i, safeJavaScriptURL); | 625 request.token.appendToAttributeValue(i, safeJavaScriptURL); |
| 615 didBlockScript = true; | 626 didBlockScript = true; |
| 616 } | 627 } |
| 617 return didBlockScript; | 628 return didBlockScript; |
| 618 } | 629 } |
| 619 | 630 |
| 620 bool XSSAuditor::eraseAttributeIfInjected(const FilterTokenRequest& request, con
st QualifiedName& attributeName, const String& replacementValue, TruncationKind
treatment) | 631 bool XSSAuditor::eraseAttributeIfInjected(const FilterTokenRequest& request, con
st QualifiedName& attributeName, const String& replacementValue, TruncationKind
treatment) |
| (...skipping 20 matching lines...) Expand all Loading... |
| 641 | 652 |
| 642 return true; | 653 return true; |
| 643 } | 654 } |
| 644 | 655 |
| 645 String XSSAuditor::canonicalizedSnippetForTagName(const FilterTokenRequest& requ
est) | 656 String XSSAuditor::canonicalizedSnippetForTagName(const FilterTokenRequest& requ
est) |
| 646 { | 657 { |
| 647 // Grab a fixed number of characters equal to the length of the token's name
plus one (to account for the "<"). | 658 // Grab a fixed number of characters equal to the length of the token's name
plus one (to account for the "<"). |
| 648 return canonicalize(request.sourceTracker.sourceForToken(request.token).subs
tring(0, request.token.name().size() + 1), NoTruncation); | 659 return canonicalize(request.sourceTracker.sourceForToken(request.token).subs
tring(0, request.token.name().size() + 1), NoTruncation); |
| 649 } | 660 } |
| 650 | 661 |
| 662 String XSSAuditor::nameFromAttribute(const FilterTokenRequest& request, const HT
MLToken::Attribute& attribute) |
| 663 { |
| 664 // The range inlcudes the character which terminates the name. So, |
| 665 // for an input of |name="value"|, the snippet is |name=|. |
| 666 int start = attribute.nameRange.start - request.token.startIndex(); |
| 667 int end = attribute.valueRange.start - request.token.startIndex(); |
| 668 return request.sourceTracker.sourceForToken(request.token).substring(start,
end - start); |
| 669 } |
| 670 |
| 651 String XSSAuditor::snippetFromAttribute(const FilterTokenRequest& request, const
HTMLToken::Attribute& attribute) | 671 String XSSAuditor::snippetFromAttribute(const FilterTokenRequest& request, const
HTMLToken::Attribute& attribute) |
| 652 { | 672 { |
| 653 // The range doesn't inlcude the character which terminates the value. So, | 673 // The range doesn't include the character which terminates the value. So, |
| 654 // for an input of |name="value"|, the snippet is |name="value|. For an | 674 // for an input of |name="value"|, the snippet is |name="value|. For an |
| 655 // unquoted input of |name=value |, the snippet is |name=value|. | 675 // unquoted input of |name=value |, the snippet is |name=value|. |
| 656 // FIXME: We should grab one character before the name also. | 676 // FIXME: We should grab one character before the name also. |
| 657 int start = attribute.nameRange.start - request.token.startIndex(); | 677 int start = attribute.nameRange.start - request.token.startIndex(); |
| 658 int end = attribute.valueRange.end - request.token.startIndex(); | 678 int end = attribute.valueRange.end - request.token.startIndex(); |
| 659 return request.sourceTracker.sourceForToken(request.token).substring(start,
end - start); | 679 return request.sourceTracker.sourceForToken(request.token).substring(start,
end - start); |
| 660 } | 680 } |
| 661 | 681 |
| 662 String XSSAuditor::canonicalize(String snippet, TruncationKind treatment) | 682 String XSSAuditor::canonicalize(String snippet, TruncationKind treatment) |
| 663 { | 683 { |
| (...skipping 116 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 780 | 800 |
| 781 bool XSSAuditor::isSafeToSendToAnotherThread() const | 801 bool XSSAuditor::isSafeToSendToAnotherThread() const |
| 782 { | 802 { |
| 783 return m_documentURL.isSafeToSendToAnotherThread() | 803 return m_documentURL.isSafeToSendToAnotherThread() |
| 784 && m_decodedURL.isSafeToSendToAnotherThread() | 804 && m_decodedURL.isSafeToSendToAnotherThread() |
| 785 && m_decodedHTTPBody.isSafeToSendToAnotherThread() | 805 && m_decodedHTTPBody.isSafeToSendToAnotherThread() |
| 786 && m_httpBodyAsString.isSafeToSendToAnotherThread(); | 806 && m_httpBodyAsString.isSafeToSendToAnotherThread(); |
| 787 } | 807 } |
| 788 | 808 |
| 789 } // namespace WebCore | 809 } // namespace WebCore |
| OLD | NEW |