regression: negative Content-Length check lost in v2.0.0 rewrite #4

Open
opened 2026-06-08 04:05:38 -04:00 by everestmcarthur · 0 comments
everestmcarthur commented 2026-06-08 04:05:38 -04:00 (Migrated from github.com)

Summary

The v2.0.0 rewrite of forge-paste.py re-introduced the negative Content-Length vulnerability that was fixed in the previous version (issue #2 / PR #3).

Vulnerable Code

# forge-paste.py, _read_body(), ~line 907
def _read_body(self):
    try:
        length = int(self.headers.get("Content-Length", 0))
    except (ValueError, TypeError):
        return None, "Invalid Content-Length"
    if length > MAX_SIZE:
        return None, f"Content too large (max {format_bytes(MAX_SIZE)})"
    return self.rfile.read(length), None  # ← rfile.read(-1) reads until EOF if length < 0

A client sending Content-Length: -1 bypasses the > MAX_SIZE check and calls rfile.read(-1), which reads until EOF — hanging the server thread indefinitely and potentially causing a denial-of-service.

Fix

Add a length < 0 guard (same fix as the previous version had):

def _read_body(self):
    try:
        length = int(self.headers.get("Content-Length", 0))
    except (ValueError, TypeError):
        return None, "Invalid Content-Length"
    if length < 0:
        return None, "Invalid Content-Length"
    if length > MAX_SIZE:
        return None, f"Content too large (max {format_bytes(MAX_SIZE)})"
    return self.rfile.read(length), None

Context

This was previously fixed in PR #3 but was lost when v2.0.0 was written as a single-commit full rewrite. The TTL/expiry fix from #3 was correctly carried over — this specific guard was missed.

## Summary The v2.0.0 rewrite of `forge-paste.py` re-introduced the negative `Content-Length` vulnerability that was fixed in the previous version (issue #2 / PR #3). ## Vulnerable Code ```python # forge-paste.py, _read_body(), ~line 907 def _read_body(self): try: length = int(self.headers.get("Content-Length", 0)) except (ValueError, TypeError): return None, "Invalid Content-Length" if length > MAX_SIZE: return None, f"Content too large (max {format_bytes(MAX_SIZE)})" return self.rfile.read(length), None # ← rfile.read(-1) reads until EOF if length < 0 ``` A client sending `Content-Length: -1` bypasses the `> MAX_SIZE` check and calls `rfile.read(-1)`, which reads until EOF — hanging the server thread indefinitely and potentially causing a denial-of-service. ## Fix Add a `length < 0` guard (same fix as the previous version had): ```python def _read_body(self): try: length = int(self.headers.get("Content-Length", 0)) except (ValueError, TypeError): return None, "Invalid Content-Length" if length < 0: return None, "Invalid Content-Length" if length > MAX_SIZE: return None, f"Content too large (max {format_bytes(MAX_SIZE)})" return self.rfile.read(length), None ``` ## Context This was previously fixed in PR #3 but was lost when v2.0.0 was written as a single-commit full rewrite. The TTL/expiry fix from #3 was correctly carried over — this specific guard was missed.
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference: SheWhoReignsSupreme/forge-paste#4
No description provided.