Title: [207848] trunk
Revision
207848
Author
[email protected]
Date
2016-10-25 15:07:20 -0700 (Tue, 25 Oct 2016)

Log Message

REGRESSION (r178265): XSS Auditor fails to block document.write() of incomplete tag
https://bugs.webkit.org/show_bug.cgi?id=163978
<rdar://problem/25962131>

Reviewed by Darin Adler.

Source/WebCore:

During the tokenization process of an HTML tag the start and end positions of each of its
attributes is tracked so that the XSS Auditor can request a snippet around a suspected
injected attribute. We need to take care to consider document.write() boundaries when
tracking the start and end positions of each HTML tag and attribute so that the XSS Auditor
receives the correct snippet. Following r178265 we no longer consider document.write()
boundaries when tracking the start and end positions of attributes. So, the substring
represented by the start and end positions of an attribute may correspond to some other
attribute in the tag. Therefore the XSS Auditor may fail to block an injection because the
snippet it requested may not be the snippet that it intended to request.

Tests: http/tests/security/xssAuditor/dom-write-location-dom-write-open-img-onerror.html
       http/tests/security/xssAuditor/dom-write-location-open-img-onerror.html
       http/tests/security/xssAuditor/nested-dom-write-location-open-img-onerror.html

* html/parser/HTMLSourceTracker.cpp:
(WebCore::HTMLSourceTracker::startToken): Set the attribute base offset to be the token
start position.
(WebCore::HTMLSourceTracker::source): Use the specified attribute start position as-is. We no
longer adjust it here because it was adjusted with respect to the attribute base offset, which
takes into account document.write() boundaries.
* html/parser/HTMLToken.h:
(WebCore::HTMLToken::setAttributeBaseOffset): Added.
(WebCore::HTMLToken::beginAttribute): Subtract attribute base offset from the specified offset.
(WebCore::HTMLToken::endAttribute): Ditto.
* html/parser/HTMLTokenizer.h:
(WebCore::HTMLTokenizer::setTokenAttributeBaseOffset): Added.

LayoutTests:

Add tests to ensure that the XSS Auditor blocks a document.write() of an incomplete HTML image tag.

* http/tests/security/xssAuditor/dom-write-location-dom-write-open-img-onerror-expected.txt: Added.
* http/tests/security/xssAuditor/dom-write-location-dom-write-open-img-onerror.html: Added.
* http/tests/security/xssAuditor/dom-write-location-open-img-onerror-expected.txt: Added.
* http/tests/security/xssAuditor/dom-write-location-open-img-onerror.html: Added.
* http/tests/security/xssAuditor/nested-dom-write-location-open-img-onerror-expected.txt: Added.
* http/tests/security/xssAuditor/nested-dom-write-location-open-img-onerror.html: Added.
* http/tests/security/xssAuditor/resources/echo-nested-dom-write-location.html: Added.

Modified Paths

Added Paths

Diff

Modified: trunk/LayoutTests/ChangeLog (207847 => 207848)


--- trunk/LayoutTests/ChangeLog	2016-10-25 22:01:55 UTC (rev 207847)
+++ trunk/LayoutTests/ChangeLog	2016-10-25 22:07:20 UTC (rev 207848)
@@ -1,3 +1,21 @@
+2016-10-25  Daniel Bates  <[email protected]>
+
+        REGRESSION (r178265): XSS Auditor fails to block document.write() of incomplete tag
+        https://bugs.webkit.org/show_bug.cgi?id=163978
+        <rdar://problem/25962131>
+
+        Reviewed by Darin Adler.
+
+        Add tests to ensure that the XSS Auditor blocks a document.write() of an incomplete HTML image tag.
+
+        * http/tests/security/xssAuditor/dom-write-location-dom-write-open-img-onerror-expected.txt: Added.
+        * http/tests/security/xssAuditor/dom-write-location-dom-write-open-img-onerror.html: Added.
+        * http/tests/security/xssAuditor/dom-write-location-open-img-onerror-expected.txt: Added.
+        * http/tests/security/xssAuditor/dom-write-location-open-img-onerror.html: Added.
+        * http/tests/security/xssAuditor/nested-dom-write-location-open-img-onerror-expected.txt: Added.
+        * http/tests/security/xssAuditor/nested-dom-write-location-open-img-onerror.html: Added.
+        * http/tests/security/xssAuditor/resources/echo-nested-dom-write-location.html: Added.
+
 2016-10-25  Brady Eidson  <[email protected]>
 
         IndexedDB 2.0: Support IDBObjectStore openKeyCursor.

