Medium chrome Policy Bypass 🔧 Commit mapped

Overview

Medium
Severity
—
CVSS
No
Exploited ITW
Fixed
Fix Status
ImpactOut of bounds read in FileSystem
DescriptionOut of bounds read in FileSystem
ComponentFileSystem
Sub-componentFSA
Bug ClassPolicy Bypass
Tracker569012543
Fix commitd6f2c70dbb4c (chromium/src) +161/-8
CISA KEVNot listed
CreditedGoogle
Disclosed2026-10-29

Background

File System Access API (FSA)
Web API letting a granted origin read/write files and directories on the user’s real filesystem via showOpenFilePicker/showDirectoryPicker/showSaveFilePicker and programmatic handles.
`ChromeFileSystemAccessPermissionContext`
The browser-process policy class (chrome/browser/file_system_access/chrome_file_system_access_permission_context.cc) that decides, per path, whether an FSA access is permitted. ConfirmSensitiveEntryAccess consults a static blocklist of sensitive paths.
`BlockPath` / `BlockType`
Blocklist entries: CreateRelative/CreateAbsolute/CreateSuffix with kBlockAllChildren (no access), kBlockWrite (reads allowed, writes denied). Matching is path/suffix based and case-insensitive (the test checks .GIT/CONFIG).
`AccessTrigger`
The operation kind (kOpen, kSave, kProgrammaticRead, kProgrammaticWrite) evaluated against the blocklist to produce SensitiveDirectoryResult::kAllowed/kAbort.

Root Cause Analysis

The sensitive-path blocklist in ChromeFileSystemAccessPermissionContext was incomplete and inconsistent across platforms, so several security-sensitive locations remained reachable through FSA. The patch adds entries to close the gaps: write-blocks on */.git/config and */.git/modules (via BlockPath::CreateSuffix(..., kBlockWrite), alongside the existing .git/hooks), while keeping reads allowed so web Git clients still work; a kBlockAllChildren block on ~/.config on all platforms (previously it was blocked only on Linux/ChromeOS/Android — the diff moves it out of the Linux-only section into the shared list); blocks on shell startup files in the home directory (.bash_profile, .bash_login, .bashrc, .profile, .zshenv, .zprofile, .zshrc, .zlogin, including the ones Git Bash reads on Windows); a /dev block on macOS to match Linux; and, on Linux/ChromeOS, blocks on ~/.local/share, the legacy fontconfig files ~/.fonts.conf and ~/.fonts.conf.d, and the NSS database directory ~/.pki.

