chromium/chrome/browser/ash/extensions/extensions_permissions_tracker_unittest.cc

// 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.

#ifdef UNSAFE_BUFFERS_BUILD
// TODO(crbug.com/40285824): Remove this and convert code to safer constructs.
#pragma allow_unsafe_buffers
#endif

#include "chrome/browser/ash/extensions/extensions_permissions_tracker.h"

#include "base/memory/raw_ptr.h"
#include "chrome/browser/browser_process.h"
#include "chrome/common/pref_names.h"
#include "chrome/test/base/scoped_testing_local_state.h"
#include "chrome/test/base/testing_browser_process.h"
#include "chrome/test/base/testing_profile.h"
#include "chromeos/ash/components/login/login_state/scoped_test_public_session_login_state.h"
#include "components/sync_preferences/testing_pref_service_syncable.h"
#include "content/public/test/browser_task_environment.h"
#include "extensions/browser/pref_names.h"
#include "extensions/common/extension_builder.h"
#include "extensions/common/permissions/permissions_data.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"

using ::testing::_;
using ::testing::Return;

namespace extensions {

namespace {
constexpr char kExtensionId1[] = "id1";
constexpr char kExtensionId2[] = "id2";
constexpr char kExtensionId3[] = "id3";
constexpr char kExtensionUrl1[] = "url1";
constexpr char kExtensionUrl2[] = "url2";
constexpr char kExtensionUrl3[] = "url3";
const char* const kSafePermissionsSet1[] = {"accessibilityFeatures.modify",
                                            "accessibilityFeatures.read"};

const char* const kSafePermissionsSet2[] = {"background", "alarms"};

const char* const kUnsafePermissionsSet1[] = {"debugger", "history", "input"};

const char* const kUnsafePermissionsSet2[] = {"topSites", "ttsEngine",
                                              "webNavigation"};
}  // namespace

class MockExtensionsPermissionsTracker : public ExtensionsPermissionsTracker {
 public:
  MockExtensionsPermissionsTracker(ExtensionRegistry* registry,
                                   content::BrowserContext* browser_context)
      : ExtensionsPermissionsTracker(registry, browser_context) {
    safe_permissions_.insert(
        kSafePermissionsSet1,
        kSafePermissionsSet1 + std::size(kSafePermissionsSet1));
    safe_permissions_.insert(
        kSafePermissionsSet2,
        kSafePermissionsSet2 + std::size(kSafePermissionsSet2));
  }

  // ExtensionsPermissionsTracker:
  bool IsSafePerms(const PermissionsData* perms_data) const override {
    std::set<std::string> perms_strings =
        perms_data->active_permissions().GetAPIsAsStrings();
    for (const auto& perm : perms_strings) {
      if (safe_permissions_.find(perm) == safe_permissions_.end())
        return false;
    }
    return true;
  }

 private:
  std::set<std::string> safe_permissions_;
};

class ExtensionsPermissionsTrackerTest : public testing::Test {
 public:
  ExtensionsPermissionsTrackerTest()
      : prefs_(profile_.GetTestingPrefService()),
        registry_(ExtensionRegistry::Get(&profile_)),
        testing_local_state_(TestingBrowserProcess::GetGlobal()) {}

  ExtensionsPermissionsTrackerTest(const ExtensionsPermissionsTrackerTest&) =
      delete;
  ExtensionsPermissionsTrackerTest& operator=(
      const ExtensionsPermissionsTrackerTest&) = delete;

  base::Value::Dict SetupForceList() {
    base::Value::Dict dict;
    dict.Set(kExtensionId1, kExtensionUrl1);
    dict.Set(kExtensionId2, kExtensionUrl2);
    prefs_->SetManagedPref(pref_names::kInstallForceList, dict.Clone());
    return dict;
  }

  void SetupEmptyForceList() {
    prefs_->SetManagedPref(pref_names::kInstallForceList, base::Value::Dict());
  }

  void CreateExtensionsPermissionsTracker() {
    permissions_tracker_ = std::make_unique<MockExtensionsPermissionsTracker>(
        registry_, &profile_);
  }

  void AddExtensionWithIdAndPermissions(
      const std::string& extension_id,
      const std::vector<std::string>& permissions) {
    auto extension = ExtensionBuilder(extension_id)
                         .SetID(extension_id)
                         .AddAPIPermissions(permissions)
                         .Build();
    registry_->AddEnabled(extension);

    registry_->TriggerOnLoaded(extension.get());
  }

