Pre release security auditing#27
Merged
Merged
Conversation
…_PAIRS_MEMBERS and one virtual hub fallback option
/api/external wrote a session file per request with no cap on count or total bytes, reclaimed only by a 24h age sweep. An unauthenticated caller could fill disk with ~20MB session files faster than the sweep reclaims. - TTL 24h -> 1h (handoff tokens are consumed within minutes) - add MAX_TOKENS=1000, enforced before every write via _enforce_cap(), evicting oldest sessions (evict, not reject, so the feature can't be denied to legit callers by keeping the store full) - tmp/ now hard-bounded to MAX_TOKENS * MAX_UPLOAD_BYTES Regression test: post 20 with cap 5, assert <=5 files remain. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
parse_network_tsv leaked two uncaught exceptions as HTTP 500s: - empty upload -> pandas EmptyDataError - blank mandatory cell -> NaN -> EdgeModel ValidationError Catch EmptyDataError and reject empty/whitespace mandatory cells in _validate, both as NetworkValidationError -> clean 400. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
_validate assumed every layers/nodes/edges entry is a dict and called
.get()/subscripts on it, so a payload like {"layers":["x"],...} 500'd with
AttributeError. Reachable from /api/session/import and /api/external.
Guard that each is a list of objects, raising SessionValidationError.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
- topology betweenness 500'd on non-positive scaled_weight, and inf/nan weights propagated into responses as invalid JSON. - build_graph now clamps any scaled_weight that isn't strictly-positive- finite to a small floor, fixing both at the single point layout and topology share. Layout ignores weights, so only topology input changes. - parser rejects non-finite Weight values at parse time, keeping the /api/network response finite. Clamped (not rejected at the model): a Field(allow_inf_nan=False) makes FastAPI's 422 handler try to echo the inf input and itself 500. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Escaped & < > " but not ' — a latent XSS footgun for any single-quoted attribute template. Add the ' -> ' replacement so the helper is safe by default regardless of quote style at the call site. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
An imported session's edge color flows into channelColors unvalidated and was interpolated raw into a type="color" value="..." attribute inside an innerHTML template. CSP blocks execution, but escape it so the attribute can't be broken out of. Defense-in-depth. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
resolveExternal interpolated the ?session= token straight into the fetch path; a '/' or '?' in it builds a malformed URL. Wrap with encodeURIComponent. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
/api/layout and /api/topology run O(V*E) betweenness with no auth; per- request size caps bound one request but not the rate. Add an nginx limit_req (20 r/s, burst 40 nodelay, per client IP) — interactive bursts pass, sustained floods throttle. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
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.
High-priority fixes:
clustering.py: addedMAX_ALL_PAIRS_MEMBERS = 1000. Communities at/below the cap keep the exact v2 all-pairs padding; above it, members connect to a single O(n) virtual hub (dropped from the returned coords) — same "keep disconnected members spread" effect without the quadratic edge count. Verified: an 8 000-member community now peaks at +3.2 MB / 0.064 s (was multiple GB). New regression testtest_large_community_uses_hub_not_all_pairs; full suite 66 passed.external.py: TTL cut from 24 h → 1 h; addedMAX_TOKENS = 1000enforced by_enforce_cap(), which runs before every write and evicts the oldest sessions (not reject — so an attacker can't deny the feature by keeping the store full). Combined with the per-requestMAX_UPLOAD_BYTEScap,tmp/is now hard-bounded toMAX_TOKENS * MAX_UPLOAD_BYTESregardless of request volume. New regression testtest_external_storage_capped(posts 20 with cap 5, asserts ≤ 5 files remain); full suite 67 passed. Single-use delete-on-resolve was not taken — it would 404 a legitimate page refresh; TTL + count cap bound disk without that UX cost. Proxylimit_reqand the chunked-transfer note remain deployment-side recommendations.Medium-priority fixes: