security: reject oversized request bodies, document file attachments - #122
Merged
Conversation
- Add limits.max_request_bytes (off/unlimited by default): rejects a request whose Content-Length exceeds the cap with 413 before the body is read into memory (server.py::MaxBodySizeMiddleware). Nothing server-side previously bounded request size, which matters now that the playground's file-attachment feature can send large multimodal payloads directly to the API, bypassing the UI's client-side 10 MB check. - Document the new setting in README/ARCHITECTURE.md's security table/ CHANGELOG/example config, and document the file-attachment feature itself in README's Playground section (shipped but never documented). Found via an automated repo-quality review. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Mdwk4pysKSkFrAMG7P5y1V
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What & why
limits.pycaps concurrency and rate (max_in_flight,rate_limit_per_minute, both off by default), but a single request of any size was read fully into memory viaawait request.json(). This matters more now that the playground's file-attachment feature (shipped in feat: file attachments + live panel progress counter in playground #107) sends multimodal payloads with base64-encoded file/image content — the UI enforces a 10 MB client-side cap, but nothing stops a direct API client from sending an arbitrarily large body and exhausting memory before the rate limiter even sees the request.limits.max_request_bytes(off/unlimited by default, consistent with the otherlimits.pyknobs) andserver.py::MaxBodySizeMiddleware, which checksContent-Lengthagainst the cap and returns a clean 413 before the body is read, in the same OpenAI-compatible error shape as the rest of the API.limits.py: a client that omitsContent-Length(chunked transfer-encoding) or lies about it isn't caught by this check — documented as a follow-up indocs/ARCHITECTURE.md's security table, same pattern used for the existing concurrency/rate-limit row.How it was tested
ruff check .passespytest -qpasses (no live network) — 473 passed (was 469; 4 new tests: oversized body → 413 + upstream never called, request within limit passes, unlimited by default preserves current behavior, malformedContent-Lengthheader falls through rather than raising)tests/test_limits.py)docs/ARCHITECTURE.mdsecurity table,CHANGELOG.md,examples/preset.yaml.example)bench/run.pymypy openfusion/ --ignore-missing-imports --disable-error-code import-untyped --exclude openfusion/cli.py— clean, 23 source filespytest --cov=openfusion— 99.53% overall, no regressions; new middleware fully coveredNotes for reviewers
0(unlimited), matching the existing "off by default" convention formax_in_flight/rate_limit_per_minute— this is opt-in for operators, not a behavior change out of the box.app_config.limitspath (same asRequestLimitertoday); aconfig_resolver-based multi-tenant deployment doesn't get a per-tenant value here, consistent with how the existing rate/concurrency limits already work in that mode.Generated by Claude Code