Title: [110734] branches/chromium/1025/Source/WebCore/html/parser
Revision
110734
Author
[email protected]
Date
2012-03-14 12:52:35 -0700 (Wed, 14 Mar 2012)

Log Message

Merge 108881 - XSS Auditor targeting legitimate frames as false positives.
https://bugs.webkit.org/show_bug.cgi?id=79397

Reviewed by Adam Barth.

[email protected]
Review URL: https://chromiumcodereview.appspot.com/9701035

Modified Paths

Diff

Modified: branches/chromium/1025/Source/WebCore/html/parser/XSSAuditor.cpp (110733 => 110734)


--- branches/chromium/1025/Source/WebCore/html/parser/XSSAuditor.cpp	2012-03-14 19:51:26 UTC (rev 110733)
+++ branches/chromium/1025/Source/WebCore/html/parser/XSSAuditor.cpp	2012-03-14 19:52:35 UTC (rev 110734)
@@ -277,7 +277,7 @@
     case AfterScriptStartTag:
         didBlockScript = filterTokenAfterScriptStartTag(token);
         ASSERT(m_state == Initial);
-        m_cachedSnippet = String();
+        m_cachedDecodedSnippet = String();
         break;
     }
 
@@ -341,16 +341,10 @@
         return false;
     }
 
-    TextResourceDecoder* decoder = m_parser->document()->decoder();
-    if (isContainedInRequest(fullyDecodeString(m_cachedSnippet, decoder))) {
-        int start = 0;
-        int end = token.endIndex() - token.startIndex();
-        String snippet = snippetForJavaScript(snippetForRange(token, start, end));
-        if (isContainedInRequest(fullyDecodeString(snippet, decoder))) {
-            token.eraseCharacters();
-            token.appendToCharacter(' '); // Technically, character tokens can't be empty.
-            return true;
-        }
+    if (isContainedInRequest(m_cachedDecodedSnippet) && isContainedInRequest(decodedSnippetForJavaScript(token))) {
+        token.eraseCharacters();
+        token.appendToCharacter(' '); // Technically, character tokens can't be empty.
+        return true;
     }
     return false;
 }
@@ -361,11 +355,12 @@
     ASSERT(token.type() == HTMLTokenTypes::StartTag);
     ASSERT(hasName(token, scriptTag));
 
-    if (eraseAttributeIfInjected(token, srcAttr, blankURL().string(), SrcLikeAttribute))
-        return true;
+    m_state = AfterScriptStartTag;
+    m_cachedDecodedSnippet = stripLeadingAndTrailingHTMLSpaces(decodedSnippetForToken(token));
 
-    m_state = AfterScriptStartTag;
-    m_cachedSnippet = m_parser->sourceForToken(token);
+    if (isContainedInRequest(decodedSnippetForName(token)))
+        return eraseAttributeIfInjected(token, srcAttr, blankURL().string(), SrcLikeAttribute);
+
     return false;
 }
 
@@ -376,11 +371,11 @@
     ASSERT(hasName(token, objectTag));
 
     bool didBlockScript = false;
-
-    didBlockScript |= eraseAttributeIfInjected(token, dataAttr, blankURL().string(), SrcLikeAttribute);
-    didBlockScript |= eraseAttributeIfInjected(token, typeAttr);
-    didBlockScript |= eraseAttributeIfInjected(token, classidAttr);
-
+    if (isContainedInRequest(decodedSnippetForName(token))) {
+        didBlockScript |= eraseAttributeIfInjected(token, dataAttr, blankURL().string(), SrcLikeAttribute);
+        didBlockScript |= eraseAttributeIfInjected(token, typeAttr);
+        didBlockScript |= eraseAttributeIfInjected(token, classidAttr);
+    }
     return didBlockScript;
 }
 
@@ -410,11 +405,11 @@
     ASSERT(hasName(token, embedTag));
 
     bool didBlockScript = false;