The invariant being restored is that FSA must never let a web origin obtain write access to files that are executed, sourced, or trusted as configuration by the user’s shell, Git, or security tooling (and must not expose device nodes or credential stores). The most dangerous previously-reachable targets are the writable ones: .git/config (whose core.fsmonitor/alias/core.hooksPath directives can run arbitrary commands on the next git invocation), .git/modules/* (submodule git-dirs carrying their own hooks/config), and the shell rc/profile files (sourced on the next shell start). The tests confirm the intended granularity: .git/config and everything under .git/modules abort for kSave/kProgrammaticWrite but are allowed for kOpen/kProgrammaticRead; sibling files like config.bak or a config outside .git stay allowed; and the .git directory itself remains savable.

Critically, the advisory classifies this as “Out of bounds read in FileSystem,” but the diff contains no array/buffer indexing, no bounds arithmetic, and no memory-safety sink at all. It is purely an access-control/policy blocklist gap: sensitive paths that should have been denied were permitted. The real bug class is a policy bypass (sandbox/trust-boundary enforcement failure), not an OOB read — this is a clear advisory mislabel.

Key insight
FSA’s sensitive-path blocklist omitted (or only platform-partially covered) files that grant code execution or credential access when written — Git repo config/submodules and shell startup files — plus device nodes and credential stores, letting a granted origin reach them. The single mistake is an incomplete, platform-inconsistent denylist; the advisory’s “out of bounds read” label is not substantiated by the diff, which adds access-control entries, not bounds checks.

Attack Path

  1. Obtain an FSA handle A web origin persuades the user to grant a directory or file handle (e.g. “open your project folder” / a save dialog), the normal FSA permission step.
  2. Target a sensitive path Using the granted (or picker-selected) handle, the page resolves a path into the repo’s .git/config, .git/modules/*, or the user’s home-dir shell rc/profile files — paths the blocklist previously did not deny for write.
  3. Write malicious content The page writes a .git/config with a command-executing directive (e.g. a hooks path or fsmonitor hook) or appends an attacker command to ~/.bashrc/~/.zshrc/.bash_profile.
  4. Wait for trust The next time the user runs git in that repo, or opens a new shell, the attacker-controlled configuration/script is sourced and executes.
  5. Primitive Arbitrary command execution on the host outside the browser sandbox (via Git/shell trust), or, for the read-side gaps, disclosure of credentials from ~/.pki (NSS DB) and other sensitive config. This is a sandbox/policy-boundary bypass, not a memory-corruption primitive.

Impact Assessment

The policy runs in the browser process; the attack is initiated by untrusted web content but requires the user to grant an FSA handle (the gating precondition). Within that grant, the pre-patch blocklist let the origin write files that are trusted and executed by other programs — Git repository config/submodules and shell startup files — turning an FSA write into out-of-sandbox code execution the next time the user runs Git or opens a shell, and let it read credential/config stores (~/.pki, ~/.config) on platforms where they were unblocked. The strength is high (host code execution / credential theft) but contingent on the user granting FSA access to a location containing those files and on a subsequent Git/shell invocation. No memory-safety primitive is involved, contrary to the advisory’s OOB-read label.

Files Changed

  • chrome/browser/file_system_access/chrome_file_system_access_permission_context_unittest.cc
  • chrome/browser/file_system_access/chrome_file_system_access_permission_context.cc

Audit Directions

  • Cross-platform consistency of the blocklist
    Diff the per-BUILDFLAG sections for other sensitive paths blocked on one OS but not others (this fix moved ~/.config and /dev); credential, keyring, and config directories are prime candidates.
  • Other "write enables code execution" targets
    Review for files beyond .git and shell rc that are auto-sourced/executed by tools: CI configs, editor dotfiles, .gitconfig include directives, .npmrc, systemd user units, and similar, which an FSA write could weaponize.
  • Suffix vs. absolute matching and symlinks
    Audit CreateSuffix matching for bypasses (alternate separators, trailing dots, .git nesting) and whether handle resolution can reach blocked paths through symlinks or bind mounts not normalized before the blocklist check.
  • Read-allowed sensitive files
    Since .git/config reads stay permitted, review what other read-allowed config files can leak secrets (tokens embedded in URLs, credential helpers) via FSA.
From d6f2c70dbb4cd97f6eaf62aed88c8f2e57ef3fce Mon Sep 17 00:00:00 2001
From: Mingyu Lei <leimy@chromium.org>
Date: Wed, 21 Oct 2026 23:00:11 -0700
Subject: [PATCH] FSA: Add missing sensitive paths to the blocklist

Several sensitive locations are still reachable through File System
Access because they are missing from the sensitive-path blocklist, or
are only blocked on some platforms. Add them:

- Block write access to */.git/config and */.git/modules on all
  platforms, alongside */.git/hooks. Reads stay allowed, so web Git
  clients can still read repository configuration.
- Block ~/.config on all platforms. It was only blocked on Linux,
  ChromeOS and Android.
- Block shell startup files in the home directory on all platforms,
  including the ones Git Bash reads on Windows.
- Block /dev on macOS, matching Linux.
- Block ~/.local/share, the legacy fontconfig files ~/.fonts.conf and
  ~/.fonts.conf.d, and the NSS database directory ~/.pki on Linux and
  ChromeOS.

