File: third_party_blocking_browsertest.cc

package info (click to toggle)
chromium 138.0.7204.183-1~deb12u1
  • links: PTS, VCS
  • area: main
  • in suites: bookworm-proposed-updates
  • size: 6,080,960 kB
  • sloc: cpp: 34,937,079; ansic: 7,176,967; javascript: 4,110,704; python: 1,419,954; asm: 946,768; xml: 739,971; pascal: 187,324; sh: 89,623; perl: 88,663; objc: 79,944; sql: 50,304; cs: 41,786; fortran: 24,137; makefile: 21,811; php: 13,980; tcl: 13,166; yacc: 8,925; ruby: 7,485; awk: 3,720; lisp: 3,096; lex: 1,327; ada: 727; jsp: 228; sed: 36
file content (210 lines) | stat: -rw-r--r-- 7,908 bytes parent folder | download | duplicates (4)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
// Copyright 2018 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/base_paths.h"
#include "base/files/file_util.h"
#include "base/files/scoped_temp_dir.h"
#include "base/functional/bind.h"
#include "base/functional/callback.h"
#include "base/path_service.h"
#include "base/run_loop.h"
#include "base/scoped_native_library.h"
#include "base/test/bind.h"
#include "base/test/scoped_feature_list.h"
#include "base/test/test_reg_util_win.h"
#include "base/win/registry.h"
#include "chrome/browser/win/conflicts/module_blocklist_cache_updater.h"
#include "chrome/browser/win/conflicts/module_blocklist_cache_util.h"
#include "chrome/browser/win/conflicts/module_database.h"
#include "chrome/browser/win/conflicts/proto/module_list.pb.h"
#include "chrome/browser/win/conflicts/third_party_conflicts_manager.h"
#include "chrome/chrome_elf/third_party_dlls/packed_list_format.h"
#include "chrome/common/chrome_features.h"
#include "chrome/install_static/install_util.h"
#include "chrome/test/base/in_process_browser_test.h"
#include "content/public/test/browser_test.h"

namespace {

// This classes watches the third-party blocking key to detect when the path to
// the freshly created cache is written into the registry.
class ThirdPartyRegistryKeyObserver {
 public:
  ThirdPartyRegistryKeyObserver()
      : registry_key_(HKEY_CURRENT_USER,
                      GetRegistryKeyPath().c_str(),
                      KEY_CREATE_SUB_KEY | KEY_READ | KEY_NOTIFY) {}

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

  bool StartWatching() {
    return registry_key_.StartWatching(base::BindOnce(
        &ThirdPartyRegistryKeyObserver::OnChange, base::Unretained(this)));
  }

  void WaitForCachePathWritten() {
    if (path_written_)
      return;

    base::RunLoop run_loop;
    run_loop_quit_closure_ = run_loop.QuitClosure();
    run_loop.Run();
  }

  void OnChange() {
    if (!registry_key_.HasValue(third_party_dlls::kBlFilePathRegValue))
      return;

    path_written_ = true;

    if (run_loop_quit_closure_)
      std::move(run_loop_quit_closure_).Run();
  }

 private:
  std::wstring GetRegistryKeyPath() {
    return install_static::GetRegistryPath().append(
        third_party_dlls::kThirdPartyRegKeyName);
  }

  base::win::RegKey registry_key_;

  // Remembers if the path of the cache was written in the registry in case the
  // callback is invoked before WaitForCachePathWritten() was called.
  bool path_written_ = false;

  base::OnceClosure run_loop_quit_closure_;
};

// Creates an empty serialized ModuleList proto in the module list component
// directory and returns its path.
void CreateModuleList(base::FilePath* module_list_path) {
  chrome::conflicts::ModuleList module_list;
  // Include an empty blocklist and allowlist.
  module_list.mutable_blocklist();
  module_list.mutable_allowlist();

  std::string contents;
  ASSERT_TRUE(module_list.SerializeToString(&contents));

  // Put the module list beside the module blocklist cache.
  *module_list_path = ModuleBlocklistCacheUpdater::GetModuleBlocklistCachePath()
                          .DirName()
                          .Append(FILE_PATH_LITERAL("ModuleList.bin"));

  base::ScopedAllowBlockingForTesting scoped_allow_blocking;
  ASSERT_TRUE(base::CreateDirectory(module_list_path->DirName()));
  ASSERT_TRUE(base::WriteFile(*module_list_path, contents));
}

class ThirdPartyBlockingBrowserTest : public InProcessBrowserTest {
 public:
  ThirdPartyBlockingBrowserTest(const ThirdPartyBlockingBrowserTest&) = delete;
  ThirdPartyBlockingBrowserTest& operator=(
      const ThirdPartyBlockingBrowserTest&) = delete;

