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 |