Bug: 572269631, 571913154, 569012015, 558362029
Bug: 569012543, 561996724, 559780982, 557367241
Test: unit_tests --gtest_filter=ChromeFileSystemAccessPermissionContextTest.*ConfirmSensitiveEntryAccess*
Change-Id: Ia6fe9e51807e8106fbc6ac69e28d550660e49b38
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/8619619
Reviewed-by: Fergal Daly <fergal@chromium.org>
Commit-Queue: Mingyu Lei <leimy@chromium.org>
Cr-Commit-Position: refs/heads/main@{#1724465}
---
diff --git a/chrome/browser/file_system_access/chrome_file_system_access_permission_context.cc b/chrome/browser/file_system_access/chrome_file_system_access_permission_context.cc
index 112be22..be365904 100644
--- a/chrome/browser/file_system_access/chrome_file_system_access_permission_context.cc
+++ b/chrome/browser/file_system_access/chrome_file_system_access_permission_context.cc
@@ -441,10 +441,41 @@
       // And limit access to ~/.gnupg as well.
       BlockPath::CreateRelative(base::DIR_HOME, FILE_PATH_LITERAL(".gnupg"),
                                 BlockType::kBlockAllChildren),
+      // ~/.config holds configuration for many cross-platform tools, so block
+      // it on all platforms, see crbug.com/569012015.
+      BlockPath::CreateRelative(base::DIR_HOME, FILE_PATH_LITERAL(".config"),
+                                BlockType::kBlockAllChildren),
+      // Shell startup files in the home directory, including the ones read by
+      // Git Bash on Windows, see crbug.com/558362029.
+      BlockPath::CreateRelative(base::DIR_HOME,
+                                FILE_PATH_LITERAL(".bash_profile"),
+                                BlockType::kBlockAllChildren),
+      BlockPath::CreateRelative(base::DIR_HOME,
+                                FILE_PATH_LITERAL(".bash_login"),
+                                BlockType::kBlockAllChildren),
+      BlockPath::CreateRelative(base::DIR_HOME, FILE_PATH_LITERAL(".bashrc"),
+                                BlockType::kBlockAllChildren),
+      BlockPath::CreateRelative(base::DIR_HOME, FILE_PATH_LITERAL(".profile"),
+                                BlockType::kBlockAllChildren),
+      BlockPath::CreateRelative(base::DIR_HOME, FILE_PATH_LITERAL(".zshenv"),
+                                BlockType::kBlockAllChildren),
+      BlockPath::CreateRelative(base::DIR_HOME, FILE_PATH_LITERAL(".zprofile"),
+                                BlockType::kBlockAllChildren),
+      BlockPath::CreateRelative(base::DIR_HOME, FILE_PATH_LITERAL(".zshrc"),
+                                BlockType::kBlockAllChildren),
+      BlockPath::CreateRelative(base::DIR_HOME, FILE_PATH_LITERAL(".zlogin"),
+                                BlockType::kBlockAllChildren),
       // Block write access to */.git/hooks, see crbug.com/465668234 and
       // crbug.com/553115714.
       BlockPath::CreateSuffix(FILE_PATH_LITERAL(".git/hooks"),
                               BlockType::kBlockWrite),
+      // Block write access to the repository config and to submodule git
+      // directories, which have their own hooks and config, see
+      // crbug.com/572269631 and crbug.com/571913154.
+      BlockPath::CreateSuffix(FILE_PATH_LITERAL(".git/config"),
+                              BlockType::kBlockWrite),
+      BlockPath::CreateSuffix(FILE_PATH_LITERAL(".git/modules"),
+                              BlockType::kBlockWrite),
 #if BUILDFLAG(IS_WIN)
       // Some Windows specific directories to block, basically all apps, the
       // operating system itself, as well as configuration data for apps.
@@ -475,6 +506,10 @@
       // volumes).
       BlockPath::CreateAbsolute(FILE_PATH_LITERAL("/System/Volumes"),
                                 BlockType::kBlockAllChildren),
+      // Block access to devices via /dev, matching Linux, see
+      // crbug.com/569012543.
+      BlockPath::CreateAbsolute(FILE_PATH_LITERAL("/dev"),
+                                BlockType::kBlockAllChildren),
       BlockPath::CreateRelative(base::DIR_APP_DATA,
                                 BlockType::kBlockAllChildren),
       // Block access to the current bundle directory.
@@ -525,14 +560,25 @@
                                 BlockType::kBlockAllChildren),
       BlockPath::CreateAbsolute(FILE_PATH_LITERAL("/etc"),
                                 BlockType::kBlockAllChildren),
