[Payment Request] Validate payment method identifiers
authoraestes@apple.com <aestes@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Tue, 3 Oct 2017 00:37:50 +0000 (00:37 +0000)
committeraestes@apple.com <aestes@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Tue, 3 Oct 2017 00:37:50 +0000 (00:37 +0000)
https://bugs.webkit.org/show_bug.cgi?id=177794

Reviewed by Sam Weinig.

LayoutTests/imported/w3c:

* web-platform-tests/payment-request/payment-request-ctor-pmi-handling.https-expected.txt:

Source/WebCore:

* Modules/paymentrequest/PaymentRequest.cpp:
(WebCore::isValidStandardizedPaymentMethodIdentifier):
(WebCore::isValidURLBasedPaymentMethodIdentifier):
(WebCore::convertAndValidatePaymentMethodIdentifier):
(WebCore::PaymentRequest::create):
* Modules/paymentrequest/PaymentRequest.h:

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

LayoutTests/imported/w3c/ChangeLog
LayoutTests/imported/w3c/web-platform-tests/payment-request/payment-request-ctor-pmi-handling.https-expected.txt
Source/WebCore/ChangeLog
Source/WebCore/Modules/paymentrequest/PaymentRequest.cpp
Source/WebCore/Modules/paymentrequest/PaymentRequest.h

index 1ddc938..82a47ca 100644 (file)
@@ -1,5 +1,14 @@
 2017-10-02  Andy Estes  <aestes@apple.com>
 
+        [Payment Request] Validate payment method identifiers
+        https://bugs.webkit.org/show_bug.cgi?id=177794
+
+        Reviewed by Sam Weinig.
+
+        * web-platform-tests/payment-request/payment-request-ctor-pmi-handling.https-expected.txt:
+
+2017-10-02  Andy Estes  <aestes@apple.com>
+
         [Payment Request] Update payment-request imported tests
         https://bugs.webkit.org/show_bug.cgi?id=177786
 
index a22de6d..7ebd562 100644 (file)
@@ -1,12 +1,6 @@
 
 PASS Must support valid standard URL PMIs 
 PASS Must not throw on syntactically valid standardized payment method identifiers, even if they are not supported 
-FAIL Test for validity of payment method identifiers during construction assert_throws: expected RangeError processing invalid standardized PMI "basic-💳" function "() => {
-        const methods = [{ supportedMethods: invalidMethod }];
-        new PaymentRequest(methods, defaultDetails);
-      }" did not throw
-FAIL Constructor MUST throw if given an invalid URL-based payment method identifier assert_throws: expected RangeError processing invalid URL PMI "https://username@example.com/pay" function "() => {
-        const methods = [{ supportedMethods: invalidMethod }];
-        new PaymentRequest(methods, defaultDetails);
-      }" did not throw
+PASS Test for validity of payment method identifiers during construction 
+PASS Constructor MUST throw if given an invalid URL-based payment method identifier 
 
index bf545a7..be0bd32 100644 (file)
@@ -1,3 +1,17 @@
+2017-10-02  Andy Estes  <aestes@apple.com>
+
+        [Payment Request] Validate payment method identifiers
+        https://bugs.webkit.org/show_bug.cgi?id=177794
+
+        Reviewed by Sam Weinig.
+
+        * Modules/paymentrequest/PaymentRequest.cpp:
+        (WebCore::isValidStandardizedPaymentMethodIdentifier):
+        (WebCore::isValidURLBasedPaymentMethodIdentifier):
+        (WebCore::convertAndValidatePaymentMethodIdentifier):
+        (WebCore::PaymentRequest::create):
+        * Modules/paymentrequest/PaymentRequest.h:
+
 2017-10-02  Ryosuke Niwa  <rniwa@webkit.org>
 
         Move more code into PasteboardCustomData
index 80feb9c..26e135a 100644 (file)
@@ -159,6 +159,83 @@ static ExceptionOr<void> checkAndCanonicalizeTotal(PaymentCurrencyAmount& total)
     return { };
 }
 
