Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(39)

Side by Side Diff: Source/core/html/parser/XSSAuditor.cpp

Issue 337143004: Fix XSSAuditor handling of semicolon-separated attributes. (Closed) Base URL: svn://svn.chromium.org/blink/trunk
Patch Set: Use emptyString() Created 6 years, 6 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
« no previous file with comments | « Source/core/html/parser/XSSAuditor.h ('k') | no next file » | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
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
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
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
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
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
OLDNEW
« no previous file with comments | « Source/core/html/parser/XSSAuditor.h ('k') | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698