From f90e84de283579e38f15b99e2d5647fd93bab784 Mon Sep 17 00:00:00 2001 From: matthew <52920416+gg0h@users.noreply.github.com> Date: Fri, 2 Jan 2026 15:58:06 +0000 Subject: [PATCH 1/2] prevent traversal --- beaker/util.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/beaker/util.py b/beaker/util.py index 64b61f3..57fd60e 100644 --- a/beaker/util.py +++ b/beaker/util.py @@ -233,7 +233,7 @@ def encoded_path(root, identifiers, extension=".enc", depth=3, else: ident = sha1(ident).hexdigest() - ident = os.path.basename(ident) + ident = os.path.basename(ident).lstrip('.') tokens = [] for d in range(1, depth): From f657ea1192e8e223a3ce603671297f9f49bc18f5 Mon Sep 17 00:00:00 2001 From: gg0h Date: Fri, 2 Jan 2026 17:17:19 +0000 Subject: [PATCH 2/2] tests --- tests/test_encoded_path.py | 32 ++++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) create mode 100644 tests/test_encoded_path.py diff --git a/tests/test_encoded_path.py b/tests/test_encoded_path.py new file mode 100644 index 0000000..01aee5d --- /dev/null +++ b/tests/test_encoded_path.py @@ -0,0 +1,32 @@ +from beaker.util import encoded_path +import pathlib + +def test_strips_leading_periods(tmp_path): + """ + Ensure that leading periods in the identifier are stripped when + digest_filenames=False to prevent limited traversal + """ + + out = encoded_path( + root=tmp_path, + identifiers=["..poc"], + digest_filenames=False + ) + + p = pathlib.Path(out) + + # The resulting filename must not begin with a dot + assert not p.name.startswith("."), "Leading periods should be stripped" + + # After stripping leading dots, the stem should match + assert p.stem == "poc", "Filename should preserve content minus leading dots" + + # And extension should still be present + assert p.suffix == ".enc" + + # encoded path should be a child of input, ./po/p/poc.enc + assert p.is_relative_to(tmp_path) + + # check no traversal has put the encoded directory back to the input + assert str(p.parent) != str(tmp_path.absolute()) +