 protected:
  ThirdPartyBlockingBrowserTest() = default;
  ~ThirdPartyBlockingBrowserTest() override = default;

  // InProcessBrowserTest:
  void SetUp() override {
    scoped_feature_list_.InitAndEnableFeature(
        features::kThirdPartyModulesBlocking);

    ASSERT_TRUE(scoped_temp_dir_.CreateUniqueTempDir());
    ASSERT_NO_FATAL_FAILURE(
        registry_override_manager_.OverrideRegistry(HKEY_CURRENT_USER));

    InProcessBrowserTest::SetUp();
  }

  // Creates a copy of a test DLL into a temp directory that will act as the
  // third-party module and return its path. It can't be located in the output
  // directory because modules in the same directory as chrome.exe are
  // allowlisted in non-official builds.
  void CreateThirdPartyModule(base::FilePath* third_party_module_path) {
    base::FilePath test_dll_path;
    ASSERT_TRUE(base::PathService::Get(base::DIR_EXE, &test_dll_path));
    test_dll_path =
        test_dll_path.Append(FILE_PATH_LITERAL("conflicts_dll.dll"));
    *third_party_module_path = scoped_temp_dir_.GetPath().Append(
        FILE_PATH_LITERAL("third_party_module.dll"));
    base::ScopedAllowBlockingForTesting scoped_allow_blocking;
    ASSERT_TRUE(base::CopyFile(test_dll_path, *third_party_module_path));
  }

  // Enables the ThirdPartyModulesBlocking feature.
  base::test::ScopedFeatureList scoped_feature_list_;

  registry_util::RegistryOverrideManager registry_override_manager_;

  // Temp directory where the third-party module is located.
  base::ScopedTempDir scoped_temp_dir_;
};

}  // namespace

// This is an integration test for the blocking of third-party modules.
//
// This test makes sure that all the different classes interact together
// correctly to produce a valid module blocklist cache and to write its path in
// the registry.
//
// Note: This doesn't test that the modules are actually blocked on the next
//       browser launch.
IN_PROC_BROWSER_TEST_F(ThirdPartyBlockingBrowserTest,
                       CreateModuleBlocklistCache) {
  // Create the observer early so the change is guaranteed to be observed.
  ThirdPartyRegistryKeyObserver third_party_registry_key_observer;
  ASSERT_TRUE(third_party_registry_key_observer.StartWatching());

  base::RunLoop run_loop;
  ModuleDatabase::GetTaskRunner()->PostTask(
      FROM_HERE,
      base::BindLambdaForTesting([quit_closure = run_loop.QuitClosure()]() {
        ModuleDatabase* module_database = ModuleDatabase::GetInstance();

        base::FilePath module_list_path;
        ASSERT_NO_FATAL_FAILURE(CreateModuleList(&module_list_path));
        ASSERT_FALSE(module_list_path.empty());

        // Simulate the download of the module list component.
        module_database->third_party_conflicts_manager()->LoadModuleList(
            module_list_path);

        quit_closure.Run();
      }));
  run_loop.Run();

  // Injects the third-party DLL into the process.
  base::FilePath third_party_module_path;
  ASSERT_NO_FATAL_FAILURE(CreateThirdPartyModule(&third_party_module_path));
  ASSERT_FALSE(third_party_module_path.empty());

  base::ScopedAllowBlockingForTesting scoped_allow_blocking;
  base::ScopedNativeLibrary dll(third_party_module_path);
  ASSERT_TRUE(dll.is_valid());

  // Now the module blocklist cache will eventually be created and its path
  // written in the registry.
  third_party_registry_key_observer.WaitForCachePathWritten();

  base::FilePath module_blocklist_cache_path =
      ModuleBlocklistCacheUpdater::GetModuleBlocklistCachePath();
  ASSERT_FALSE(module_blocklist_cache_path.empty());
  ASSERT_TRUE(base::PathExists(module_blocklist_cache_path));

  // Now check that the third-party DLL was added to the module blocklist cache.
  third_party_dlls::PackedListMetadata metadata;
  std::vector<third_party_dlls::PackedListModule> blocklisted_modules;
  base::MD5Digest md5_digest;
  ASSERT_EQ(ReadResult::kSuccess,
            ReadModuleBlocklistCache(module_blocklist_cache_path, &metadata,
                                     &blocklisted_modules, &md5_digest));

  EXPECT_GE(blocklisted_modules.size(), 1u);
}