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

#include "extensions/browser/permissions/scripting_permissions_modifier.h"

#include "base/functional/callback_helpers.h"
#include "extensions/browser/extension_prefs.h"
#include "extensions/browser/extension_registry.h"
#include "extensions/browser/extension_util.h"
#include "extensions/browser/extensions_browser_client.h"
#include "extensions/browser/permissions/permissions_updater.h"
#include "extensions/browser/permissions_manager.h"
#include "extensions/buildflags/buildflags.h"
#include "extensions/common/extension.h"
#include "extensions/common/manifest_handlers/permissions_parser.h"
#include "extensions/common/permissions/permission_set.h"
#include "extensions/common/permissions/permissions_data.h"
#include "extensions/common/url_pattern.h"
#include "extensions/common/url_pattern_set.h"
#include "extensions/common/user_script.h"

static_assert(BUILDFLAG(ENABLE_EXTENSIONS_CORE));

namespace extensions {

ScriptingPermissionsModifier::ScriptingPermissionsModifier(
    content::BrowserContext* browser_context,
    const scoped_refptr<const Extension>& extension)
    : browser_context_(browser_context),
      extension_(extension),
      extension_prefs_(ExtensionPrefs::Get(browser_context_)),
      permissions_manager_(PermissionsManager::Get(browser_context_)) {
  DCHECK(extension_);
}

ScriptingPermissionsModifier::~ScriptingPermissionsModifier() = default;

void ScriptingPermissionsModifier::SetWithholdHostPermissions(
    bool should_withhold) {
  DCHECK(permissions_manager_->CanAffectExtension(*extension_));

  if (permissions_manager_->HasWithheldHostPermissions(*extension_) ==
      should_withhold) {
    return;
  }

  // Set the pref first, so that listeners for permission changes get the proper
  // value if they query HasWithheldHostPermissions().
  extension_prefs_->SetWithholdingPermissions(extension_->id(),
                                              should_withhold);

  if (should_withhold) {
    RemoveAllGrantedHostPermissions();
  } else {
    GrantWithheldHostPermissions();
  }
}

void ScriptingPermissionsModifier::GrantHostPermission(const GURL& url) {
  CHECK(permissions_manager_->CanAffectExtension(*extension_));
  // Check that we don't grant host permission to a restricted URL.
  CHECK(
      !extension_->permissions_data()->IsRestrictedUrl(url, /*error=*/nullptr))
      << "Cannot grant access to a restricted URL.";

  URLPatternSet explicit_hosts;
  explicit_hosts.AddOrigin(Extension::kValidHostPermissionSchemes, url);
  URLPatternSet scriptable_hosts;
  scriptable_hosts.AddOrigin(UserScript::ValidUserScriptSchemes(), url);

  GrantHostPermission(std::move(explicit_hosts), std::move(scriptable_hosts),
                      base::DoNothing());
}

void ScriptingPermissionsModifier::GrantHostPermission(
    const URLPattern& site,
    base::OnceClosure done_callback) {
  CHECK(permissions_manager_->CanAffectExtension(*extension_));

  URLPatternSet explicit_hosts;
  URLPattern explicit_host(site);
  explicit_host.SetValidSchemes(Extension::kValidHostPermissionSchemes);
  explicit_hosts.AddPattern(std::move(explicit_host));

  URLPatternSet scriptable_hosts;
  URLPattern scriptable_host(site);
  scriptable_host.SetValidSchemes(UserScript::ValidUserScriptSchemes());
  scriptable_hosts.AddPattern(std::move(scriptable_host));

  GrantHostPermission(std::move(explicit_hosts), std::move(scriptable_hosts),
                      std::move(done_callback));
}

void ScriptingPermissionsModifier::RemoveGrantedHostPermission(
    const GURL& url) {
  CHECK(permissions_manager_->CanAffectExtension(*extension_));
  CHECK(permissions_manager_->HasGrantedHostPermission(*extension_, url));

  std::unique_ptr<const PermissionSet> runtime_permissions =
      permissions_manager_->GetRuntimePermissionsFromPrefs(*extension_);

  URLPatternSet explicit_hosts;
  for (const auto& pattern : runtime_permissions->explicit_hosts()) {
    if (pattern.MatchesSecurityOrigin(url)) {
      explicit_hosts.AddPattern(pattern);
    }
  }
  URLPatternSet scriptable_hosts;
  for (const auto& pattern : runtime_permissions->scriptable_hosts()) {
    if (pattern.MatchesSecurityOrigin(url)) {
      scriptable_hosts.AddPattern(pattern);
    }
  }

  WithholdHostPermissions(std::move(explicit_hosts),
                          std::move(scriptable_hosts), base::DoNothing());
}

void ScriptingPermissionsModifier::RemoveHostPermissions(
    const URLPattern& pattern,
    base::OnceClosure done_callback) {
  CHECK(permissions_manager_->CanAffectExtension(*extension_));

  // Returns the runtime hosts that overlap the pattern with valid schemes.
  auto get_matching_hosts = [](const URLPattern& pattern, int valid_schemes,
                               const URLPatternSet& runtime_hosts) {
    URLPattern host(pattern);
    host.SetValidSchemes(valid_schemes);

    URLPatternSet matching_hosts;
    for (const auto& runtime_host : runtime_hosts) {
      if (host.OverlapsWith(runtime_host)) {
        matching_hosts.AddPattern(runtime_host);
      }
    }
    return matching_hosts;
  };

  // Revoke all sites which have some intersection with `pattern` from the
  // extension's set of runtime granted host permissions.
  std::unique_ptr<const PermissionSet> runtime_permissions =
      permissions_manager_->GetRuntimePermissionsFromPrefs(*extension_);
  URLPatternSet explicit_hosts =
      get_matching_hosts(pattern, Extension::kValidHostPermissionSchemes,
                         runtime_permissions->explicit_hosts());
  URLPatternSet scriptable_hosts =
      get_matching_hosts(pattern, UserScript::ValidUserScriptSchemes(),
                         runtime_permissions->scriptable_hosts());

  WithholdHostPermissions(std::move(explicit_hosts),
                          std::move(scriptable_hosts),
                          std::move(done_callback));
}

void ScriptingPermissionsModifier::RemoveBroadGrantedHostPermissions() {
  DCHECK(permissions_manager_->CanAffectExtension(*extension_));

  std::unique_ptr<const PermissionSet> runtime_permissions =
      permissions_manager_->GetRuntimePermissionsFromPrefs(*extension_);

  URLPatternSet explicit_hosts;
  for (const auto& pattern : runtime_permissions->explicit_hosts()) {
    if (pattern.MatchesEffectiveTld()) {
      explicit_hosts.AddPattern(pattern);
    }
  }
  URLPatternSet scriptable_hosts;
  for (const auto& pattern : runtime_permissions->scriptable_hosts()) {
    if (pattern.MatchesEffectiveTld()) {
      scriptable_hosts.AddPattern(pattern);
    }
  }

  PermissionsUpdater(browser_context_)
      .RevokeRuntimePermissions(
          *extension_,
          PermissionSet(APIPermissionSet(), ManifestPermissionSet(),
                        std::move(explicit_hosts), std::move(scriptable_hosts)),
          base::DoNothing());
}

void ScriptingPermissionsModifier::RemoveAllGrantedHostPermissions() {
  DCHECK(permissions_manager_->CanAffectExtension(*extension_));

  std::unique_ptr<const PermissionSet> revokable_permissions =
      permissions_manager_->GetRevokablePermissions(*extension_);
  DCHECK(revokable_permissions);
  PermissionsUpdater(browser_context_)
      .RevokeRuntimePermissions(*extension_, *revokable_permissions,
                                base::DoNothing());
}

void ScriptingPermissionsModifier::GrantHostPermission(
    URLPatternSet explicit_hosts,
    URLPatternSet scriptable_hosts,
    base::OnceClosure done_callback) {
  PermissionsUpdater(browser_context_)
      .GrantRuntimePermissions(
          *extension_,
          PermissionSet(APIPermissionSet(), ManifestPermissionSet(),
                        std::move(explicit_hosts), std::move(scriptable_hosts)),
          std::move(done_callback));
}

void ScriptingPermissionsModifier::GrantWithheldHostPermissions() {
  const PermissionSet& withheld =
      extension_->permissions_data()->withheld_permissions();

  GrantHostPermission(withheld.explicit_hosts().Clone(),
                      withheld.scriptable_hosts().Clone(), base::DoNothing());
}

void ScriptingPermissionsModifier::WithholdHostPermissions(
    URLPatternSet explicit_hosts,
    URLPatternSet scriptable_hosts,
    base::OnceClosure done_callback) {
  std::unique_ptr<const PermissionSet> permissions_to_remove =
      PermissionSet::CreateIntersection(
          PermissionSet(APIPermissionSet(), ManifestPermissionSet(),
                        std::move(explicit_hosts), std::move(scriptable_hosts)),
          *permissions_manager_->GetRevokablePermissions(*extension_),
          URLPatternSet::IntersectionBehavior::kDetailed);
  if (permissions_to_remove->IsEmpty()) {
    std::move(done_callback).Run();
    return;
  }

  PermissionsUpdater(browser_context_)
      .RevokeRuntimePermissions(*extension_, *permissions_to_remove,
                                std::move(done_callback));
}

}  // namespace extensions