harden(ft,auth,net): cap transfers/frames, evict stale SRP, distrust XFF
CI / rust client (hh) (macos-latest) (push) Has been cancelled
CI / rust client (hh) (ubuntu-latest) (push) Has been cancelled
CI / rust coverage (push) Has been cancelled
CI / python server (3.10) (push) Has been cancelled
CI / python server (3.11) (push) Has been cancelled
CI / python server (3.12) (push) Has been cancelled
CI / headless e2e smoke (push) Has been cancelled
CI / dependency audit (push) Has been cancelled
CI / secret scanning (push) Has been cancelled
CI / rust client (hh) (macos-latest) (push) Has been cancelled
CI / rust client (hh) (ubuntu-latest) (push) Has been cancelled
CI / rust coverage (push) Has been cancelled
CI / python server (3.10) (push) Has been cancelled
CI / python server (3.11) (push) Has been cancelled
CI / python server (3.12) (push) Has been cancelled
CI / headless e2e smoke (push) Has been cancelled
CI / dependency audit (push) Has been cancelled
CI / secret scanning (push) Has been cancelled
M1: enforce the declared transfer size (clamped to MAX_SIZE) on chunk receipt in both the Rust and Python clients — a malicious sender can no longer grow the receive buffer unboundedly. M2: only honor X-Forwarded-For when TRUST_PROXY is set, so a direct client can't spoof a source IP to dodge the per-IP rate limiter. M3: evict unverified SRP sessions after a 60s TTL on each new handshake, preventing half-finished auths from exhausting memory. M4: drop WS frames larger than 256 KB before they hit the store or broadcast, bounding per-message memory and flood blast radius. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -319,10 +319,21 @@ class Client:
|
||||
elif ft_type == "chunk":
|
||||
if transfer_id in self.received_chunks:
|
||||
chunk_data = base64.b64decode(ft_data.get("data", ""))
|
||||
self.received_chunks[transfer_id].append(chunk_data)
|
||||
meta = self.transfer_meta.get(transfer_id, {})
|
||||
total = meta.get("size", 0)
|
||||
total = meta.get("size", 0) or 0
|
||||
# Enforce the declared size (clamped to MAX_FILE_SIZE) on receipt:
|
||||
# a sender can lie about `size` or just keep streaming, so abort
|
||||
# the moment the accumulated bytes would exceed the cap.
|
||||
cap = min(total, MAX_FILE_SIZE) if total else MAX_FILE_SIZE
|
||||
received = sum(len(c) for c in self.received_chunks[transfer_id])
|
||||
if received + len(chunk_data) > cap:
|
||||
self.received_chunks.pop(transfer_id, None)
|
||||
self.transfer_meta.pop(transfer_id, None)
|
||||
self.console.print()
|
||||
self.error("Transfer exceeds declared size — aborted.")
|
||||
return True
|
||||
self.received_chunks[transfer_id].append(chunk_data)
|
||||
received += len(chunk_data)
|
||||
pct = int(received * 100 / total) if total else 0
|
||||
self.console.print(
|
||||
f"\r[cyan]Receiving: {pct}% ({_human_size(received)}/{_human_size(total)})[/]",
|
||||
|
||||
Reference in New Issue
Block a user