From 681ec1174d3bd3007aba8d22d014263ddce72b00 Mon Sep 17 00:00:00 2001 From: Andras Schmelczer Date: Tue, 27 Oct 2026 19:52:12 +0000 Subject: [PATCH] Enforce canonical LaTeX input directory boundaries --- .../external/pylatexenc/latex2text/__init__.py | 15 ++++++++++----- tests/utilities/test_latex_regressions.py | 18 ++++++++++++++++++ 2 files changed, 28 insertions(+), 5 deletions(-) create mode 100644 tests/utilities/test_latex_regressions.py diff --git a/great_ai/utilities/external/pylatexenc/latex2text/__init__.py b/great_ai/utilities/external/pylatexenc/latex2text/__init__.py index 41b23b0..54360d1 100644 --- a/great_ai/utilities/external/pylatexenc/latex2text/__init__.py +++ b/great_ai/utilities/external/pylatexenc/latex2text/__init__.py @@ -974,11 +974,20 @@ class LatexNodes2Text(object): return "" fnfull = os.path.realpath(os.path.join(self.tex_input_directory, fn)) + if not os.path.exists(fnfull) and os.path.exists(fnfull + ".tex"): + fnfull = fnfull + ".tex" + if not os.path.exists(fnfull) and os.path.exists(fnfull + ".latex"): + fnfull = fnfull + ".latex" + fnfull = os.path.realpath(fnfull) if self.strict_input: # make sure that the input file is strictly within dirfull, and # didn't escape with '../..' tricks or via symlinks. dirfull = os.path.realpath(self.tex_input_directory) - if not fnfull.startswith(dirfull): + try: + within_directory = os.path.commonpath([fnfull, dirfull]) == dirfull + except ValueError: # paths on different Windows drives + within_directory = False + if not within_directory: logger.warning( "Can't access path '%s' leading outside of mandated directory " "[strict input mode]", @@ -986,10 +995,6 @@ class LatexNodes2Text(object): ) return "" - if not os.path.exists(fnfull) and os.path.exists(fnfull + ".tex"): - fnfull = fnfull + ".tex" - if not os.path.exists(fnfull) and os.path.exists(fnfull + ".latex"): - fnfull = fnfull + ".latex" if not os.path.isfile(fnfull): logger.warning("Error, file doesn't exist: '%s'", fn) return "" diff --git a/tests/utilities/test_latex_regressions.py b/tests/utilities/test_latex_regressions.py new file mode 100644 index 0000000..3dfe0ca --- /dev/null +++ b/tests/utilities/test_latex_regressions.py @@ -0,0 +1,18 @@ +from pathlib import Path + +from great_ai.utilities.external.pylatexenc.latex2text import LatexNodes2Text + + +def test_strict_input_checks_path_ancestry(tmp_path: Path) -> None: + allowed = tmp_path / "allowed" + sibling = tmp_path / "allowed_neighbor" + allowed.mkdir() + sibling.mkdir() + (allowed / "inside.tex").write_text("inside") + (sibling / "outside.tex").write_text("outside") + (allowed / "link.tex").symlink_to(sibling / "outside.tex") + converter = LatexNodes2Text() + converter.set_tex_input_directory(str(allowed), strict_input=True) + assert converter.latex_to_text(r"\input{inside}") == "inside" + assert converter.latex_to_text(r"\input{../allowed_neighbor/outside}") == "" + assert converter.latex_to_text(r"\input{link}") == ""