asf-tooling opened a new issue, #1064:
URL: https://github.com/apache/tooling-trusted-releases/issues/1064
**ASVS Level(s):** L2-only
**Description:**
### Summary
The ZIP download endpoint streams an archive of all files in a release
directory without checking total size, file count, or imposing streaming
timeouts. For releases with many large files (50,000 files, 20 GB total), this
causes extended resource consumption during ZIP generation and transfer,
potentially holding server resources for hours on slow client connections.
Authentication and rate limiting provide some protection.
### Details
The issue exists in `atr/get/download.py` in the `zip_selected()` function.
ZIP streaming is unbounded in both file count and total size.
### Recommended Remediation
Add resource limits before streaming:
```python
_MAX_ZIP_FILES = 10000
_MAX_ZIP_TOTAL_BYTES = 10 * 1024 * 1024 * 1024 # 10 GB
@download.get
async def zip_selected(
session: web.Committer,
project_key: safe.ProjectKey,
version_key: safe.VersionKey
) -> web.QuartResponse:
"""Stream ZIP archive with resource limits."""
# Collect files and track size
files_to_zip = []
total_size = 0
for file_path in release_directory.iterdir():
if len(files_to_zip) >= _MAX_ZIP_FILES:
return quart.Response(
f"Release contains more than {_MAX_ZIP_FILES} files. "
f"Please download individual files or contact support.",
status=413
)
file_size = file_path.stat().st_size
total_size += file_size
if total_size > _MAX_ZIP_TOTAL_BYTES:
return quart.Response(
f"Release total size exceeds {_MAX_ZIP_TOTAL_BYTES //
(1024**3)} GB. "
f"Please download individual files or contact support.",
status=413
)
files_to_zip.append(file_path)
# Log metrics for monitoring
log.info(
"Starting ZIP download",
project=project_key,
version=version_key,
file_count=len(files_to_zip),
total_size=total_size
)
# Stream ZIP with collected files
return await stream_zip(files_to_zip)
```
**Alternative approach for very large releases:**
Provide manifest file with individual download links instead of ZIP
streaming:
```python
if len(files_to_zip) > _MAX_ZIP_FILES or total_size > _MAX_ZIP_TOTAL_BYTES:
# Generate manifest with download links
manifest = generate_download_manifest(files_to_zip)
return web.TextResponse(manifest, mimetype='text/plain')
```
### Acceptance Criteria
- [ ] File count limit enforced (_MAX_ZIP_FILES)
- [ ] Total size limit enforced (_MAX_ZIP_TOTAL_BYTES)
- [ ] 413 status returned with helpful message when limits exceeded
- [ ] Metrics logged for monitoring (file count, total size)
- [ ] Unit tests verify limits are enforced
- [ ] Integration tests verify large releases handled correctly
- [ ] OR: Manifest-based approach implemented for large releases
- [ ] Documentation updated with ZIP download limits
### References
- Source reports: L2:15.1.3.md
- Related findings: FINDING-052
- ASVS sections: 15.1.3
- CWE: CWE-770
### Priority
Medium
---
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: [email protected]
For queries about this service, please contact Infrastructure at:
[email protected]
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]