 protected:
  content::BrowserTaskEnvironment task_environment_;
  TestingProfile profile_;
  raw_ptr<sync_preferences::TestingPrefServiceSyncable> prefs_;
  raw_ptr<ExtensionRegistry> registry_;
  ScopedTestingLocalState testing_local_state_;
  std::unique_ptr<MockExtensionsPermissionsTracker> permissions_tracker_;
};

TEST_F(ExtensionsPermissionsTrackerTest, EmptyForceList) {
  EXPECT_TRUE(testing_local_state_.Get()->GetBoolean(
      prefs::kManagedSessionUseFullLoginWarning));

  SetupEmptyForceList();
  CreateExtensionsPermissionsTracker();

  EXPECT_FALSE(testing_local_state_.Get()->GetBoolean(
      prefs::kManagedSessionUseFullLoginWarning));
}

TEST_F(ExtensionsPermissionsTrackerTest, SafeForceListInstalled) {
  EXPECT_TRUE(testing_local_state_.Get()->GetBoolean(
      prefs::kManagedSessionUseFullLoginWarning));

  SetupForceList();
  CreateExtensionsPermissionsTracker();

  std::vector<std::string> v1(
      kSafePermissionsSet1,
      kSafePermissionsSet1 + std::size(kSafePermissionsSet1));
  AddExtensionWithIdAndPermissions(kExtensionId1, v1);

  std::vector<std::string> v2(
      kSafePermissionsSet2,
      kSafePermissionsSet2 + std::size(kSafePermissionsSet2));
  AddExtensionWithIdAndPermissions(kExtensionId2, v2);

  EXPECT_FALSE(testing_local_state_.Get()->GetBoolean(
      prefs::kManagedSessionUseFullLoginWarning));
}

TEST_F(ExtensionsPermissionsTrackerTest, UnsafeForceListInstalled) {
  SetupForceList();
  CreateExtensionsPermissionsTracker();

  std::vector<std::string> v1(
      kUnsafePermissionsSet1,
      kUnsafePermissionsSet1 + std::size(kUnsafePermissionsSet1));

  std::vector<std::string> v2(
      kUnsafePermissionsSet2,
      kUnsafePermissionsSet2 + std::size(kUnsafePermissionsSet2));

  AddExtensionWithIdAndPermissions(kExtensionId1, v1);
  AddExtensionWithIdAndPermissions(kExtensionId2, v2);

  EXPECT_TRUE(testing_local_state_.Get()->GetBoolean(
      prefs::kManagedSessionUseFullLoginWarning));
}

TEST_F(ExtensionsPermissionsTrackerTest, MixedForceListInstalled) {
  SetupForceList();
  CreateExtensionsPermissionsTracker();

  std::vector<std::string> v1(
      kUnsafePermissionsSet1,
      kUnsafePermissionsSet1 + std::size(kUnsafePermissionsSet1));
  AddExtensionWithIdAndPermissions(kExtensionId1, v1);

  std::vector<std::string> v2(
      kSafePermissionsSet2,
      kSafePermissionsSet2 + std::size(kSafePermissionsSet2));
  AddExtensionWithIdAndPermissions(kExtensionId2, v2);

  EXPECT_TRUE(testing_local_state_.Get()->GetBoolean(
      prefs::kManagedSessionUseFullLoginWarning));
}

TEST_F(ExtensionsPermissionsTrackerTest, ForceListIncreased) {
  auto dict = SetupForceList();
  CreateExtensionsPermissionsTracker();

  std::vector<std::string> v1(
      kSafePermissionsSet1,
      kSafePermissionsSet1 + std::size(kSafePermissionsSet1));
  AddExtensionWithIdAndPermissions(kExtensionId1, v1);

  std::vector<std::string> v2(
      kSafePermissionsSet2,
      kSafePermissionsSet2 + std::size(kSafePermissionsSet2));
  AddExtensionWithIdAndPermissions(kExtensionId2, v2);

  EXPECT_FALSE(testing_local_state_.Get()->GetBoolean(
      prefs::kManagedSessionUseFullLoginWarning));

  dict.Set(kExtensionId3, kExtensionUrl3);
  prefs_->SetManagedPref(pref_names::kInstallForceList, std::move(dict));

  std::vector<std::string> v3(
      kUnsafePermissionsSet1,
      kUnsafePermissionsSet1 + std::size(kUnsafePermissionsSet1));
  AddExtensionWithIdAndPermissions(kExtensionId3, v3);

  EXPECT_TRUE(testing_local_state_.Get()->GetBoolean(
      prefs::kManagedSessionUseFullLoginWarning));
}

TEST_F(ExtensionsPermissionsTrackerTest, ForceListDecreased) {
  auto dict = SetupForceList();
  CreateExtensionsPermissionsTracker();

  std::vector<std::string> v1(
      kUnsafePermissionsSet1,
      kUnsafePermissionsSet1 + std::size(kUnsafePermissionsSet1));
  AddExtensionWithIdAndPermissions(kExtensionId1, v1);

  std::vector<std::string> v2(
      kSafePermissionsSet2,
      kSafePermissionsSet2 + std::size(kSafePermissionsSet2));
  AddExtensionWithIdAndPermissions(kExtensionId2, v2);

  EXPECT_TRUE(testing_local_state_.Get()->GetBoolean(
      prefs::kManagedSessionUseFullLoginWarning));

  dict.Remove(kExtensionId1);
  prefs_->SetManagedPref(pref_names::kInstallForceList, std::move(dict));
  EXPECT_FALSE(testing_local_state_.Get()->GetBoolean(
      prefs::kManagedSessionUseFullLoginWarning));
}

TEST_F(ExtensionsPermissionsTrackerTest, SafePendingExtensions) {
  auto dict = SetupForceList();
  CreateExtensionsPermissionsTracker();

  std::vector<std::string> v1(
      kSafePermissionsSet1,
      kSafePermissionsSet1 + std::size(kSafePermissionsSet1));
  AddExtensionWithIdAndPermissions(kExtensionId1, v1);

  EXPECT_TRUE(testing_local_state_.Get()->GetBoolean(
      prefs::kManagedSessionUseFullLoginWarning));

  std::vector<std::string> v2(
      kSafePermissionsSet2,
      kSafePermissionsSet2 + std::size(kSafePermissionsSet2));
  AddExtensionWithIdAndPermissions(kExtensionId2, v2);

  EXPECT_FALSE(testing_local_state_.Get()->GetBoolean(
      prefs::kManagedSessionUseFullLoginWarning));
}

TEST_F(ExtensionsPermissionsTrackerTest, UnsafePendingExtensions) {
  SetupForceList();
  CreateExtensionsPermissionsTracker();

  std::vector<std::string> v1(
      kSafePermissionsSet1,
      kSafePermissionsSet1 + std::size(kSafePermissionsSet1));
  AddExtensionWithIdAndPermissions(kExtensionId1, v1);

  EXPECT_TRUE(testing_local_state_.Get()->GetBoolean(
      prefs::kManagedSessionUseFullLoginWarning));

  std::vector<std::string> v2(
      kUnsafePermissionsSet1,
      kUnsafePermissionsSet1 + std::size(kUnsafePermissionsSet1));
  AddExtensionWithIdAndPermissions(kExtensionId2, v2);

  EXPECT_TRUE(testing_local_state_.Get()->GetBoolean(
      prefs::kManagedSessionUseFullLoginWarning));
}

TEST_F(ExtensionsPermissionsTrackerTest, UnsafeForceListChanged) {
  auto dict = SetupForceList();
  CreateExtensionsPermissionsTracker();

  std::vector<std::string> v1(
      kSafePermissionsSet1,
      kSafePermissionsSet1 + std::size(kSafePermissionsSet1));
  AddExtensionWithIdAndPermissions(kExtensionId1, v1);

  std::vector<std::string> v2(
      kUnsafePermissionsSet1,
      kUnsafePermissionsSet1 + std::size(kUnsafePermissionsSet1));
  AddExtensionWithIdAndPermissions(kExtensionId2, v2);

  EXPECT_TRUE(testing_local_state_.Get()->GetBoolean(
      prefs::kManagedSessionUseFullLoginWarning));

  dict.Remove(kExtensionId1);
  prefs_->SetManagedPref(pref_names::kInstallForceList, dict.Clone());

  EXPECT_TRUE(testing_local_state_.Get()->GetBoolean(
      prefs::kManagedSessionUseFullLoginWarning));

  dict.Remove(kExtensionId2);
  prefs_->SetManagedPref(pref_names::kInstallForceList, dict.Clone());

  EXPECT_FALSE(testing_local_state_.Get()->GetBoolean(
      prefs::kManagedSessionUseFullLoginWarning));
}

TEST_F(ExtensionsPermissionsTrackerTest, OtherExtensionsLoaded) {
  SetupForceList();
  CreateExtensionsPermissionsTracker();

  std::vector<std::string> v1(
      kSafePermissionsSet1,
      kSafePermissionsSet1 + std::size(kSafePermissionsSet1));
  AddExtensionWithIdAndPermissions(kExtensionId1, v1);

  std::vector<std::string> v2(
      kSafePermissionsSet1,
      kSafePermissionsSet1 + std::size(kSafePermissionsSet1));
  AddExtensionWithIdAndPermissions(kExtensionId2, v2);

  std::vector<std::string> v3(
      kUnsafePermissionsSet1,
      kUnsafePermissionsSet1 + std::size(kUnsafePermissionsSet1));
  AddExtensionWithIdAndPermissions(kExtensionId3, v3);

  EXPECT_FALSE(testing_local_state_.Get()->GetBoolean(
      prefs::kManagedSessionUseFullLoginWarning));
}

}  // namespace extensions