Source/WebCore:
authorbfulgham@apple.com <bfulgham@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Tue, 8 Aug 2017 02:28:25 +0000 (02:28 +0000)
committerbfulgham@apple.com <bfulgham@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Tue, 8 Aug 2017 02:28:25 +0000 (02:28 +0000)
Implement the HTML5 same-origin restriction specification
https://bugs.webkit.org/show_bug.cgi?id=175226
<rdar://problem/11079948>

Reviewed by Chris Dumez.

Follow the algorithms defined in the HTML5 specification for relaxing
the same-origin restriction. We were missing a few steps related to
checking for public suffix and presence of a browsing context.

Tested by new TestWebKitAPI tests.

* dom/Document.cpp:
(WebCore::Document::domainIsRegisterable): Added helper function.
(WebCore::Document::setDomain):

Tools:
Prevent domain from being set to a TLD
https://bugs.webkit.org/show_bug.cgi?id=175226
<rdar://problem/11079948>

Reviewed by Chris Dumez.

Extend the public suffix tests to include cases used by the
Public Domain 'Public Suffix List'.

* TestWebKitAPI/Tests/mac/PublicSuffix.cpp:
(TestWebKitAPI::TEST):

git-svn-id: https://svn.webkit.org/repository/webkit/trunk@220376 268f45cc-cd09-0410-ab3c-d52691b4dbfc

Source/WebCore/ChangeLog
Source/WebCore/dom/Document.cpp
Source/WebCore/dom/Document.h
Tools/ChangeLog
Tools/TestWebKitAPI/Tests/mac/PublicSuffix.mm

index e883691..1eebe33 100644 (file)
@@ -1,3 +1,21 @@
+2017-08-04  Brent Fulgham  <bfulgham@apple.com>
+
+        Implement the HTML5 same-origin restriction specification
+        https://bugs.webkit.org/show_bug.cgi?id=175226
+        <rdar://problem/11079948>
+
+        Reviewed by Chris Dumez.
+
+        Follow the algorithms defined in the HTML5 specification for relaxing
+        the same-origin restriction. We were missing a few steps related to
+        checking for public suffix and presence of a browsing context.
+
+        Tested by new TestWebKitAPI tests.
+
+        * dom/Document.cpp:
+        (WebCore::Document::domainIsRegisterable): Added helper function.
+        (WebCore::Document::setDomain):
+
 2017-08-07  Devin Rousso  <drousso@apple.com>
 
         Web Inspector: Preview Canvas path when viewing a recording
index d7c8eed..b74e5ef 100644 (file)
 #include "PointerLockController.h"
 #include "PopStateEvent.h"
 #include "ProcessingInstruction.h"
+#include "PublicSuffix.h"
 #include "RealtimeMediaSourceCenter.h"
 #include "RenderChildIterator.h"
 #include "RenderLayerCompositor.h"
@@ -4438,45 +4439,64 @@ String Document::domain() const
     return securityOrigin().domain();
 }
 
-ExceptionOr<void> Document::setDomain(const String& newDomain)
+bool Document::domainIsRegisterable(const String& newDomain) const
 {
-    if (SchemeRegistry::isDomainRelaxationForbiddenForURLScheme(securityOrigin().protocol()))
-        return Exception { SecurityError };
-
-    // Both NS and IE specify that changing the domain is only allowed when
-    // the new domain is a suffix of the old domain.
-
-    // FIXME: We should add logging indicating why a domain was not allowed.
+    if (newDomain.isEmpty())
+        return false;
 
-    String oldDomain = domain();
+    const String& effectiveDomain = domain();
 
-    // If the new domain is the same as the old domain, still call
-    // securityOrigin().setDomainForDOM. This will change the
+    // If the new domain is the same as the old domain, return true so that
+    // we still call securityOrigin().setDomainForDOM. This will change the
     // security check behavior. For example, if a page loaded on port 8000
     // assigns its current domain using document.domain, the page will
     // allow other pages loaded on different ports in the same domain that
     // have also assigned to access this page.
-    if (equalIgnoringASCIICase(oldDomain, newDomain)) {
-        securityOrigin().setDomainFromDOM(newDomain);
-        return { };
-    }
+    if (equalIgnoringASCIICase(effectiveDomain, newDomain))
+        return true;
 
     // e.g. newDomain = webkit.org (10) and domain() = www.webkit.org (14)
-    unsigned oldLength = oldDomain.length();
+    unsigned oldLength = effectiveDomain.length();
     unsigned newLength = newDomain.length();
     if (newLength >= oldLength)
-        return Exception { SecurityError };
+        return false;
 
     auto ipAddressSetting = settings().treatIPAddressAsDomain() ? OriginAccessEntry::TreatIPAddressAsDomain : OriginAccessEntry::TreatIPAddressAsIPAddress;
     OriginAccessEntry accessEntry { securityOrigin().protocol(), newDomain, OriginAccessEntry::AllowSubdomains, ipAddressSetting };
     if (!accessEntry.matchesOrigin(securityOrigin()))
-        return Exception { SecurityError };
+        return false;
 
-    if (oldDomain[oldLength - newLength - 1] != '.')
-        return Exception { SecurityError };
-    if (StringView { oldDomain }.substring(oldLength - newLength) != newDomain)
+    if (effectiveDomain[oldLength - newLength - 1] != '.')
+        return false;
+    if (StringView { effectiveDomain }.substring(oldLength - newLength) != newDomain)
+        return false;
+
+    auto potentialPublicSuffix = newDomain;
+    if (potentialPublicSuffix.startsWith('.'))
+        potentialPublicSuffix.remove(0, 1);
+
+    return !isPublicSuffix(potentialPublicSuffix);
+}
+
+ExceptionOr<void> Document::setDomain(const String& newDomain)
+{
+    if (!frame())
+        return Exception { SecurityError, "A browsing context is required to set a domain." };
+
+    if (SchemeRegistry::isDomainRelaxationForbiddenForURLScheme(securityOrigin().protocol()))
         return Exception { SecurityError };
 
+    // FIXME(175281): Check for 'document.domain' sandbox flag and return an exception if present.
+
+    // FIXME: We should add logging indicating why a domain was not allowed.
+
+    const String& effectiveDomain = domain();
+    if (effectiveDomain.isEmpty())
+        return Exception { SecurityError, "The document has a null effectiveDomain." };
+
+    if (!domainIsRegisterable(newDomain))
+        return Exception { SecurityError, "Attempted to use a non-registrable domain." };
+
     securityOrigin().setDomainFromDOM(newDomain);
     return { };
 }
