This is an automated email from the ASF dual-hosted git repository.
sbp pushed a commit to branch sbp
in repository https://gitbox.apache.org/repos/asf/tooling-trusted-releases.git
The following commit(s) were added to refs/heads/sbp by this push:
new afc8c087 Set length limits on reads of KEYS, LICENSE, and NOTICE files
afc8c087 is described below
commit afc8c087aa01b0cd9c51ce2cd829af5770b3250c
Author: Sean B. Palmer <[email protected]>
AuthorDate: Wed Mar 11 20:21:49 2026 +0000
Set length limits on reads of KEYS, LICENSE, and NOTICE files
---
atr/post/keys.py | 21 ++++++++++++++++++++-
atr/tasks/checks/license.py | 15 +++++++++++++--
2 files changed, 33 insertions(+), 3 deletions(-)
diff --git a/atr/post/keys.py b/atr/post/keys.py
index df295ea1..92b678a3 100644
--- a/atr/post/keys.py
+++ b/atr/post/keys.py
@@ -41,6 +41,9 @@ import atr.web as web
_KEYS_BASE_URL: Final[str] = "https://downloads.apache.org"
+# The Apache Subversion KEYS file is largest at 3732091 bytes
+_MAX_KEYS_SIZE: Final[int] = 10 * 1024 * 1024
+
@post.typed
async def add(
@@ -273,7 +276,23 @@ async def _fetch_keys_from_url(keys_url: str) -> str:
async with util.create_secure_session(timeout=timeout) as session:
async with session.get(keys_url, allow_redirects=True) as response:
response.raise_for_status()
- return await response.text()
+ content_length = response.content_length
+ if (content_length is not None) and (content_length >
_MAX_KEYS_SIZE):
+ raise base.ASFQuartException(
+ f"KEYS file too large ({content_length} bytes, limit
{_MAX_KEYS_SIZE})",
+ errorcode=502,
+ )
+ chunks: list[bytes] = []
+ size = 0
+ async for chunk in response.content.iter_chunked(65536):
+ size += len(chunk)
+ if size > _MAX_KEYS_SIZE:
+ raise base.ASFQuartException(
+ f"KEYS file too large (limit {_MAX_KEYS_SIZE}
bytes)",
+ errorcode=502,
+ )
+ chunks.append(chunk)
+ return b"".join(chunks).decode("utf-8")
except aiohttp.ClientResponseError as e:
raise base.ASFQuartException(f"Unable to fetch keys from remote
server: {e.status} {e.message}", errorcode=502)
except aiohttp.ClientError as e:
diff --git a/atr/tasks/checks/license.py b/atr/tasks/checks/license.py
index b5858deb..ff28087e 100644
--- a/atr/tasks/checks/license.py
+++ b/atr/tasks/checks/license.py
@@ -84,6 +84,8 @@ INPUT_EXTRA_ARGS: Final[list[str]] = ["is_podling"]
CHECK_VERSION_FILES: Final[str] = "3"
CHECK_VERSION_HEADERS: Final[str] = "3"
+_MAX_LICENSE_NOTICE_SIZE: Final[int] = 1024 * 1024
+
# Types
@@ -298,7 +300,11 @@ def _files_check_core_logic(cache_dir: pathlib.Path,
is_podling: bool) -> Iterat
def _files_check_core_logic_license(file_path: pathlib.Path) -> str | None:
"""Verify that the start of the LICENSE file matches the Apache 2.0
license."""
- package_license_bytes = file_path.read_bytes()
+ with open(file_path, "rb") as f:
+ package_license_bytes = f.read(_MAX_LICENSE_NOTICE_SIZE + 1)
+ if len(package_license_bytes) > _MAX_LICENSE_NOTICE_SIZE:
+ log.warning(f"LICENSE file exceeds {_MAX_LICENSE_NOTICE_SIZE} byte
limit: {file_path}")
+ package_license_bytes =
package_license_bytes[:_MAX_LICENSE_NOTICE_SIZE]
sha3e = hashlib.sha3_256()
sha3e.update(constants.APACHE_LICENSE_2_0.encode("utf-8"))
@@ -331,7 +337,12 @@ def _files_check_core_logic_license(file_path:
pathlib.Path) -> str | None:
def _files_check_core_logic_notice(file_path: pathlib.Path) -> tuple[bool,
list[str], str]:
"""Verify that the NOTICE file follows the required format."""
try:
- content = file_path.read_bytes().decode("utf-8")
+ with open(file_path, "rb") as f:
+ raw = f.read(_MAX_LICENSE_NOTICE_SIZE + 1)
+ if len(raw) > _MAX_LICENSE_NOTICE_SIZE:
+ log.warning(f"NOTICE file exceeds {_MAX_LICENSE_NOTICE_SIZE} byte
limit: {file_path}")
+ raw = raw[:_MAX_LICENSE_NOTICE_SIZE]
+ content = raw.decode("utf-8")
except UnicodeDecodeError:
return False, ["the NOTICE file is not valid UTF-8"], ""
preamble = "".join(content.splitlines(keepends=True)[:3])
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]