Repository: brave/brave-core
Pull request #28389: [plaster] Initial prototype
Author: login: cdesouza-chromium
Labels: name: CI/skip
description: Do not run CI builds (except noplatform)
name: needs-security-review
name: puLL-Merge
Created: 2025-03-30T01:20:53Z | Merged: 2025-05-15T23:03:14Z

This is the initial prototype for 🩹 Plaster. This work is based on a
recenlty worked out proposal:

https://docs.google.com/document/d/1aPf7cMB5Pc_ssKFC_I4-mLHr-7D4lTqsvItSXY-WzQM/

Run this tool with:

```
tools/cr/plaster.py
```

The tool at the moment is in a very early prototyping stage, and there
are many tickets opened for it, to get it into a mature state. The
intent is to manage to deliver this in gradual steps, while acquiring
experience alongside the way.

- Plaster files.

These are TOML files, even though they look pretty similar to ini files.

Each `[[substitution]]` entry represents an individual regex operation to be
applied to a file. The format for plasters is something like this

```toml
[[substitution]]
description = ''
re_pattern = ''
replace = ''
re_flags = ''
count = 0
```

A user can list as many of these entries as necessary for each regex
that one desires to apply to a given source.

<!-- Add brave-browser issue below that this PR will resolve -->
Resolves https://github.com/brave/brave-browser/issues/45051

<!-- CI-related labels that can be applied to this PR:
* CI/run-audit-deps (1) - check for known npm/cargo vulnerabilities (audit_deps)
* CI/run-network-audit (1) - run network-audit
* CI/run-upstream-tests - run Chromium unit and browser tests on Linux and Windows (otherwise only on Linux)
* CI/run-linux-arm64, CI/run-macos-x64, CI/run-windows-arm64, CI/run-windows-x86 - run builds that would otherwise be skipped
* CI/skip - do not run CI builds (except noplatform)
* CI/skip-linux-x64, CI/skip-android, CI/skip-macos-arm64, CI/skip-ios, CI/skip-windows-x64 - skip CI builds for specific platforms
* CI/skip-upstream-tests - do not run Chromium unit, or browser tests (otherwise only on Linux)
* CI/skip-all-linters - do not run presubmit and lint checks
* CI/storybook-url (1) - deploy storybook and provide a unique URL for each build