index e99a152..3eb8c0e 100644 (file)
@@ -1468,6 +1468,8 @@ private:
 
     void platformSuspendOrStopActiveDOMObjects();
 
+    bool domainIsRegisterable(const String&) const;
+
     const Ref<Settings> m_settings;
 
     std::unique_ptr<StyleResolver> m_userAgentShadowTreeStyleResolver;
index 54fd142..0617def 100644 (file)
@@ -1,3 +1,17 @@
+2017-08-04  Brent Fulgham  <bfulgham@apple.com>
+
+        Prevent domain from being set to a TLD
+        https://bugs.webkit.org/show_bug.cgi?id=175226
+        <rdar://problem/11079948>
+
+        Reviewed by Chris Dumez.
+
+        Extend the public suffix tests to include cases used by the
+        Public Domain 'Public Suffix List'.
+
+        * TestWebKitAPI/Tests/mac/PublicSuffix.cpp: 
+        (TestWebKitAPI::TEST):
+
 2017-08-07  Stephan Szabo  <stephan.szabo@sony.com>
 
         [XCode] webkit-patch should run sort-Xcode-project-file
index f820755..f500baa 100644 (file)
@@ -62,6 +62,94 @@ TEST_F(PublicSuffix, IsPublicSuffix)
     EXPECT_TRUE(isPublicSuffix(utf16String(u"\u6803\u6728.jp")));
     EXPECT_FALSE(isPublicSuffix(""));
     EXPECT_FALSE(isPublicSuffix("åäö"));
