75 lines
2.3 KiB
Markdown
75 lines
2.3 KiB
Markdown
# PRD 0041: Git HTTP Request Bounds
|
|
|
|
- **Status:** Active
|
|
- **Author:** didericis-codex
|
|
- **Created:** 2026-06-02
|
|
- **Issue:** #138
|
|
|
|
## Summary
|
|
|
|
Add Content-Length validation and a body-size cap to `git_http_backend.py` so
|
|
malformed or oversized smart-HTTP requests fail cleanly rather than crashing
|
|
the handler or exhausting memory.
|
|
|
|
## Problem
|
|
|
|
`bot_bottle/git_http_backend.py` calls `int(self.headers.get("Content-Length",
|
|
0))` without catching `ValueError`. A request with a non-numeric Content-Length
|
|
raises an unhandled exception in the request handler.
|
|
|
|
The handler reads the full declared length into memory before passing the body
|
|
to `git http-backend` with no upper bound. A local or compromised client can
|
|
force arbitrarily high memory use. For comparison, `supervise_server.py` caps
|
|
request bodies at 1 MiB.
|
|
|
|
## Goals / Success Criteria
|
|
|
|
- A missing or non-numeric Content-Length returns HTTP 400.
|
|
- A negative Content-Length returns HTTP 400.
|
|
- A body larger than the cap (1 MiB, matching `supervise_server.py`) returns
|
|
HTTP 413.
|
|
- Valid Git smart-HTTP pushes and fetches continue to work.
|
|
- Unit tests cover: missing length, non-numeric length, negative length,
|
|
over-cap length, and a valid push/fetch passthrough.
|
|
|
|
## Non-goals
|
|
|
|
- No changes to git-gate authentication or route logic.
|
|
- No changes to `supervise_server.py`.
|
|
- No streaming / chunked-transfer-encoding support.
|
|
- No TLS changes.
|
|
|
|
## Scope
|
|
|
|
In scope:
|
|
|
|
- `bot_bottle/git_http_backend.py` request parsing and body reading.
|
|
- Unit tests in `tests/unit/test_git_http_backend.py`.
|
|
|
|
Out of scope:
|
|
|
|
- Integration tests that drive a real Git client through the handler.
|
|
|
|
## Design
|
|
|
|
Wrap the Content-Length parse in a try/except and return 400 on `ValueError`.
|
|
Add an explicit check for negative values. After parsing, compare the declared
|
|
length against a module-level `_MAX_BODY_BYTES` constant (default 1 MiB) and
|
|
return 413 if exceeded. Read exactly `min(content_length, _MAX_BODY_BYTES)`
|
|
bytes.
|
|
|
|
## Testing Strategy
|
|
|
|
- Unit tests using `unittest.mock` to drive the handler with crafted headers.
|
|
- Test cases: no Content-Length header, `Content-Length: abc`, `Content-Length:
|
|
-1`, `Content-Length: 2097152` (over cap), and a normal small POST body.
|
|
|
|
Run:
|
|
|
|
- `python3 -m unittest tests.unit.test_git_http_backend`
|
|
- `python3 -m unittest discover -s tests/unit`
|
|
|
|
## Open Questions
|
|
|
|
None.
|