-      // And block all of ~/.config, matching the similar restrictions on mac
-      // and windows.
-      BlockPath::CreateRelative(base::DIR_HOME, FILE_PATH_LITERAL(".config"),
-                                BlockType::kBlockAllChildren),
       // Block ~/.dbus as well, just in case, although there probably isn't much
       // a website can do with access to that directory and its contents.
       BlockPath::CreateRelative(base::DIR_HOME, FILE_PATH_LITERAL(".dbus"),
                                 BlockType::kBlockAllChildren),
+      // Block per-user application data, see crbug.com/561996724.
+      BlockPath::CreateRelative(base::DIR_HOME,
+                                FILE_PATH_LITERAL(".local/share"),
+                                BlockType::kBlockAllChildren),
+      // Block the legacy per-user fontconfig configuration, see
+      // crbug.com/559780982. The current location is under ~/.config.
+      BlockPath::CreateRelative(base::DIR_HOME,
+                                FILE_PATH_LITERAL(".fonts.conf"),
+                                BlockType::kBlockAllChildren),
+      BlockPath::CreateRelative(base::DIR_HOME,
+                                FILE_PATH_LITERAL(".fonts.conf.d"),
+                                BlockType::kBlockAllChildren),
+      // Block the per-user NSS database, see crbug.com/557367241.
+      BlockPath::CreateRelative(base::DIR_HOME, FILE_PATH_LITERAL(".pki"),
+                                BlockType::kBlockAllChildren),
       // And block all of ~/.cache, matching the similar restrictions on mac
       // and windows.
       BlockPath::CreateRelative(base::DIR_CACHE, BlockType::kBlockAllChildren),
