Low watch CVSS 8.3 chrome Logic Error 🔧 Commit mapped

Overview

Low
Severity
8.3
CVSS
No
Exploited ITW
Fixed
Fix Status
ImpactIncorrect reference resolution in WebProtect
DescriptionIncorrect reference resolution in WebProtect
ComponentWebProtect
Bug ClassLogic Error
Action tierwatch — logic/policy bug — security-relevant, no memory-unsafety
Computed by PatchGap's deterministic exploitability ruler from the fixing commit. patch-now = RCE-class & attacker-reachable in a default config, or exploited in the wild; watch = real security signal below that bar; noise = no security relevance.
Tracker513403696
Fix commit4dfeb8af5a6b (chromium/src) +196/-17
CWECWE-706
CVSS vectorCVSS:3.1/AV:N/AC:H/PR:N/UI:R/S:C/C:H/I:H/A:H
CISA KEVNot listed
CreditedGoogle
Reported2026-05-15 (per vendor advisory)
Fix landed2026-08-27 (public git)
Disclosed2026-09-22
Patch gap26 days — fix public in source before the advisory

Background

`enterprise.reportingPrivate` API
a restricted extension API used by enterprise device-signals reporting that resolves and inspects file paths supplied by the caller.
UNC/network path
a path of the form \\server\share that references a remote host; detected in Chromium by base::FilePath::IsNetwork().
NT-object / device namespace path
a Windows path prefixed with \??\ or \Device\ (or their / variants) that reaches kernel object names and devices, bypassing normal Win32 path semantics.
Mojo `StructTraits`
deserialization traits that reconstruct a typed struct (here containing a base::FilePath) from IPC bytes crossing a process boundary.

Root Cause Analysis

The enterprise.reportingPrivate file-inspection path only rejected UNC/network paths via a single file_path.IsNetwork() check in enterprise_reporting_private_api.cc, and the shared path-resolution helpers in platform_utils_posix.cc and platform_utils_win.cc applied no equivalent guard at all. The intended invariant — that a caller-supplied path must be a strictly local, ordinary filesystem path — was therefore violated, because NT-object and device-namespace paths such as \??\... and \Device\... are not classified as network paths and slipped past the check to be resolved and acted upon.

The fix introduces IsSupportedLocalPath(), which rejects empty paths, UNC paths (leading double separator), and any path beginning with the \??\, /??/, \Device\, or /Device/ prefixes, and calls it at every entry point: the API handler, the Mojo StructTraits deserializer, and the POSIX/Windows ResolvePath implementations. This works because the same allow-list-style validation now gates every code path that turns an untrusted string into a resolved file reference, rather than relying on the narrow, easily-bypassed network-only test.

Key insight
The single mistake was treating “not a network path” as equivalent to “a safe local path,” which left the NT-object/device namespace (\??\, \Device\) unguarded; the fix replaces the narrow IsNetwork() check with a centralized IsSupportedLocalPath() allow-list enforced at every path-resolution entry point, including the cross-process Mojo boundary.

Attack Path

  1. Craft a namespace path An attacker who can drive the enterprise.reportingPrivate API supplies a path like \??\... or \Device\... that is neither empty nor a UNC path, so the original IsNetwork() check does not reject it.
  2. Bypass the network-only filter The path passes the sole file_path.IsNetwork() guard in enterprise_reporting_private_api.cc and is handed to path resolution.
  3. Resolve an unintended reference ResolvePath() in the POSIX/Windows platform utils resolves the NT-object/device path to a reference the caller should not be able to name, and the value can also cross the Mojo boundary through unguarded StructTraits deserialization.
  4. Read signals about an unintended target (inferred) The resolved reference is inspected by the device-signals machinery, allowing the caller to obtain information about, or interact with, a device/object path outside the intended local-filesystem scope. (inferred)

Impact Assessment

An attacker able to reach the enterprise.reportingPrivate API can cause the browser to resolve NT-object and device-namespace paths that were intended to be out of scope, achieving incorrect reference resolution in the WebProtect/device-signals reporting flow. The bug is a logic error reachable in the browser process where path resolution occurs, with the precondition that the caller can invoke the enterprise reporting API with a controlled path (typically an enterprise-provisioned extension context). Any escalation to a broader information-disclosure or object-access primitive beyond the incorrect resolution demonstrated by the patch is (inferred); the advisory states the impact only as “Incorrect reference resolution in WebProtect” and does not indicate exploitation in the wild.

Changed Functions

