Title: [108881] trunk
Revision
108881
Author
[email protected]
Date
2012-02-24 18:39:10 -0800 (Fri, 24 Feb 2012)

Log Message

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

Reviewed by Adam Barth.

Source/WebCore:

Test: http/tests/security/xssAuditor/script-tag-safe4.html

* html/parser/XSSAuditor.cpp:
(WebCore::XSSAuditor::filterCharacterToken):
(WebCore::XSSAuditor::filterScriptToken):
(WebCore::XSSAuditor::filterObjectToken):
(WebCore::XSSAuditor::filterEmbedToken):
(WebCore::XSSAuditor::filterAppletToken):
(WebCore::XSSAuditor::filterIframeToken):
(WebCore::XSSAuditor::decodedSnippetForToken):
(WebCore):
(WebCore::XSSAuditor::decodedSnippetForName):
(WebCore::XSSAuditor::decodedSnippetForAttribute):
(WebCore::XSSAuditor::decodedSnippetForJavascript):
(WebCore::XSSAuditor::isContainedInRequest):
(WebCore::XSSAuditor::isSameOriginResource):
* html/parser/XSSAuditor.h:

LayoutTests:

* http/tests/security/xssAuditor/resources/script-tag-safe4-frame.html: Added.
* http/tests/security/xssAuditor/script-tag-safe4-expected.txt: Added.
* http/tests/security/xssAuditor/script-tag-safe4.html: Added.

Modified Paths

Added Paths

Diff

Modified: trunk/LayoutTests/ChangeLog (108880 => 108881)


--- trunk/LayoutTests/ChangeLog	2012-02-25 02:19:36 UTC (rev 108880)
+++ trunk/LayoutTests/ChangeLog	2012-02-25 02:39:10 UTC (rev 108881)
@@ -1,3 +1,14 @@
+2012-02-24  Tom Sepez  <[email protected]>
+
+        XSS Auditor targeting legitimate frames as false positives.
+        https://bugs.webkit.org/show_bug.cgi?id=79397
+
+        Reviewed by Adam Barth.
+
+        * http/tests/security/xssAuditor/resources/script-tag-safe4-frame.html: Added.
+        * http/tests/security/xssAuditor/script-tag-safe4-expected.txt: Added.
+        * http/tests/security/xssAuditor/script-tag-safe4.html: Added.
+
 2012-02-24  Abhishek Arya  <[email protected]>
 
         Regression(r107477): Crash in StaticNodeList::itemWithName.

Added: trunk/LayoutTests/http/tests/security/xssAuditor/resources/script-tag-safe4-frame.html (0 => 108881)


--- trunk/LayoutTests/http/tests/security/xssAuditor/resources/script-tag-safe4-frame.html	                        (rev 0)
+++ trunk/LayoutTests/http/tests/security/xssAuditor/resources/script-tag-safe4-frame.html	2012-02-25 02:39:10 UTC (rev 108881)
@@ -0,0 +1,9 @@
+<!DOCTYPE html>
+<html>
+<head>
+<script src=""
+<img src=""
+</head>
+<body>
+</body>
+</html>

Added: trunk/LayoutTests/http/tests/security/xssAuditor/script-tag-safe4-expected.txt (0 => 108881)


--- trunk/LayoutTests/http/tests/security/xssAuditor/script-tag-safe4-expected.txt	                        (rev 0)
+++ trunk/LayoutTests/http/tests/security/xssAuditor/script-tag-safe4-expected.txt	2012-02-25 02:39:10 UTC (rev 108881)
@@ -0,0 +1 @@
+ Test passes if XSSAuditor is not triggered.

Added: trunk/LayoutTests/http/tests/security/xssAuditor/script-tag-safe4.html (0 => 108881)


--- trunk/LayoutTests/http/tests/security/xssAuditor/script-tag-safe4.html	                        (rev 0)
+++ trunk/LayoutTests/http/tests/security/xssAuditor/script-tag-safe4.html	2012-02-25 02:39:10 UTC (rev 108881)
@@ -0,0 +1,16 @@
+<!DOCTYPE html>
+<html>
+<head>
+<script>
+if (window.layoutTestController) {
+    layoutTestController.dumpAsText();
+    layoutTestController.setXSSAuditorEnabled(true);
+}
+</script>
+</head>
+<body>
+<iframe src=""
+</iframe>
+Test passes if XSSAuditor is not triggered.
+</body>
+</html>

Modified: trunk/Source/WebCore/ChangeLog (108880 => 108881)