diff --git a/chrome/browser/file_system_access/chrome_file_system_access_permission_context_unittest.cc b/chrome/browser/file_system_access/chrome_file_system_access_permission_context_unittest.cc
index 1bd9323..d500135 100644
--- a/chrome/browser/file_system_access/chrome_file_system_access_permission_context_unittest.cc
+++ b/chrome/browser/file_system_access/chrome_file_system_access_permission_context_unittest.cc
@@ -862,9 +862,8 @@
 
 TEST_F(ChromeFileSystemAccessPermissionContextTest,
        ConfirmSensitiveEntryAccess_ExplicitPathBlock) {
-// Linux is the only OS where we have some blocked directories with explicit
-// paths (as opposed to PathService provided paths).
-#if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS) || BUILDFLAG(IS_ANDROID)
+#if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS) || BUILDFLAG(IS_ANDROID) || \
+    BUILDFLAG(IS_MAC)
   // /dev should be blocked.
   EXPECT_EQ(ConfirmSensitiveEntryAccessSync(
                 permission_context(), PathInfo(FILE_PATH_LITERAL("/dev")),
@@ -1604,6 +1603,101 @@
             SensitiveDirectoryResult::kAllowed);
 }
 
+// Testing that writes to */.git/config and */.git/modules are blocked, while
+// reads stay allowed.
+TEST_F(ChromeFileSystemAccessPermissionContextTest,
+       ConfirmSensitiveEntryAccess_GitConfigAndModulesWriteBlock) {
+#if defined(FILE_PATH_USES_DRIVE_LETTERS)
+  base::FilePath root(FILE_PATH_LITERAL("c:\\"));
+#else
+  base::FilePath root(FILE_PATH_LITERAL("/"));
+#endif
+  const base::FilePath git_dir =
+      root.Append(FILE_PATH_LITERAL("a")).Append(FILE_PATH_LITERAL(".git"));
+  const base::FilePath git_config = git_dir.Append(FILE_PATH_LITERAL("config"));
+  const base::FilePath git_modules =
+      git_dir.Append(FILE_PATH_LITERAL("modules"));
+  const base::FilePath submodule_dir =
+      git_modules.Append(FILE_PATH_LITERAL("sub"));
+
+  // .git/config is blocked for save and programmatic write.
+  EXPECT_EQ(ConfirmSensitiveEntryAccessSync(
+                permission_context(), PathInfo(git_config), HandleType::kFile,
+                AccessTrigger::kSave),
+            SensitiveDirectoryResult::kAbort);
+  EXPECT_EQ(ConfirmSensitiveEntryAccessSync(
+                permission_context(), PathInfo(git_config), HandleType::kFile,
+                AccessTrigger::kProgrammaticWrite),
+            SensitiveDirectoryResult::kAbort);
+  // .git/config is not blocked for read.
+  EXPECT_EQ(ConfirmSensitiveEntryAccessSync(
+                permission_context(), PathInfo(git_config), HandleType::kFile,
+                AccessTrigger::kOpen),
+            SensitiveDirectoryResult::kAllowed);
+  EXPECT_EQ(ConfirmSensitiveEntryAccessSync(
+                permission_context(), PathInfo(git_config), HandleType::kFile,
+                AccessTrigger::kProgrammaticRead),
+            SensitiveDirectoryResult::kAllowed);
+  // Case variations of .git/config are blocked for programmatic write.
+  EXPECT_EQ(ConfirmSensitiveEntryAccessSync(
+                permission_context(),
+                PathInfo(root.Append(FILE_PATH_LITERAL("a"))
+                             .Append(FILE_PATH_LITERAL(".GIT"))
+                             .Append(FILE_PATH_LITERAL("CONFIG"))),
+                HandleType::kFile, AccessTrigger::kProgrammaticWrite),
+            SensitiveDirectoryResult::kAbort);
+  // Other files under .git, and files named config outside of .git, are
+  // allowed.
+  EXPECT_EQ(ConfirmSensitiveEntryAccessSync(
+                permission_context(),
+                PathInfo(git_dir.Append(FILE_PATH_LITERAL("config.bak"))),
+                HandleType::kFile, AccessTrigger::kProgrammaticWrite),
+            SensitiveDirectoryResult::kAllowed);
+  EXPECT_EQ(ConfirmSensitiveEntryAccessSync(
+                permission_context(),
+                PathInfo(root.Append(FILE_PATH_LITERAL("a"))
+                             .Append(FILE_PATH_LITERAL("config"))),
+                HandleType::kFile, AccessTrigger::kProgrammaticWrite),
+            SensitiveDirectoryResult::kAllowed);
+
+  // .git/modules and everything inside it is blocked for save and programmatic
+  // write.
+  EXPECT_EQ(ConfirmSensitiveEntryAccessSync(
+                permission_context(), PathInfo(git_modules),
+                HandleType::kDirectory, AccessTrigger::kSave),
+            SensitiveDirectoryResult::kAbort);
+  EXPECT_EQ(ConfirmSensitiveEntryAccessSync(
+                permission_context(), PathInfo(git_modules),
+                HandleType::kDirectory, AccessTrigger::kProgrammaticWrite),
+            SensitiveDirectoryResult::kAbort);
+  EXPECT_EQ(ConfirmSensitiveEntryAccessSync(
+                permission_context(),
+                PathInfo(submodule_dir.Append(FILE_PATH_LITERAL("hooks"))
+                             .Append(FILE_PATH_LITERAL("pre-commit"))),
+                HandleType::kFile, AccessTrigger::kProgrammaticWrite),
+            SensitiveDirectoryResult::kAbort);
+  EXPECT_EQ(ConfirmSensitiveEntryAccessSync(
+                permission_context(),
+                PathInfo(submodule_dir.Append(FILE_PATH_LITERAL("config"))),
+                HandleType::kFile, AccessTrigger::kProgrammaticWrite),
+            SensitiveDirectoryResult::kAbort);
+  // .git/modules is not blocked for read.
+  EXPECT_EQ(ConfirmSensitiveEntryAccessSync(
+                permission_context(), PathInfo(git_modules),
+                HandleType::kDirectory, AccessTrigger::kOpen),
+            SensitiveDirectoryResult::kAllowed);
+  EXPECT_EQ(ConfirmSensitiveEntryAccessSync(
+                permission_context(),
+                PathInfo(submodule_dir.Append(FILE_PATH_LITERAL("config"))),
+                HandleType::kFile, AccessTrigger::kProgrammaticRead),
+            SensitiveDirectoryResult::kAllowed);
+  // The .git directory itself is still allowed for save.
+  EXPECT_EQ(ConfirmSensitiveEntryAccessSync(
+                permission_context(), PathInfo(git_dir), HandleType::kDirectory,
+                AccessTrigger::kSave),
+            SensitiveDirectoryResult::kAllowed);
+}
+
 #if BUILDFLAG(IS_ANDROID)
 TEST_F(ChromeFileSystemAccessPermissionContextTest,
        ConfirmSensitiveEntryAccess_ContentUri) {
@@ -1867,6 +1961,15 @@
       {chrome::DIR_USER_DATA, nullptr, true, false},
       {base::DIR_HOME, FILE_PATH_LITERAL(".ssh"), true, false},
       {base::DIR_HOME, FILE_PATH_LITERAL(".gnupg"), true, false},
+      {base::DIR_HOME, FILE_PATH_LITERAL(".config"), true, false},
+      {base::DIR_HOME, FILE_PATH_LITERAL(".bash_profile"), true, false},
+      {base::DIR_HOME, FILE_PATH_LITERAL(".bash_login"), true, false},
+      {base::DIR_HOME, FILE_PATH_LITERAL(".bashrc"), true, false},
+      {base::DIR_HOME, FILE_PATH_LITERAL(".profile"), true, false},
+      {base::DIR_HOME, FILE_PATH_LITERAL(".zshenv"), true, false},
+      {base::DIR_HOME, FILE_PATH_LITERAL(".zprofile"), true, false},
+      {base::DIR_HOME, FILE_PATH_LITERAL(".zshrc"), true, false},
+      {base::DIR_HOME, FILE_PATH_LITERAL(".zlogin"), true, false},
 
 #if BUILDFLAG(IS_WIN)
       {base::DIR_PROGRAM_FILES, nullptr, true, false},
@@ -1880,6 +1983,7 @@
 #endif
 
 #if BUILDFLAG(IS_MAC)
+      {kNoBasePathKey, FILE_PATH_LITERAL("/dev"), true, false},
       {base::DIR_APP_DATA, nullptr, true, false},
       {chrome::DIR_OUTER_BUNDLE, nullptr, true, false},
       {base::DIR_HOME, FILE_PATH_LITERAL("Applications"), true, false},
@@ -1900,8 +2004,11 @@
       {kNoBasePathKey, FILE_PATH_LITERAL("/sys"), true, false},
       {kNoBasePathKey, FILE_PATH_LITERAL("/boot"), true, false},
       {kNoBasePathKey, FILE_PATH_LITERAL("/etc"), true, false},
-      {base::DIR_HOME, FILE_PATH_LITERAL(".config"), true, false},
       {base::DIR_HOME, FILE_PATH_LITERAL(".dbus"), true, false},
+      {base::DIR_HOME, FILE_PATH_LITERAL(".local/share"), true, false},
+      {base::DIR_HOME, FILE_PATH_LITERAL(".fonts.conf"), true, false},
+      {base::DIR_HOME, FILE_PATH_LITERAL(".fonts.conf.d"), true, false},
+      {base::DIR_HOME, FILE_PATH_LITERAL(".pki"), true, false},
 #endif
 
 #if BUILDFLAG(IS_ANDROID)
Loading diff…

Regression Test / PoC

shipped with the fix
diff --git a/chrome/browser/file_system_access/chrome_file_system_access_permission_context_unittest.cc b/chrome/browser/file_system_access/chrome_file_system_access_permission_context_unittest.cc
index 1bd9323..d500135 100644
--- a/chrome/browser/file_system_access/chrome_file_system_access_permission_context_unittest.cc
+++ b/chrome/browser/file_system_access/chrome_file_system_access_permission_context_unittest.cc
@@ -862,9 +862,8 @@
 
 TEST_F(ChromeFileSystemAccessPermissionContextTest,
        ConfirmSensitiveEntryAccess_ExplicitPathBlock) {
-// Linux is the only OS where we have some blocked directories with explicit
-// paths (as opposed to PathService provided paths).
-#if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS) || BUILDFLAG(IS_ANDROID)
+#if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS) || BUILDFLAG(IS_ANDROID) || \
+    BUILDFLAG(IS_MAC)
   // /dev should be blocked.
   EXPECT_EQ(ConfirmSensitiveEntryAccessSync(
                 permission_context(), PathInfo(FILE_PATH_LITERAL("/dev")),
@@ -1604,6 +1603,101 @@
             SensitiveDirectoryResult::kAllowed);
 }
 
+// Testing that writes to */.git/config and */.git/modules are blocked, while
+// reads stay allowed.
+TEST_F(ChromeFileSystemAccessPermissionContextTest,
+       ConfirmSensitiveEntryAccess_GitConfigAndModulesWriteBlock) {
+#if defined(FILE_PATH_USES_DRIVE_LETTERS)
+  base::FilePath root(FILE_PATH_LITERAL("c:\\"));
+#else
+  base::FilePath root(FILE_PATH_LITERAL("/"));
+#endif
+  const base::FilePath git_dir =
+      root.Append(FILE_PATH_LITERAL("a")).Append(FILE_PATH_LITERAL(".git"));
+  const base::FilePath git_config = git_dir.Append(FILE_PATH_LITERAL("config"));
+  const base::FilePath git_modules =
+      git_dir.Append(FILE_PATH_LITERAL("modules"));
+  const base::FilePath submodule_dir =
+      git_modules.Append(FILE_PATH_LITERAL("sub"));
+
+  // .git/config is blocked for save and programmatic write.
+  EXPECT_EQ(ConfirmSensitiveEntryAccessSync(
+                permission_context(), PathInfo(git_config), HandleType::kFile,
+                AccessTrigger::kSave),
+            SensitiveDirectoryResult::kAbort);
+  EXPECT_EQ(ConfirmSensitiveEntryAccessSync(
+                permission_context(), PathInfo(git_config), HandleType::kFile,
+                AccessTrigger::kProgrammaticWrite),
+            SensitiveDirectoryResult::kAbort);
+  // .git/config is not blocked for read.
+  EXPECT_EQ(ConfirmSensitiveEntryAccessSync(
+                permission_context(), PathInfo(git_config), HandleType::kFile,
+                AccessTrigger::kOpen),
+            SensitiveDirectoryResult::kAllowed);
+  EXPECT_EQ(ConfirmSensitiveEntryAccessSync(
+                permission_context(), PathInfo(git_config), HandleType::kFile,
+                AccessTrigger::kProgrammaticRead),
+            SensitiveDirectoryResult::kAllowed);
+  // Case variations of .git/config are blocked for programmatic write.
+  EXPECT_EQ(ConfirmSensitiveEntryAccessSync(
+                permission_context(),
+                PathInfo(root.Append(FILE_PATH_LITERAL("a"))
+                             .Append(FILE_PATH_LITERAL(".GIT"))
+                             .Append(FILE_PATH_LITERAL("CONFIG"))),
+                HandleType::kFile, AccessTrigger::kProgrammaticWrite),
+            SensitiveDirectoryResult::kAbort);
+  // Other files under .git, and files named config outside of .git, are
+  // allowed.
+  EXPECT_EQ(ConfirmSensitiveEntryAccessSync(
+                permission_context(),
+                PathInfo(git_dir.Append(FILE_PATH_LITERAL("config.bak"))),
+                HandleType::kFile, AccessTrigger::kProgrammaticWrite),
+            SensitiveDirectoryResult::kAllowed);
+  EXPECT_EQ(ConfirmSensitiveEntryAccessSync(
+                permission_context(),
+                PathInfo(root.Append(FILE_PATH_LITERAL("a"))
+                             .Append(FILE_PATH_LITERAL("config"))),
+                HandleType::kFile, AccessTrigger::kProgrammaticWrite),
+            SensitiveDirectoryResult::kAllowed);
+
+  // .git/modules and everything inside it is blocked for save and programmatic
+  // write.
+  EXPECT_EQ(ConfirmSensitiveEntryAccessSync(
+                permission_context(), PathInfo(git_modules),
+                HandleType::kDirectory, AccessTrigger::kSave),
+            SensitiveDirectoryResult::kAbort);
+  EXPECT_EQ(ConfirmSensitiveEntryAccessSync(
+                permission_context(), PathInfo(git_modules),
+                HandleType::kDirectory, AccessTrigger::kProgrammaticWrite),
+            SensitiveDirectoryResult::kAbort);
+  EXPECT_EQ(ConfirmSensitiveEntryAccessSync(
+                permission_context(),
+                PathInfo(submodule_dir.Append(FILE_PATH_LITERAL("hooks"))
+                             .Append(FILE_PATH_LITERAL("pre-commit"))),
+                HandleType::kFile, AccessTrigger::kProgrammaticWrite),
+            SensitiveDirectoryResult::kAbort);
+  EXPECT_EQ(ConfirmSensitiveEntryAccessSync(
+                permission_context(),
+                PathInfo(submodule_dir.Append(FILE_PATH_LITERAL("config"))),
+                HandleType::kFile, AccessTrigger::kProgrammaticWrite),
+            SensitiveDirectoryResult::kAbort);
+  // .git/modules is not blocked for read.
+  EXPECT_EQ(ConfirmSensitiveEntryAccessSync(
+                permission_context(), PathInfo(git_modules),
+                HandleType::kDirectory, AccessTrigger::kOpen),
+            SensitiveDirectoryResult::kAllowed);
+  EXPECT_EQ(ConfirmSensitiveEntryAccessSync(
+                permission_context(),
+                PathInfo(submodule_dir.Append(FILE_PATH_LITERAL("config"))),
+                HandleType::kFile, AccessTrigger::kProgrammaticRead),
+            SensitiveDirectoryResult::kAllowed);
+  // The .git directory itself is still allowed for save.
+  EXPECT_EQ(ConfirmSensitiveEntryAccessSync(
+                permission_context(), PathInfo(git_dir), HandleType::kDirectory,
+                AccessTrigger::kSave),
+            SensitiveDirectoryResult::kAllowed);
+}
+
 #if BUILDFLAG(IS_ANDROID)
 TEST_F(ChromeFileSystemAccessPermissionContextTest,
        ConfirmSensitiveEntryAccess_ContentUri) {
@@ -1867,6 +1961,15 @@
       {chrome::DIR_USER_DATA, nullptr, true, false},
       {base::DIR_HOME, FILE_PATH_LITERAL(".ssh"), true, false},
       {base::DIR_HOME, FILE_PATH_LITERAL(".gnupg"), true, false},
+      {base::DIR_HOME, FILE_PATH_LITERAL(".config"), true, false},
+      {base::DIR_HOME, FILE_PATH_LITERAL(".bash_profile"), true, false},
+      {base::DIR_HOME, FILE_PATH_LITERAL(".bash_login"), true, false},
+      {base::DIR_HOME, FILE_PATH_LITERAL(".bashrc"), true, false},
+      {base::DIR_HOME, FILE_PATH_LITERAL(".profile"), true, false},
+      {base::DIR_HOME, FILE_PATH_LITERAL(".zshenv"), true, false},
+      {base::DIR_HOME, FILE_PATH_LITERAL(".zprofile"), true, false},
+      {base::DIR_HOME, FILE_PATH_LITERAL(".zshrc"), true, false},
+      {base::DIR_HOME, FILE_PATH_LITERAL(".zlogin"), true, false},
 
 #if BUILDFLAG(IS_WIN)
       {base::DIR_PROGRAM_FILES, nullptr, true, false},
@@ -1880,6 +1983,7 @@
 #endif
 
 #if BUILDFLAG(IS_MAC)
+      {kNoBasePathKey, FILE_PATH_LITERAL("/dev"), true, false},
       {base::DIR_APP_DATA, nullptr, true, false},
       {chrome::DIR_OUTER_BUNDLE, nullptr, true, false},
       {base::DIR_HOME, FILE_PATH_LITERAL("Applications"), true, false},
@@ -1900,8 +2004,11 @@
       {kNoBasePathKey, FILE_PATH_LITERAL("/sys"), true, false},
       {kNoBasePathKey, FILE_PATH_LITERAL("/boot"), true, false},
       {kNoBasePathKey, FILE_PATH_LITERAL("/etc"), true, false},
-      {base::DIR_HOME, FILE_PATH_LITERAL(".config"), true, false},
       {base::DIR_HOME, FILE_PATH_LITERAL(".dbus"), true, false},
+      {base::DIR_HOME, FILE_PATH_LITERAL(".local/share"), true, false},
+      {base::DIR_HOME, FILE_PATH_LITERAL(".fonts.conf"), true, false},
+      {base::DIR_HOME, FILE_PATH_LITERAL(".fonts.conf.d"), true, false},
+      {base::DIR_HOME, FILE_PATH_LITERAL(".pki"), true, false},
 #endif
 
 #if BUILDFLAG(IS_ANDROID)
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.