FunctionChangeNotes
if
components/device_signals/core/common/win/platform_utils_win.cc
modified
for
chrome/browser/extensions/api/enterprise_reporting_private/enterprise_reporting_private_unittest.cc
modified
TEST_F
chrome/browser/extensions/api/enterprise_reporting_private/enterprise_reporting_private_unittest.cc
modified
TEST_F
components/device_signals/core/common/platform_utils_unittest.cc
modified
TEST_F
components/device_signals/core/system_signals/posix/posix_platform_delegate_unittest.cc
modified
for
components/device_signals/core/system_signals/posix/posix_platform_delegate_unittest.cc
modified
TEST_F
components/device_signals/core/system_signals/win/win_platform_delegate_unittest.cc
modified
for
components/device_signals/core/system_signals/win/win_platform_delegate_unittest.cc
modified
if
components/device_signals/core/common/BUILD.gn
modified

Files Changed

  • chrome/browser/extensions/api/enterprise_reporting_private/enterprise_reporting_private_unittest.cc
  • chrome/browser/extensions/api/enterprise_reporting_private/enterprise_reporting_private_api.cc
  • components/device_signals/core/common/posix/platform_utils_posix.cc
  • components/device_signals/core/common/platform_utils.h
  • components/device_signals/core/common/platform_utils_unittest.cc
  • components/device_signals/core/common/platform_utils.cc
  • components/device_signals/core/system_signals/posix/posix_platform_delegate_unittest.cc
  • components/device_signals/core/common/BUILD.gn
  • components/device_signals/core/common/mojom/system_signals_mojom_traits.cc
  • components/device_signals/core/common/win/platform_utils_win.cc
  • components/device_signals/core/system_signals/win/win_platform_delegate_unittest.cc

Audit Directions

  • Network-only path filters
    Search for code that treats IsNetwork() (or any single “is remote” test) as sufficient validation of a caller-supplied path and audit whether NT-object (\??\), device (\Device\), or UNC variants can slip through.
  • Unvalidated IPC path deserialization
    Review Mojo StructTraits and other IPC deserializers that carry base::FilePath fields to confirm each re-validates the path rather than trusting the sending process.
  • Divergent validation across entry points
    Look for path-resolution helpers (e.g. ResolvePath variants) reached by multiple callers where validation lives only at one caller, and centralize the check so every entry point enforces the same local-path invariant.
From 4dfeb8af5a6b2302a63d401e2df2cf5dc0ac4ef7 Mon Sep 17 00:00:00 2001
From: hamda mare <hmare@google.com>
Date: Wed, 26 Aug 2026 19:51:41 -0700
Subject: [PATCH] Reject network and NT-object paths in enterprise.reportingPrivate API

Bug: 513403696
Change-Id: I238a576ecd23abe162c5f5767ffd0481b2fb750d
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/8268850
Reviewed-by: Dominic Farolino <dom@chromium.org>
Reviewed-by: Ari Chivukula <arichiv@chromium.org>
Reviewed-by: Sebastien Lalancette <seblalancette@chromium.org>
Commit-Queue: Hamda Mare <hmare@google.com>
Cr-Commit-Position: refs/heads/main@{#1686903}
---
diff --git a/chrome/browser/extensions/api/enterprise_reporting_private/enterprise_reporting_private_api.cc b/chrome/browser/extensions/api/enterprise_reporting_private/enterprise_reporting_private_api.cc
index 6ccf614e..718adf6 100644
--- a/chrome/browser/extensions/api/enterprise_reporting_private/enterprise_reporting_private_api.cc
+++ b/chrome/browser/extensions/api/enterprise_reporting_private/enterprise_reporting_private_api.cc
@@ -39,6 +39,7 @@
 #include "components/device_signals/core/browser/signals_aggregator.h"
 #include "components/device_signals/core/browser/signals_types.h"
 #include "components/device_signals/core/browser/user_context.h"
+#include "components/device_signals/core/common/platform_utils.h"
 #include "components/device_signals/core/common/signals_features.h"  // nogncheck
 #endif  // BUILDFLAG(IS_WIN) || BUILDFLAG(IS_MAC) || BUILDFLAG(IS_LINUX)
 
@@ -675,7 +676,7 @@
     EXTENSION_FUNCTION_VALIDATE(base::IsStringUTF8(api_options_param.path));
     base::FilePath file_path =
         base::FilePath::FromUTF8Unsafe(api_options_param.path);
-    if (file_path.IsNetwork()) {
+    if (!device_signals::IsSupportedLocalPath(file_path)) {
       return RespondNow(Error("Network paths are not supported."));
     }
   }
diff --git a/components/device_signals/core/common/mojom/system_signals_mojom_traits.cc b/components/device_signals/core/common/mojom/system_signals_mojom_traits.cc
index caa4664..31ee87d8 100644
--- a/components/device_signals/core/common/mojom/system_signals_mojom_traits.cc
+++ b/components/device_signals/core/common/mojom/system_signals_mojom_traits.cc
@@ -5,6 +5,7 @@
 #include "components/device_signals/core/common/mojom/system_signals_mojom_traits.h"
 
 #include "base/notreached.h"
+#include "components/device_signals/core/common/platform_utils.h"
 #include "mojo/public/cpp/base/byte_string_mojom_traits.h"
 #include "mojo/public/cpp/base/file_path_mojom_traits.h"
 
@@ -90,6 +91,10 @@
     return false;
   }
 
