VYPR
High severity7.5NVD Advisory· Published Apr 29, 2026· Updated May 6, 2026

CVE-2026-40560

CVE-2026-40560

Description

Starman versions before 0.4018 for Perl allows HTTP Request Smuggling via Improper Header Precedence.

Starman incorrectly prioritizes "Content-Length" over "Transfer-Encoding: chunked" when both headers are present in an HTTP request. Per RFC 7230 3.3.3, Transfer-Encoding must take precedence.

An attacker could exploit this to smuggle malicious HTTP requests via a front-end reverse proxy.

Affected products

2
  • Miyagawa/Starmanreferences2 versions
    (expand)+ 1 more
    • (no CPE)
    • cpe:2.3:a:miyagawa:starman:*:*:*:*:*:perl:*:*range: <0.4018

Patches

1
ced205f08050

Fix request smuggling (CVE-2026-40560): Transfer-Encoding takes precedence over Content-Length

https://github.com/miyagawa/StarmanTatsuhiko MiyagawaApr 27, 2026via nvd-ref
3 files changed · +73 14
  • Changes+2 0 modified
    @@ -1,6 +1,8 @@
     Revision history for Perl extension Starman
     
     {{$NEXT}}
    +        - Fix HTTP request smuggling: Transfer-Encoding now takes precedence
    +          over Content-Length per RFC 7230 §3.3.3 (CVE-2026-40560)
     
     0.4017  2023-09-13 13:27:02 PDT
             - Handle EINTR when doing sysread calls (Rob Mueller) #148
    
  • lib/Starman/Server.pm+14 14 modified
    @@ -415,20 +415,7 @@ sub _prepare_env {
     
         my $chunked = do { no warnings; lc delete $env->{HTTP_TRANSFER_ENCODING} eq 'chunked' };
     
    -    if (my $cl = $env->{CONTENT_LENGTH}) {
    -        my $buf = Plack::TempBuffer->new($cl);
    -        while ($cl > 0) {
    -            my($chunk, $read) = $get_chunk->();
    -
    -            if ( !defined $read || $read == 0 ) {
    -                die "Read error: $!\n";
    -            }
    -
    -            $cl -= $read;
    -            $buf->print($chunk);
    -        }
    -        $env->{'psgi.input'} = $buf->rewind;
    -    } elsif ($chunked) {
    +    if ($chunked) {
             my $buf = Plack::TempBuffer->new;
             my $chunk_buffer = '';
             my $length;
    @@ -460,6 +447,19 @@ sub _prepare_env {
     
             $env->{CONTENT_LENGTH} = $length;
             $env->{'psgi.input'}   = $buf->rewind;
    +    } elsif (my $cl = $env->{CONTENT_LENGTH}) {
    +        my $buf = Plack::TempBuffer->new($cl);
    +        while ($cl > 0) {
    +            my($chunk, $read) = $get_chunk->();
    +
    +            if ( !defined $read || $read == 0 ) {
    +                die "Read error: $!\n";
    +            }
    +
    +            $cl -= $read;
    +            $buf->print($chunk);
    +        }
    +        $env->{'psgi.input'} = $buf->rewind;
         } else {
             $env->{'psgi.input'} = $null_io;
         }
    
  • t/te_cl_precedence.t+57 0 added
    @@ -0,0 +1,57 @@
    +use strict;
    +use warnings;
    +use Test::TCP;
    +use IO::Socket::INET qw/ SHUT_WR /;
    +use HTTP::Response;
    +use Plack::Loader;
    +use Test::More;
    +
    +# RFC 7230 §3.3.3: when both Transfer-Encoding and Content-Length are
    +# present, Transfer-Encoding must override Content-Length.
    +test_tcp(
    +    client => sub {
    +        my $port = shift;
    +
    +        my $socket = IO::Socket::INET->new(
    +            PeerAddr => 'localhost',
    +            PeerPort => $port,
    +            Proto    => 'tcp',
    +        ) or die "Failed to connect: $!";
    +
    +        # Chunked body encodes "Hello World" (0xb = 11 bytes).
    +        # Content-Length: 5 is intentionally wrong — it must be ignored.
    +        my $chunked_body = "b\r\nHello World\r\n0\r\n\r\n";
    +        my $req = "POST / HTTP/1.1\r\n"
    +                . "Host: localhost\r\n"
    +                . "Transfer-Encoding: chunked\r\n"
    +                . "Content-Length: 5\r\n"
    +                . "\r\n"
    +                . $chunked_body;
    +
    +        $socket->send($req);
    +        $socket->shutdown(SHUT_WR);
    +
    +        my $response = '';
    +        while (1) {
    +            my $n = $socket->sysread(my $buf, 4096);
    +            last unless $n;
    +            $response .= $buf;
    +        }
    +
    +        my $res = HTTP::Response->parse($response);
    +        is $res->content, 'Hello World',
    +            'Transfer-Encoding: chunked takes precedence over Content-Length';
    +    },
    +    server => sub {
    +        my $port = shift;
    +        my $server = Plack::Loader->load('Starman', port => $port, host => '127.0.0.1');
    +        $server->run(sub {
    +            my $env = shift;
    +            my $body = '';
    +            $env->{'psgi.input'}->read($body, 8192);
    +            return [ 200, [ 'Content-Type', 'text/plain', 'Content-Length', length($body) ], [ $body ] ];
    +        });
    +    },
    +);
    +
    +done_testing;
    

Vulnerability mechanics

AI mechanics synthesis has not run for this CVE yet.

References

4

News mentions

0

No linked articles in our index yet.