-
-    didBlockScript |= eraseAttributeIfInjected(token, codeAttr, String(), SrcLikeAttribute);
-    didBlockScript |= eraseAttributeIfInjected(token, srcAttr, blankURL().string(), SrcLikeAttribute);
-    didBlockScript |= eraseAttributeIfInjected(token, typeAttr);
-
+    if (isContainedInRequest(decodedSnippetForName(token))) {
+        didBlockScript |= eraseAttributeIfInjected(token, codeAttr, String(), SrcLikeAttribute);
+        didBlockScript |= eraseAttributeIfInjected(token, srcAttr, blankURL().string(), SrcLikeAttribute);
+        didBlockScript |= eraseAttributeIfInjected(token, typeAttr);
+    }
     return didBlockScript;
 }
 
@@ -425,10 +420,10 @@
     ASSERT(hasName(token, appletTag));
 
     bool didBlockScript = false;
-
-    didBlockScript |= eraseAttributeIfInjected(token, codeAttr, String(), SrcLikeAttribute);
-    didBlockScript |= eraseAttributeIfInjected(token, objectAttr);
-
+    if (isContainedInRequest(decodedSnippetForName(token))) {
+        didBlockScript |= eraseAttributeIfInjected(token, codeAttr, String(), SrcLikeAttribute);
+        didBlockScript |= eraseAttributeIfInjected(token, objectAttr);
+    }
     return didBlockScript;
 }
 
@@ -438,7 +433,10 @@
     ASSERT(token.type() == HTMLTokenTypes::StartTag);
     ASSERT(hasName(token, iframeTag));
 
-    return eraseAttributeIfInjected(token, srcAttr, String(), SrcLikeAttribute);
+    if (isContainedInRequest(decodedSnippetForName(token)))
+        return eraseAttributeIfInjected(token, srcAttr, String(), SrcLikeAttribute);
+
+    return false;
 }
 
 bool XSSAuditor::filterMetaToken(HTMLToken& token)
@@ -531,13 +529,19 @@
     return false;
 }
 
-String XSSAuditor::snippetForRange(const HTMLToken& token, int start, int end)
+String XSSAuditor::decodedSnippetForToken(const HTMLToken& token)
 {
-    // FIXME: There's an extra allocation here that we could save by
-    //        passing the range to the parser.
-    return m_parser->sourceForToken(token).substring(start, end - start);
+    String snippet = m_parser->sourceForToken(token);
+    return fullyDecodeString(snippet, m_parser->document()->decoder());
 }
 
+String XSSAuditor::decodedSnippetForName(const HTMLToken& token)
+{
+    // Grab a fixed number of characters equal to the length of the token's
+    // name plus one (to account for the "<").
+    return decodedSnippetForToken(token).substring(0, token.name().size() + 1);
+}
+
 String XSSAuditor::decodedSnippetForAttribute(const HTMLToken& token, const HTMLToken::Attribute& attribute, AttributeKind treatment)
 {
     const size_t kMaximumSnippetLength = 100;
@@ -548,7 +552,7 @@
     // FIXME: We should grab one character before the name also.
     int start = attribute.m_nameRange.m_start - token.startIndex();
     int end = attribute.m_valueRange.m_end - token.startIndex();
-    String decodedSnippet = fullyDecodeString(snippetForRange(token, start, end), m_parser->document()->decoder());
+    String decodedSnippet = fullyDecodeString(m_parser->sourceForToken(token).substring(start, end - start), m_parser->document()->decoder());
     decodedSnippet.truncate(kMaximumSnippetLength);
     if (treatment == SrcLikeAttribute) {
         int slashCount;
@@ -567,31 +571,9 @@
     return decodedSnippet;
 }
 
-bool XSSAuditor::isContainedInRequest(const String& decodedSnippet)
+String XSSAuditor::decodedSnippetForJavaScript(const HTMLToken& token)
 {
-    if (decodedSnippet.isEmpty())
-        return false;
-    if (m_decodedURL.find(decodedSnippet, 0, false) != notFound)
-        return true;
-    if (m_decodedHTTPBodySuffixTree && !m_decodedHTTPBodySuffixTree->mightContain(decodedSnippet))
-        return false;
-    return m_decodedHTTPBody.find(decodedSnippet, 0, false) != notFound;
-}
-
-bool XSSAuditor::isSameOriginResource(const String& url)
-{
-    // If the resource is loaded from the same URL as the enclosing page, it's
-    // probably not an XSS attack, so we reduce false positives by allowing the
-    // request. If the resource has a query string, we're more suspicious,
-    // however, because that's pretty rare and the attacker might be able to
-    // trick a server-side script into doing something dangerous with the query
-    // string.
-    KURL resourceURL(m_parser->document()->url(), url);
-    return (m_parser->document()->url().host() == resourceURL.host() && resourceURL.query().isEmpty());
-}
-
-String XSSAuditor::snippetForJavaScript(const String& string)
-{
+    String string = m_parser->sourceForToken(token);
     const size_t kMaximumFragmentLengthTarget = 100;
 
     size_t startPosition = 0;
@@ -637,7 +619,30 @@
         }
     }
     