+  if (!device_signals::IsSupportedLocalPath(output->file_path)) {
+    return false;
+  }
+
   return true;
 }
 
diff --git a/components/device_signals/core/common/platform_utils.cc b/components/device_signals/core/common/platform_utils.cc
index 491e80e..49f425ad 100644
--- a/components/device_signals/core/common/platform_utils.cc
+++ b/components/device_signals/core/common/platform_utils.cc
@@ -9,11 +9,41 @@
 #include <string>
 #include <vector>
 
+#include "base/files/file_path.h"
 #include "base/strings/string_util.h"
+#include "build/build_config.h"
 #include "components/policy/core/common/cloud/cloud_policy_util.h"
 
 namespace device_signals {
 
+bool IsSupportedLocalPath(const base::FilePath& file_path) {
+  if (file_path.empty() || file_path.IsNetwork()) {
+    return false;
+  }
+
+  const auto& path_str = file_path.value();
+  if (path_str.length() >= 2 &&
+      (base::FilePath::IsSeparator(path_str[0]) ||
+       path_str[0] == FILE_PATH_LITERAL('\\')) &&
+      (base::FilePath::IsSeparator(path_str[1]) ||
+       path_str[1] == FILE_PATH_LITERAL('\\'))) {
+    return false;
+  }
+
+  if (base::StartsWith(path_str, FILE_PATH_LITERAL("\\??\\"),
+                       base::CompareCase::INSENSITIVE_ASCII) ||
+      base::StartsWith(path_str, FILE_PATH_LITERAL("/??/"),
+                       base::CompareCase::INSENSITIVE_ASCII) ||
+      base::StartsWith(path_str, FILE_PATH_LITERAL("\\Device\\"),
+                       base::CompareCase::INSENSITIVE_ASCII) ||
+      base::StartsWith(path_str, FILE_PATH_LITERAL("/Device/"),
+                       base::CompareCase::INSENSITIVE_ASCII)) {
+    return false;
+  }
+
+  return true;
+}
+
 namespace {
 
 void NormalizeMacAddresses(std::vector<std::string>& mac_addresses) {
@@ -47,9 +77,10 @@
   if (test_addresses.has_value()) {
     mac_addresses = test_addresses.value();
   } else {
-#if !BUILDFLAG(IS_ANDROID)
+#if BUILDFLAG(IS_WIN) || BUILDFLAG(IS_MAC) || BUILDFLAG(IS_LINUX) || \
+    BUILDFLAG(IS_CHROMEOS)
     mac_addresses = internal::GetMacAddressesImpl();
-#endif // !BUILDFLAG(IS_ANDROID)
+#endif
   }
   NormalizeMacAddresses(mac_addresses);
   return mac_addresses;
diff --git a/components/device_signals/core/common/platform_utils.h b/components/device_signals/core/common/platform_utils.h
index b496c1f..31b0520a 100644
--- a/components/device_signals/core/common/platform_utils.h
+++ b/components/device_signals/core/common/platform_utils.h
@@ -21,6 +21,11 @@
 
 struct CrowdStrikeSignals;
 
+// Returns true if `file_path` is a non-empty, strictly local file path (i.e.
+// not a UNC/network path and not an NT-object or device namespace path like
+// \??\... or \Device\...).
+bool IsSupportedLocalPath(const base::FilePath& file_path);
+
 // Extracts the common details for resolving a file path on different
 // platforms. Resolves environment variables and relative markers in
 // `file_path`, and returns the path via `resolved_file_path`. For
diff --git a/components/device_signals/core/common/posix/platform_utils_posix.cc b/components/device_signals/core/common/posix/platform_utils_posix.cc
index a4de44c2..2814273c 100644
--- a/components/device_signals/core/common/posix/platform_utils_posix.cc
+++ b/components/device_signals/core/common/posix/platform_utils_posix.cc
@@ -8,6 +8,7 @@
 #include "base/environment.h"
 #include "base/files/file_path.h"
 #include "base/files/file_util.h"
+#include "base/strings/string_util.h"
 #include "base/strings/stringprintf.h"
 #include "components/device_signals/core/common/common_types.h"
 
@@ -45,6 +46,10 @@
 
 bool ResolvePath(const base::FilePath& file_path,
                  base::FilePath* resolved_file_path) {
+  if (!IsSupportedLocalPath(file_path)) {
+    return false;
+  }
+
   auto environment = base::Environment::Create();
   // Expand the first component of the path if it is either a tilde or an
   // environment variable.
@@ -88,12 +93,15 @@
     }
 
     expanded_file_path = CreatePathFromComponents(path_components);
+    if (!IsSupportedLocalPath(expanded_file_path)) {
+      return false;
+    }
   }
 
   // Resolve any relative path traversals that may exist (e.g. "..");
   base::FilePath local_resolved_file_path =
       base::MakeAbsoluteFilePath(expanded_file_path);
-  if (local_resolved_file_path.empty()) {
+  if (!IsSupportedLocalPath(local_resolved_file_path)) {
     return false;
   }
 
diff --git a/components/device_signals/core/common/win/platform_utils_win.cc b/components/device_signals/core/common/win/platform_utils_win.cc
index 62420f5..af64985 100644
--- a/components/device_signals/core/common/win/platform_utils_win.cc
+++ b/components/device_signals/core/common/win/platform_utils_win.cc
@@ -193,6 +193,9 @@
 
 bool ResolvePath(const base::FilePath& file_path,
                  base::FilePath* resolved_file_path) {
+  if (!IsSupportedLocalPath(file_path)) {
+    return false;
+  }
   auto expanded_path_wstring =
       base::win::ExpandEnvironmentVariables(file_path.value());
   if (!expanded_path_wstring) {
@@ -200,10 +203,20 @@
   }
 
   auto expanded_file_path = base::FilePath(expanded_path_wstring.value());
-  if (!base::PathExists(expanded_file_path)) {
+  if (!IsSupportedLocalPath(expanded_file_path)) {
     return false;
   }
-  *resolved_file_path = base::MakeAbsoluteFilePath(expanded_file_path);
+
+  auto absolute_file_path = base::MakeAbsoluteFilePath(expanded_file_path);
+  if (!IsSupportedLocalPath(absolute_file_path)) {
+    return false;
+  }
+
+  if (!base::PathExists(absolute_file_path)) {
+    return false;
+  }
+
+  *resolved_file_path = absolute_file_path;
   return true;
 }
 
diff --git a/chrome/browser/extensions/api/enterprise_reporting_private/enterprise_reporting_private_unittest.cc b/chrome/browser/extensions/api/enterprise_reporting_private/enterprise_reporting_private_unittest.cc
index ba5f4ef..e579b0d 100644
--- a/chrome/browser/extensions/api/enterprise_reporting_private/enterprise_reporting_private_unittest.cc
+++ b/chrome/browser/extensions/api/enterprise_reporting_private/enterprise_reporting_private_unittest.cc
@@ -1145,21 +1145,36 @@
 
 TEST_F(EnterpriseReportingPrivateGetFileSystemInfoTest,
        NetworkPathFailsGracefully) {
-  enterprise_reporting_private::GetFileSystemInfoRequest request;
-  request.user_context = GetFakeUserContext();
+  const std::vector<std::string> invalid_paths = {
+      "//server/share/file.txt",
+      "\\\\server\\share\\file.txt",
+      "\\??\\UNC\\server\\share\\file.txt",
+      "/??/UNC/server/share/file.txt",
+      "\\??\\C:\\file.txt",
+      "/??/C:/file.txt",
+      "\\Device\\HarddiskVolume1\\file.txt",
+      "/Device/HarddiskVolume1/file.txt",
+  };
 
-  enterprise_reporting_private::GetFileSystemInfoOptions option;
-  option.path = "//server/share/file.txt";
-  request.options.push_back(std::move(option));
+  for (const auto& invalid_path : invalid_paths) {
+    auto function = base::MakeRefCounted<
+        extensions::EnterpriseReportingPrivateGetFileSystemInfoFunction>();
+    enterprise_reporting_private::GetFileSystemInfoRequest request;
+    request.user_context = GetFakeUserContext();
 
-  base::ListValue args;
-  args.Append(request.ToValue());
-  std::string json_args = base::WriteJson(args).value_or("");
+    enterprise_reporting_private::GetFileSystemInfoOptions option;
+    option.path = invalid_path;
+    request.options.push_back(std::move(option));
 
-  std::string error = api_test_utils::RunFunctionAndReturnError(
-      function_.get(), json_args, profile());
+    base::ListValue args;
+    args.Append(request.ToValue());
+    std::string json_args = base::WriteJson(args).value_or("");
 
-  EXPECT_EQ(error, "Network paths are not supported.");
+    std::string error = api_test_utils::RunFunctionAndReturnError(
+        function.get(), json_args, profile());
+
+    EXPECT_EQ(error, "Network paths are not supported.");
+  }
 }
 
 TEST_F(EnterpriseReportingPrivateGetFileSystemInfoTest, TopLevelError) {
diff --git a/components/device_signals/core/common/platform_utils_unittest.cc b/components/device_signals/core/common/platform_utils_unittest.cc
index 8de4215..cf71278 100644
--- a/components/device_signals/core/common/platform_utils_unittest.cc
+++ b/components/device_signals/core/common/platform_utils_unittest.cc
@@ -47,4 +47,39 @@
   EXPECT_TRUE(result.empty());
 }
 
+TEST_F(PlatformUtilsTest, IsSupportedLocalPath) {
+  // Empty paths are not supported.
+  EXPECT_FALSE(IsSupportedLocalPath(base::FilePath()));
+
+  // Network and UNC paths.
+  EXPECT_FALSE(IsSupportedLocalPath(
+      base::FilePath::FromUTF8Unsafe("//server/share/file.txt")));
+  EXPECT_FALSE(IsSupportedLocalPath(
+      base::FilePath::FromUTF8Unsafe("\\\\server\\share\\file.txt")));
+
+  // NT-object namespace paths.
+  EXPECT_FALSE(IsSupportedLocalPath(
+      base::FilePath::FromUTF8Unsafe("\\??\\UNC\\server\\share\\file.txt")));
+  EXPECT_FALSE(IsSupportedLocalPath(
+      base::FilePath::FromUTF8Unsafe("/??/UNC/server/share/file.txt")));
+  EXPECT_FALSE(IsSupportedLocalPath(
+      base::FilePath::FromUTF8Unsafe("\\??\\C:\\file.txt")));
+  EXPECT_FALSE(
+      IsSupportedLocalPath(base::FilePath::FromUTF8Unsafe("/??/C:/file.txt")));
+
+  // Device paths.
+  EXPECT_FALSE(IsSupportedLocalPath(
+      base::FilePath::FromUTF8Unsafe("\\Device\\HarddiskVolume1\\file.txt")));
+  EXPECT_FALSE(IsSupportedLocalPath(
+      base::FilePath::FromUTF8Unsafe("/Device/HarddiskVolume1/file.txt")));
+
+  // Valid local paths.
+  EXPECT_TRUE(
+      IsSupportedLocalPath(base::FilePath::FromUTF8Unsafe("/tmp/file.txt")));
+  EXPECT_TRUE(IsSupportedLocalPath(
+      base::FilePath::FromUTF8Unsafe("C:\\Windows\\file.txt")));
+  EXPECT_TRUE(IsSupportedLocalPath(
+      base::FilePath::FromUTF8Unsafe("relative/path.txt")));
+}
+
 }  // namespace device_signals
diff --git a/components/device_signals/core/system_signals/posix/posix_platform_delegate_unittest.cc b/components/device_signals/core/system_signals/posix/posix_platform_delegate_unittest.cc
index 428d71cc..9e9064c 100644
--- a/components/device_signals/core/system_signals/posix/posix_platform_delegate_unittest.cc
+++ b/components/device_signals/core/system_signals/posix/posix_platform_delegate_unittest.cc
@@ -134,4 +134,31 @@
   EXPECT_TRUE(base::ContentsEqual(resolved_file_path, binary_path_));
 }
 
+TEST_F(PosixPlatformDelegateTest, ResolveFilePath_NetworkAndNtPaths) {
+  const std::vector<std::string> invalid_paths = {
+      "//server/share/file",
+      "\\\\server\\share\\file",
+      "\\??\\UNC\\server\\share\\file",
+      "/??/UNC/server/share/file",
+      "\\??\\C:\\file",
+      "/??/C:/file",
+      "\\Device\\HarddiskVolume1\\file",
+      "/Device/HarddiskVolume1/file",
+  };
+
+  for (const auto& invalid_path : invalid_paths) {
+    base::FilePath resolved_file_path;
+    EXPECT_FALSE(platform_delegate_.ResolveFilePath(
+        base::FilePath::FromUTF8Unsafe(invalid_path), &resolved_file_path));
+  }
+
+  base::ScopedEnvironmentVariableOverride env_override(kHome2EnvVariableName,
+                                                       "//server/share");
+  base::FilePath resolved_file_path;
+  EXPECT_FALSE(platform_delegate_.ResolveFilePath(
+      base::FilePath::FromUTF8Unsafe(
+          base::StringPrintf("$%s/%s", kHome2EnvVariableName, kTestFileName)),
+      &resolved_file_path));
+}
+
 }  // namespace device_signals
diff --git a/components/device_signals/core/system_signals/win/win_platform_delegate_unittest.cc b/components/device_signals/core/system_signals/win/win_platform_delegate_unittest.cc
index dc3387fa..932f46f 100644
--- a/components/device_signals/core/system_signals/win/win_platform_delegate_unittest.cc
+++ b/components/device_signals/core/system_signals/win/win_platform_delegate_unittest.cc
@@ -88,6 +88,42 @@
   EXPECT_EQ(resolved_fp, base::FilePath());
 }
 
