From cd209131ee13a4b2e02304ca8f456f4f98e2695a Mon Sep 17 00:00:00 2001 From: Vito Sansevero Date: Wed, 7 Oct 2026 19:50:58 -0700 Subject: [PATCH] fix: check thumbnail directory containment with a real path test clear_thumbnails compared resolved paths with str.startswith, so a thumbnails directory that resolved to a sibling with a shared prefix (out vs out2) passed the safety check and was emptied. It now uses the module's _is_within (realpath + normcase + Path.is_relative_to). --- py/api/images.py | 11 ++++------- tests/test_api_images.py | 16 ++++++++++++++++ 2 files changed, 20 insertions(+), 7 deletions(-) diff --git a/py/api/images.py b/py/api/images.py index bde28c1..3335773 100644 --- a/py/api/images.py +++ b/py/api/images.py @@ -1049,15 +1049,12 @@ class ImageRoutesMixin: } ) - # Verify this is actually our thumbnails directory + # Verify this is actually our thumbnails directory: a real child + # of the output root (symlinks resolved), not merely a path that + # shares its prefix such as "2/thumbnails". try: - thumbnails_dir_resolved = thumbnails_dir.resolve() - output_path_resolved = output_path.resolve() - if ( - not str(thumbnails_dir_resolved).startswith( - str(output_path_resolved) - ) + not _is_within(thumbnails_dir, output_path) or thumbnails_dir.name != "thumbnails" ): self.logger.error( diff --git a/tests/test_api_images.py b/tests/test_api_images.py index db0d598..7f879ea 100644 --- a/tests/test_api_images.py +++ b/tests/test_api_images.py @@ -1254,6 +1254,22 @@ class TestClearThumbnails(ImageRouteCoverageCase): status, _ = await self._json("POST", self.URL) self.assertEqual(status, 500) + async def test_sibling_prefix_directory_is_refused(self): + # "/out2/thumbnails" starts with "/out" as a string but is + # not inside it; a symlinked thumbnails dir pointing there must be + # refused rather than emptied. + out = self.comfy_dir / "out" + out.mkdir() + sibling = make_png(self.comfy_dir / "out2" / "thumbnails" / "a_thumb.png") + self._make_symlink(sibling.parent, out / "thumbnails") + self.api._find_comfyui_output_dir = lambda: str(out) + + status, data = await self._json("POST", self.URL) + + self.assertEqual(status, 400) + self.assertFalse(data["success"]) + self.assertTrue(sibling.is_file()) + class TestImagePromptLookup(ImageRouteCoverageCase):