Summary
static.php has several bugs in its range request handling and file serving logic that cause incorrect responses and potential corruption on the PHP built-in server.
Bugs
1. Suffix-range requests return wrong bytes (RFC 7233 violation)
Per RFC 7233 Section 2.1, bytes=-500 means "the last 500 bytes". The current code splits on - to get ["", "500"], then sets the empty first element to 0:
if ($range[0] === '') {
$range[0] = 0;
}
This turns bytes=-500 into bytes=0-500, serving the first 501 bytes instead of the last 500 bytes. The Content-Range header is also wrong (bytes 0-500/1058 instead of bytes 558-1057/1058).
2. Suffix-range larger than file returns 416 instead of 206
Per RFC 7233: "If the selected representation is shorter than the specified suffix-length, the entire representation is used."
bytes=-9999 on a 1058-byte file is interpreted as bytes=0-9999. The bounds check $range[1] <= $size - 1 fails (9999 > 1057), returning 416 "Range Not Satisfiable" instead of serving the full file.
3. Headers sent before file handle validation
http_response_code(206) and response headers are sent before fopen() is called. If fopen() fails, the 500 error code cannot take effect because headers were already sent. The client receives a 206 with the promised Content-Length but an empty body.
4. Output buffer corruption on PHP built-in server
When output buffering is active (e.g., php.ini output_buffering=On), buffer content is prepended to the file output. Content-Length is calculated from filesize() and doesn't account for the buffer, so HTTP clients read Content-Length bytes starting with the buffer junk, truncating the end of the actual file. This causes broken CSS/JS delivery.
Steps to reproduce
Suffix-range bug (using any HTTP client):
curl -H "Range: bytes=-500" http://localhost/static.php/program/resources/dummy.pdf -o /dev/null -D -
The Content-Range header will show bytes 0-500/1058 instead of the expected bytes 558-1057/1058.
Environment
- Roundcube: master branch (1.7-beta / 1.7-rc)
- PHP: 8.1+
Summary
static.phphas several bugs in its range request handling and file serving logic that cause incorrect responses and potential corruption on the PHP built-in server.Bugs
1. Suffix-range requests return wrong bytes (RFC 7233 violation)
Per RFC 7233 Section 2.1,
bytes=-500means "the last 500 bytes". The current code splits on-to get["", "500"], then sets the empty first element to0:This turns
bytes=-500intobytes=0-500, serving the first 501 bytes instead of the last 500 bytes. TheContent-Rangeheader is also wrong (bytes 0-500/1058instead ofbytes 558-1057/1058).2. Suffix-range larger than file returns 416 instead of 206
Per RFC 7233: "If the selected representation is shorter than the specified suffix-length, the entire representation is used."
bytes=-9999on a 1058-byte file is interpreted asbytes=0-9999. The bounds check$range[1] <= $size - 1fails (9999 > 1057), returning 416 "Range Not Satisfiable" instead of serving the full file.3. Headers sent before file handle validation
http_response_code(206)and response headers are sent beforefopen()is called. Iffopen()fails, the 500 error code cannot take effect because headers were already sent. The client receives a 206 with the promisedContent-Lengthbut an empty body.4. Output buffer corruption on PHP built-in server
When output buffering is active (e.g.,
php.ini output_buffering=On), buffer content is prepended to the file output.Content-Lengthis calculated fromfilesize()and doesn't account for the buffer, so HTTP clients readContent-Lengthbytes starting with the buffer junk, truncating the end of the actual file. This causes broken CSS/JS delivery.Steps to reproduce
Suffix-range bug (using any HTTP client):
The
Content-Rangeheader will showbytes 0-500/1058instead of the expectedbytes 558-1057/1058.Environment