+TEST_F(WinPlatformDelegateTest, ResolveFilePath_NetworkAndNtPaths) {
+  const std::vector<std::string> invalid_paths = {
+      "//server/share/file.txt",
+      "\\\\server\\share\\file.txt",
+      "\\??\\UNC\\server\\share\\file.txt",
+      "/??/UNC/server/share/file.txt",
+      "\\??\\C:\\file.txt",
+      "/??/C:/file.txt",
+      "\\Device\\HarddiskVolume1\\file.txt",
+      "/Device/HarddiskVolume1/file.txt",
+  };
+
+  for (const auto& invalid_path : invalid_paths) {
+    base::FilePath resolved_fp;
+    EXPECT_FALSE(platform_delegate_.ResolveFilePath(
+        base::FilePath::FromUTF8Unsafe(invalid_path), &resolved_fp));
+    EXPECT_EQ(resolved_fp, base::FilePath());
+  }
+
+  // Environment variable expanding to a network path.
+  env_->SetVar("UncEnvVar", "\\\\server\\share");
+  base::FilePath resolved_fp;
+  EXPECT_FALSE(platform_delegate_.ResolveFilePath(
+      base::FilePath::FromUTF8Unsafe("%UncEnvVar%\\file.txt"), &resolved_fp));
+  EXPECT_EQ(resolved_fp, base::FilePath());
+
+  // Environment variable expanding to an NT-object path.
+  env_->SetVar("NtEnvVar", "\\??\\UNC\\server\\share");
+  EXPECT_FALSE(platform_delegate_.ResolveFilePath(
+      base::FilePath::FromUTF8Unsafe("%NtEnvVar%\\file.txt"), &resolved_fp));
+  EXPECT_EQ(resolved_fp, base::FilePath());
+
+  env_->UnSetVar("UncEnvVar");
+  env_->UnSetVar("NtEnvVar");
+}
+
 TEST_F(WinPlatformDelegateTest, GetSigningCertificatesPublicKeys_InvalidPath) {
   auto public_keys =
       platform_delegate_.GetSigningCertificatesPublicKeys(base::FilePath());
diff --git a/components/device_signals/core/common/BUILD.gn b/components/device_signals/core/common/BUILD.gn
index d2dc253..4aac4f0b 100644
--- a/components/device_signals/core/common/BUILD.gn
+++ b/components/device_signals/core/common/BUILD.gn
@@ -33,6 +33,9 @@
   if (is_win) {
     sources += [ "win/platform_utils_win.cc" ]
     libs = [
+      "iphlpapi.lib",
+      "netapi32.lib",
+      "powrprof.lib",
... (file diff truncated)
Loading diff…

Regression Test / PoC

shipped with the fix
diff --git a/chrome/browser/extensions/api/enterprise_reporting_private/enterprise_reporting_private_unittest.cc b/chrome/browser/extensions/api/enterprise_reporting_private/enterprise_reporting_private_unittest.cc
index ba5f4ef..e579b0d 100644
--- a/chrome/browser/extensions/api/enterprise_reporting_private/enterprise_reporting_private_unittest.cc
+++ b/chrome/browser/extensions/api/enterprise_reporting_private/enterprise_reporting_private_unittest.cc
@@ -1145,21 +1145,36 @@
 
 TEST_F(EnterpriseReportingPrivateGetFileSystemInfoTest,
        NetworkPathFailsGracefully) {
-  enterprise_reporting_private::GetFileSystemInfoRequest request;
-  request.user_context = GetFakeUserContext();
+  const std::vector<std::string> invalid_paths = {
+      "//server/share/file.txt",
+      "\\\\server\\share\\file.txt",
+      "\\??\\UNC\\server\\share\\file.txt",
+      "/??/UNC/server/share/file.txt",
+      "\\??\\C:\\file.txt",
+      "/??/C:/file.txt",
+      "\\Device\\HarddiskVolume1\\file.txt",
+      "/Device/HarddiskVolume1/file.txt",
+  };
 
-  enterprise_reporting_private::GetFileSystemInfoOptions option;
-  option.path = "//server/share/file.txt";
-  request.options.push_back(std::move(option));
+  for (const auto& invalid_path : invalid_paths) {
+    auto function = base::MakeRefCounted<
+        extensions::EnterpriseReportingPrivateGetFileSystemInfoFunction>();
+    enterprise_reporting_private::GetFileSystemInfoRequest request;
+    request.user_context = GetFakeUserContext();
 
-  base::ListValue args;
-  args.Append(request.ToValue());
-  std::string json_args = base::WriteJson(args).value_or("");
+    enterprise_reporting_private::GetFileSystemInfoOptions option;
+    option.path = invalid_path;
+    request.options.push_back(std::move(option));
 
-  std::string error = api_test_utils::RunFunctionAndReturnError(
-      function_.get(), json_args, profile());
+    base::ListValue args;
+    args.Append(request.ToValue());
+    std::string json_args = base::WriteJson(args).value_or("");
 
-  EXPECT_EQ(error, "Network paths are not supported.");
+    std::string error = api_test_utils::RunFunctionAndReturnError(
+        function.get(), json_args, profile());
+
+    EXPECT_EQ(error, "Network paths are not supported.");
+  }
 }
 
 TEST_F(EnterpriseReportingPrivateGetFileSystemInfoTest, TopLevelError) {
diff --git a/components/device_signals/core/common/platform_utils_unittest.cc b/components/device_signals/core/common/platform_utils_unittest.cc
index 8de4215..cf71278 100644
--- a/components/device_signals/core/common/platform_utils_unittest.cc
+++ b/components/device_signals/core/common/platform_utils_unittest.cc
@@ -47,4 +47,39 @@
   EXPECT_TRUE(result.empty());
 }
 
+TEST_F(PlatformUtilsTest, IsSupportedLocalPath) {
+  // Empty paths are not supported.
+  EXPECT_FALSE(IsSupportedLocalPath(base::FilePath()));
+
+  // Network and UNC paths.
+  EXPECT_FALSE(IsSupportedLocalPath(
+      base::FilePath::FromUTF8Unsafe("//server/share/file.txt")));
+  EXPECT_FALSE(IsSupportedLocalPath(
+      base::FilePath::FromUTF8Unsafe("\\\\server\\share\\file.txt")));
+
+  // NT-object namespace paths.
+  EXPECT_FALSE(IsSupportedLocalPath(
+      base::FilePath::FromUTF8Unsafe("\\??\\UNC\\server\\share\\file.txt")));
+  EXPECT_FALSE(IsSupportedLocalPath(
+      base::FilePath::FromUTF8Unsafe("/??/UNC/server/share/file.txt")));
+  EXPECT_FALSE(IsSupportedLocalPath(
+      base::FilePath::FromUTF8Unsafe("\\??\\C:\\file.txt")));
+  EXPECT_FALSE(
+      IsSupportedLocalPath(base::FilePath::FromUTF8Unsafe("/??/C:/file.txt")));
+
+  // Device paths.
+  EXPECT_FALSE(IsSupportedLocalPath(
+      base::FilePath::FromUTF8Unsafe("\\Device\\HarddiskVolume1\\file.txt")));
+  EXPECT_FALSE(IsSupportedLocalPath(
+      base::FilePath::FromUTF8Unsafe("/Device/HarddiskVolume1/file.txt")));
+
+  // Valid local paths.
+  EXPECT_TRUE(
+      IsSupportedLocalPath(base::FilePath::FromUTF8Unsafe("/tmp/file.txt")));
+  EXPECT_TRUE(IsSupportedLocalPath(
+      base::FilePath::FromUTF8Unsafe("C:\\Windows\\file.txt")));
+  EXPECT_TRUE(IsSupportedLocalPath(
+      base::FilePath::FromUTF8Unsafe("relative/path.txt")));
+}
+
 }  // namespace device_signals
diff --git a/components/device_signals/core/system_signals/posix/posix_platform_delegate_unittest.cc b/components/device_signals/core/system_signals/posix/posix_platform_delegate_unittest.cc
index 428d71cc..9e9064c 100644
--- a/components/device_signals/core/system_signals/posix/posix_platform_delegate_unittest.cc
+++ b/components/device_signals/core/system_signals/posix/posix_platform_delegate_unittest.cc
@@ -134,4 +134,31 @@
   EXPECT_TRUE(base::ContentsEqual(resolved_file_path, binary_path_));
 }
 
+TEST_F(PosixPlatformDelegateTest, ResolveFilePath_NetworkAndNtPaths) {
+  const std::vector<std::string> invalid_paths = {
+      "//server/share/file",
+      "\\\\server\\share\\file",
+      "\\??\\UNC\\server\\share\\file",
+      "/??/UNC/server/share/file",
+      "\\??\\C:\\file",
+      "/??/C:/file",
+      "\\Device\\HarddiskVolume1\\file",
+      "/Device/HarddiskVolume1/file",
+  };
+
+  for (const auto& invalid_path : invalid_paths) {
+    base::FilePath resolved_file_path;
+    EXPECT_FALSE(platform_delegate_.ResolveFilePath(
+        base::FilePath::FromUTF8Unsafe(invalid_path), &resolved_file_path));
+  }
+
+  base::ScopedEnvironmentVariableOverride env_override(kHome2EnvVariableName,
+                                                       "//server/share");
+  base::FilePath resolved_file_path;
+  EXPECT_FALSE(platform_delegate_.ResolveFilePath(
+      base::FilePath::FromUTF8Unsafe(
+          base::StringPrintf("$%s/%s", kHome2EnvVariableName, kTestFileName)),
+      &resolved_file_path));
+}
+
 }  // namespace device_signals