Added: trunk/LayoutTests/http/tests/security/xssAuditor/dom-write-location-dom-write-open-img-onerror-expected.txt (0 => 207848)


--- trunk/LayoutTests/http/tests/security/xssAuditor/dom-write-location-dom-write-open-img-onerror-expected.txt	                        (rev 0)
+++ trunk/LayoutTests/http/tests/security/xssAuditor/dom-write-location-dom-write-open-img-onerror-expected.txt	2016-10-25 22:07:20 UTC (rev 207848)
@@ -0,0 +1,2 @@
+CONSOLE MESSAGE: line 6: The XSS Auditor refused to execute a script in 'http://localhost:8000/security/xssAuditor/resources/echo-dom-write-location.html?q=%3Cscript%3Edocument.write(%22%3Cimg%20src=1%20%22);%3C/script%3Eonerror=alert(/XSS/)' because its source code was found within the request. The auditor was enabled because the server did not send an 'X-XSS-Protection' header.
+

Added: trunk/LayoutTests/http/tests/security/xssAuditor/dom-write-location-dom-write-open-img-onerror.html (0 => 207848)


--- trunk/LayoutTests/http/tests/security/xssAuditor/dom-write-location-dom-write-open-img-onerror.html	                        (rev 0)
+++ trunk/LayoutTests/http/tests/security/xssAuditor/dom-write-location-dom-write-open-img-onerror.html	2016-10-25 22:07:20 UTC (rev 207848)
@@ -0,0 +1,15 @@
+<!DOCTYPE html>
+<html>
+<head>
+<script>
+if (window.testRunner) {
+    testRunner.dumpAsText();
+    testRunner.setXSSAuditorEnabled(true);
+}
+</script>
+</head>
+<body>
+<iframe src=''>
+</iframe>
+</body>
+</html>

Added: trunk/LayoutTests/http/tests/security/xssAuditor/dom-write-location-open-img-onerror-expected.txt (0 => 207848)


--- trunk/LayoutTests/http/tests/security/xssAuditor/dom-write-location-open-img-onerror-expected.txt	                        (rev 0)
+++ trunk/LayoutTests/http/tests/security/xssAuditor/dom-write-location-open-img-onerror-expected.txt	2016-10-25 22:07:20 UTC (rev 207848)
@@ -0,0 +1,2 @@
+CONSOLE MESSAGE: line 7: The XSS Auditor refused to execute a script in 'http://localhost:8000/security/xssAuditor/resources/echo-dom-write-location.html?q=%3Cimg%20src=1%20onerror=alert(/XSS/)' because its source code was found within the request. The auditor was enabled because the server did not send an 'X-XSS-Protection' header.
+

Added: trunk/LayoutTests/http/tests/security/xssAuditor/dom-write-location-open-img-onerror.html (0 => 207848)


--- trunk/LayoutTests/http/tests/security/xssAuditor/dom-write-location-open-img-onerror.html	                        (rev 0)
+++ trunk/LayoutTests/http/tests/security/xssAuditor/dom-write-location-open-img-onerror.html	2016-10-25 22:07:20 UTC (rev 207848)
@@ -0,0 +1,15 @@
+<!DOCTYPE html>
+<html>
+<head>
+<script>
+if (window.testRunner) {
+    testRunner.dumpAsText();
+    testRunner.setXSSAuditorEnabled(true);
+}
+</script>
+</head>
+<body>
+<iframe src=''>
+</iframe>
+</body>
+</html>

Added: trunk/LayoutTests/http/tests/security/xssAuditor/nested-dom-write-location-open-img-onerror-expected.txt (0 => 207848)


--- trunk/LayoutTests/http/tests/security/xssAuditor/nested-dom-write-location-open-img-onerror-expected.txt	                        (rev 0)
+++ trunk/LayoutTests/http/tests/security/xssAuditor/nested-dom-write-location-open-img-onerror-expected.txt	2016-10-25 22:07:20 UTC (rev 207848)
@@ -0,0 +1,2 @@
+CONSOLE MESSAGE: line 7: The XSS Auditor refused to execute a script in 'http://localhost:8000/security/xssAuditor/resources/echo-nested-dom-write-location.html?q=%3Cimg%20src=1%20onerror=alert(/XSS/)' because its source code was found within the request. The auditor was enabled because the server did not send an 'X-XSS-Protection' header.
+