(1) applied automatically when some files are changed (see: https://github.com/brave/brave-core/blob/master/.github/labeler.yml)
-->

<!--
## Checklist:

- Review design docs
  [Browser design principles](https://chromium.googlesource.com/chromium/src/+/refs/heads/main/docs/chrome_browser_design_principles.md)
  [Style guide](https://chromium.googlesource.com/chromium/src/+/main/styleguide/c++/c++.md)
  [Core principles](https://www.chromium.org/developers/core-principles/)
- Ensure there are (tests)[https://www.chromium.org/developers/testing/]. Unit test as much as possible (including edge cases), but also include browser tests covering high level functionality.
- Ensure that there are comments explaining what classes/methods are/do. The "why" is often more important than the "what" in comments. Also update any relevant docs (moving docs from wiki to brave-core if necessary).
- Request security or other review (third-party libraries, rust code, etc...) if applicable [security/privacy review is needed](https://github.com/brave/brave-browser/wiki/Security-reviews) [other review](https://github.com/brave/reviews/issues/new/choose)
  Also see [adding third-party libraries](https://chromium.googlesource.com/chromium/src/+/refs/heads/main/docs/adding_to_third_party.md) for general guidelines on using third party code
- Maks sure there is a [ticket](https://github.com/brave/brave-browser/issues) for your issue
- Use Github [auto-closing keywords](https://docs.github.com/en/github/managing-your-work-on-github/linking-a-pull-request-to-an-issue) in the PR description above
- Write a good [PR/commit description](https://google.github.io/eng-practices/review/developer/cl-descriptions.html)
- Squash any review feedback or "fixup" commits before merge, so that history is a record of what happened in the repo, not your PR
- Add appropriate labels (`QA/Yes` or `QA/No`; `release-notes/include` or `release-notes/exclude`; `OS/...`) to the associated issue
- Checked the PR locally:
  * `npm run test -- brave_browser_tests`, `npm run test -- brave_unit_tests` [wiki](https://github.com/brave/brave-browser/wiki/Tests)
  * `npm run presubmit` [wiki](https://github.com/brave/brave-browser/wiki/Presubmit-checks), `npm run gn_check`, `npm run tslint`
- Run `git rebase master` (if needed)
-->



Diff:
diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS
index c6f47eecf7a4..b98d4406866d 100644
--- a/.github/CODEOWNERS
+++ b/.github/CODEOWNERS
@@ -7,6 +7,7 @@ script/uplift.py @bsclifton
 /README.md @bsclifton
 script/deps.py @brave/deps-reviewers
 package*.json @brave/js-deps-reviewers
+rewrite/ @bridiver @cdesouza-chromium
 
 # Views
 chromium_src/ui/views/view.h @bridiver
diff --git a/chromium_presubmit_config.json5 b/chromium_presubmit_config.json5
index 4cd10f7fc914..fce47eaa333e 100644
--- a/chromium_presubmit_config.json5
+++ b/chromium_presubmit_config.json5
@@ -62,6 +62,7 @@
     "ios/brave-ios/Sources/Brave/Frontend/UserContent/UserScripts/Scripts_Dynamic/Scripts/Sandboxed/DarkReaderScript.js",
     "components/brave_wallet/resources/solana_web3_script\\.js",
     "third_party/rust/",
+    "tools/cr/test/plasters/",
     "tools/crates/",
     "win_build_output/",
     "third_party/ios_deps/",
diff --git a/docs/README.md b/docs/README.md
index 263c74d7fd0b..71ddb0eef53e 100644
--- a/docs/README.md
+++ b/docs/README.md
@@ -30,8 +30,10 @@ This is only an estimate. The **github** view may differ.
 ## Document Index
 
 ### Checking Out and Patching
-*   [Upgrading Chromium](chromium_version_upgrade.md) Upgrading `brave-core` to a newer
-    Chromium version.
+*   [Upgrading Chromium](chromium_version_upgrade.md) Upgrading `brave-core` to
+    a newer Chromium version.
+*   [Patching with `plaster`](plaster.md) A patching engine for semantical
+    patching of upstream Chromium.
 *   [WebUI Overriding](webui_overriding.md)
 *   [DevTools Frontend Patching](devtools_frontend_patching.md) Managing
     changes to the upstream project.
diff --git a/docs/plaster.md b/docs/plaster.md
new file mode 100644
index 000000000000..c4605a3395d4
--- /dev/null
+++ b/docs/plaster.md
@@ -0,0 +1,130 @@
+# 🩹 Plaster and semantical patching
+
+Plaster is an experimental tool being introduced in Brave to allow us to apply
+changes to upstream sources files, by relying on regex transformations for to
+search for patterns and apply substitutions.
+
+## Why 🩹 Plaster
+
+The two traditional approaches to introduce changes to Chromium have been `git`
+patches (i.e. `patches/`), and language overrides (i.e. `chromium_src/`). The
+use of patches is in general avoided whenever possible, as they tend to easily
+run into conflicts when rebasing Brave onto a newer Chromium version. With
+language overrides, although more flexible than patches, they tend to be
+invisible in the source code target, hard to interpret, and lead to many cases
+of unintended replacements.
+
+Using regexes, Plaster avoids the brittleness of patch files, while providing
+matching mechanisms that are more flexible than the methods currently employed
+for macro replacement. This also means a language-agnostic way to approach
+source changes semantically, and in place. Plaster changes can be both seen in
+place, as well as audit as patch files.
+
+## How does it work
+
+Plaster files are placed under `rewrite/`, using a `.toml` extension, and they
+are supposed to match the path for the file being plastered.
+
+> [!WARNING]
+> At the moment, only plaster files to sources in Chromium's `src` repo are
+> supported.
+
+Each plaster file will be used to apply changes into a given source, and then
+generate a patch for the effected changes.
+
+For example, imagine you want to add changes to
+`chrome/browser/autocomplete/autocomplete_classifier_factory.cc`. You would
+care about the following files.
+
+ * **Plaster file:** `brave/rewrite/chrome/browser/autocomplete/autocomplete_classifier_factory.cc.toml`
+ * **Source file:** `chrome/browser/autocomplete/autocomplete_classifier_factory.cc`
+ * **Patch file** `brave/patches/chrome-browser-autocomplete-autocomplete_classifier_factory.cc.patch`
+
+### Creating a plaster file
+
+A plaster file is a `toml` file used to list regexes operations to be applied
+in the corresponding source, based on its path. The following is plaster file
+to apply a few changes to
+`chrome/browser/autocomplete/autocomplete_classifier_factory.cc`.
+
+Creating the source file with `vscode`:
+```sh
+code rewrite/chrome/browser/autocomplete/autocomplete_classifier_factory.cc.toml
+```
+
+This file below has two substitutions to be applied on its source: The first
+adds a header to the list of headers in the source. The second one replaces all
+occurrences of `ChromeAutocompleteSchemeClassifier` with
+`BraveAutocompleteSchemeClassifier`.
+
+```toml
+# Copyright (c) 2025 The Brave Authors. All rights reserved.
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this file,
+# You can obtain one at https://mozilla.org/MPL/2.0/.
+
+[[substitutions]]
+description = 'Adding header for BraveAutocompleteSchemeClassifier'
+pattern = '#include "extensions/buildflags/buildflags.h"'
+re_pattern = '''#include "extensions/buildflags/buildflags.h"
+#include "brave/browser/autocomplete/brave_autocomplete_scheme_classifier.h"'''
+
+[[substitutions]]
+description = 'Patching in BraveAutocompleteSchemeClassifier'
+re_pattern = 'ChromeAutocompleteSchemeClassifier'
+replace = 'BraveAutocompleteSchemeClassifier'
+```
+
+The basic format for a `[[substitution]]` entry is as follow:
+
+```toml
+[[plaster]]
+description = ''
+re_pattern = ''
+replace = ''
+re_flags = ''  # These are traditional python regex flags.
+count = 0
+```
+
+To apply this plaster file, just run `plaster.py`:
+
+```sh
+tools/cr/plaster.py
+```
+
+Running `plaster.py` will cause the substitutions to be applied in
+`chrome/browser/autocomplete/autocomplete_classifier_factory.cc`, but it will
+cause the creation/update of the corresponding patch file for this source. For
+exmample, it may produce a diff file like this:
+
+```
+diff --git a/patches/chrome-browser-autocomplete-autocomplete_classifier_factory.cc.patch b/patches/chrome-browser-autocomplete-autocomplete_classifier_factory.cc.patch
+new file mode 100644
+index 00000000000..63703e6940b
+--- /dev/null
++++ b/patches/chrome-browser-autocomplete-autocomplete_classifier_factory.cc.patch
+@@ -0,0 +1,21 @@
++diff --git a/chrome/browser/autocomplete/autocomplete_classifier_factory.cc b/chrome/browser/autocomplete/autocomplete_classifier_factory.cc
++index db73283fbc4f5146bb42e2a8abfcdfa4567ab252..f241c6c5f0611dcbcad3e468a7595be75324a8ba 100644
++--- a/chrome/browser/autocomplete/autocomplete_classifier_factory.cc
+++++ b/chrome/browser/autocomplete/autocomplete_classifier_factory.cc
++@@ -16,6 +16,7 @@
++ #include "components/omnibox/browser/autocomplete_classifier.h"
++ #include "components/omnibox/browser/autocomplete_controller.h"
++ #include "extensions/buildflags/buildflags.h"
+++#include "brave/browser/autocomplete/brave_autocomplete_scheme_classifier.h"
++
++ #if BUILDFLAG(ENABLE_EXTENSIONS)
++ #include "extensions/browser/extension_system_provider.h"
++@@ -43,7 +44,7 @@ std::unique_ptr<KeyedService> AutocompleteClassifierFactory::BuildInstanceFor(
++       std::make_unique<AutocompleteController>(
++           std::make_unique<ChromeAutocompleteProviderClient>(profile),
++           AutocompleteClassifier::DefaultOmniboxProviders()),
++-      std::make_unique<ChromeAutocompleteSchemeClassifier>(profile));
+++      std::make_unique<BraveAutocompleteSchemeClassifier>(profile));
++ }
++
++ AutocompleteClassifierFactory::AutocompleteClassifierFactory()
+```
+
+The produced patch is supposed to be committed in the repository as usual.
\ No newline at end of file
diff --git a/tools/DEPS b/tools/DEPS
new file mode 100644
index 000000000000..20c1efd034d5
--- /dev/null
+++ b/tools/DEPS
@@ -0,0 +1,4 @@
+# checkdeps.py shouldn't check include paths for files in these dirs:
+skip_child_includes = [
+  "cr/test/plasters",
+]
diff --git a/tools/cr/plaster.py b/tools/cr/plaster.py
new file mode 100755
index 000000000000..e1be0ef98a3f
--- /dev/null
+++ b/tools/cr/plaster.py
@@ -0,0 +1,152 @@
+#!/usr/bin/env vpython3
+# Copyright (c) 2025 The Brave Authors. All rights reserved.
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this file,
+# You can obtain one at https://mozilla.org/MPL/2.0/.
+
+import argparse
+from dataclasses import dataclass, field
+import logging
+from pathlib import Path, PurePath
+import os
+import re
+import sys
+import tomllib
+
+from terminal import console, terminal
+from incendiary_error_handler import IncendiaryErrorHandler
+import repository
+from repository import Repository
+
+# The path to the directory containing overlay files in brave-core.
+OVERLAY_FILES_PATH = repository.BRAVE_CORE_PATH / 'rewrite'
+
+# The path to the directory where patch files are stored in brave-core.
+PATCHES_PATH = Path('patches/')
+
+
+@dataclass
+class PlasterFile:
+    """ Class representing an overlay file.
+    This class is used to apply overlay files to sources in other repositories.
+    """
+
+    # The path to the overlay file. This path is relative to
+    # BRAVE_CORE_PATH.
+    path: Path
+
+    # The source file path. This path is relative to the source's own
+    # repository.
+    source: Path = field(init=False)
+
+    def __post_init__(self):
+        # TODO(https://github.com/brave/brave-browser/issues/45052): For now
+        # there's only support for `src`, but eventually there's going to be
+        # the need to support other repositories as well.
+        object.__setattr__(
+            self, 'source',
+            self.path.relative_to(OVERLAY_FILES_PATH).with_suffix(''))
+
+        # This is made as a path validation, as the path has to be under brave
+        # to have its own path used as a reference.
+        self.path = self.path.relative_to(repository.BRAVE_CORE_PATH)
+
+    @property
+    def patchfile(self) -> Path:
+        """ Returns the path to the patch file in brave-core.
+        """
+        return PATCHES_PATH / f'{str(self.source).replace("/", "-")}.patch'
+
+    @classmethod
+    def find_all(cls) -> list["PlasterFile"]:
+        """ Finds all overlay files in the overlay directory.
+        Returns:
+            A list of PlasterFile objects.
+        """
+        overlay_files = []
+
+        for root, _, files in os.walk(OVERLAY_FILES_PATH):
+            for file in files:
+                if file.endswith('.toml'):
+                    overlay_files.append(cls(Path(root) / file))
+
+        return overlay_files
+
+    def apply(self):
+        overlay = tomllib.loads(self.path.read_text())
+        contents = repository.chromium.read_file(self.source)
+
+        for substitution in overlay.get('substitution', []):
+            pattern = substitution.get('re_pattern', '')
+            replace = substitution.get('replace', '')
+            count = substitution.get('count', 0)
+            flags = substitution.get('re_flags', '')
+
+            re_flags = 0
+            if 'IGNORECASE' in flags:
+                re_flags |= re.IGNORECASE
+            if 'MULTILINE' in flags:
+                re_flags |= re.MULTILINE
+            if 'DOTALL' in flags:
+                re_flags |= re.DOTALL
+            if 'VERBOSE' in flags:
+                re_flags |= re.VERBOSE
+
+            contents, num_changes = re.subn(pattern,
+                                            replace,
+                                            contents,
+                                            flags=re_flags,
+                                            count=count)
+
+            if num_changes == 0:
+                raise ValueError(
+                    f'No matches found for pattern {pattern} in {self.source}')
+
+        def save_if_has_changes(path: Path, content: str):
+            """Saves the file content if it has changed.
+            """
+            if not path.parent.exists():
+                path.parent.mkdir(parents=True, exist_ok=True)
+            if path.exists() and path.read_text(encoding='utf-8') == content:
+                return
+            logging.debug('Saving: %s', path)
+            path.write_text(content, encoding='utf-8')
+
+        save_if_has_changes(Path(repository.chromium.from_brave(self.source)),
+                            contents)
+
+        patch_content = (repository.chromium.run_git('diff',
+                                                     '--src-prefix=a/',
+                                                     '--dst-prefix=b/',
+                                                     '--default-prefix',
+                                                     '--full-index',
+                                                     self.source,
+                                                     no_trim=True))
+        save_if_has_changes(Path(self.patchfile), patch_content)
+
+
+def main():
+    parser = argparse.ArgumentParser(
+        description=
+        '🩹 A tool to generate patches into Chromium using overlay files.')
+    parser.add_argument('--verbose',
+                        action='store_true',
+                        help='Enable verbose logging')
+    args = parser.parse_args()
+
+    logging.basicConfig(
+        level=logging.DEBUG if args.verbose else logging.INFO,
+        format='%(message)s',
+        handlers=[IncendiaryErrorHandler(markup=True, rich_tracebacks=True)])
+
+    with terminal.with_status('Applying overlay files'):
+        overlay_files = PlasterFile.find_all()
+        for overlay_file in overlay_files:
+            console.log(f'Applying overlay file: {overlay_file.path}')
+            overlay_file.apply()
+
+    return 0
+
+
+if __name__ == '__main__':
+    sys.exit(main())
diff --git a/tools/cr/plaster_test.py b/tools/cr/plaster_test.py
new file mode 100755
index 000000000000..e326e82c6841
--- /dev/null
+++ b/tools/cr/plaster_test.py
@@ -0,0 +1,70 @@
+#!/usr/bin/env vpython3
+# Copyright (c) 2025 The Brave Authors. All rights reserved.
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this file,
+# You can obtain one at https://mozilla.org/MPL/2.0/.
+
+import unittest
+from pathlib import Path
+
+import plaster
+
+from test.fake_chromium_src import FakeChromiumSrc
+
+
+class PlasterTest(unittest.TestCase):
+
+    def setUp(self):
+        """Set up a fake Chromium repository for testing."""
+        self.fake_chromium_src = FakeChromiumSrc()
+        self.fake_chromium_src.setup()
+        self.addCleanup(self.fake_chromium_src.cleanup)
+
+        # Override OVERLAY_FILES_PATH to use the rewrite path in the fake Brave
+        # repo
+        plaster.OVERLAY_FILES_PATH = self.fake_chromium_src.brave / 'rewrite'
+
+    def test_original_expected_toml_rules(self):
+        """Test applying all .toml files in the test/ folder."""
+        test_folder = Path(__file__).parent / 'test/plasters'
+        toml_files = test_folder.rglob('*.toml')
+
+        for toml_file in toml_files:
+            with self.subTest(toml_file=toml_file):
+                # Deduce the path for the original file for the toml to be
+                # applied to.
+                original_file_to = (toml_file.parent / toml_file.stem)
+                original_file_from = original_file_to.with_stem(
+                    original_file_to.stem + '-original')
+                expected_file = original_file_to.with_stem(
+                    original_file_to.stem + '-expected')
+
+                # Commit the original file to the fake Chromium repository.
+                self.fake_chromium_src.write_and_stage_file(
+                    relative_path=original_file_to.name,
+                    content=original_file_from.read_text(),
+                    repo_path=self.fake_chromium_src.chromium)
+                self.fake_chromium_src.commit(
+                    commit_message=f'Add {original_file_from.name}',
+                    repo_path=self.fake_chromium_src.chromium)
+
+                # Copy the .toml file to the fake Brave rewrite path.
+                rewrite_path = plaster.OVERLAY_FILES_PATH / toml_file.name
+                rewrite_path.parent.mkdir(parents=True, exist_ok=True)
+                rewrite_path.write_text(toml_file.read_text())
+
+                # Use PlasterFile to apply the .toml file to the committed file.
+                overlay_file = plaster.PlasterFile(rewrite_path)
+                overlay_file.apply()
+
+                # Check that the committed file matches the expected file.
+                committed_file_path = (self.fake_chromium_src.chromium /
+                                       original_file_to.name)
+                self.assertEqual(
+                    committed_file_path.read_text(), expected_file.read_text(),
+                    f'File {committed_file_path} does not match '
+                    f'expected {expected_file}')
+
+
+if __name__ == '__main__':
+    unittest.main()
diff --git a/tools/cr/test/plasters/.clang-format b/tools/cr/test/plasters/.clang-format
new file mode 100644
index 000000000000..9d159247d518
--- /dev/null
+++ b/tools/cr/test/plasters/.clang-format
@@ -0,0 +1,2 @@
+DisableFormat: true
+SortIncludes: false
diff --git a/tools/cr/test/plasters/add_header_after_line-expected.cc b/tools/cr/test/plasters/add_header_after_line-expected.cc
new file mode 100644
index 000000000000..e94ee395ecb7
--- /dev/null
+++ b/tools/cr/test/plasters/add_header_after_line-expected.cc
@@ -0,0 +1,24 @@
+// Copyright 2012 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/autocomplete/autocomplete_classifier_factory.h"
+
+#include <memory>
+
+#include "chrome/browser/autocomplete/chrome_autocomplete_provider_client.h"
+#include "chrome/browser/autocomplete/chrome_autocomplete_scheme_classifier.h"
+#include "chrome/browser/autocomplete/in_memory_url_index_factory.h"
+#include "chrome/browser/autocomplete/remote_suggestions_service_factory.h"
+#include "chrome/browser/autocomplete/shortcuts_backend_factory.h"
+#include "chrome/browser/profiles/profile.h"
+#include "chrome/browser/search_engines/template_url_service_factory.h"
+#include "components/omnibox/browser/autocomplete_classifier.h"
+#include "components/omnibox/browser/autocomplete_controller.h"
+#include "extensions/buildflags/buildflags.h"
+#include "brave/browser/autocomplete/brave_autocomplete_scheme_classifier.h"
+
+#if BUILDFLAG(ENABLE_EXTENSIONS)
+#include "extensions/browser/extension_system_provider.h"
+#include "extensions/browser/extensions_browser_client.h"
+#endif
diff --git a/tools/cr/test/plasters/add_header_after_line-original.cc b/tools/cr/test/plasters/add_header_after_line-original.cc
new file mode 100644
index 000000000000..8270fc57dcee
--- /dev/null
+++ b/tools/cr/test/plasters/add_header_after_line-original.cc
@@ -0,0 +1,23 @@
+// Copyright 2012 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/autocomplete/autocomplete_classifier_factory.h"
+
+#include <memory>
+
+#include "chrome/browser/autocomplete/chrome_autocomplete_provider_client.h"
+#include "chrome/browser/autocomplete/chrome_autocomplete_scheme_classifier.h"
+#include "chrome/browser/autocomplete/in_memory_url_index_factory.h"
+#include "chrome/browser/autocomplete/remote_suggestions_service_factory.h"
+#include "chrome/browser/autocomplete/shortcuts_backend_factory.h"
+#include "chrome/browser/profiles/profile.h"
+#include "chrome/browser/search_engines/template_url_service_factory.h"
+#include "components/omnibox/browser/autocomplete_classifier.h"
+#include "components/omnibox/browser/autocomplete_controller.h"
+#include "extensions/buildflags/buildflags.h"
+
+#if BUILDFLAG(ENABLE_EXTENSIONS)
+#include "extensions/browser/extension_system_provider.h"
+#include "extensions/browser/extensions_browser_client.h"
+#endif
diff --git a/tools/cr/test/plasters/add_header_after_line.cc.toml b/tools/cr/test/plasters/add_header_after_line.cc.toml
new file mode 100644
index 000000000000..af2f51b14c18
--- /dev/null
+++ b/tools/cr/test/plasters/add_header_after_line.cc.toml
@@ -0,0 +1,10 @@
+# Copyright (c) 2025 The Brave Authors. All rights reserved.
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this file,
+# You can obtain one at https://mozilla.org/MPL/2.0/.
+
+[[substitution]]
+description = 'Test for adding a header after another line'
+re_pattern = '#include "extensions/buildflags/buildflags.h"'
+replace = '''#include "extensions/buildflags/buildflags.h"
+#include "brave/browser/autocomplete/brave_autocomplete_scheme_classifier.h"'''
diff --git a/tools/cr/test/plasters/simple_replacement-expected.cc b/tools/cr/test/plasters/simple_replacement-expected.cc
new file mode 100644
index 000000000000..52930d2cc43d
--- /dev/null
+++ b/tools/cr/test/plasters/simple_replacement-expected.cc
@@ -0,0 +1,16 @@
+// Copyright 2012 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/autocomplete/autocomplete_classifier_factory.h"
+
+// static
+std::unique_ptr<KeyedService> AutocompleteClassifierFactory::BuildInstanceFor(
+    content::BrowserContext* context) {
+  Profile* profile = static_cast<Profile*>(context);
+  return std::make_unique<AutocompleteClassifier>(
+      std::make_unique<AutocompleteController>(
+          std::make_unique<ChromeAutocompleteProviderClient>(profile),
+          AutocompleteClassifier::DefaultOmniboxProviders()),
+      std::make_unique<BraveAutocompleteSchemeClassifier>(profile));
+}
diff --git a/tools/cr/test/plasters/simple_replacement-original.cc b/tools/cr/test/plasters/simple_replacement-original.cc
new file mode 100644
index 000000000000..49e3dbed3468
--- /dev/null
+++ b/tools/cr/test/plasters/simple_replacement-original.cc
@@ -0,0 +1,16 @@
+// Copyright 2012 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/autocomplete/autocomplete_classifier_factory.h"
+
+// static
+std::unique_ptr<KeyedService> AutocompleteClassifierFactory::BuildInstanceFor(
+    content::BrowserContext* context) {
+  Profile* profile = static_cast<Profile*>(context);
+  return std::make_unique<AutocompleteClassifier>(
+      std::make_unique<AutocompleteController>(
+          std::make_unique<ChromeAutocompleteProviderClient>(profile),
+          AutocompleteClassifier::DefaultOmniboxProviders()),
+      std::make_unique<ChromeAutocompleteSchemeClassifier>(profile));
+}
diff --git a/tools/cr/test/plasters/simple_replacement.cc.toml b/tools/cr/test/plasters/simple_replacement.cc.toml
new file mode 100644
index 000000000000..a900ad7ab2ad
--- /dev/null
+++ b/tools/cr/test/plasters/simple_replacement.cc.toml
@@ -0,0 +1,9 @@
+# Copyright (c) 2025 The Brave Authors. All rights reserved.
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this file,
+# You can obtain one at https://mozilla.org/MPL/2.0/.
+
+[[substitution]]
+description = 'Test for indiscriminte replacements'
+re_pattern = 'ChromeAutocompleteSchemeClassifier'
+replace = 'BraveAutocompleteSchemeClassifier'


Reviews:
user: login: github-actions[bot]
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-30T01:27:29Z
user: login: cdesouza-chromium
state: COMMENTED
author_association: COLLABORATOR
submitted_at: 2025-03-30T05:07:25Z
user: login: cdesouza-chromium
state: COMMENTED
author_association: COLLABORATOR
submitted_at: 2025-03-30T05:08:00Z
user: login: cdesouza-chromium
state: COMMENTED
author_association: COLLABORATOR
submitted_at: 2025-03-30T05:08:24Z
user: login: cdesouza-chromium
state: COMMENTED
author_association: COLLABORATOR
submitted_at: 2025-03-30T05:08:33Z
user: login: copilot-pull-request-reviewer[bot]
body: ## Pull Request Overview

This PR introduces the initial prototype for 🩹 Plaster, a tool to apply overlay file patches to the Chromium source. Key changes include:
- Implementation of a Terminal class with an infra mode and keep-alive mechanism.
- New repository utilities to manage source paths and git operations.
- A new overlay file processor (plaster.py) that applies regex‐based changes and generates patch files.

### Reviewed Changes

Copilot reviewed 6 out of 13 changed files in this pull request and generated 2 comments.

<details>
<summary>Show a summary per file</summary>

| File                                    | Description                                                            |
| --------------------------------------- | ---------------------------------------------------------------------- |
| tools/cr/terminal.py                    | New Terminal class with infra mode, keep-alive pings and status updates  |
| tools/cr/repository.py                  | Repository helper functions for git operations and path handling       |
| tools/cr/plaster.py                     | Overlay file processing and regex-based patch application logic         |
| tools/cr/incendiary_error_handler.py    | Custom rich error handler introducing emoji-enhanced error messages     |
| tools/cr/brockit.py                     | Removal of redundant terminal code in favor of shared utilities           |
| build/commands/scripts/updatePatches.js | Updated path filter logic with minor comment improvements                 |
</details>


<details>
<summary>Files not reviewed (7)</summary>

* **build/update_patches_exclusions.cfg**: Language not supported
* **chromium_src/chrome/browser/browser_process_impl.h**: Language not supported
* **chromium_src/content/browser/web_package/signed_exchange_utils.cc**: Language not supported
* **patches/chrome-browser-browser_process_impl.h.patch**: Language not supported
* **patches/content-browser-web_package-signed_exchange_utils.cc.patch**: Language not supported
* **rewrite/chrome/browser/browser_process_impl.h.overlay**: Language not supported
* **rewrite/content/browser/web_package/signed_exchange_utils.cc.overlay**: Language not supported
</details>



state: COMMENTED
author_association: NONE
submitted_at: 2025-03-30T13:26:46Z
user: login: cdesouza-chromium
state: COMMENTED
author_association: COLLABORATOR
submitted_at: 2025-03-30T13:29:06Z
user: login: cdesouza-chromium
state: COMMENTED
author_association: COLLABORATOR
submitted_at: 2025-03-30T13:29:14Z
user: login: bridiver
state: COMMENTED
author_association: COLLABORATOR
submitted_at: 2025-04-07T16:19:59Z
user: login: bridiver
state: COMMENTED
author_association: COLLABORATOR
submitted_at: 2025-04-07T16:20:36Z
user: login: bridiver
state: COMMENTED
author_association: COLLABORATOR
submitted_at: 2025-04-07T16:24:37Z
user: login: bridiver
state: COMMENTED
author_association: COLLABORATOR
submitted_at: 2025-04-07T16:28:03Z
user: login: bridiver
state: COMMENTED
author_association: COLLABORATOR
submitted_at: 2025-04-07T16:37:57Z
user: login: bridiver
state: COMMENTED
author_association: COLLABORATOR
submitted_at: 2025-04-07T16:40:40Z
user: login: cdesouza-chromium
state: COMMENTED
author_association: COLLABORATOR
submitted_at: 2025-04-07T16:51:54Z
user: login: cdesouza-chromium
state: COMMENTED
author_association: COLLABORATOR
submitted_at: 2025-04-07T17:16:51Z
user: login: cdesouza-chromium
state: COMMENTED
author_association: COLLABORATOR
submitted_at: 2025-04-07T17:17:46Z
user: login: cdesouza-chromium
state: COMMENTED
author_association: COLLABORATOR
submitted_at: 2025-04-07T17:18:27Z
user: login: cdesouza-chromium
state: COMMENTED
author_association: COLLABORATOR
submitted_at: 2025-04-07T20:56:39Z
user: login: cdesouza-chromium
state: COMMENTED
author_association: COLLABORATOR
submitted_at: 2025-04-13T12:37:28Z
user: login: cdesouza-chromium
state: COMMENTED
author_association: COLLABORATOR
submitted_at: 2025-04-19T12:23:20Z
user: login: cdesouza-chromium
state: COMMENTED
author_association: COLLABORATOR
submitted_at: 2025-04-19T12:26:08Z
user: login: bridiver
state: COMMENTED
author_association: COLLABORATOR
submitted_at: 2025-04-30T22:20:50Z
user: login: bridiver
state: COMMENTED
author_association: COLLABORATOR
submitted_at: 2025-04-30T22:21:48Z
user: login: bridiver
state: COMMENTED
author_association: COLLABORATOR
submitted_at: 2025-04-30T22:32:32Z
user: login: cdesouza-chromium
state: COMMENTED
author_association: COLLABORATOR
submitted_at: 2025-05-13T13:52:43Z
user: login: cdesouza-chromium
state: COMMENTED
author_association: COLLABORATOR
submitted_at: 2025-05-13T13:55:30Z
user: login: cdesouza-chromium
state: COMMENTED
author_association: COLLABORATOR
submitted_at: 2025-05-13T13:55:55Z
user: login: emerick
state: APPROVED
author_association: CONTRIBUTOR
submitted_at: 2025-05-14T15:58:34Z
user: login: bridiver
state: APPROVED
author_association: COLLABORATOR
submitted_at: 2025-05-14T17:08:38Z

Review comments:
diff_hunk: @@ -0,0 +1,259 @@
+#!/usr/bin/env python3
+# Copyright (c) 2025 The Brave Authors. All rights reserved.
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this file,
+# You can obtain one at https://mozilla.org/MPL/2.0/.
+
+import argparse
+import logging
+from pathlib import Path, PurePath
+import os
+import re
+import sys
+from dataclasses import dataclass, field
+
+from terminal import console, terminal
+from incendiary_error_handler import IncendiaryErrorHandler
+from repository import Repository, BRAVE_CORE_PATH, CHROMIUM_SRC_PATH
+
+# The path to the directory containing overlay files in brave-core.
+OVERLAY_FILES_PATH = BRAVE_CORE_PATH / 'rewrite'
+
+# The path to the directory where patch files are stored in brave-core.
+PATCHES_PATH = PurePath('patches/')
+
+
+# TODO(https://github.com/brave/brave-browser/issues/45061): These templates
+# should be inside some sort of class/module, that we can import in bulk based
+# on the language they are better fit for use (e.g CxxTemplates).
+def CancelFunctionUsingEarlyReturn(return_type: str, function_name: str,
+                                   replacement: str) -> dict:
+    return {
+        "pattern": rf"""
+       (                # Group 1: Capture the function header up to and
+                        # including the opening brace
+           \b{re.escape(return_type)}\s+
+                        # Match the return type at a word boundary followed by
+                        # one or more spaces
+
+                        # Match the exact function name
+           {re.escape(function_name)}"""
+        r"""
+           \s*\(.*?\)   # Match the parameter list: optional spaces,
+                        # literal '(', non-greedy capture of parameters, then
+                        # literal ')'
+           \s*\{        # Match optional spaces and then the opening curly
+                        # brace '{'
+           \s*\n        # Match any spaces and the following newline character.
+       )
+       ([ \t]*)         # Group 2: Capture any indentation (spaces or tabs)
+                        # from the beginning of the next line
+       """,
+        "replacement": rf"\1\2if ((true)) {replacement}\n\2",
+        "flags": ["MULTILINE", "DOTALL", "VERBOSE"],
+    }
+
+
+def VirtualiseFunction(function_name: str) -> dict:
+    return {
+        "pattern": r"""
+        ^(?P<indent>\s*)    # Capture any leading indentation.
+        (?!virtual\s)       # Do not match if 'virtual' follows the indentation.
+        (?P<ret>.*?)        # Non-greedily capture the return type & qualifiers.
+        \b(?P<fn>{func})\b  # The function name, with word boundaries.
+        (?P<args>\s*        # Capture the following as 'args':
+            \( .*? \)       #   - Non-greedy capture of the parameter list in
+                            #     parentheses.
+            \s*             #   - Optional whitespace.
+            (?:const\s*)?   #   - Optional trailing 'const'.
+            ;               #   - The semicolon ending the declaration.
+        )
+    """.format(func=re.escape(function_name)),
+        "replacement": r"\g<indent>virtual \g<ret>\g<fn>\g<args>",
+        "flags": ["MULTILINE", "VERBOSE"],
+    }
+
+
+def MakeFriendClassOf(class_name: str, friend_class: str) -> dict:
+    return {
+        "pattern": r"""
+        (?sx)                       # Enable DOTALL & VERBOSE modes
+        (                           # Begin Group 1: the header of the class
+            class\s+{classname}\b   # Match the 'class' keyword and the given
+                                    # class name
+            [^{{]*\{{               # Consume characters (until the first curly
+                                    # braces)
+            .*?                     # Lazily consume the rest of the header
+            \n                      # Until we get to a newline
+            (?P<indent>[ \t]*)      # Capture the indentation that follows for
+                                    # the next line
+            private:\s*\n           # Match the private access specifier line
+        )
+    """.format(classname=re.escape(class_name)),
+        "replacement": r"\1\g<indent> friend class " + friend_class + r";\n",
+        "flags": ["MULTILINE", "DOTALL", "VERBOSE"],
+    }
+
+
+@dataclass
+class OverlayFile:
+    """ Class representing an overlay file.
+
+    This class is used to apply overlay files to sources in other repositories.
+    """
+
+    # The path to the overlay file. This path is relative to
+    # BRAVE_CORE_PATH.
+    path: PurePath
+
+    # The source file path. This path is relative to the source's own
+    # repository.
+    source: PurePath = field(init=False)
+
+    def __post_init__(self):
+        # TODO(https://github.com/brave/brave-browser/issues/45052): For now
+        # there's only support for `src`, but eventually there's going to be
+        # the need to support other repositories as well.
+        object.__setattr__(
+            self, 'source',
+            self.path.relative_to(OVERLAY_FILES_PATH).with_suffix(''))
+
+        # This is made as a path validation, as the path has to be under brave
+        # to have its own path used as a reference.
+        self.path = self.path.relative_to(BRAVE_CORE_PATH)
+
+    @property
+    def patchfile(self) -> PurePath:
+        """ Returns the path to the patch file in brave-core.
+        """
+        return PATCHES_PATH / f'{str(self.source).replace("/", "-")}.patch'
+
+    @classmethod
+    def find_all(cls) -> list["OverlayFile"]:
+        """ Finds all overlay files in the overlay directory.
+
+        Returns:
+            A list of OverlayFile objects.
+        """
+        overlay_files = []
+
+        for root, _, files in os.walk(OVERLAY_FILES_PATH):
+            for file in files:
+                if file.endswith('.overlay'):
+                    overlay_files.append(cls(PurePath(root) / file))
+
+        return overlay_files
+
+    def apply(self):
+        """ Applies the overlay file to the source file.
+
+        This method loads an overlay file, executes it to get the
+        substitutions, and applies them to the source file. The result is
+        written to the source file, and a patch file is created in the
+        patches directory.
+        """
+
+        overlay_globals = {
+            'CancelFunctionUsingEarlyReturn': CancelFunctionUsingEarlyReturn,
+            'VirtualiseFunction': VirtualiseFunction,
+            'MakeFriendClassOf': MakeFriendClassOf,
+            'escape': re.escape,  # Provide the escape function
+        }
+
+        with open(self.path, 'r', encoding='utf-8') as f:
+            exec(f.read(), overlay_globals)
path: tools/cr/plaster.py
user: login: github-actions[bot]
body: <sub>reported by [reviewdog](https://github.com/reviewdog/reviewdog) :dog:</sub><br>[semgrep] Detected the use of exec(). exec() can be dangerous if used to evaluate dynamic content. If this content can be input from outside the program, this may be a code injection vulnerability. Ensure evaluated content is not definable by external sources.<br><br>Source: https://semgrep.dev/r/python.lang.security.audit.exec-detected.exec-detected<br><br><!-- Category: security --><br>Cc @thypon @kdenhartog 
<!-- __reviewdog__:Cg81NWU3YzMzNzdmYjU4YjM= -->

created_at: 2025-03-30T01:27:28Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,259 @@
+#!/usr/bin/env python3
+# Copyright (c) 2025 The Brave Authors. All rights reserved.
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this file,
+# You can obtain one at https://mozilla.org/MPL/2.0/.
+
+import argparse
+import logging
+from pathlib import Path, PurePath
+import os
+import re
+import sys
+from dataclasses import dataclass, field
+
+from terminal import console, terminal
+from incendiary_error_handler import IncendiaryErrorHandler
+from repository import Repository, BRAVE_CORE_PATH, CHROMIUM_SRC_PATH
+
+# The path to the directory containing overlay files in brave-core.
+OVERLAY_FILES_PATH = BRAVE_CORE_PATH / 'rewrite'
+
+# The path to the directory where patch files are stored in brave-core.
+PATCHES_PATH = PurePath('patches/')
+
+
+# TODO(https://github.com/brave/brave-browser/issues/45061): These templates
+# should be inside some sort of class/module, that we can import in bulk based
+# on the language they are better fit for use (e.g CxxTemplates).
+def CancelFunctionUsingEarlyReturn(return_type: str, function_name: str,
+                                   replacement: str) -> dict:
+    return {
+        "pattern": rf"""
+       (                # Group 1: Capture the function header up to and
+                        # including the opening brace
+           \b{re.escape(return_type)}\s+
+                        # Match the return type at a word boundary followed by
+                        # one or more spaces
+
+                        # Match the exact function name
+           {re.escape(function_name)}"""
+        r"""
+           \s*\(.*?\)   # Match the parameter list: optional spaces,
+                        # literal '(', non-greedy capture of parameters, then
+                        # literal ')'
+           \s*\{        # Match optional spaces and then the opening curly
+                        # brace '{'
+           \s*\n        # Match any spaces and the following newline character.
+       )
+       ([ \t]*)         # Group 2: Capture any indentation (spaces or tabs)
+                        # from the beginning of the next line
+       """,
+        "replacement": rf"\1\2if ((true)) {replacement}\n\2",
+        "flags": ["MULTILINE", "DOTALL", "VERBOSE"],
+    }
+
+
+def VirtualiseFunction(function_name: str) -> dict:
+    return {
+        "pattern": r"""
+        ^(?P<indent>\s*)    # Capture any leading indentation.
+        (?!virtual\s)       # Do not match if 'virtual' follows the indentation.
+        (?P<ret>.*?)        # Non-greedily capture the return type & qualifiers.
+        \b(?P<fn>{func})\b  # The function name, with word boundaries.
+        (?P<args>\s*        # Capture the following as 'args':
+            \( .*? \)       #   - Non-greedy capture of the parameter list in
+                            #     parentheses.
+            \s*             #   - Optional whitespace.
+            (?:const\s*)?   #   - Optional trailing 'const'.
+            ;               #   - The semicolon ending the declaration.
+        )
+    """.format(func=re.escape(function_name)),
+        "replacement": r"\g<indent>virtual \g<ret>\g<fn>\g<args>",
+        "flags": ["MULTILINE", "VERBOSE"],
+    }
+
+
+def MakeFriendClassOf(class_name: str, friend_class: str) -> dict:
+    return {
+        "pattern": r"""
+        (?sx)                       # Enable DOTALL & VERBOSE modes
+        (                           # Begin Group 1: the header of the class
+            class\s+{classname}\b   # Match the 'class' keyword and the given
+                                    # class name
+            [^{{]*\{{               # Consume characters (until the first curly
+                                    # braces)
+            .*?                     # Lazily consume the rest of the header
+            \n                      # Until we get to a newline
+            (?P<indent>[ \t]*)      # Capture the indentation that follows for
+                                    # the next line
+            private:\s*\n           # Match the private access specifier line
+        )
+    """.format(classname=re.escape(class_name)),
+        "replacement": r"\1\g<indent> friend class " + friend_class + r";\n",
+        "flags": ["MULTILINE", "DOTALL", "VERBOSE"],
+    }
+
+
+@dataclass
+class OverlayFile:
+    """ Class representing an overlay file.
+
+    This class is used to apply overlay files to sources in other repositories.
+    """
+
+    # The path to the overlay file. This path is relative to
+    # BRAVE_CORE_PATH.
+    path: PurePath
+
+    # The source file path. This path is relative to the source's own
+    # repository.
+    source: PurePath = field(init=False)
+
+    def __post_init__(self):
+        # TODO(https://github.com/brave/brave-browser/issues/45052): For now
+        # there's only support for `src`, but eventually there's going to be
+        # the need to support other repositories as well.
+        object.__setattr__(
+            self, 'source',
+            self.path.relative_to(OVERLAY_FILES_PATH).with_suffix(''))
+
+        # This is made as a path validation, as the path has to be under brave
+        # to have its own path used as a reference.
+        self.path = self.path.relative_to(BRAVE_CORE_PATH)
+
+    @property
+    def patchfile(self) -> PurePath:
+        """ Returns the path to the patch file in brave-core.
+        """
+        return PATCHES_PATH / f'{str(self.source).replace("/", "-")}.patch'
+
+    @classmethod
+    def find_all(cls) -> list["OverlayFile"]:
+        """ Finds all overlay files in the overlay directory.
+
+        Returns:
+            A list of OverlayFile objects.
+        """
+        overlay_files = []
+
+        for root, _, files in os.walk(OVERLAY_FILES_PATH):
+            for file in files:
+                if file.endswith('.overlay'):
+                    overlay_files.append(cls(PurePath(root) / file))
+
+        return overlay_files
+
+    def apply(self):
+        """ Applies the overlay file to the source file.
+
+        This method loads an overlay file, executes it to get the
+        substitutions, and applies them to the source file. The result is
+        written to the source file, and a patch file is created in the
+        patches directory.
+        """
+
+        overlay_globals = {
+            'CancelFunctionUsingEarlyReturn': CancelFunctionUsingEarlyReturn,
+            'VirtualiseFunction': VirtualiseFunction,
+            'MakeFriendClassOf': MakeFriendClassOf,
+            'escape': re.escape,  # Provide the escape function
+        }
+
+        with open(self.path, 'r', encoding='utf-8') as f:
+            exec(f.read(), overlay_globals)
path: tools/cr/plaster.py
user: login: github-actions[bot]
body: <sub>reported by [reviewdog](https://github.com/reviewdog/reviewdog) :dog:</sub><br>[semgrep] The application was found calling the `exec` function with a non-literal variable. If the<br/>variable comes from user-supplied input, an adversary could compromise the entire system by<br/>executing arbitrary python code.<br/><br/>To remediate this issue, remove all calls to `exec` and consider alternative methods for<br/>executing the necessary business logic. There is almost no safe method of calling `eval` <br/>with user-supplied input.<br/><br/>If the application only needs to convert strings into objects, consider using `json.loads`.<br/>In some cases `ast.literal_eval` is recommended, but this should be avoided as it can still<br/>suffer from other issues such as the ability for malicious code to crash the python<br/>interpreter or application.<br/><br/>Example using `json.loads`` to load in arbitrary data to create data structures:<br/>```<br/># User supplied data as a blob of JSON<br/>user_supplied_data = """{"user": "test", "metadata": [1,2,3]}"""<br/># Load the JSON<br/>user_object = json.loads(user_supplied_data)<br/># Manually add protected properties _after_ loading, never before<br/>user_object["is_admin"] = False<br/># Work with the object<br/>```<br/><br><br>Source: https://semgrep.dev/r/gitlab.bandit.B102<br><br><!-- Category: security --><br>Cc @thypon @kdenhartog 
<!-- __reviewdog__:ChBkZjA5NTU0Nzk3N2E5ZTU5 -->

created_at: 2025-03-30T01:27:28Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,156 @@
+# Copyright (c) 2025 The Brave Authors. All rights reserved.
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this file,
+# You can obtain one at https://mozilla.org/MPL/2.0/.
+
+from dataclasses import dataclass
+from pathlib import PurePath
+import subprocess
path: tools/cr/repository.py
user: login: github-actions[bot]
body: <sub>reported by [reviewdog](https://github.com/reviewdog/reviewdog) :dog:</sub><br>[semgrep] Consider possible security implications associated with subprocess module.<br/><br><br>Source: https://semgrep.dev/r/gitlab.bandit.B404<br><br><!-- Category: security --><br>Cc @thypon @kdenhartog 
<!-- __reviewdog__:ChAxZjAzYTM2YjQ3YzAwOGNm -->

created_at: 2025-03-30T01:27:28Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,203 @@
+# Copyright (c) 2025 The Brave Authors. All rights reserved.
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this file,
+# You can obtain one at https://mozilla.org/MPL/2.0/.
+
+from contextlib import contextmanager
+import logging
+import platform
+import random
+import subprocess
path: tools/cr/terminal.py
user: login: github-actions[bot]
body: <sub>reported by [reviewdog](https://github.com/reviewdog/reviewdog) :dog:</sub><br>[semgrep] Consider possible security implications associated with subprocess module.<br/><br><br>Source: https://semgrep.dev/r/gitlab.bandit.B404<br><br><!-- Category: security --><br>Cc @thypon @kdenhartog 
<!-- __reviewdog__:ChBhNGJhMTExODgxOTcyZDgx -->

created_at: 2025-03-30T01:27:29Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,259 @@
+#!/usr/bin/env python3
+# Copyright (c) 2025 The Brave Authors. All rights reserved.
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this file,
+# You can obtain one at https://mozilla.org/MPL/2.0/.
+
+import argparse
+import logging
+from pathlib import Path, PurePath
+import os
+import re
+import sys
+from dataclasses import dataclass, field
+
+from terminal import console, terminal
+from incendiary_error_handler import IncendiaryErrorHandler
+from repository import Repository, BRAVE_CORE_PATH, CHROMIUM_SRC_PATH
+
+# The path to the directory containing overlay files in brave-core.
+OVERLAY_FILES_PATH = BRAVE_CORE_PATH / 'rewrite'
+
+# The path to the directory where patch files are stored in brave-core.
+PATCHES_PATH = PurePath('patches/')
+
+
+# TODO(https://github.com/brave/brave-browser/issues/45061): These templates
+# should be inside some sort of class/module, that we can import in bulk based
+# on the language they are better fit for use (e.g CxxTemplates).
+def CancelFunctionUsingEarlyReturn(return_type: str, function_name: str,
+                                   replacement: str) -> dict:
+    return {
+        "pattern": rf"""
+       (                # Group 1: Capture the function header up to and
+                        # including the opening brace
+           \b{re.escape(return_type)}\s+
+                        # Match the return type at a word boundary followed by
+                        # one or more spaces
+
+                        # Match the exact function name
+           {re.escape(function_name)}"""
+        r"""
+           \s*\(.*?\)   # Match the parameter list: optional spaces,
+                        # literal '(', non-greedy capture of parameters, then
+                        # literal ')'
+           \s*\{        # Match optional spaces and then the opening curly
+                        # brace '{'
+           \s*\n        # Match any spaces and the following newline character.
+       )
+       ([ \t]*)         # Group 2: Capture any indentation (spaces or tabs)
+                        # from the beginning of the next line
+       """,
+        "replacement": rf"\1\2if ((true)) {replacement}\n\2",
+        "flags": ["MULTILINE", "DOTALL", "VERBOSE"],
+    }
+
+
+def VirtualiseFunction(function_name: str) -> dict:
+    return {
+        "pattern": r"""
+        ^(?P<indent>\s*)    # Capture any leading indentation.
+        (?!virtual\s)       # Do not match if 'virtual' follows the indentation.
+        (?P<ret>.*?)        # Non-greedily capture the return type & qualifiers.
+        \b(?P<fn>{func})\b  # The function name, with word boundaries.
+        (?P<args>\s*        # Capture the following as 'args':
+            \( .*? \)       #   - Non-greedy capture of the parameter list in
+                            #     parentheses.
+            \s*             #   - Optional whitespace.
+            (?:const\s*)?   #   - Optional trailing 'const'.
+            ;               #   - The semicolon ending the declaration.
+        )
+    """.format(func=re.escape(function_name)),
+        "replacement": r"\g<indent>virtual \g<ret>\g<fn>\g<args>",
+        "flags": ["MULTILINE", "VERBOSE"],
+    }
+
+
+def MakeFriendClassOf(class_name: str, friend_class: str) -> dict:
+    return {
+        "pattern": r"""
+        (?sx)                       # Enable DOTALL & VERBOSE modes
+        (                           # Begin Group 1: the header of the class
+            class\s+{classname}\b   # Match the 'class' keyword and the given
+                                    # class name
+            [^{{]*\{{               # Consume characters (until the first curly
+                                    # braces)
+            .*?                     # Lazily consume the rest of the header
+            \n                      # Until we get to a newline
+            (?P<indent>[ \t]*)      # Capture the indentation that follows for
+                                    # the next line
+            private:\s*\n           # Match the private access specifier line
+        )
+    """.format(classname=re.escape(class_name)),
+        "replacement": r"\1\g<indent> friend class " + friend_class + r";\n",
+        "flags": ["MULTILINE", "DOTALL", "VERBOSE"],
+    }
+
+
+@dataclass
+class OverlayFile:
+    """ Class representing an overlay file.
+
+    This class is used to apply overlay files to sources in other repositories.
+    """
+
+    # The path to the overlay file. This path is relative to
+    # BRAVE_CORE_PATH.
+    path: PurePath
+
+    # The source file path. This path is relative to the source's own
+    # repository.
+    source: PurePath = field(init=False)
+
+    def __post_init__(self):
+        # TODO(https://github.com/brave/brave-browser/issues/45052): For now
+        # there's only support for `src`, but eventually there's going to be
+        # the need to support other repositories as well.
+        object.__setattr__(
+            self, 'source',
+            self.path.relative_to(OVERLAY_FILES_PATH).with_suffix(''))
+
+        # This is made as a path validation, as the path has to be under brave
+        # to have its own path used as a reference.
+        self.path = self.path.relative_to(BRAVE_CORE_PATH)
+
+    @property
+    def patchfile(self) -> PurePath:
+        """ Returns the path to the patch file in brave-core.
+        """
+        return PATCHES_PATH / f'{str(self.source).replace("/", "-")}.patch'
+
+    @classmethod
+    def find_all(cls) -> list["OverlayFile"]:
+        """ Finds all overlay files in the overlay directory.
+
+        Returns:
+            A list of OverlayFile objects.
+        """
+        overlay_files = []
+
+        for root, _, files in os.walk(OVERLAY_FILES_PATH):
+            for file in files:
+                if file.endswith('.overlay'):
+                    overlay_files.append(cls(PurePath(root) / file))
+
+        return overlay_files
+
+    def apply(self):
+        """ Applies the overlay file to the source file.
+
+        This method loads an overlay file, executes it to get the
+        substitutions, and applies them to the source file. The result is
+        written to the source file, and a patch file is created in the
+        patches directory.
+        """
+
+        overlay_globals = {
+            'CancelFunctionUsingEarlyReturn': CancelFunctionUsingEarlyReturn,
+            'VirtualiseFunction': VirtualiseFunction,
+            'MakeFriendClassOf': MakeFriendClassOf,
+            'escape': re.escape,  # Provide the escape function
+        }
+
+        with open(self.path, 'r', encoding='utf-8') as f:
+            exec(f.read(), overlay_globals)
path: tools/cr/plaster.py
user: login: cdesouza-chromium
body: Two things to bear in mind about this use of exec:

1. What is being done is similar to what Chromium's `depot_tools` does with DEPS and gclient files.
2. This is not meant to be run in the CI. It is a development tool.
created_at: 2025-03-30T05:07:25Z
author_association: COLLABORATOR
diff_hunk: @@ -0,0 +1,259 @@
+#!/usr/bin/env python3
+# Copyright (c) 2025 The Brave Authors. All rights reserved.
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this file,
+# You can obtain one at https://mozilla.org/MPL/2.0/.
+
+import argparse
+import logging
+from pathlib import Path, PurePath
+import os
+import re
+import sys
+from dataclasses import dataclass, field
+
+from terminal import console, terminal
+from incendiary_error_handler import IncendiaryErrorHandler
+from repository import Repository, BRAVE_CORE_PATH, CHROMIUM_SRC_PATH
+
+# The path to the directory containing overlay files in brave-core.
+OVERLAY_FILES_PATH = BRAVE_CORE_PATH / 'rewrite'
+
+# The path to the directory where patch files are stored in brave-core.
+PATCHES_PATH = PurePath('patches/')
+
+
+# TODO(https://github.com/brave/brave-browser/issues/45061): These templates
+# should be inside some sort of class/module, that we can import in bulk based
+# on the language they are better fit for use (e.g CxxTemplates).
+def CancelFunctionUsingEarlyReturn(return_type: str, function_name: str,
+                                   replacement: str) -> dict:
+    return {
+        "pattern": rf"""
+       (                # Group 1: Capture the function header up to and
+                        # including the opening brace
+           \b{re.escape(return_type)}\s+
+                        # Match the return type at a word boundary followed by
+                        # one or more spaces
+
+                        # Match the exact function name
+           {re.escape(function_name)}"""
+        r"""
+           \s*\(.*?\)   # Match the parameter list: optional spaces,
+                        # literal '(', non-greedy capture of parameters, then
+                        # literal ')'
+           \s*\{        # Match optional spaces and then the opening curly
+                        # brace '{'
+           \s*\n        # Match any spaces and the following newline character.
+       )
+       ([ \t]*)         # Group 2: Capture any indentation (spaces or tabs)
+                        # from the beginning of the next line
+       """,
+        "replacement": rf"\1\2if ((true)) {replacement}\n\2",
+        "flags": ["MULTILINE", "DOTALL", "VERBOSE"],
+    }
+
+
+def VirtualiseFunction(function_name: str) -> dict:
+    return {
+        "pattern": r"""
+        ^(?P<indent>\s*)    # Capture any leading indentation.
+        (?!virtual\s)       # Do not match if 'virtual' follows the indentation.
+        (?P<ret>.*?)        # Non-greedily capture the return type & qualifiers.
+        \b(?P<fn>{func})\b  # The function name, with word boundaries.
+        (?P<args>\s*        # Capture the following as 'args':
+            \( .*? \)       #   - Non-greedy capture of the parameter list in
+                            #     parentheses.
+            \s*             #   - Optional whitespace.
+            (?:const\s*)?   #   - Optional trailing 'const'.
+            ;               #   - The semicolon ending the declaration.
+        )
+    """.format(func=re.escape(function_name)),
+        "replacement": r"\g<indent>virtual \g<ret>\g<fn>\g<args>",
+        "flags": ["MULTILINE", "VERBOSE"],
+    }
+
+
+def MakeFriendClassOf(class_name: str, friend_class: str) -> dict:
+    return {
+        "pattern": r"""
+        (?sx)                       # Enable DOTALL & VERBOSE modes
+        (                           # Begin Group 1: the header of the class
+            class\s+{classname}\b   # Match the 'class' keyword and the given
+                                    # class name
+            [^{{]*\{{               # Consume characters (until the first curly
+                                    # braces)
+            .*?                     # Lazily consume the rest of the header
+            \n                      # Until we get to a newline
+            (?P<indent>[ \t]*)      # Capture the indentation that follows for
+                                    # the next line
+            private:\s*\n           # Match the private access specifier line
+        )
+    """.format(classname=re.escape(class_name)),
+        "replacement": r"\1\g<indent> friend class " + friend_class + r";\n",
+        "flags": ["MULTILINE", "DOTALL", "VERBOSE"],
+    }
+
+
+@dataclass
+class OverlayFile:
+    """ Class representing an overlay file.
+
+    This class is used to apply overlay files to sources in other repositories.
+    """
+
+    # The path to the overlay file. This path is relative to
+    # BRAVE_CORE_PATH.
+    path: PurePath
+
+    # The source file path. This path is relative to the source's own
+    # repository.
+    source: PurePath = field(init=False)
+
+    def __post_init__(self):
+        # TODO(https://github.com/brave/brave-browser/issues/45052): For now
+        # there's only support for `src`, but eventually there's going to be
+        # the need to support other repositories as well.
+        object.__setattr__(
+            self, 'source',
+            self.path.relative_to(OVERLAY_FILES_PATH).with_suffix(''))
+
+        # This is made as a path validation, as the path has to be under brave
+        # to have its own path used as a reference.
+        self.path = self.path.relative_to(BRAVE_CORE_PATH)
+
+    @property
+    def patchfile(self) -> PurePath:
+        """ Returns the path to the patch file in brave-core.
+        """
+        return PATCHES_PATH / f'{str(self.source).replace("/", "-")}.patch'
+
+    @classmethod
+    def find_all(cls) -> list["OverlayFile"]:
+        """ Finds all overlay files in the overlay directory.
+
+        Returns:
+            A list of OverlayFile objects.
+        """
+        overlay_files = []
+
+        for root, _, files in os.walk(OVERLAY_FILES_PATH):
+            for file in files:
+                if file.endswith('.overlay'):
+                    overlay_files.append(cls(PurePath(root) / file))
+
+        return overlay_files
+
+    def apply(self):
+        """ Applies the overlay file to the source file.
+
+        This method loads an overlay file, executes it to get the
+        substitutions, and applies them to the source file. The result is
+        written to the source file, and a patch file is created in the
+        patches directory.
+        """
+
+        overlay_globals = {
+            'CancelFunctionUsingEarlyReturn': CancelFunctionUsingEarlyReturn,
+            'VirtualiseFunction': VirtualiseFunction,
+            'MakeFriendClassOf': MakeFriendClassOf,
+            'escape': re.escape,  # Provide the escape function
+        }
+
+        with open(self.path, 'r', encoding='utf-8') as f:
+            exec(f.read(), overlay_globals)
path: tools/cr/plaster.py
user: login: cdesouza-chromium
body: Left comment on a thread above.
created_at: 2025-03-30T05:08:00Z
author_association: COLLABORATOR
diff_hunk: @@ -0,0 +1,156 @@
+# Copyright (c) 2025 The Brave Authors. All rights reserved.
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this file,
+# You can obtain one at https://mozilla.org/MPL/2.0/.
+
+from dataclasses import dataclass
+from pathlib import PurePath
+import subprocess
path: tools/cr/repository.py
user: login: cdesouza-chromium
body: This is not meant to be run in CI. It is a development tool.
created_at: 2025-03-30T05:08:24Z
author_association: COLLABORATOR
diff_hunk: @@ -0,0 +1,203 @@
+# Copyright (c) 2025 The Brave Authors. All rights reserved.
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this file,
+# You can obtain one at https://mozilla.org/MPL/2.0/.
+
+from contextlib import contextmanager
+import logging
+import platform
+import random
+import subprocess
path: tools/cr/terminal.py
user: login: cdesouza-chromium
body: This is not meant to be run in CI. It is a development tool.
created_at: 2025-03-30T05:08:33Z
author_association: COLLABORATOR
diff_hunk: @@ -0,0 +1,208 @@
+# Copyright (c) 2025 The Brave Authors. All rights reserved.
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this file,
+# You can obtain one at https://mozilla.org/MPL/2.0/.
+
+from contextlib import contextmanager
+import logging
+import platform
+import secrets
+import subprocess
+import threading
+import time
+from typing import Optional, Dict
+
+from rich.console import Console
+
+# The interval in second for the keep-alive ping on infra mode to be printed.
+KEEP_ALIVE_PING_INTERVAL = 20
+
+KEEP_ALIVE_PING_ART = [
+    '(-_-)', '(⊙_⊙)', '(¬_¬)', '(－‸ლ)', '(◎_◎;)', '(⌐■_■)', '(•‿•)', '(≖_≖)'
+]
+
+
+class Terminal:
+    """A class that holds the application data and methods.
+    """
+
+    def __init__(self):
+        # The status object to update with the terminal.
+        self.status = None
+
+        # The inital part of the status message, used as a prefix for all
+        # updates.
+        self.starting_status_message = ''
+
+        # flag indicating if the terminal is running on infra.
+        self.infra_mode = False
+
+        # The time when a commmand run was started to check for keep alive
+        # pings. Only relevant when running on infra.
+        self.current_command_start_time = None
+
+        # The keep-alive thread for terminal pings on infra mode.
+        self.keep_alive_thread = threading.Thread(
+            target=self.keep_alive_ci_feedback, daemon=True)
+
+        # The command that is currently running on infra mode.
+        self.running_command = None
+
+    def keep_alive_ci_feedback(self):
+        """Main routine for the keep-alive ping on infra mode.
+
+    This routine runs on a separate thread for the entirety of the  run when on
+    infra mode, sleeping for a set ping interval.
+
+    The routine effects are only visible when `current_command_start_time` is
+    set, at which point there's a chance a ping will be printed to the console
+    in the set time for the ping interval.
+
+    Leaving the back thread running is the best way to ensure we can avoid
+    joining the thread, and all sorts of complexities relating to sleeping and
+    joining, which could result in a minimu time every command has to take.
path: tools/cr/terminal.py
user: login: Copilot
body: Typo in docstring: 'minimu' should be 'minimum'.
```suggestion
    joining, which could result in a minimum time every command has to take.
```
created_at: 2025-03-30T13:26:45Z
author_association: NONE
diff_hunk: @@ -17,26 +17,19 @@ function loadChromiumPathFilter(filePath) {
 
   const prefixes = [];
   const suffixes = [];
-  const exactMatches = new Set();
 
-  // This should be revisited in the future once `path.matchesGlob` is stable
-  // and available in node to use, as this current implementation is a bit
-  // naive.
+  // This can be revisited once `path.matchesGlob` is available on stable node.
+  // This naive implementation is closer to how chromium handles .cfg
+  // exlusion files.
path: build/commands/scripts/updatePatches.js
user: login: Copilot
body: Typo in comment: 'exlusion' should be 'exclusion'.
```suggestion
  // exclusion files.
```
created_at: 2025-03-30T13:26:46Z
author_association: NONE
diff_hunk: @@ -0,0 +1,208 @@
+# Copyright (c) 2025 The Brave Authors. All rights reserved.
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this file,
+# You can obtain one at https://mozilla.org/MPL/2.0/.
+
+from contextlib import contextmanager
+import logging
+import platform
+import secrets
+import subprocess
+import threading
+import time
+from typing import Optional, Dict
+
+from rich.console import Console
+
+# The interval in second for the keep-alive ping on infra mode to be printed.
+KEEP_ALIVE_PING_INTERVAL = 20
+
+KEEP_ALIVE_PING_ART = [
+    '(-_-)', '(⊙_⊙)', '(¬_¬)', '(－‸ლ)', '(◎_◎;)', '(⌐■_■)', '(•‿•)', '(≖_≖)'
+]
+
+
+class Terminal:
+    """A class that holds the application data and methods.
+    """
+
+    def __init__(self):
+        # The status object to update with the terminal.
+        self.status = None
+
+        # The inital part of the status message, used as a prefix for all
+        # updates.
+        self.starting_status_message = ''
+
+        # flag indicating if the terminal is running on infra.
+        self.infra_mode = False
+
+        # The time when a commmand run was started to check for keep alive
+        # pings. Only relevant when running on infra.
+        self.current_command_start_time = None
+
+        # The keep-alive thread for terminal pings on infra mode.
+        self.keep_alive_thread = threading.Thread(
+            target=self.keep_alive_ci_feedback, daemon=True)
+
+        # The command that is currently running on infra mode.
+        self.running_command = None
+
+    def keep_alive_ci_feedback(self):
+        """Main routine for the keep-alive ping on infra mode.
+
+    This routine runs on a separate thread for the entirety of the  run when on
+    infra mode, sleeping for a set ping interval.
+
+    The routine effects are only visible when `current_command_start_time` is
+    set, at which point there's a chance a ping will be printed to the console
+    in the set time for the ping interval.
+
+    Leaving the back thread running is the best way to ensure we can avoid
+    joining the thread, and all sorts of complexities relating to sleeping and
+    joining, which could result in a minimu time every command has to take.
path: tools/cr/terminal.py
user: login: cdesouza-chromium
body: Fixed
created_at: 2025-03-30T13:29:06Z
author_association: COLLABORATOR
diff_hunk: @@ -17,26 +17,19 @@ function loadChromiumPathFilter(filePath) {
 
   const prefixes = [];
   const suffixes = [];
-  const exactMatches = new Set();
 
-  // This should be revisited in the future once `path.matchesGlob` is stable
-  // and available in node to use, as this current implementation is a bit
-  // naive.
+  // This can be revisited once `path.matchesGlob` is available on stable node.
+  // This naive implementation is closer to how chromium handles .cfg
+  // exlusion files.
path: build/commands/scripts/updatePatches.js
user: login: cdesouza-chromium
body: Fixed.
created_at: 2025-03-30T13:29:14Z
author_association: COLLABORATOR
diff_hunk: @@ -17,26 +17,19 @@ function loadChromiumPathFilter(filePath) {
 
   const prefixes = [];
   const suffixes = [];
-  const exactMatches = new Set();
 
-  // This should be revisited in the future once `path.matchesGlob` is stable
-  // and available in node to use, as this current implementation is a bit
-  // naive.
+  // This can be revisited once `path.matchesGlob` is available on stable node.
path: build/commands/scripts/updatePatches.js
user: login: bridiver
body: seems this is in lts now? https://nairihar.medium.com/what-is-new-in-node-js-v22-5-8899620ddebf
created_at: 2025-04-07T16:19:59Z
author_association: COLLABORATOR
diff_hunk: @@ -17,26 +17,19 @@ function loadChromiumPathFilter(filePath) {
 
   const prefixes = [];
   const suffixes = [];
-  const exactMatches = new Set();
 
-  // This should be revisited in the future once `path.matchesGlob` is stable
-  // and available in node to use, as this current implementation is a bit
-  // naive.
+  // This can be revisited once `path.matchesGlob` is available on stable node.
+  // This naive implementation is closer to how chromium handles .cfg
+  // exclusion files.
   for (const line of configLines) {
     if (line.startsWith('*')) {
       suffixes.push(line.slice(1));
-    } else if (line.endsWith('*')) {
path: build/commands/scripts/updatePatches.js
user: login: bridiver
body: were these deleted lines just not needed anymore? If that's the case, maybe they should be removed separately from this PR to avoid confusion?
created_at: 2025-04-07T16:20:36Z
author_association: COLLABORATOR
diff_hunk: @@ -0,0 +1,12 @@
+diff --git a/content/browser/web_package/signed_exchange_utils.cc b/content/browser/web_package/signed_exchange_utils.cc
+index 4b7d75e14e16cd94c5397ca6beb06d5a2d5bce81..af02f1841f94bcce9b9aa53dc166fd6e46b47fae 100644
+--- a/content/browser/web_package/signed_exchange_utils.cc
++++ b/content/browser/web_package/signed_exchange_utils.cc
+@@ -55,6 +55,7 @@ bool IsSignedExchangeHandlingEnabled(BrowserContext* context) {
+ }
+ 
+ bool IsSignedExchangeReportingForDistributorsEnabled() {
++  if ((true)) return false;
path: patches/content-browser-web_package-signed_exchange_utils.cc.patch
user: login: bridiver
body: why are we patching this in the first place? This must be from an old patch/override? Shouldn't we just change the flag?
created_at: 2025-04-07T16:24:37Z
author_association: COLLABORATOR
diff_hunk: @@ -0,0 +1,23 @@
+# Copyright (c) 2025 The Brave Authors. All rights reserved.
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this file,
+# You can obtain one at https://mozilla.org/MPL/2.0/.
+
+plasters = [
+    Template('VirtualiseFunction').with_args(function_name='Init').serialise(),
path: rewrite/chrome/browser/browser_process_impl.h.overlay
user: login: bridiver
body: I think this is getting away from the original idea of trying to make this very simple matching. While these templates may be useful, I don't want to overly complicate it or add to the learning curve, especially in the beginning. 
created_at: 2025-04-07T16:28:03Z
author_association: COLLABORATOR
diff_hunk: @@ -0,0 +1,22 @@
+# Copyright (c) 2025 The Brave Authors. All rights reserved.
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this file,
+# You can obtain one at https://mozilla.org/MPL/2.0/.
+
+plasters = [
path: rewrite/base/metrics/histogram_functions.h.overlay
user: login: bridiver
body: this format for defining the replacements isn't very IDE friendly in terms of syntax highlighting, etc... Are they just python files? I definitely don't want to have to deal with escaping characters in general. I need to think about this a little bit because I want it to be very simple and straightforward. 
created_at: 2025-04-07T16:37:57Z
author_association: COLLABORATOR
diff_hunk: @@ -0,0 +1,15 @@
+# Copyright (c) 2025 The Brave Authors. All rights reserved.
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this file,
+# You can obtain one at https://mozilla.org/MPL/2.0/.
+
+plasters = [
+    {
+        'pattern': r'ChromeAutocompleteSchemeClassifier',
+        'replacement': r'BraveAutocompleteSchemeClassifier',
+    },
+    {
+        'op': 'AddHeader',
path: rewrite/chrome/browser/autocomplete/autocomplete_classifier_factory.cc.overlay
user: login: bridiver
body: similar to https://github.com/brave/brave-core/pull/28389/files#r2031605222 I think this is trying to be a little too clever. Let's talk about this in DM
created_at: 2025-04-07T16:40:40Z
author_association: COLLABORATOR
diff_hunk: @@ -17,26 +17,19 @@ function loadChromiumPathFilter(filePath) {
 
   const prefixes = [];
   const suffixes = [];
-  const exactMatches = new Set();
 
-  // This should be revisited in the future once `path.matchesGlob` is stable
-  // and available in node to use, as this current implementation is a bit
-  // naive.
+  // This can be revisited once `path.matchesGlob` is available on stable node.
+  // This naive implementation is closer to how chromium handles .cfg
+  // exclusion files.
   for (const line of configLines) {
     if (line.startsWith('*')) {
       suffixes.push(line.slice(1));
-    } else if (line.endsWith('*')) {
path: build/commands/scripts/updatePatches.js
user: login: cdesouza-chromium
body: I'll move this to another PR.
created_at: 2025-04-07T16:51:54Z
author_association: COLLABORATOR
diff_hunk: @@ -0,0 +1,12 @@
+diff --git a/content/browser/web_package/signed_exchange_utils.cc b/content/browser/web_package/signed_exchange_utils.cc
+index 4b7d75e14e16cd94c5397ca6beb06d5a2d5bce81..af02f1841f94bcce9b9aa53dc166fd6e46b47fae 100644
+--- a/content/browser/web_package/signed_exchange_utils.cc
++++ b/content/browser/web_package/signed_exchange_utils.cc
+@@ -55,6 +55,7 @@ bool IsSignedExchangeHandlingEnabled(BrowserContext* context) {
+ }
+ 
+ bool IsSignedExchangeReportingForDistributorsEnabled() {
++  if ((true)) return false;
path: patches/content-browser-web_package-signed_exchange_utils.cc.patch
user: login: cdesouza-chromium
body: The flag was deleted some time ago, so an override was introduced. This patch is a demo of the override being thrown out and the patch introduced.
created_at: 2025-04-07T17:16:51Z
author_association: COLLABORATOR
diff_hunk: @@ -0,0 +1,22 @@
+# Copyright (c) 2025 The Brave Authors. All rights reserved.
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this file,
+# You can obtain one at https://mozilla.org/MPL/2.0/.
+
+plasters = [
path: rewrite/base/metrics/histogram_functions.h.overlay
user: login: cdesouza-chromium
body: They are just python files yeah, so you can just match with that for highlighting.
created_at: 2025-04-07T17:17:46Z
author_association: COLLABORATOR
diff_hunk: @@ -0,0 +1,15 @@
+# Copyright (c) 2025 The Brave Authors. All rights reserved.
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this file,
+# You can obtain one at https://mozilla.org/MPL/2.0/.
+
+plasters = [
+    {
+        'pattern': r'ChromeAutocompleteSchemeClassifier',
+        'replacement': r'BraveAutocompleteSchemeClassifier',
+    },
+    {
+        'op': 'AddHeader',
path: rewrite/chrome/browser/autocomplete/autocomplete_classifier_factory.cc.overlay
user: login: cdesouza-chromium
body: I added this other one as a demo for things that we may eventually decide to support some other way that doesn't involve regexes.
created_at: 2025-04-07T17:18:27Z
author_association: COLLABORATOR
diff_hunk: @@ -0,0 +1,23 @@
+# Copyright (c) 2025 The Brave Authors. All rights reserved.
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this file,
+# You can obtain one at https://mozilla.org/MPL/2.0/.
+
+plasters = [
+    Template('VirtualiseFunction').with_args(function_name='Init').serialise(),
path: rewrite/chrome/browser/browser_process_impl.h.overlay
user: login: cdesouza-chromium
body: Noted
created_at: 2025-04-07T20:56:39Z
author_association: COLLABORATOR
diff_hunk: @@ -17,26 +17,19 @@ function loadChromiumPathFilter(filePath) {
 
   const prefixes = [];
   const suffixes = [];
-  const exactMatches = new Set();
 
-  // This should be revisited in the future once `path.matchesGlob` is stable
-  // and available in node to use, as this current implementation is a bit
-  // naive.
+  // This can be revisited once `path.matchesGlob` is available on stable node.
path: build/commands/scripts/updatePatches.js
user: login: cdesouza-chromium
body: I push this as a separate PR.
created_at: 2025-04-13T12:37:28Z
author_association: COLLABORATOR
diff_hunk: @@ -0,0 +1,11 @@
+# Copyright (c) 2025 The Brave Authors. All rights reserved.
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this file,
+# You can obtain one at https://mozilla.org/MPL/2.0/.
+
+[[plaster]]
+description = "Adding deps to generated_resources grit."
+pattern = '(grit\("generated_resources"\)\s*\{.*?)(\n\})'
+replace = '''\1
+  deps = [ "//brave/app:brave_generated_resources_grit" ]\2'''
+flags = "DOTALL"
path: rewrite/chrome/app/BUILD.gn.toml
user: login: cdesouza-chromium
body: This one is doing this:

```
 grit("generated_resources") {
   source = "generated_resources.grd"
   defines = chrome_grit_defines + [ "is_cfm=${is_cfm}" ]
   output_dir = "$root_gen_dir/chrome"
   outputs =
       [ "grit/generated_resources.h" ] +
       process_file_template(all_chrome_locales,
                             [ "generated_resources_{{source_name_part}}.pak" ])
   if (is_android) {
     outputs += android_generated_java_resources
   }
+  deps = [ "//brave/app:brave_generated_resources_grit" ]
 }
```

So it is matching with the `grit("generated_resources")` but adding the line just before the last closing curly bracket. This not only means we are patching a non-C++ file, but we are also patching it in a way that will also sort out the issue regular conflicts in .gn files due to adjacency change.

The resulting patch from this regex is identitical to the one already in the repo, so there are no changes to commit for that patch.
created_at: 2025-04-19T12:23:20Z
author_association: COLLABORATOR
diff_hunk: @@ -0,0 +1,11 @@
+# Copyright (c) 2025 The Brave Authors. All rights reserved.
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this file,
+# You can obtain one at https://mozilla.org/MPL/2.0/.
+
+[[plaster]]
+description = 'Cancelling the signin promo through a common function.'
+pattern = '(^bool ShouldShowSignInPromoCommon.*?$)'
+replace = '''\1
+  if ((true)) return false;'''
+flags = 'MULTILINE'
path: rewrite/chrome/browser/signin/signin_promo_util.cc.toml
user: login: cdesouza-chromium
body: This one is doing this:
```
 bool ShouldShowSignInPromoCommon(Profile& profile, SignInPromoType type) {
+  if ((true)) return false;
   // Don't show the promo if it does not pass the sync base checks.
   if (!signin::ShouldShowSyncPromo(profile)) {
     return false;
   }
```

This patch is necessary as `ShouldShowSignInPromoCommon` is in an anonymous namespace, however with the patch being generated this way, the `chromium_src` shadow file is now deleted.
created_at: 2025-04-19T12:26:07Z
author_association: COLLABORATOR
diff_hunk: @@ -0,0 +1,11 @@
+# Copyright (c) 2025 The Brave Authors. All rights reserved.
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this file,
+# You can obtain one at https://mozilla.org/MPL/2.0/.
+
+[[plaster]]
+description = "Adding deps to generated_resources grit."
+pattern = '(grit\("generated_resources"\)\s*\{.*?)(\n\})'
path: rewrite/chrome/app/BUILD.gn.toml
user: login: bridiver
body: this is still a full regex though? Maybe we should name it `re_pattern` and then later we can add a simplified blob matching as just `pattern`? 
created_at: 2025-04-30T22:20:49Z
author_association: COLLABORATOR
diff_hunk: @@ -0,0 +1,11 @@
+# Copyright (c) 2025 The Brave Authors. All rights reserved.
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this file,
+# You can obtain one at https://mozilla.org/MPL/2.0/.
+
+[[plaster]]
+description = 'Cancelling the signin promo through a common function.'
+pattern = '(^bool ShouldShowSignInPromoCommon.*?$)'
+replace = '''\1
+  if ((true)) return false;'''
+flags = 'MULTILINE'
path: rewrite/chrome/browser/signin/signin_promo_util.cc.toml
user: login: bridiver
body: per above comment about `pattern` this should probably be `re_flags` just like the code. I was a bit confused at first about what `flags` was
created_at: 2025-04-30T22:21:48Z
author_association: COLLABORATOR
diff_hunk: @@ -0,0 +1,11 @@
+# Copyright (c) 2025 The Brave Authors. All rights reserved.
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this file,
+# You can obtain one at https://mozilla.org/MPL/2.0/.
+
+[[plaster]]
+description = "Adding deps to generated_resources grit."
+pattern = '(grit\("generated_resources"\)\s*\{.*?)(\n\})'
path: rewrite/chrome/app/BUILD.gn.toml
user: login: bridiver
body: this regex might be simpler and more robust if we added the patch to the beginning of the block instead of the end? We'll have to think about some simplified way to define stuff like this because we don't want overly complex regexes if we can avoid them. Not sure what exactly, but this does highlight what can be a somewhat tricky case for simple matching. 
created_at: 2025-04-30T22:32:32Z
author_association: COLLABORATOR
diff_hunk: @@ -0,0 +1,11 @@
+# Copyright (c) 2025 The Brave Authors. All rights reserved.
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this file,
+# You can obtain one at https://mozilla.org/MPL/2.0/.
+
+[[plaster]]
+description = "Adding deps to generated_resources grit."
+pattern = '(grit\("generated_resources"\)\s*\{.*?)(\n\})'
path: rewrite/chrome/app/BUILD.gn.toml
user: login: cdesouza-chromium
body: I will submit these in a separate PR, so we can carry on that discussion on the individual file.
created_at: 2025-05-13T13:52:43Z
author_association: COLLABORATOR
diff_hunk: @@ -0,0 +1,11 @@
+# Copyright (c) 2025 The Brave Authors. All rights reserved.
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this file,
+# You can obtain one at https://mozilla.org/MPL/2.0/.
+
+[[plaster]]
+description = 'Cancelling the signin promo through a common function.'
+pattern = '(^bool ShouldShowSignInPromoCommon.*?$)'
+replace = '''\1
+  if ((true)) return false;'''
+flags = 'MULTILINE'
path: rewrite/chrome/browser/signin/signin_promo_util.cc.toml
user: login: cdesouza-chromium
body: ```
[[substitution]]
description = 'Test for adding a header after another line'
re_pattern = '#include "extensions/buildflags/buildflags.h"'
replace = '''#include "extensions/buildflags/buildflags.h"
#include "brave/browser/autocomplete/brave_autocomplete_scheme_classifier.h"'''
```

So posting the test above because I have recently decide to change the array element to `substitution`. Maybe a bit of a mouthful. I'm doing this because you mentioned about globs, etc, so I think we should in have different labels for different operation types. For example, for now `[[substitution]]` means regex use. In the event of some glob matching we could have `[[glob]]` and then we can have different keys, etc.
created_at: 2025-05-13T13:55:30Z
author_association: COLLABORATOR
diff_hunk: @@ -0,0 +1,11 @@
+# Copyright (c) 2025 The Brave Authors. All rights reserved.
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this file,
+# You can obtain one at https://mozilla.org/MPL/2.0/.
+
+[[plaster]]
+description = 'Cancelling the signin promo through a common function.'
+pattern = '(^bool ShouldShowSignInPromoCommon.*?$)'
+replace = '''\1
+  if ((true)) return false;'''
+flags = 'MULTILINE'
path: rewrite/chrome/browser/signin/signin_promo_util.cc.toml
user: login: cdesouza-chromium
body: Done `re_pattern`, and `re_flag`.
created_at: 2025-05-13T13:55:55Z
author_association: COLLABORATOR

PR comments:
user: login: github-actions[bot]
created_at: 2025-05-14T00:03:23Z
body: Chromium major version is behind target branch (136.0.7103.93 vs 137.0.7151.16). Please rebase.
author_association: CONTRIBUTOR
user: login: brave-builds
created_at: 2025-05-16T00:04:19Z
body: Released in [v1.80.72](https://github.com/brave/brave-browser/releases/tag/v1.80.72)
author_association: COLLABORATOR