diff --git a/components/device_signals/core/system_signals/win/win_platform_delegate_unittest.cc b/components/device_signals/core/system_signals/win/win_platform_delegate_unittest.cc
index dc3387fa..932f46f 100644
--- a/components/device_signals/core/system_signals/win/win_platform_delegate_unittest.cc
+++ b/components/device_signals/core/system_signals/win/win_platform_delegate_unittest.cc
@@ -88,6 +88,42 @@
   EXPECT_EQ(resolved_fp, base::FilePath());
 }
 
+TEST_F(WinPlatformDelegateTest, ResolveFilePath_NetworkAndNtPaths) {
+  const std::vector<std::string> invalid_paths = {
+      "//server/share/file.txt",
+      "\\\\server\\share\\file.txt",
+      "\\??\\UNC\\server\\share\\file.txt",
+      "/??/UNC/server/share/file.txt",
+      "\\??\\C:\\file.txt",
+      "/??/C:/file.txt",
+      "\\Device\\HarddiskVolume1\\file.txt",
+      "/Device/HarddiskVolume1/file.txt",
+  };
+
+  for (const auto& invalid_path : invalid_paths) {
+    base::FilePath resolved_fp;
+    EXPECT_FALSE(platform_delegate_.ResolveFilePath(
+        base::FilePath::FromUTF8Unsafe(invalid_path), &resolved_fp));
+    EXPECT_EQ(resolved_fp, base::FilePath());
+  }
+
+  // Environment variable expanding to a network path.
+  env_->SetVar("UncEnvVar", "\\\\server\\share");
+  base::FilePath resolved_fp;
+  EXPECT_FALSE(platform_delegate_.ResolveFilePath(
+      base::FilePath::FromUTF8Unsafe("%UncEnvVar%\\file.txt"), &resolved_fp));
+  EXPECT_EQ(resolved_fp, base::FilePath());
+
+  // Environment variable expanding to an NT-object path.
+  env_->SetVar("NtEnvVar", "\\??\\UNC\\server\\share");
+  EXPECT_FALSE(platform_delegate_.ResolveFilePath(
+      base::FilePath::FromUTF8Unsafe("%NtEnvVar%\\file.txt"), &resolved_fp));
+  EXPECT_EQ(resolved_fp, base::FilePath());
+
+  env_->UnSetVar("UncEnvVar");
+  env_->UnSetVar("NtEnvVar");
+}
+
 TEST_F(WinPlatformDelegateTest, GetSigningCertificatesPublicKeys_InvalidPath) {
   auto public_keys =
       platform_delegate_.GetSigningCertificatesPublicKeys(base::FilePath());
Loading diff…

Original Bug Report

The reporter's bug is still restricted on the tracker. Chrome de-restricts security bugs ~30–90 days after the fix ships; a later run will backfill it here.