+
+    // UK
+    EXPECT_TRUE(isPublicSuffix("uk"));
+    EXPECT_FALSE(isPublicSuffix("webkit.uk"));
+    EXPECT_TRUE(isPublicSuffix("co.uk"));
+    EXPECT_FALSE(isPublicSuffix("company.co.uk"));
+
+    // Note: These tests are based on the Public Domain TLD test suite
+    // https://raw.githubusercontent.com/publicsuffix/list/master/tests/test_psl.txt
+    //
+    // That file states:
+    //     Any copyright is dedicated to the Public Domain.
+    //     https://creativecommons.org/publicdomain/zero/1.0/
+
+    // null input.
+    EXPECT_FALSE(isPublicSuffix(""));
+    // Mixed case.
+    EXPECT_TRUE(isPublicSuffix("COM"));
+    EXPECT_FALSE(isPublicSuffix("example.COM"));
+    EXPECT_FALSE(isPublicSuffix("WwW.example.COM"));
+    // Unlisted TLD.
+    EXPECT_FALSE(isPublicSuffix("example"));
+    EXPECT_FALSE(isPublicSuffix("example.example"));
+    EXPECT_FALSE(isPublicSuffix("b.example.example"));
+    EXPECT_FALSE(isPublicSuffix("a.b.example.example"));
+    // TLD with only 1 rule.
+    EXPECT_TRUE(isPublicSuffix("biz"));
+    EXPECT_FALSE(isPublicSuffix("domain.biz"));
+    EXPECT_FALSE(isPublicSuffix("b.domain.biz"));
+    EXPECT_FALSE(isPublicSuffix("a.b.domain.biz"));
+    // TLD with some 2-level rules.
+    EXPECT_FALSE(isPublicSuffix("example.com"));
+    EXPECT_FALSE(isPublicSuffix("b.example.com"));
+    EXPECT_FALSE(isPublicSuffix("a.b.example.com"));
+    EXPECT_TRUE(isPublicSuffix("uk.com"));
+    EXPECT_FALSE(isPublicSuffix("example.uk.com"));
+    EXPECT_FALSE(isPublicSuffix("b.example.uk.com"));
+    EXPECT_FALSE(isPublicSuffix("a.b.example.uk.com"));
+    EXPECT_FALSE(isPublicSuffix("test.ac"));
+    // TLD with only 1 (wildcard) rule.
+    EXPECT_TRUE(isPublicSuffix("mm"));
+    EXPECT_TRUE(isPublicSuffix("c.mm"));
+    EXPECT_FALSE(isPublicSuffix("b.c.mm"));
+    EXPECT_FALSE(isPublicSuffix("a.b.c.mm"));
+    // More complex TLD.
+    EXPECT_TRUE(isPublicSuffix("jp"));
+    EXPECT_FALSE(isPublicSuffix("test.jp"));
+    EXPECT_FALSE(isPublicSuffix("www.test.jp"));
+    EXPECT_TRUE(isPublicSuffix("ac.jp"));
+    EXPECT_FALSE(isPublicSuffix("test.ac.jp"));
+    EXPECT_FALSE(isPublicSuffix("www.test.ac.jp"));
+    EXPECT_TRUE(isPublicSuffix("kyoto.jp"));
+    EXPECT_FALSE(isPublicSuffix("test.kyoto.jp"));
+    EXPECT_TRUE(isPublicSuffix("ide.kyoto.jp"));
+    EXPECT_FALSE(isPublicSuffix("b.ide.kyoto.jp"));
+    EXPECT_FALSE(isPublicSuffix("a.b.ide.kyoto.jp"));
+    EXPECT_TRUE(isPublicSuffix("c.kobe.jp"));
+    EXPECT_FALSE(isPublicSuffix("b.c.kobe.jp"));
+    EXPECT_FALSE(isPublicSuffix("a.b.c.kobe.jp"));
+    EXPECT_FALSE(isPublicSuffix("city.kobe.jp"));
+    EXPECT_FALSE(isPublicSuffix("www.city.kobe.jp"));
+    // TLD with a wildcard rule and exceptions.
+    EXPECT_TRUE(isPublicSuffix("ck"));
+    EXPECT_TRUE(isPublicSuffix("test.ck"));
+    EXPECT_FALSE(isPublicSuffix("b.test.ck"));
+    EXPECT_FALSE(isPublicSuffix("a.b.test.ck"));
+    EXPECT_FALSE(isPublicSuffix("www.ck"));
+    EXPECT_FALSE(isPublicSuffix("www.www.ck"));
+    // US K12.
+    EXPECT_TRUE(isPublicSuffix("us"));
+    EXPECT_FALSE(isPublicSuffix("test.us"));
+    EXPECT_FALSE(isPublicSuffix("www.test.us"));
+    EXPECT_TRUE(isPublicSuffix("ak.us"));
+    EXPECT_FALSE(isPublicSuffix("test.ak.us"));
+    EXPECT_FALSE(isPublicSuffix("www.test.ak.us"));
+    EXPECT_TRUE(isPublicSuffix("k12.ak.us"));
+    EXPECT_FALSE(isPublicSuffix("test.k12.ak.us"));
+    EXPECT_FALSE(isPublicSuffix("www.test.k12.ak.us"));
+    // IDN labels (punycoded)
+    EXPECT_FALSE(isPublicSuffix("xn--85x722f.com.cn"));
+    EXPECT_FALSE(isPublicSuffix("xn--85x722f.xn--55qx5d.cn"));
+    EXPECT_FALSE(isPublicSuffix("www.xn--85x722f.xn--55qx5d.cn"));
+    EXPECT_FALSE(isPublicSuffix("shishi.xn--55qx5d.cn"));
+    EXPECT_TRUE(isPublicSuffix("xn--55qx5d.cn"));
+    EXPECT_FALSE(isPublicSuffix("xn--85x722f.xn--fiqs8s"));
+    EXPECT_FALSE(isPublicSuffix("www.xn--85x722f.xn--fiqs8s"));
+    EXPECT_FALSE(isPublicSuffix("shishi.xn--fiqs8s"));
+    EXPECT_TRUE(isPublicSuffix("xn--fiqs8s"));
 }
 
 TEST_F(PublicSuffix, TopPrivatelyControlledDomain)