Add initial annotation blocklist policy handler

Add initial version of a policy handler to generate a set of network
annotations that are expected be blocklisted. This set is stored into
a newly created per-profile pref, which is consumed by Network
Annotation Monitor.

All functionality is behind a disabled-by-default feature and ChromeOS
build flag. Also updated existing Network Annotation Monitor to be
guarded by ChromeOS build flag for consistency, and disabled the monitor
for multi-profile. This means the monitor is only enabled for ChromeOS
Ash and Lacros single-profile. Multi-profile support can be added later.

Bug: 332725685
Test: out/Default/browser_tests --gtest_filter='All/NetworkAnnotationMonitor*'
Test: out/Default/unit_tests --gtest_filter='NetworkAnnotation*'
Change-Id: I1cb544eb15780e6bcd91f45c11b56fc8158a0703
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/5393462
Reviewed-by: Yann Dago <[email protected]>
Reviewed-by: mmenke <[email protected]>
Reviewed-by: Gabriel Charette <[email protected]>
Commit-Queue: Aiden Chiavatti <[email protected]>
Cr-Commit-Position: refs/heads/main@{#1290821}
diff --git a/chrome/browser/BUILD.gn b/chrome/browser/BUILD.gn
index 8acb4079e..20d68b24 100644
--- a/chrome/browser/BUILD.gn
+++ b/chrome/browser/BUILD.gn
@@ -881,8 +881,6 @@
     "net/net_error_tab_helper.h",
     "net/net_export_helper.cc",
     "net/net_export_helper.h",
-    "net/network_annotation_monitor.cc",
-    "net/network_annotation_monitor.h",
     "net/probe_message.cc",
     "net/probe_message.h",
     "net/profile_network_context_service.cc",
@@ -5879,6 +5877,8 @@
       "memory/oom_kills_monitor.h",
       "metrics/structured/oobe_structured_metrics_watcher.cc",
       "metrics/structured/oobe_structured_metrics_watcher.h",
+      "net/network_annotation_monitor.cc",
+      "net/network_annotation_monitor.h",
       "notifications/passphrase_textfield.cc",
       "notifications/passphrase_textfield.h",
       "obsolete_system/obsolete_system_stub.cc",
@@ -5886,6 +5886,8 @@
       "performance_manager/policies/oom_score_policy_chromeos.h",
       "performance_manager/policies/report_page_processes_policy.cc",
       "performance_manager/policies/report_page_processes_policy.h",
+      "policy/annotations/blocklist_handler.cc",
+      "policy/annotations/blocklist_handler.h",
       "policy/restricted_mgs_policy_provider.cc",
       "policy/restricted_mgs_policy_provider.h",
       "policy/system_features_disable_list_policy_handler.cc",
diff --git a/chrome/browser/net/network_annotation_monitor.cc b/chrome/browser/net/network_annotation_monitor.cc
index fce2f33..2d76b7d 100644
--- a/chrome/browser/net/network_annotation_monitor.cc
+++ b/chrome/browser/net/network_annotation_monitor.cc
@@ -7,17 +7,34 @@
 #include <utility>
 
 #include "base/metrics/histogram_functions.h"
+#include "base/strings/string_number_conversions.h"
+#include "chrome/browser/profiles/profile_manager.h"
+#include "chrome/browser/profiles/profiles_state.h"
+#include "chrome/common/pref_names.h"
+#include "components/prefs/pref_service.h"
 
-NetworkAnnotationMonitor::NetworkAnnotationMonitor() {
-  // Add some example hard-coded annotations, for now. Later this list will be
-  // generated dynamically based on policy values.
-  disabled_annotations_.insert(88863520);  // autofill_query
-}
-
+NetworkAnnotationMonitor::NetworkAnnotationMonitor() = default;
 NetworkAnnotationMonitor::~NetworkAnnotationMonitor() = default;
 
 void NetworkAnnotationMonitor::Report(int32_t hash_code) {
-  if (disabled_annotations_.contains(hash_code)) {
+  // Multi-profile is not currently supported, so only run on ChromeOS for now.
+  static_assert(BUILDFLAG(IS_CHROMEOS));
+
+#if BUILDFLAG(IS_CHROMEOS_LACROS)
+  // Lacros allows multi-profile if enabled by policy, so skip reporting in this
+  // case. In the future we could consider using ProfileNetworkContext for this.
+  if (profiles::AreSecondaryProfilesAllowed()) {
+    return;
+  }
+#endif  // BUILDFLAG(IS_CHROMEOS_LACROS)
+
+  // Get blocklist prefs from the current active profile, which on ChromeOS
+  // should be the only profile based on the above check.
+  const base::Value::Dict& blocklist =
+      ProfileManager::GetActiveUserProfile()->GetPrefs()->GetDict(
+          prefs::kNetworkAnnotationBlocklist);
+
+  if (blocklist.contains(base::NumberToString(hash_code))) {
     base::UmaHistogramSparse("NetworkAnnotationMonitor.PolicyViolation",
                              hash_code);
   }
@@ -36,11 +53,6 @@
   return client;
 }
 
-void NetworkAnnotationMonitor::SetDisabledAnnotationsForTesting(
-    base::flat_set<int32_t> disabled_annotations) {
-  disabled_annotations_ = std::move(disabled_annotations);
-}
-
 void NetworkAnnotationMonitor::FlushForTesting() {
   receiver_.FlushForTesting();  // IN-TEST
 }
diff --git a/chrome/browser/net/network_annotation_monitor.h b/chrome/browser/net/network_annotation_monitor.h
index cf5852a..a3dfed10 100644
--- a/chrome/browser/net/network_annotation_monitor.h
+++ b/chrome/browser/net/network_annotation_monitor.h
@@ -12,9 +12,10 @@
 
 // NetworkAnnotationMonitor monitors network calls reported via the `Report`
 // method. Network calls are identified by their Network Annotation hash_code.
-// It also maintains a set of network annotations that are expected to be
-// disabled. When a network annotation that matches an expected disabled
-// annotation is reported, then an UMA metric is emitted for that hash_code.
+// It reads from profile prefs containing a set of network annotations that
+// are expected to be disabled based on policy values. When a network annotation
+// that matches an expected disabled annotation is reported, then an UMA metric
+// is emitted for that hash_code.
 class NetworkAnnotationMonitor
     : public network::mojom::NetworkAnnotationMonitor {
  public:
@@ -25,16 +26,12 @@
 
   mojo::PendingRemote<network::mojom::NetworkAnnotationMonitor> GetClient();
 
-  void SetDisabledAnnotationsForTesting(
-      base::flat_set<int32_t> disabled_annotations);
-
   void FlushForTesting();
 
  private:
   void Report(int32_t hash_code) override;
 
   mojo::Receiver<network::mojom::NetworkAnnotationMonitor> receiver_{this};
-  base::flat_set<int32_t> disabled_annotations_;
 };
 
 #endif  // CHROME_BROWSER_NET_NETWORK_ANNOTATION_MONITOR_H_
diff --git a/chrome/browser/net/network_annotation_monitor_browsertest.cc b/chrome/browser/net/network_annotation_monitor_browsertest.cc
index ee3759f..4ce4a0d5 100644
--- a/chrome/browser/net/network_annotation_monitor_browsertest.cc
+++ b/chrome/browser/net/network_annotation_monitor_browsertest.cc
@@ -8,9 +8,11 @@
 #include "chrome/browser/browser_features.h"
 #include "chrome/browser/browser_process.h"
 #include "chrome/browser/net/system_network_context_manager.h"
+#include "chrome/browser/policy/policy_test_utils.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/ui/browser.h"
 #include "chrome/test/base/in_process_browser_test.h"
+#include "components/policy/policy_constants.h"
 #include "content/public/browser/network_service_instance.h"
 #include "content/public/browser/storage_partition.h"
 #include "content/public/test/browser_test.h"
@@ -26,15 +28,15 @@
   kDisabled,
 };
 
-// Network annotation that will trigger a violation. Note: This annotation is
-// currently hard-coded as a "disabled" annotation in the feature itself. In the
-// future, this test should set specific policy values to disable this
-// annotation.
+// Network annotation that can trigger a violation.
 constexpr net::NetworkTrafficAnnotationTag kTestDisabledAnnotation =
     net::DefineNetworkTrafficAnnotation("autofill_query", "");
+// Policy that controls the above annotation. When set to false, any occurrences
+// of the above annotation should trigger a violation.
+constexpr char kTestPolicy[] = "PasswordManagerEnabled";
 
 class NetworkAnnotationMonitorBrowserTest
-    : public InProcessBrowserTest,
+    : public policy::PolicyTest,
       public ::testing::WithParamInterface<TestCase> {
  public:
   NetworkAnnotationMonitorBrowserTest() {
@@ -67,6 +69,14 @@
 IN_PROC_BROWSER_TEST_P(NetworkAnnotationMonitorBrowserTest, FeatureTest) {
   base::HistogramTester histogram_tester;
 
+  policy::PolicyMap policies;
+  SetPolicy(&policies, kTestPolicy, base::Value(false));
+  // Disable secondary profiles policy since we skip reporting on lacros when
+  // this is enabled.
+  SetPolicy(&policies, policy::key::kLacrosSecondaryProfilesAllowed,
+            base::Value(false));
+  provider_.UpdateChromePolicy(policies);
+
   // Send network request with arbitrary network annotation that will never
   // trigger a violation.
   SendFakeNetworkRequest(TRAFFIC_ANNOTATION_FOR_TESTS);
diff --git a/chrome/browser/net/network_annotation_monitor_unittest.cc b/chrome/browser/net/network_annotation_monitor_unittest.cc
index b993ceb..a715fdfb 100644
--- a/chrome/browser/net/network_annotation_monitor_unittest.cc
+++ b/chrome/browser/net/network_annotation_monitor_unittest.cc
@@ -4,19 +4,40 @@
 
 #include "chrome/browser/net/network_annotation_monitor.h"
 
+#include "base/strings/string_number_conversions.h"
 #include "base/test/metrics/histogram_tester.h"
 #include "base/test/task_environment.h"
+#include "chrome/browser/prefs/browser_prefs.h"
+#include "chrome/browser/profiles/profile_manager.h"
+#include "chrome/common/pref_names.h"
+#include "chrome/test/base/testing_browser_process.h"
+#include "chrome/test/base/testing_profile_manager.h"
+#include "components/prefs/pref_registry_simple.h"
+#include "content/public/test/browser_task_environment.h"
 #include "mojo/public/cpp/bindings/remote.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
 TEST(NetworkAnnotationMonitorTest, ReportTest) {
   constexpr int32_t kTestDisabledHashCode = 123;
   constexpr int32_t kTestAllowedHashCode = 456;
-  base::test::SingleThreadTaskEnvironment task_environment;
+  content::BrowserTaskEnvironment task_environment;
   base::HistogramTester histogram_tester;
 
+  // Setup profile with the disabled hash code in blocklist pref.
+  TestingProfileManager profile_manager_(TestingBrowserProcess::GetGlobal());
+  ASSERT_TRUE(profile_manager_.SetUp());
+  profile_manager_.CreateTestingProfile("testing_profile", true);
+  ProfileManager::GetActiveUserProfile()->GetPrefs()->SetDict(
+      prefs::kNetworkAnnotationBlocklist,
+      base::Value::Dict().Set(base::NumberToString(kTestDisabledHashCode),
+                              true));
+
+  // Disable secondary profiles pref since we skip reporting on lacros when this
+  // is enabled.
+  profile_manager_.local_state()->Get()->SetBoolean(
+      prefs::kLacrosSecondaryProfilesAllowed, false);
+
   NetworkAnnotationMonitor monitor;
-  monitor.SetDisabledAnnotationsForTesting({kTestDisabledHashCode});
   mojo::Remote<network::mojom::NetworkAnnotationMonitor> remote;
   remote.Bind(monitor.GetClient());
 
diff --git a/chrome/browser/net/system_network_context_manager.cc b/chrome/browser/net/system_network_context_manager.cc
index c5dd9b29..a5989da 100644
--- a/chrome/browser/net/system_network_context_manager.cc
+++ b/chrome/browser/net/system_network_context_manager.cc
@@ -29,7 +29,6 @@
 #include "chrome/browser/component_updater/pki_metadata_component_installer.h"
 #include "chrome/browser/net/chrome_mojo_proxy_resolver_factory.h"
 #include "chrome/browser/net/convert_explicitly_allowed_network_ports_pref.h"
-#include "chrome/browser/net/network_annotation_monitor.h"
 #include "chrome/browser/safe_browsing/safe_browsing_service.h"
 #include "chrome/browser/ssl/sct_reporting_service.h"
 #include "chrome/browser/ssl/ssl_config_service_manager.h"
@@ -86,6 +85,10 @@
 #include "third_party/blink/public/common/features.h"
 #include "url/gurl.h"
 
+#if BUILDFLAG(IS_CHROMEOS)
+#include "chrome/browser/net/network_annotation_monitor.h"
+#endif  // BUILDFLAG(IS_CHROMEOS)
+
 #if BUILDFLAG(IS_CHROMEOS_ASH)
 #include "chrome/browser/ash/net/dhcp_wpad_url_client.h"
 #include "chrome/browser/ash/policy/core/browser_policy_connector_ash.h"
@@ -817,6 +820,7 @@
 
   UpdateIPv6ReachabilityOverrideEnabled();
 
+#if BUILDFLAG(IS_CHROMEOS)
   if (base::FeatureList::IsEnabled(features::kNetworkAnnotationMonitoring)) {
     // Create NetworkAnnotationMonitor.
     if (!network_annotation_monitor_) {
@@ -829,6 +833,7 @@
     network_service->SetNetworkAnnotationMonitor(
         network_annotation_monitor_->GetClient());
   }
+#endif  // BUILDFLAG(IS_CHROMEOS)
 }
 
 void SystemNetworkContextManager::DisableQuic() {
@@ -994,11 +999,13 @@
     url_loader_factory_.FlushForTesting();
 }
 
+#if BUILDFLAG(IS_CHROMEOS)
 void SystemNetworkContextManager::FlushNetworkAnnotationMonitorForTesting() {
   if (network_annotation_monitor_) {
     network_annotation_monitor_->FlushForTesting();  // IN-TEST
   }
 }
+#endif  // BUILDFLAG(IS_CHROMEOS)
 
 network::mojom::HttpAuthStaticParamsPtr
 SystemNetworkContextManager::GetHttpAuthStaticParamsForTesting() {
diff --git a/chrome/browser/net/system_network_context_manager.h b/chrome/browser/net/system_network_context_manager.h
index 426fe63..38dfcb6 100644
--- a/chrome/browser/net/system_network_context_manager.h
+++ b/chrome/browser/net/system_network_context_manager.h
@@ -167,8 +167,10 @@
   // use only.
   void FlushNetworkInterfaceForTesting();
 
+#if BUILDFLAG(IS_CHROMEOS)
   // Call |FlushForTesting()| on NetworkAnnotationMonitor. For test use only.
   void FlushNetworkAnnotationMonitorForTesting();
+#endif  // BUILDFLAG(IS_CHROMEOS)
 
   static network::mojom::HttpAuthStaticParamsPtr
   GetHttpAuthStaticParamsForTesting();
@@ -284,7 +286,9 @@
 
   static std::optional<bool> certificate_transparency_enabled_for_testing_;
 
+#if BUILDFLAG(IS_CHROMEOS)
   std::unique_ptr<NetworkAnnotationMonitor> network_annotation_monitor_;
+#endif  // BUILDFLAG(IS_CHROMEOS)
 
 #if BUILDFLAG(IS_LINUX)
   GssapiLibraryLoadObserver gssapi_library_loader_observer_{this};
diff --git a/chrome/browser/policy/annotations/blocklist_handler.cc b/chrome/browser/policy/annotations/blocklist_handler.cc
new file mode 100644
index 0000000..a8ef6af
--- /dev/null
+++ b/chrome/browser/policy/annotations/blocklist_handler.cc
@@ -0,0 +1,65 @@
+// Copyright 2024 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/policy/annotations/blocklist_handler.h"
+
+#include <string>
+
+#include "base/values.h"
+#include "chrome/browser/browser_features.h"
+#include "chrome/common/pref_names.h"
+
+namespace policy {
+
+NetworkAnnotationBlocklistHandler::NetworkAnnotationBlocklistHandler() =
+    default;
+NetworkAnnotationBlocklistHandler::~NetworkAnnotationBlocklistHandler() =
+    default;
+
+// Only check policies if `kNetworkAnnotationMonitoring` is enabled.
+// ApplyPolicySettings(...) is only called when this function returns true.
+bool NetworkAnnotationBlocklistHandler::CheckPolicySettings(
+    const policy::PolicyMap& policies,
+    policy::PolicyErrorMap* errors) {
+  if (base::FeatureList::IsEnabled(features::kNetworkAnnotationMonitoring)) {
+    return true;
+  }
+  return false;
+}
+
+// Check policy values to determine which network annotations should be
+// disabled.
+// TODO(b/330181218): We hardcoded one annotation initially. Add logic to check
+// all annotations we are interested in.
+void NetworkAnnotationBlocklistHandler::ApplyPolicySettings(
+    const PolicyMap& policies,
+    PrefValueMap* prefs) {
+  const std::string kAutofillQueryHashCode = "88863520";  // autofill_query
+  const std::string kAutofillQueryPolicy = "PasswordManagerEnabled";
+
+  base::Value::Dict blocklist_prefs = base::Value::Dict();
+  if (IsPolicyDisabled(policies, kAutofillQueryPolicy)) {
+    blocklist_prefs.Set(kAutofillQueryHashCode, true);
+  }
+  prefs->SetValue(prefs::kNetworkAnnotationBlocklist,
+                  base::Value(std::move(blocklist_prefs)));
+}
+
+void NetworkAnnotationBlocklistHandler::RegisterPrefs(
+    PrefRegistrySimple* registry) {
+  registry->RegisterDictionaryPref(prefs::kNetworkAnnotationBlocklist);
+}
+
+bool NetworkAnnotationBlocklistHandler::IsPolicyDisabled(
+    const PolicyMap& policies,
+    std::string policy_name) {
+  const base::Value* current_policy_value =
+      policies.GetValue(policy_name, base::Value::Type::BOOLEAN);
+  if (current_policy_value != nullptr && !current_policy_value->GetBool()) {
+    return true;
+  }
+  return false;
+}
+
+}  // namespace policy
diff --git a/chrome/browser/policy/annotations/blocklist_handler.h b/chrome/browser/policy/annotations/blocklist_handler.h
new file mode 100644
index 0000000..9c53a5f
--- /dev/null
+++ b/chrome/browser/policy/annotations/blocklist_handler.h
@@ -0,0 +1,45 @@
+// Copyright 2024 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_BROWSER_POLICY_ANNOTATIONS_BLOCKLIST_HANDLER_H_
+#define CHROME_BROWSER_POLICY_ANNOTATIONS_BLOCKLIST_HANDLER_H_
+
+#include "chrome/common/pref_names.h"
+#include "components/policy/core/browser/configuration_policy_handler.h"
+#include "components/prefs/pref_registry_simple.h"
+
+namespace policy {
+
+// Policy handler that determines which network annotations should be disabled
+// based on current policy values. When all policies for a specific network
+// annotation are set to disabled, then the annotation id (hash code) is added
+// to the `kNetworkAnnotationBlocklist` pref.
+//
+// Note: The `kNetworkAnnotationBlocklist` pref is currently only used for
+// monitoring/reporting purposes to determine potential incorrect policy to
+// annotation mappings. No network requests are blocked at this time.
+class NetworkAnnotationBlocklistHandler : public ConfigurationPolicyHandler {
+ public:
+  NetworkAnnotationBlocklistHandler();
+  NetworkAnnotationBlocklistHandler(const NetworkAnnotationBlocklistHandler&) =
+      delete;
+  NetworkAnnotationBlocklistHandler& operator=(
+      const NetworkAnnotationBlocklistHandler&) = delete;
+  ~NetworkAnnotationBlocklistHandler() override;
+
+  // ConfigurationPolicyHandler methods:
+  bool CheckPolicySettings(const policy::PolicyMap& policies,
+                           policy::PolicyErrorMap* errors) override;
+  void ApplyPolicySettings(const PolicyMap& policies,
+                           PrefValueMap* prefs) override;
+
+  static void RegisterPrefs(PrefRegistrySimple* registry);
+
+ private:
+  bool IsPolicyDisabled(const PolicyMap& policies, std::string policy_name);
+};
+
+}  // namespace policy
+
+#endif  // CHROME_BROWSER_POLICY_ANNOTATIONS_BLOCKLIST_HANDLER_H_
diff --git a/chrome/browser/policy/annotations/blocklist_handler_unittest.cc b/chrome/browser/policy/annotations/blocklist_handler_unittest.cc
new file mode 100644
index 0000000..3bff7e0
--- /dev/null
+++ b/chrome/browser/policy/annotations/blocklist_handler_unittest.cc
@@ -0,0 +1,84 @@
+// Copyright 2024 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/policy/annotations/blocklist_handler.h"
+
+#include "base/test/scoped_feature_list.h"
+#include "chrome/browser/browser_features.h"
+#include "chrome/common/pref_names.h"
+#include "components/policy/core/common/policy_map.h"
+#include "components/policy/core/common/policy_types.h"
+#include "components/prefs/testing_pref_service.h"
+#include "testing/gmock/include/gmock/gmock-matchers.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+using testing::Eq;
+
+namespace policy {
+
+const char kAutofillQueryPolicy[] = "PasswordManagerEnabled";
+
+class NetworkAnnotationBlocklistHandlerTest : public testing::Test {
+ protected:
+  base::Value::Dict blocklist_prefs() {
+    return prefs_.AsDict()
+        .FindDict(prefs::kNetworkAnnotationBlocklist)
+        ->Clone();
+  }
+
+  // Helper function to create a simple boolean policy with given value.
+  PolicyMap::Entry BooleanPolicy(bool value) {
+    PolicyMap::Entry policy;
+    policy.set_value(base::Value(value));
+    return policy;
+  }
+
+  NetworkAnnotationBlocklistHandler handler_;
+  PrefValueMap prefs_;
+  PolicyMap policies_;
+};
+
+TEST_F(NetworkAnnotationBlocklistHandlerTest, DisabledByDefault) {
+  EXPECT_FALSE(handler_.CheckPolicySettings(PolicyMap(), nullptr));
+}
+
+TEST_F(NetworkAnnotationBlocklistHandlerTest, EnabledWithFeature) {
+  base::test::ScopedFeatureList feature_list;
+  feature_list.InitAndEnableFeature(features::kNetworkAnnotationMonitoring);
+
+  EXPECT_TRUE(handler_.CheckPolicySettings(PolicyMap(), nullptr));
+}
+
+TEST_F(NetworkAnnotationBlocklistHandlerTest, ApplyPolicySettings) {
+  // Unrelated policy should not set pref.
+  policies_.Set("FooPolicy", BooleanPolicy(false));
+  handler_.ApplyPolicySettings(policies_, &prefs_);
+  EXPECT_THAT(blocklist_prefs().size(), Eq(0));
+
+  // Policy should not set pref if enabled.
+  policies_.Set(kAutofillQueryPolicy, BooleanPolicy(true));
+  handler_.ApplyPolicySettings(policies_, &prefs_);
+  EXPECT_THAT(blocklist_prefs().size(), Eq(0));
+
+  // Policy should set pref if disabled.
+  policies_.Set(kAutofillQueryPolicy, BooleanPolicy(false));
+  handler_.ApplyPolicySettings(policies_, &prefs_);
+  EXPECT_THAT(blocklist_prefs().size(), Eq(1));
+
+  // Policy should not set pref if policy value unset. Also verifies the pref
+  // can be unset.
+  policies_.Set(kAutofillQueryPolicy, PolicyMap::Entry());
+  handler_.ApplyPolicySettings(policies_, &prefs_);
+  EXPECT_THAT(blocklist_prefs().size(), Eq(0));
+}
+
+TEST_F(NetworkAnnotationBlocklistHandlerTest, RegisterPref) {
+  TestingPrefServiceSimple pref_service_;
+  EXPECT_FALSE(
+      pref_service_.FindPreference(prefs::kNetworkAnnotationBlocklist));
+  handler_.RegisterPrefs(pref_service_.registry());
+  EXPECT_TRUE(pref_service_.FindPreference(prefs::kNetworkAnnotationBlocklist));
+}
+
+}  // namespace policy
diff --git a/chrome/browser/policy/configuration_policy_handler_list_factory.cc b/chrome/browser/policy/configuration_policy_handler_list_factory.cc
index 0231d07..fe8055c 100644
--- a/chrome/browser/policy/configuration_policy_handler_list_factory.cc
+++ b/chrome/browser/policy/configuration_policy_handler_list_factory.cc
@@ -31,6 +31,7 @@
 #include "chrome/browser/net/explicitly_allowed_network_ports_policy_handler.h"
 #include "chrome/browser/net/secure_dns_policy_handler.h"
 #include "chrome/browser/performance_manager/public/user_tuning/memory_saver_policy_handler.h"
+#include "chrome/browser/policy/annotations/blocklist_handler.h"
 #include "chrome/browser/policy/browsing_history_policy_handler.h"
 #include "chrome/browser/policy/developer_tools_policy_handler.h"
 #include "chrome/browser/policy/drive_file_sync_available_policy_handler.h"
@@ -2639,6 +2640,7 @@
       SCHEMA_ALLOW_UNKNOWN_AND_INVALID_LIST_ENTRY,
       SimpleSchemaValidatingPolicyHandler::RECOMMENDED_PROHIBITED,
       SimpleSchemaValidatingPolicyHandler::MANDATORY_ALLOWED));
+  handlers->AddHandler(std::make_unique<NetworkAnnotationBlocklistHandler>());
 #if BUILDFLAG(USE_CUPS)
   handlers->AddHandler(std::make_unique<extensions::ExtensionListPolicyHandler>(
       key::kPrintingAPIExtensionsAllowlist,
diff --git a/chrome/browser/prefs/browser_prefs.cc b/chrome/browser/prefs/browser_prefs.cc
index 74f968f8..4d3d0a7d2 100644
--- a/chrome/browser/prefs/browser_prefs.cc
+++ b/chrome/browser/prefs/browser_prefs.cc
@@ -319,6 +319,7 @@
 #include "chrome/browser/extensions/api/document_scan/document_scan_api_handler.h"
 #include "chrome/browser/extensions/api/enterprise_platform_keys/enterprise_platform_keys_api.h"
 #include "chrome/browser/memory/oom_kills_monitor.h"
+#include "chrome/browser/policy/annotations/blocklist_handler.h"
 #include "chrome/browser/policy/networking/policy_cert_service.h"
 #include "chrome/browser/policy/system_features_disable_list_policy_handler.h"
 #include "chrome/browser/ui/webui/certificates_handler.h"
@@ -2023,6 +2024,7 @@
   certificate_manager::CertificatesHandler::RegisterProfilePrefs(registry);
   chromeos::cloud_storage::RegisterProfilePrefs(registry);
   chromeos::cloud_upload::RegisterProfilePrefs(registry);
+  policy::NetworkAnnotationBlocklistHandler::RegisterPrefs(registry);
   policy::PolicyCertService::RegisterProfilePrefs(registry);
   quickoffice::RegisterProfilePrefs(registry);
   registry->RegisterBooleanPref(prefs::kDeskAPIThirdPartyAccessEnabled, false);