--- trunk/Source/WebCore/ChangeLog	2012-02-25 02:19:36 UTC (rev 108880)
+++ trunk/Source/WebCore/ChangeLog	2012-02-25 02:39:10 UTC (rev 108881)
@@ -1,3 +1,28 @@
+2012-02-24  Tom Sepez  <[email protected]>
+
+        XSS Auditor targeting legitimate frames as false positives.
+        https://bugs.webkit.org/show_bug.cgi?id=79397
+
+        Reviewed by Adam Barth.
+
+        Test: http/tests/security/xssAuditor/script-tag-safe4.html
+
+        * html/parser/XSSAuditor.cpp:
+        (WebCore::XSSAuditor::filterCharacterToken):
+        (WebCore::XSSAuditor::filterScriptToken):
+        (WebCore::XSSAuditor::filterObjectToken):
+        (WebCore::XSSAuditor::filterEmbedToken):
+        (WebCore::XSSAuditor::filterAppletToken):
+        (WebCore::XSSAuditor::filterIframeToken):
+        (WebCore::XSSAuditor::decodedSnippetForToken):
+        (WebCore):
+        (WebCore::XSSAuditor::decodedSnippetForName):
+        (WebCore::XSSAuditor::decodedSnippetForAttribute):
+        (WebCore::XSSAuditor::decodedSnippetForJavascript):
+        (WebCore::XSSAuditor::isContainedInRequest):
+        (WebCore::XSSAuditor::isSameOriginResource):
+        * html/parser/XSSAuditor.h:
+
 2012-02-24  Ian Vollick  <[email protected]>
 
         [chromium] Plumb animation started notifications from CCLayerTreeHost to GraphicsLayerChromium

Modified: trunk/Source/WebCore/html/parser/XSSAuditor.cpp (108880 => 108881)


--- trunk/Source/WebCore/html/parser/XSSAuditor.cpp	2012-02-25 02:19:36 UTC (rev 108880)
+++ trunk/Source/WebCore/html/parser/XSSAuditor.cpp	2012-02-25 02:39:10 UTC (rev 108881)
@@ -313,16 +313,10 @@
 bool XSSAuditor::filterCharacterToken(HTMLToken& token)
 {
     ASSERT(m_scriptTagNestingLevel);
-    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;
 }
@@ -332,11 +326,12 @@
     ASSERT(token.type() == HTMLTokenTypes::StartTag);
     ASSERT(hasName(token, scriptTag));
 
-    if (eraseAttributeIfInjected(token, srcAttr, blankURL().string(), SrcLikeAttribute))
-        return true;
+    m_cachedDecodedSnippet = stripLeadingAndTrailingHTMLSpaces(decodedSnippetForToken(token));
+    m_shouldAllowCDATA = m_parser->tokenizer()->shouldAllowCDATA();
 
-    m_cachedSnippet = m_parser->sourceForToken(token);
-    m_shouldAllowCDATA = m_parser->tokenizer()->shouldAllowCDATA();
+    if (isContainedInRequest(decodedSnippetForName(token)))
+        return eraseAttributeIfInjected(token, srcAttr, blankURL().string(), SrcLikeAttribute);
+
     return false;
 }
 
@@ -346,11 +341,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;
 }
 
@@ -378,11 +373,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;
 }
 
@@ -392,10 +387,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;
 }
 
@@ -404,7 +399,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)
@@ -494,13 +492,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)
 {
     // The range doesn't inlcude the character which terminates the value. So,
@@ -509,7 +513,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(kMaximumFragmentLengthTarget);
     if (treatment == SrcLikeAttribute) {
         int slashCount;
@@ -528,31 +532,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);
     size_t startPosition = 0;
     size_t endPosition = string.length();
     size_t foundPosition = notFound;
@@ -605,7 +587,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: trunk/Source/WebCore/html/parser/XSSAuditor.h (108880 => 108881)


--- trunk/Source/WebCore/html/parser/XSSAuditor.h	2012-02-25 02:19:36 UTC (rev 108880)
+++ trunk/Source/WebCore/html/parser/XSSAuditor.h	2012-02-25 02:39:10 UTC (rev 108881)
@@ -72,9 +72,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);
@@ -88,7 +89,7 @@
     OwnPtr<SuffixTree<ASCIICodebook> > m_decodedHTTPBodySuffixTree;
 
     State m_state;
-    String m_cachedSnippet;
+    String m_cachedDecodedSnippet;
     bool m_shouldAllowCDATA;
     unsigned m_scriptTagNestingLevel;
     bool m_notifiedClient;
_______________________________________________
webkit-changes mailing list
[email protected]
http://lists.webkit.org/mailman/listinfo.cgi/webkit-changes

Reply via email to