-    return string.substring(startPosition, endPosition - startPosition);
+    return fullyDecodeString(string.substring(startPosition, endPosition - startPosition), m_parser->document()->decoder());
 }
 
+bool XSSAuditor::isContainedInRequest(const String& decodedSnippet)
+{
+    if (decodedSnippet.isEmpty())
+        return false;
+    if (m_decodedURL.find(decodedSnippet, 0, false) != notFound)
+        return true;
+    if (m_decodedHTTPBodySuffixTree && !m_decodedHTTPBodySuffixTree->mightContain(decodedSnippet))
+        return false;
+    return m_decodedHTTPBody.find(decodedSnippet, 0, false) != notFound;
+}
+
+bool XSSAuditor::isSameOriginResource(const String& url)
+{
+    // If the resource is loaded from the same URL as the enclosing page, it's
+    // probably not an XSS attack, so we reduce false positives by allowing the
+    // request. If the resource has a query string, we're more suspicious,
+    // however, because that's pretty rare and the attacker might be able to
+    // trick a server-side script into doing something dangerous with the query
+    // string.
+    KURL resourceURL(m_parser->document()->url(), url);
+    return (m_parser->document()->url().host() == resourceURL.host() && resourceURL.query().isEmpty());
+}
+
 } // namespace WebCore

Modified: branches/chromium/1025/Source/WebCore/html/parser/XSSAuditor.h (110733 => 110734)


--- branches/chromium/1025/Source/WebCore/html/parser/XSSAuditor.h	2012-03-14 19:51:26 UTC (rev 110733)
+++ branches/chromium/1025/Source/WebCore/html/parser/XSSAuditor.h	2012-03-14 19:52:35 UTC (rev 110734)
@@ -71,9 +71,10 @@
     bool eraseDangerousAttributesIfInjected(HTMLToken&);
     bool eraseAttributeIfInjected(HTMLToken&, const QualifiedName&, const String& replacementValue = String(), AttributeKind treatment = NormalAttribute);
 
-    String snippetForRange(const HTMLToken&, int start, int end);
-    String snippetForJavaScript(const String&);
+    String decodedSnippetForToken(const HTMLToken&);
+    String decodedSnippetForName(const HTMLToken&);
     String decodedSnippetForAttribute(const HTMLToken&, const HTMLToken::Attribute&, AttributeKind treatment = NormalAttribute);
+    String decodedSnippetForJavaScript(const HTMLToken&);
 
     bool isContainedInRequest(const String&);
     bool isSameOriginResource(const String& url);
@@ -87,7 +88,7 @@
     OwnPtr<SuffixTree<ASCIICodebook> > m_decodedHTTPBodySuffixTree;
 
     State m_state;
-    String m_cachedSnippet;
+    String m_cachedDecodedSnippet;
     bool m_notifiedClient;
 };
 
_______________________________________________
webkit-changes mailing list
[email protected]
http://lists.webkit.org/mailman/listinfo.cgi/webkit-changes

Reply via email to