From 672e1f43bd3f6970ee3c297580d16e24d9366caa Mon Sep 17 00:00:00 2001 From: AEliu <17284002+AEliu@users.noreply.github.com> Date: Tue, 21 Apr 2026 15:23:52 +0800 Subject: [PATCH] Fix encrypted tile bounds checks and refresh docs --- docs/project-status.md | 28 ++++++++++++++-------------- src/artx/download/tiles.py | 10 +++++++++- tests/test_tile_cache.py | 17 +++++++++++++++++ 3 files changed, 40 insertions(+), 15 deletions(-) diff --git a/docs/project-status.md b/docs/project-status.md index 6eb10d0..fa98154 100644 --- a/docs/project-status.md +++ b/docs/project-status.md @@ -43,25 +43,25 @@ The project currently supports: Application and shared modules: -- `src/googleart_download/cli/` -- `src/googleart_download/batch/` -- `src/googleart_download/reporting/` -- `src/googleart_download/models.py` -- `src/googleart_download/errors.py` -- `src/googleart_download/logging_utils.py` +- `src/artx/cli/` +- `src/artx/batch/` +- `src/artx/reporting/` +- `src/artx/models.py` +- `src/artx/errors.py` +- `src/artx/logging_utils.py` Download domain: -- `src/googleart_download/download/constants.py` -- `src/googleart_download/download/http_client.py` -- `src/googleart_download/download/downloader.py` -- `src/googleart_download/download/tiles.py` -- `src/googleart_download/download/image_writer.py` +- `src/artx/download/constants.py` +- `src/artx/download/http_client.py` +- `src/artx/download/downloader.py` +- `src/artx/download/tiles.py` +- `src/artx/download/image_writer.py` Metadata domain: -- `src/googleart_download/metadata/parsers.py` -- `src/googleart_download/metadata/output.py` +- `src/artx/metadata/parsers.py` +- `src/artx/metadata/output.py` Repo quality and automation: @@ -87,7 +87,7 @@ Repo quality and automation: - added batch input deduplication, targeted rerun, explicit output conflict policies, and richer size inspection - added explicit tile-only and stitch-from-tiles workflows - added conservative batch download/stitch pipelining for adjacent artworks -- removed compatibility-shell leftovers after package reorganization +- removed compatibility-shim leftovers after package reorganization - moved download-specific constants into the `download/` domain - added `ruff`, `mypy`, and GitHub Actions CI - added CI verification that generated README assets stay up to date diff --git a/src/artx/download/tiles.py b/src/artx/download/tiles.py index 8640068..dd3511d 100644 --- a/src/artx/download/tiles.py +++ b/src/artx/download/tiles.py @@ -42,10 +42,18 @@ def decrypt_tile_if_needed(data: bytes) -> bytes: header_size = int.from_bytes(data[-4:], "little") encrypted_size_offset = 4 + header_size + footer_end = len(data) - 4 + + if encrypted_size_offset + 4 > footer_end: + raise DownloadError("encrypted tile header metadata is malformed") + encrypted_size = int.from_bytes(data[encrypted_size_offset : encrypted_size_offset + 4], "little") encrypted_start = encrypted_size_offset + 4 encrypted_end = encrypted_start + encrypted_size - footer_end = len(data) - 4 + + if encrypted_end > footer_end: + raise DownloadError("encrypted tile payload length is malformed") + header = data[4 : 4 + header_size] encrypted = data[encrypted_start:encrypted_end] diff --git a/tests/test_tile_cache.py b/tests/test_tile_cache.py index 140e4e7..710a96c 100644 --- a/tests/test_tile_cache.py +++ b/tests/test_tile_cache.py @@ -62,6 +62,23 @@ def build_png_bytes(color: tuple[int, int, int]) -> bytes: class TileCacheTests(unittest.TestCase): + def test_download_tiles_rejects_malformed_encrypted_payload(self) -> None: + jobs = [TileJob(z=0, x=0, y=0, url="https://example.com/0")] + malformed_encrypted = b"\x0a\x0a\x0a\x0a\x01\x00\x00\x00" + + with TemporaryDirectory() as tmpdir: + tiles_dir = ensure_cache_layout(Path(tmpdir)) + client = FakeHttpClient(malformed_encrypted) + + with self.assertRaises(DownloadError): + download_tiles( + jobs, + workers=1, + reporter=SilentReporter(), + http_client=client, + tiles_dir=tiles_dir, + ) + def test_download_tiles_reuses_existing_cache(self) -> None: jobs = [ TileJob(z=0, x=0, y=0, url="https://example.com/0"),