Added: trunk/LayoutTests/http/tests/security/xssAuditor/nested-dom-write-location-open-img-onerror.html (0 => 207848)


--- trunk/LayoutTests/http/tests/security/xssAuditor/nested-dom-write-location-open-img-onerror.html	                        (rev 0)
+++ trunk/LayoutTests/http/tests/security/xssAuditor/nested-dom-write-location-open-img-onerror.html	2016-10-25 22:07:20 UTC (rev 207848)
@@ -0,0 +1,15 @@
+<!DOCTYPE html>
+<html>
+<head>
+<script>
+if (window.testRunner) {
+    testRunner.dumpAsText();
+    testRunner.setXSSAuditorEnabled(true);
+}
+</script>
+</head>
+<body>
+<iframe src=''>
+</iframe>
+</body>
+</html>

Added: trunk/LayoutTests/http/tests/security/xssAuditor/resources/echo-nested-dom-write-location.html (0 => 207848)


--- trunk/LayoutTests/http/tests/security/xssAuditor/resources/echo-nested-dom-write-location.html	                        (rev 0)
+++ trunk/LayoutTests/http/tests/security/xssAuditor/resources/echo-nested-dom-write-location.html	2016-10-25 22:07:20 UTC (rev 207848)
@@ -0,0 +1,8 @@
+<!DOCTYPE html>
+<html>
+<head>
+</head>
+<body>
+<script>document.write("<script>document.write(unescape(window.location));</" + "script>");</script>
+</body>
+</html>

Modified: trunk/Source/WebCore/ChangeLog (207847 => 207848)


--- trunk/Source/WebCore/ChangeLog	2016-10-25 22:01:55 UTC (rev 207847)
+++ trunk/Source/WebCore/ChangeLog	2016-10-25 22:07:20 UTC (rev 207848)
@@ -1,3 +1,38 @@
+2016-10-25  Daniel Bates  <[email protected]>
+
+        REGRESSION (r178265): XSS Auditor fails to block document.write() of incomplete tag
+        https://bugs.webkit.org/show_bug.cgi?id=163978
+        <rdar://problem/25962131>
+
+        Reviewed by Darin Adler.
+
+        During the tokenization process of an HTML tag the start and end positions of each of its
+        attributes is tracked so that the XSS Auditor can request a snippet around a suspected
+        injected attribute. We need to take care to consider document.write() boundaries when
+        tracking the start and end positions of each HTML tag and attribute so that the XSS Auditor
+        receives the correct snippet. Following r178265 we no longer consider document.write()
+        boundaries when tracking the start and end positions of attributes. So, the substring
+        represented by the start and end positions of an attribute may correspond to some other
+        attribute in the tag. Therefore the XSS Auditor may fail to block an injection because the
+        snippet it requested may not be the snippet that it intended to request.
+
+        Tests: http/tests/security/xssAuditor/dom-write-location-dom-write-open-img-onerror.html
+               http/tests/security/xssAuditor/dom-write-location-open-img-onerror.html
+               http/tests/security/xssAuditor/nested-dom-write-location-open-img-onerror.html
+
+        * html/parser/HTMLSourceTracker.cpp:
+        (WebCore::HTMLSourceTracker::startToken): Set the attribute base offset to be the token
+        start position.
+        (WebCore::HTMLSourceTracker::source): Use the specified attribute start position as-is. We no
+        longer adjust it here because it was adjusted with respect to the attribute base offset, which
+        takes into account document.write() boundaries.
+        * html/parser/HTMLToken.h:
+        (WebCore::HTMLToken::setAttributeBaseOffset): Added.
+        (WebCore::HTMLToken::beginAttribute): Subtract attribute base offset from the specified offset.
+        (WebCore::HTMLToken::endAttribute): Ditto.
+        * html/parser/HTMLTokenizer.h:
+        (WebCore::HTMLTokenizer::setTokenAttributeBaseOffset): Added.
+
 2016-10-25  Chris Dumez  <[email protected]>
 
         IDBDatabase.transaction() should take a union in parameter

Modified: trunk/Source/WebCore/html/parser/HTMLSourceTracker.cpp (207847 => 207848)