+// Implements "validate a standardized payment method identifier"
+// https://www.w3.org/TR/payment-method-id/#validity-0
+static bool isValidStandardizedPaymentMethodIdentifier(StringView identifier)
+{
+    enum class State {
+        Start,
+        Hyphen,
+        LowerAlpha,
+        Digit,
+    };
+
+    auto state = State::Start;
+    for (auto character : identifier.codeUnits()) {
+        switch (state) {
+        case State::Start:
+        case State::Hyphen:
+            if (isASCIILower(character)) {
+                state = State::LowerAlpha;
+                break;
+            }
+
+            return false;
+
+        case State::LowerAlpha:
+        case State::Digit:
+            if (isASCIILower(character)) {
+                state = State::LowerAlpha;
+                break;
+            }
+
+            if (isASCIIDigit(character)) {
+                state = State::Digit;
+                break;
+            }
+
+            if (character == '-') {
+                state = State::Hyphen;
+                break;
+            }
+
+            return false;
+        }
+    }
+
+    return state == State::LowerAlpha || state == State::Digit;
+}
+
+// Implements "validate a URL-based payment method identifier"
+// https://www.w3.org/TR/payment-method-id/#validation
+static bool isValidURLBasedPaymentMethodIdentifier(const URL& url)
+{
+    if (!url.protocolIs("https"))
+        return false;
+
+    if (!url.user().isEmpty() || !url.pass().isEmpty())
+        return false;
+
+    return true;
+}
+
+// Implements "validate a payment method identifier"
+// https://www.w3.org/TR/payment-method-id/#validity
+static std::optional<PaymentRequest::MethodIdentifier> convertAndValidatePaymentMethodIdentifier(const String& identifier)
+{
+    URL url { URL(), identifier };
+    if (!url.isValid()) {
+        if (isValidStandardizedPaymentMethodIdentifier(identifier))
+            return { identifier };
+        return std::nullopt;
+    }
+
+    if (isValidURLBasedPaymentMethodIdentifier(url))
+        return { WTFMove(url) };
+
+    return std::nullopt;
+}
+
 // Implements the PaymentRequest Constructor
 // https://www.w3.org/TR/payment-request/#constructor
 ExceptionOr<Ref<PaymentRequest>> PaymentRequest::create(Document& document, Vector<PaymentMethodData>&& methodData, PaymentDetailsInit&& details, PaymentOptions&& options)
@@ -174,8 +251,9 @@ ExceptionOr<Ref<PaymentRequest>> PaymentRequest::create(Document& document, Vect
     Vector<Method> serializedMethodData;
     serializedMethodData.reserveInitialCapacity(methodData.size());
     for (auto& paymentMethod : methodData) {
-        if (paymentMethod.supportedMethods.isEmpty())
-            return Exception { TypeError, ASCIILiteral("supportedMethods must be specified.") };
+        auto identifier = convertAndValidatePaymentMethodIdentifier(paymentMethod.supportedMethods);
+        if (!identifier)
+            return Exception { RangeError, makeString("\"", paymentMethod.supportedMethods, "\" is an invalid payment method identifier.") };
 
         String serializedData;
         if (paymentMethod.data) {
@@ -184,7 +262,7 @@ ExceptionOr<Ref<PaymentRequest>> PaymentRequest::create(Document& document, Vect
             if (scope.exception())
                 return Exception { ExistingExceptionError };
         }
-        serializedMethodData.uncheckedAppend({ paymentMethod.supportedMethods, WTFMove(serializedData) });
+        serializedMethodData.uncheckedAppend({ WTFMove(*identifier), WTFMove(serializedData) });
     }
 
     auto exception = checkAndCanonicalizeTotal(details.total.amount);
index 3e2afd9..5ec46ea 100644 (file)
@@ -33,6 +33,8 @@
 #include "JSDOMPromiseDeferred.h"
 #include "PaymentDetailsInit.h"
 #include "PaymentOptions.h"
+#include "URL.h"
+#include <wtf/Variant.h>
 
 namespace WebCore {
 
@@ -60,6 +62,7 @@ public:
     const String& shippingOption() const { return m_shippingOption; }
     std::optional<PaymentShippingType> shippingType() const;
 
+    using MethodIdentifier = Variant<String, URL>;
     using RefCounted<PaymentRequest>::ref;
     using RefCounted<PaymentRequest>::deref;
 
@@ -71,7 +74,7 @@ private:
     };
 
     struct Method {
-        String supportedMethods;
+        MethodIdentifier identifier;
         String serializedData;
     };