From 7ef0e9832845eaa8bb8bea09a6d9c965087d3277 Mon Sep 17 00:00:00 2001 From: Ben Barclay Date: Tue, 25 Aug 2026 10:54:42 +1000 Subject: [PATCH] test: pin that install-root siblings still get parent-dir hardening MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The install-tree exclusion added in #93757 has a positive test (paths inside the tree are skipped) but no negative boundary test. The guard compares path components, so a prefix-named sibling like /opt/hermes-data must still be chmod'd 0700 — but a rewrite to a string-prefix match would silently drop that hardening with the suite staying green. Add test_install_tree_siblings_still_hardened covering a prefix-named sibling and an ordinary sibling of the install root. Verified by mutation: replacing the guard with str(parent).startswith(...) turns the new test red. Follow-up to #93757. --- tests/test_hermes_constants.py | 32 ++++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/tests/test_hermes_constants.py b/tests/test_hermes_constants.py index 0aef996614..e5bb62947b 100644 --- a/tests/test_hermes_constants.py +++ b/tests/test_hermes_constants.py @@ -584,6 +584,38 @@ class TestSecureParentDir: secure_parent_dir(target2) assert called_with2 == [], "must not chmod dirs inside the install tree" + def test_install_tree_siblings_still_hardened(self, monkeypatch): + """Paths OUTSIDE the install tree must still be chmod'd. + + Negative boundary for the install-tree exclusion (#93050): the guard + compares path components, so a sibling directory whose name merely + starts with the install root's name (``-data``) must + still receive parent-dir hardening. Pins that the exclusion cannot + silently widen into a string-prefix match. + """ + install_root = Path(hermes_constants.__file__).resolve().parent + + # Prefix-named sibling of the install root (/opt/hermes-data/...). + prefix_sibling = Path(str(install_root) + "-data") + called_with = [] + monkeypatch.setattr(os, "chmod", lambda p, m: called_with.append((str(p), m))) + secure_parent_dir(prefix_sibling / "auth.json") + assert called_with == [(str(prefix_sibling), 0o700)], ( + "prefix-named siblings of the install root must still be hardened" + ) + + # Ordinary sibling next to the install root (same parent dir). + sibling = install_root.parent / "unrelated-dir" + if len(sibling.parts) >= 3 and install_root not in sibling.parents: + called_with2 = [] + monkeypatch.setattr( + os, "chmod", lambda p, m: called_with2.append((str(p), m)) + ) + secure_parent_dir(sibling / "auth.json") + assert called_with2 == [(str(sibling), 0o700)], ( + "siblings of the install root must still be hardened" + ) + @pytest.mark.require_symlinks def test_symlink_resolved(self, tmp_path, monkeypatch): """Symlinks should be resolved before checking depth."""