--- trunk/Source/WebCore/html/parser/HTMLSourceTracker.cpp	2016-10-25 22:01:55 UTC (rev 207847)
+++ trunk/Source/WebCore/html/parser/HTMLSourceTracker.cpp	2016-10-25 22:07:20 UTC (rev 207848)
@@ -49,6 +49,7 @@
 
     m_currentSource = currentInput;
     m_tokenStart = m_currentSource.numberOfCharactersConsumed() - m_previousSource.length();
+    tokenizer.setTokenAttributeBaseOffset(m_tokenStart);
 }
 
 void HTMLSourceTracker::endToken(SegmentedString& currentInput, HTMLTokenizer& tokenizer)
@@ -92,7 +93,7 @@
 
 String HTMLSourceTracker::source(const HTMLToken& token, unsigned attributeStart, unsigned attributeEnd)
 {
-    return source(token).substring(attributeStart - m_tokenStart, attributeEnd - attributeStart);
+    return source(token).substring(attributeStart, attributeEnd - attributeStart);
 }
 
 }

Modified: trunk/Source/WebCore/html/parser/HTMLToken.h (207847 => 207848)


--- trunk/Source/WebCore/html/parser/HTMLToken.h	2016-10-25 22:01:55 UTC (rev 207847)
+++ trunk/Source/WebCore/html/parser/HTMLToken.h	2016-10-25 22:07:20 UTC (rev 207848)
@@ -114,6 +114,9 @@
 
     void setSelfClosing();
 
+    // Used by HTMLTokenizer on behalf of HTMLSourceTracker.
+    void setAttributeBaseOffset(unsigned attributeBaseOffset) { m_attributeBaseOffset = attributeBaseOffset; }
+
 public:
     // Used by the XSSAuditor to nuke XSS-laden attributes.
     void eraseValueOfAttribute(unsigned index);
@@ -153,6 +156,8 @@
 
     // For DOCTYPE
     std::unique_ptr<DoctypeData> m_doctypeData;
+
+    unsigned m_attributeBaseOffset { 0 }; // Changes across document.write() boundaries.
 };
 
 const HTMLToken::Attribute* findAttribute(const Vector<HTMLToken::Attribute>&, StringView name);
@@ -315,7 +320,7 @@
     m_attributes.grow(m_attributes.size() + 1);
     m_currentAttribute = &m_attributes.last();
 
-    m_currentAttribute->startOffset = offset;
+    m_currentAttribute->startOffset = offset - m_attributeBaseOffset;
 }
 
 inline void HTMLToken::endAttribute(unsigned offset)
@@ -322,7 +327,7 @@
 {
     ASSERT(offset);
     ASSERT(m_currentAttribute);
-    m_currentAttribute->endOffset = offset;
+    m_currentAttribute->endOffset = offset - m_attributeBaseOffset;
 #if !ASSERT_DISABLED
     m_currentAttribute = nullptr;
 #endif

Modified: trunk/Source/WebCore/html/parser/HTMLTokenizer.h (207847 => 207848)


--- trunk/Source/WebCore/html/parser/HTMLTokenizer.h	2016-10-25 22:01:55 UTC (rev 207847)
+++ trunk/Source/WebCore/html/parser/HTMLTokenizer.h	2016-10-25 22:07:20 UTC (rev 207848)
@@ -43,6 +43,9 @@
     class TokenPtr;
     TokenPtr nextToken(SegmentedString&);
 
+    // Used by HTMLSourceTracker.
+    void setTokenAttributeBaseOffset(unsigned);
+
     // Returns a copy of any characters buffered internally by the tokenizer.
     // The tokenizer buffers characters when searching for the </script> token that terminates a script element.
     String bufferedCharacters() const;
@@ -282,6 +285,11 @@
     return TokenPtr(processToken(source) ? &m_token : nullptr);
 }
 
+inline void HTMLTokenizer::setTokenAttributeBaseOffset(unsigned offset)
+{
+    m_token.setAttributeBaseOffset(offset);
+}
+
 inline size_t HTMLTokenizer::numberOfBufferedCharacters() const
 {
     // Notice that we add 2 to the length of the m_temporaryBuffer to
_______________________________________________
webkit-changes mailing list
[email protected]
https://lists.webkit.org/mailman/listinfo/webkit-changes

Reply via email to