910e62b5创建于 1月15日历史提交
// Copyright 2020 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "base/values.h"
#include "chrome/browser/external_protocol/auto_launch_protocols_policy_handler.h"
#include "chrome/browser/external_protocol/constants.h"
#include "chrome/browser/external_protocol/external_protocol_handler.h"
#include "chrome/browser/policy/policy_test_utils.h"
#include "chrome/browser/ui/browser.h"
#include "components/policy/core/common/policy_map.h"
#include "components/policy/policy_constants.h"
#include "content/public/test/browser_test.h"
#include "url/gurl.h"
#include "url/origin.h"

namespace policy {

class ExternalProtocolPolicyBrowserTest : public PolicyTest {};

IN_PROC_BROWSER_TEST_F(ExternalProtocolPolicyBrowserTest,
                       AutoLaunchProtocolsMalformedPolicy) {
  const char kWildcardOrigin[] = "*";
  const char kExampleScheme[] = "custom";

  url::Origin test_origin = url::Origin::Create(GURL("https://example.test"));
  ExternalProtocolHandler::BlockState block_state =
      ExternalProtocolHandler::GetBlockState(kExampleScheme, &test_origin,
                                             browser()->profile());
  EXPECT_EQ(ExternalProtocolHandler::UNKNOWN, block_state);

  // Single dictionary for this test case, but erroneously not embedded
  // in a list.
  base::Value::Dict protocol_origins_map;
  // Set a protocol list with a matching protocol.
  protocol_origins_map.Set(policy::external_protocol::kProtocolNameKey,
                           kExampleScheme);
  // Set origins list with a wildcard origin matching pattern.
  base::Value::List origins;
  origins.Append(kWildcardOrigin);
  protocol_origins_map.Set(policy::external_protocol::kOriginListKey,
                           base::Value(std::move(origins)));
  PolicyMap policies;
  policies.Set(key::kAutoLaunchProtocolsFromOrigins, POLICY_LEVEL_MANDATORY,
               POLICY_SCOPE_USER, POLICY_SOURCE_CLOUD,
               base::Value(protocol_origins_map.Clone()), nullptr);
  UpdateProviderPolicy(policies);

  block_state = ExternalProtocolHandler::GetBlockState(
      kExampleScheme, &test_origin, browser()->profile());
  EXPECT_EQ(ExternalProtocolHandler::UNKNOWN, block_state);
}

IN_PROC_BROWSER_TEST_F(ExternalProtocolPolicyBrowserTest,
                       AutoLaunchProtocolsNullInitiatingOrigin) {
  const char kWildcardOrigin[] = "*";
  const char kExampleScheme[] = "custom";

  base::Value::List protocol_origins_map_list;
  // Single dictionary in the list for this test case.
  base::Value::Dict protocol_origins_map;
  // Set a protocol.
  protocol_origins_map.Set(policy::external_protocol::kProtocolNameKey,
                           kExampleScheme);
  // Set an origins list with the wildcard origin matching pattern.
  base::Value::List origins;
  origins.Append(kWildcardOrigin);
  protocol_origins_map.Set(policy::external_protocol::kOriginListKey,
                           base::Value(std::move(origins)));
  protocol_origins_map_list.Append(std::move(protocol_origins_map));
  PolicyMap policies;
  policies.Set(key::kAutoLaunchProtocolsFromOrigins, POLICY_LEVEL_MANDATORY,
               POLICY_SCOPE_USER, POLICY_SOURCE_CLOUD,
               base::Value(protocol_origins_map_list.Clone()), nullptr);
  UpdateProviderPolicy(policies);

  // Calling GetBlockState with a null initiating_origin should
  // return UNKNOWN.
  ExternalProtocolHandler::BlockState block_state =
      ExternalProtocolHandler::GetBlockState(kExampleScheme, nullptr,
                                             browser()->profile());
  EXPECT_EQ(ExternalProtocolHandler::UNKNOWN, block_state);
}

IN_PROC_BROWSER_TEST_F(ExternalProtocolPolicyBrowserTest,
                       AutoLaunchProtocolsEmptyOriginList) {
  const char kExampleScheme[] = "custom";

  url::Origin test_origin = url::Origin::Create(GURL("https://example.test"));
  ExternalProtocolHandler::BlockState block_state =
      ExternalProtocolHandler::GetBlockState(kExampleScheme, &test_origin,
                                             browser()->profile());
  EXPECT_EQ(ExternalProtocolHandler::UNKNOWN, block_state);

  base::Value::List protocol_origins_map_list;
  // Single dictionary in the list for this test case.
  base::Value::Dict protocol_origins_map;
  // Set a protocol list with a matching protocol.
  protocol_origins_map.Set(policy::external_protocol::kProtocolNameKey,
                           kExampleScheme);
  // Set an empty origins list.
  base::Value::List origins;
  protocol_origins_map.Set(policy::external_protocol::kOriginListKey,
                           base::Value(std::move(origins)));
  protocol_origins_map_list.Append(std::move(protocol_origins_map));
  PolicyMap policies;
  policies.Set(key::kAutoLaunchProtocolsFromOrigins, POLICY_LEVEL_MANDATORY,
               POLICY_SCOPE_USER, POLICY_SOURCE_CLOUD,
               base::Value(protocol_origins_map_list.Clone()), nullptr);
  UpdateProviderPolicy(policies);

  block_state = ExternalProtocolHandler::GetBlockState(
      kExampleScheme, &test_origin, browser()->profile());
  EXPECT_EQ(ExternalProtocolHandler::UNKNOWN, block_state);
}

IN_PROC_BROWSER_TEST_F(ExternalProtocolPolicyBrowserTest,
                       AutoLaunchProtocolsWildcardOriginList) {
  const char kWildcardOrigin[] = "*";
  const char kExampleScheme[] = "custom";

  base::Value::List protocol_origins_map_list;
  // Single dictionary in the list for this test case.
  base::Value::Dict protocol_origins_map;
  // Set a protocol to match the test.
  protocol_origins_map.Set(policy::external_protocol::kProtocolNameKey,
                           kExampleScheme);
  // Set an origins list with the wildcard origin matching pattern.
  base::Value::List origins;
  origins.Append(kWildcardOrigin);
  protocol_origins_map.Set(policy::external_protocol::kOriginListKey,
                           base::Value(std::move(origins)));
  protocol_origins_map_list.Append(std::move(protocol_origins_map));
  PolicyMap policies;
  policies.Set(key::kAutoLaunchProtocolsFromOrigins, POLICY_LEVEL_MANDATORY,
               POLICY_SCOPE_USER, POLICY_SOURCE_CLOUD,
               base::Value(protocol_origins_map_list.Clone()), nullptr);
  UpdateProviderPolicy(policies);

  url::Origin test_origin = url::Origin::Create(GURL("https://example.test"));
  ExternalProtocolHandler::BlockState block_state =
      ExternalProtocolHandler::GetBlockState(kExampleScheme, &test_origin,
                                             browser()->profile());
  EXPECT_EQ(ExternalProtocolHandler::DONT_BLOCK, block_state);
}

IN_PROC_BROWSER_TEST_F(ExternalProtocolPolicyBrowserTest,
                       AutoLaunchProtocolsInvalidProtocols) {
  const char kInvalidProtocol1[] = "custom:";
  const char kInvalidProtocol2[] = "custom://";
  const char kInvalidProtocol3[] = "custom//";
  const char kWildcardOrigin[] = "*";
  const char kExampleScheme[] = "custom";

  base::Value::List protocol_origins_map_list;

  // Three dictionaries in the list for this test case.
  base::Value::Dict protocol_origins_map1;
  base::Value::Dict protocol_origins_map2;
  base::Value::Dict protocol_origins_map3;

  // Set invalid protocols, each with the wildcard origin matching pattern.
  protocol_origins_map1.Set(policy::external_protocol::kProtocolNameKey,
                            kInvalidProtocol1);
  base::Value::List origins1;
  origins1.Append(kWildcardOrigin);
  protocol_origins_map1.Set(policy::external_protocol::kOriginListKey,
                            base::Value(std::move(origins1)));
  protocol_origins_map_list.Append(std::move(protocol_origins_map1));

  protocol_origins_map2.Set(policy::external_protocol::kProtocolNameKey,
                            kInvalidProtocol2);
  base::Value::List origins2;
  origins2.Append(kWildcardOrigin);
  protocol_origins_map2.Set(policy::external_protocol::kOriginListKey,
                            base::Value(std::move(origins2)));
  protocol_origins_map_list.Append(std::move(protocol_origins_map2));

  protocol_origins_map3.Set(policy::external_protocol::kProtocolNameKey,
                            kInvalidProtocol3);
  base::Value::List origins3;
  origins3.Append(kWildcardOrigin);
  protocol_origins_map3.Set(policy::external_protocol::kOriginListKey,
                            base::Value(std::move(origins3)));
  protocol_origins_map_list.Append(std::move(protocol_origins_map3));

  PolicyMap policies;
  policies.Set(key::kAutoLaunchProtocolsFromOrigins, POLICY_LEVEL_MANDATORY,
               POLICY_SCOPE_USER, POLICY_SOURCE_CLOUD,
               base::Value(protocol_origins_map_list.Clone()), nullptr);
  UpdateProviderPolicy(policies);

  url::Origin test_origin = url::Origin::Create(GURL("https://example.test"));
  ExternalProtocolHandler::BlockState block_state =
      ExternalProtocolHandler::GetBlockState(kExampleScheme, &test_origin,
                                             browser()->profile());
  EXPECT_EQ(ExternalProtocolHandler::UNKNOWN, block_state);
}

IN_PROC_BROWSER_TEST_F(ExternalProtocolPolicyBrowserTest,
                       AutoLaunchProtocolsOriginPatternWithMissingScheme) {
  const char kExampleScheme[] = "custom";
  const char kHost[] = "www.example.test";

  base::Value::List protocol_origins_map_list;
  // Single dictionary in the list for this test case.
  base::Value::Dict protocol_origins_map;
  // Set a protocol to match the test.
  protocol_origins_map.Set(policy::external_protocol::kProtocolNameKey,
                           kExampleScheme);
  // Set an origins list with an origin matching pattern that matches but is
  // only the host name.
  base::Value::List origins;
  origins.Append(kHost);
  protocol_origins_map.Set(policy::external_protocol::kOriginListKey,
                           base::Value(std::move(origins)));
  protocol_origins_map_list.Append(std::move(protocol_origins_map));
  PolicyMap policies;
  policies.Set(key::kAutoLaunchProtocolsFromOrigins, POLICY_LEVEL_MANDATORY,
               POLICY_SCOPE_USER, POLICY_SOURCE_CLOUD,
               base::Value(protocol_origins_map_list.Clone()), nullptr);
  UpdateProviderPolicy(policies);

  // Test that secure origin matches.
  url::Origin test_origin =
      url::Origin::Create(GURL("https://www.example.test"));
  ExternalProtocolHandler::BlockState block_state =
      ExternalProtocolHandler::GetBlockState(kExampleScheme, &test_origin,
                                             browser()->profile());
  EXPECT_EQ(ExternalProtocolHandler::DONT_BLOCK, block_state);

  // Test that insecure origin matches.
  test_origin = url::Origin::Create(GURL("http://www.example.test"));
  block_state = ExternalProtocolHandler::GetBlockState(
      kExampleScheme, &test_origin, browser()->profile());
  EXPECT_EQ(ExternalProtocolHandler::DONT_BLOCK, block_state);

  // Test that different origin does not match.
  test_origin = url::Origin::Create(GURL("http://www.other.test"));
  block_state = ExternalProtocolHandler::GetBlockState(
      kExampleScheme, &test_origin, browser()->profile());
  EXPECT_EQ(ExternalProtocolHandler::UNKNOWN, block_state);
}

IN_PROC_BROWSER_TEST_F(ExternalProtocolPolicyBrowserTest,
                       AutoLaunchProtocolsOriginPatternWithExactHostname) {
  const char kExampleScheme[] = "custom";
  const char kExactHostName[] = ".www.example.test";

  base::Value::List protocol_origins_map_list;
  // Single dictionary in the list for this test case.
  base::Value::Dict protocol_origins_map;
  // Set a protocol to match the test.
  protocol_origins_map.Set(policy::external_protocol::kProtocolNameKey,
                           kExampleScheme);
  // Set an origins list with an origin matching pattern that matches exactly
  // but has no scheme.
  base::Value::List origins;
  origins.Append(kExactHostName);
  protocol_origins_map.Set(policy::external_protocol::kOriginListKey,
                           base::Value(std::move(origins)));
  protocol_origins_map_list.Append(std::move(protocol_origins_map));
  PolicyMap policies;
  policies.Set(key::kAutoLaunchProtocolsFromOrigins, POLICY_LEVEL_MANDATORY,
               POLICY_SCOPE_USER, POLICY_SOURCE_CLOUD,
               base::Value(protocol_origins_map_list.Clone()), nullptr);
  UpdateProviderPolicy(policies);

  // Test that secure origin matches.
  url::Origin test_origin =
      url::Origin::Create(GURL("https://www.example.test"));
  ExternalProtocolHandler::BlockState block_state =
      ExternalProtocolHandler::GetBlockState(kExampleScheme, &test_origin,
                                             browser()->profile());
  EXPECT_EQ(ExternalProtocolHandler::DONT_BLOCK, block_state);

  // Test that insecure origin matches.
  test_origin = url::Origin::Create(GURL("http://www.example.test"));
  block_state = ExternalProtocolHandler::GetBlockState(
      kExampleScheme, &test_origin, browser()->profile());
  EXPECT_EQ(ExternalProtocolHandler::DONT_BLOCK, block_state);

  // Test that different origin does not match.
  test_origin = url::Origin::Create(GURL("http://www.other.test"));
  block_state = ExternalProtocolHandler::GetBlockState(
      kExampleScheme, &test_origin, browser()->profile());
  EXPECT_EQ(ExternalProtocolHandler::UNKNOWN, block_state);
}

IN_PROC_BROWSER_TEST_F(ExternalProtocolPolicyBrowserTest,
                       AutoLaunchProtocolsOriginPatternWithParentDomain) {
  const char kExampleScheme[] = "custom";
  const char kParentDomain[] = "example.test";

  base::Value::List protocol_origins_map_list;
  // Single dictionary in the list for this test case.
  base::Value::Dict protocol_origins_map;
  // Set a protocol to match the test.
  protocol_origins_map.Set(policy::external_protocol::kProtocolNameKey,
                           kExampleScheme);
  // Set an origins list with an origin matching pattern that is the parent
  // domain but should match subdomains.
  base::Value::List origins;
  origins.Append(kParentDomain);
  protocol_origins_map.Set(policy::external_protocol::kOriginListKey,
                           base::Value(std::move(origins)));
  protocol_origins_map_list.Append(std::move(protocol_origins_map));
  PolicyMap policies;
  policies.Set(key::kAutoLaunchProtocolsFromOrigins, POLICY_LEVEL_MANDATORY,
               POLICY_SCOPE_USER, POLICY_SOURCE_CLOUD,
               base::Value(protocol_origins_map_list.Clone()), nullptr);
  UpdateProviderPolicy(policies);

  // Test that a subdomain matches.
  url::Origin test_origin =
      url::Origin::Create(GURL("https://www.example.test"));
  ExternalProtocolHandler::BlockState block_state =
      ExternalProtocolHandler::GetBlockState(kExampleScheme, &test_origin,
                                             browser()->profile());
  EXPECT_EQ(ExternalProtocolHandler::DONT_BLOCK, block_state);
}

IN_PROC_BROWSER_TEST_F(ExternalProtocolPolicyBrowserTest,
                       AutoLaunchProtocolsOriginPatternWithWildcardOrigin) {
  const char kExampleScheme[] = "custom";
  const char kProtocolWithWildcardHostname[] = "https://*";

  base::Value::List protocol_origins_map_list;
  // Single dictionary in the list for this test case.
  base::Value::Dict protocol_origins_map;
  // Set a protocol to match the test.
  protocol_origins_map.Set(policy::external_protocol::kProtocolNameKey,
                           kExampleScheme);
  // Set an origins list with an origin matching pattern that matches the scheme
  // and all hosts.
  base::Value::List origins;
  origins.Append(kProtocolWithWildcardHostname);
  protocol_origins_map.Set(policy::external_protocol::kOriginListKey,
                           base::Value(std::move(origins)));
  protocol_origins_map_list.Append(std::move(protocol_origins_map));
  PolicyMap policies;
  policies.Set(key::kAutoLaunchProtocolsFromOrigins, POLICY_LEVEL_MANDATORY,
               POLICY_SCOPE_USER, POLICY_SOURCE_CLOUD,
               base::Value(protocol_origins_map_list.Clone()), nullptr);
  UpdateProviderPolicy(policies);

  // Test that secure origin matches.
  url::Origin test_origin =
      url::Origin::Create(GURL("https://www.example.test"));
  ExternalProtocolHandler::BlockState block_state =
      ExternalProtocolHandler::GetBlockState(kExampleScheme, &test_origin,
                                             browser()->profile());
  EXPECT_EQ(ExternalProtocolHandler::DONT_BLOCK, block_state);

  // Test that insecure origin does not match.
  test_origin = url::Origin::Create(GURL("http://www.example.test"));
  block_state = ExternalProtocolHandler::GetBlockState(
      kExampleScheme, &test_origin, browser()->profile());
  EXPECT_EQ(ExternalProtocolHandler::UNKNOWN, block_state);
}

IN_PROC_BROWSER_TEST_F(ExternalProtocolPolicyBrowserTest,
                       AutoLaunchProtocolsOriginPatternWithFullOrigin) {
  const char kExampleScheme[] = "custom";
  const char kFullOrigin[] = "https://www.example.test:443";

  base::Value::List protocol_origins_map_list;
  // Single dictionary in the list for this test case.
  base::Value::Dict protocol_origins_map;
  // Set a protocol to match the test.
  protocol_origins_map.Set(policy::external_protocol::kProtocolNameKey,
                           kExampleScheme);
  // Set an origins list with an origin matching pattern that matches the full
  // origin exactly.
  base::Value::List origins;
  origins.Append(kFullOrigin);
  protocol_origins_map.Set(policy::external_protocol::kOriginListKey,
                           base::Value(std::move(origins)));
  protocol_origins_map_list.Append(std::move(protocol_origins_map));
  PolicyMap policies;
  policies.Set(key::kAutoLaunchProtocolsFromOrigins, POLICY_LEVEL_MANDATORY,
               POLICY_SCOPE_USER, POLICY_SOURCE_CLOUD,
               base::Value(protocol_origins_map_list.Clone()), nullptr);
  UpdateProviderPolicy(policies);

  // Test that default HTTPS port 443 matches.
  url::Origin test_origin =
      url::Origin::Create(GURL("https://www.example.test"));
  ExternalProtocolHandler::BlockState block_state =
      ExternalProtocolHandler::GetBlockState(kExampleScheme, &test_origin,
                                             browser()->profile());
  EXPECT_EQ(ExternalProtocolHandler::DONT_BLOCK, block_state);

  // Test that explicit port 443 matches.
  test_origin = url::Origin::Create(GURL("https://www.example.test:443"));
  block_state = ExternalProtocolHandler::GetBlockState(
      kExampleScheme, &test_origin, browser()->profile());
  EXPECT_EQ(ExternalProtocolHandler::DONT_BLOCK, block_state);

  // Test that explicit other port does not match.
  test_origin = url::Origin::Create(GURL("https://www.example.test:8080"));
  block_state = ExternalProtocolHandler::GetBlockState(
      kExampleScheme, &test_origin, browser()->profile());
  EXPECT_EQ(ExternalProtocolHandler::UNKNOWN, block_state);
}

IN_PROC_BROWSER_TEST_F(ExternalProtocolPolicyBrowserTest,
                       AutoLaunchProtocolsOriginPatternWithExactParentDomain) {
  const char kExampleScheme[] = "custom";
  const char kExactParentDomain[] = ".example.com";

  base::Value::List protocol_origins_map_list;
  // Single dictionary in the list for this test case.
  base::Value::Dict protocol_origins_map;
  // Set a protocol to match the test.
  protocol_origins_map.Set(policy::external_protocol::kProtocolNameKey,
                           kExampleScheme);
  // Set an origins list with an origin matching pattern that doesn't match
  // because it is a parent domain that does not match subdomains.
  base::Value::List origins;
  origins.Append(kExactParentDomain);
  protocol_origins_map.Set(policy::external_protocol::kOriginListKey,
                           base::Value(std::move(origins)));
  protocol_origins_map_list.Append(std::move(protocol_origins_map));
  PolicyMap policies;
  policies.Set(key::kAutoLaunchProtocolsFromOrigins, POLICY_LEVEL_MANDATORY,
               POLICY_SCOPE_USER, POLICY_SOURCE_CLOUD,
               base::Value(protocol_origins_map_list.Clone()), nullptr);
  UpdateProviderPolicy(policies);

  url::Origin test_origin =
      url::Origin::Create(GURL("https://www.example.test"));
  ExternalProtocolHandler::BlockState block_state =
      ExternalProtocolHandler::GetBlockState(kExampleScheme, &test_origin,
                                             browser()->profile());
  EXPECT_EQ(ExternalProtocolHandler::UNKNOWN, block_state);
}

IN_PROC_BROWSER_TEST_F(ExternalProtocolPolicyBrowserTest,
                       AutoLaunchProtocolsOriginPatternWithPath) {
  const char kExampleScheme[] = "custom";
  const char kFullUrlWithPath[] = "https://example.test/home.html";

  base::Value::List protocol_origins_map_list;
  // Single dictionary in the list for this test case.
  base::Value::Dict protocol_origins_map;
  // Set a protocol to match the test.
  protocol_origins_map.Set(policy::external_protocol::kProtocolNameKey,
                           kExampleScheme);
  // Set an origins list with an origin matching pattern that doesn't match
  // because it contains a [/path] element.
  base::Value::List origins;
  origins.Append(kFullUrlWithPath);
  protocol_origins_map.Set(policy::external_protocol::kOriginListKey,
                           base::Value(std::move(origins)));
  protocol_origins_map_list.Append(std::move(protocol_origins_map));
  PolicyMap policies;
  policies.Set(key::kAutoLaunchProtocolsFromOrigins, POLICY_LEVEL_MANDATORY,
               POLICY_SCOPE_USER, POLICY_SOURCE_CLOUD,
               base::Value(protocol_origins_map_list.Clone()), nullptr);
  UpdateProviderPolicy(policies);

  url::Origin test_origin = url::Origin::Create(GURL(kFullUrlWithPath));
  ExternalProtocolHandler::BlockState block_state =
      ExternalProtocolHandler::GetBlockState(kExampleScheme, &test_origin,
                                             browser()->profile());
  EXPECT_EQ(ExternalProtocolHandler::UNKNOWN, block_state);
}

}  // namespace policy