Script 'mail_helper' called by obssrc Hello community, here is the log from the commit of package perl-Plack for openSUSE:Factory checked in at 2026-10-26 14:17:24 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Comparing /work/SRC/openSUSE:Factory/perl-Plack (Old) and /work/SRC/openSUSE:Factory/.perl-Plack.new.2252680 (New) ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "perl-Plack" Mon Oct 26 14:17:24 2026 rev:15 rq:1392700 version:1.0057 Changes: -------- --- /work/SRC/openSUSE:Factory/perl-Plack/perl-Plack.changes 2026-06-13 18:49:46.375500283 +0200 +++ /work/SRC/openSUSE:Factory/.perl-Plack.new.2252680/perl-Plack.changes 2026-10-26 14:17:27.580759108 +0100 @@ -1,0 +2,37 @@ +Sat Oct 24 09:36:45 UTC 2026 - Tina Müller <[email protected]> + +- updated to 1.0057 + see /usr/share/doc/packages/perl-Plack/Changes + + 1.0057 2026-10-24 02:01:28 PDT + [BUG FIXES] + - Fix Plack, Plack::Request and Plack::Response in 1.0056 still declaring $VERSION 1.0055, + which kept PAUSE from indexing 1.0056. This release contains the same fixes as 1.0056, + including CVE-2026-115825 + + 1.0056 2026-10-24 01:55:22 PDT + [SECURITY] + - Fix HTTP::Server::PSGI allocating a buffer as large as the declared Content-Length, + which let a single request crash the server with "Out of memory!" (CVE-2026-115825) bsc#1287639 + [BUG FIXES] + - Fix HTTP::Server::PSGI passing bytes after the Content-Length body to the app in psgi.input + - Fix HTTP::Server::PSGI accepting malformed, negative, duplicate or too long (more than + 18 digits) Content-Length. Such requests now get 400 Bad Request + - Fix HTTP::Server::PSGI ignoring Transfer-Encoding and passing the raw chunked body to the app. + Such requests now get 501 Not Implemented + - Fix HTTP::Server::PSGI passing stray bytes after the headers as psgi.input when the request + has no Content-Length or a Content-Length of 0 + + 1.0055 2026-10-13 15:00:27 PDT + [SECURITY] + - Fix memory leak of every request env in Plack::Middleware::Recursive (CVE-2026-110333) + - Plack::Request::Upload->basename never returns '', '.' or '..' (CVE-2026-110332) + - Plack::App::URLMap matches SERVER_PORT literally when stripping it from Host (CVE-2026-110335) + [BUG FIXES] + - Plack::Middleware::ErrorDocument restores the env after a subrequest, so outer + middleware such as AccessLog sees the original request (CVE-2026-110334) + [DOCUMENTATION] + - Document that Plack::Handler::FCGI and Apache2 keep dot segments in PATH_INFO and + warn against relying on front-server path-based access control (CVE-2026-110336) + +------------------------------------------------------------------- Old: ---- Plack-1.0054.tar.gz New: ---- Plack-1.0057.tar.gz ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Other differences: ------------------ ++++++ perl-Plack.spec ++++++ --- /var/tmp/diff_new_pack.Fi8ljl/_old 2026-10-26 14:17:28.486797016 +0100 +++ /var/tmp/diff_new_pack.Fi8ljl/_new 2026-10-26 14:17:28.488797100 +0100 @@ -18,7 +18,7 @@ %define cpan_name Plack Name: perl-Plack -Version: 1.0054 +Version: 1.0057 Release: 0 License: Artistic-1.0 OR GPL-1.0-or-later Summary: Perl Superglue for Web frameworks and Web Servers (PSGI toolkit) ++++++ Plack-1.0054.tar.gz -> Plack-1.0057.tar.gz ++++++ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/Plack-1.0054/Changes new/Plack-1.0057/Changes --- old/Plack-1.0054/Changes 2026-05-05 08:38:14.000000000 +0200 +++ new/Plack-1.0057/Changes 2026-10-24 11:01:28.000000000 +0200 @@ -1,5 +1,36 @@ Go to http://github.com/plack/Plack/issues for the roadmap and known issues. +1.0057 2026-10-24 02:01:28 PDT + [BUG FIXES] + - Fix Plack, Plack::Request and Plack::Response in 1.0056 still declaring $VERSION 1.0055, + which kept PAUSE from indexing 1.0056. This release contains the same fixes as 1.0056, + including CVE-2026-115825 + +1.0056 2026-10-24 01:55:22 PDT + [SECURITY] + - Fix HTTP::Server::PSGI allocating a buffer as large as the declared Content-Length, + which let a single request crash the server with "Out of memory!" (CVE-2026-115825) + [BUG FIXES] + - Fix HTTP::Server::PSGI passing bytes after the Content-Length body to the app in psgi.input + - Fix HTTP::Server::PSGI accepting malformed, negative, duplicate or too long (more than + 18 digits) Content-Length. Such requests now get 400 Bad Request + - Fix HTTP::Server::PSGI ignoring Transfer-Encoding and passing the raw chunked body to the app. + Such requests now get 501 Not Implemented + - Fix HTTP::Server::PSGI passing stray bytes after the headers as psgi.input when the request + has no Content-Length or a Content-Length of 0 + +1.0055 2026-10-13 15:00:27 PDT + [SECURITY] + - Fix memory leak of every request env in Plack::Middleware::Recursive (CVE-2026-110333) + - Plack::Request::Upload->basename never returns '', '.' or '..' (CVE-2026-110332) + - Plack::App::URLMap matches SERVER_PORT literally when stripping it from Host (CVE-2026-110335) + [BUG FIXES] + - Plack::Middleware::ErrorDocument restores the env after a subrequest, so outer + middleware such as AccessLog sees the original request (CVE-2026-110334) + [DOCUMENTATION] + - Document that Plack::Handler::FCGI and Apache2 keep dot segments in PATH_INFO and + warn against relying on front-server path-based access control (CVE-2026-110336) + 1.0054 2026-05-04 23:38:13 PDT [BUG FIXES] - Revert Plack::App::File REQUEST_METHOD check after locating file due to regression #725 diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/Plack-1.0054/MANIFEST new/Plack-1.0057/MANIFEST --- old/Plack-1.0054/MANIFEST 2026-05-05 08:38:14.000000000 +0200 +++ new/Plack-1.0057/MANIFEST 2026-10-24 11:01:28.000000000 +0200 @@ -108,9 +108,11 @@ t/HTTP-Message-PSGI/path_info.t t/HTTP-Message-PSGI/unknown_response.t t/HTTP-Message-PSGI/utf8_req.t +t/HTTP-Server-PSGI/framing.t t/HTTP-Server-PSGI/harakiri.t t/HTTP-Server-PSGI/listen.t t/HTTP-Server-PSGI/post.t +t/HTTP-Server-PSGI/read_size.t t/Plack-Builder/builder.t t/Plack-Builder/mount.t t/Plack-Builder/oo_interface.t @@ -161,6 +163,7 @@ t/Plack-Middleware/content_length.t t/Plack-Middleware/directory.t t/Plack-Middleware/error_document.t +t/Plack-Middleware/error_document_restore_env.t t/Plack-Middleware/error_document_streaming_app.t t/Plack-Middleware/errors/404.html t/Plack-Middleware/errors/500.html @@ -185,6 +188,7 @@ t/Plack-Middleware/psgibin.t t/Plack-Middleware/rearrange_headers.t t/Plack-Middleware/recursive/base.t +t/Plack-Middleware/recursive/leak.t t/Plack-Middleware/recursive/streaming.t t/Plack-Middleware/recursive/throw.t t/Plack-Middleware/recursive/throw_streaming.t @@ -205,6 +209,7 @@ t/Plack-Middleware/urlmap.t t/Plack-Middleware/urlmap_builder.t t/Plack-Middleware/urlmap_env.t +t/Plack-Middleware/urlmap_port_meta.t t/Plack-Middleware/urlmap_ports.t t/Plack-Middleware/wrapcgi.t t/Plack-Middleware/wrapcgi_exec.t diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/Plack-1.0054/META.json new/Plack-1.0057/META.json --- old/Plack-1.0054/META.json 2026-05-05 08:38:14.000000000 +0200 +++ new/Plack-1.0057/META.json 2026-10-24 11:01:28.000000000 +0200 @@ -102,7 +102,7 @@ "web" : "https://github.com/plack/Plack" } }, - "version" : "1.0054", + "version" : "1.0057", "x_authority" : "cpan:MIYAGAWA", "x_contributors" : [ "Aaron Trevena <[email protected]>", diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/Plack-1.0054/META.yml new/Plack-1.0057/META.yml --- old/Plack-1.0054/META.yml 2026-05-05 08:38:14.000000000 +0200 +++ new/Plack-1.0057/META.yml 2026-10-24 11:01:28.000000000 +0200 @@ -47,7 +47,7 @@ bugtracker: https://github.com/plack/Plack/issues homepage: https://github.com/plack/Plack repository: https://github.com/plack/Plack.git -version: '1.0054' +version: '1.0057' x_authority: cpan:MIYAGAWA x_contributors: - 'Aaron Trevena <[email protected]>' diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/Plack-1.0054/Makefile.PL new/Plack-1.0057/Makefile.PL --- old/Plack-1.0054/Makefile.PL 2026-05-05 08:38:14.000000000 +0200 +++ new/Plack-1.0057/Makefile.PL 2026-10-24 11:01:28.000000000 +0200 @@ -50,7 +50,7 @@ "Test::More" => "0.88", "Test::Requires" => 0 }, - "VERSION" => "1.0054", + "VERSION" => "1.0057", "test" => { "TESTS" => "t/*.t t/HTTP-Message-PSGI/*.t t/HTTP-Server-PSGI/*.t t/Plack-Builder/*.t t/Plack-HTTPParser-PP/*.t t/Plack-Handler/*.t t/Plack-Loader/*.t t/Plack-MIME/*.t t/Plack-Middleware/*.t t/Plack-Middleware/cascade/*.t t/Plack-Middleware/recursive/*.t t/Plack-Middleware/stacktrace/*.t t/Plack-Request/*.t t/Plack-Response/*.t t/Plack-Runner/*.t t/Plack-TempBuffer/*.t t/Plack-Test/*.t t/Plack-Util/*.t" } diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/Plack-1.0054/lib/HTTP/Server/PSGI.pm new/Plack-1.0057/lib/HTTP/Server/PSGI.pm --- old/Plack-1.0054/lib/HTTP/Server/PSGI.pm 2026-05-05 08:38:14.000000000 +0200 +++ new/Plack-1.0057/lib/HTTP/Server/PSGI.pm 2026-10-24 11:01:28.000000000 +0200 @@ -31,6 +31,7 @@ } use constant MAX_REQUEST_SIZE => 131072; +use constant CHUNKSIZE => 64 * 1024; use constant MSWin32 => $^O eq 'MSWin32'; sub new { @@ -159,15 +160,24 @@ my $reqlen = parse_http_request($buf, $env); if ($reqlen >= 0) { $buf = substr $buf, $reqlen; + if (defined $env->{HTTP_TRANSFER_ENCODING}) { + # request bodies with Transfer-Encoding are not supported + $res = [ 501, [ 'Content-Type' => 'text/plain' ], [ 'Not Implemented' ] ]; + last; + } + if (defined $env->{CONTENT_LENGTH} && $env->{CONTENT_LENGTH} !~ /\A[0-9]{1,18}\z/) { + # malformed, duplicate or too long (more than 18 digits) Content-Length + last; + } if (my $cl = $env->{CONTENT_LENGTH}) { my $buffer = Stream::Buffered->new($cl); while ($cl > 0) { my $chunk; if (length $buf) { - $chunk = $buf; - $buf = ''; + # $cl may be beyond UV_MAX, which substr() can't take + $chunk = substr $buf, 0, ($cl < length $buf ? $cl : length $buf), ''; } else { - $self->read_timeout($conn, \$chunk, $cl, 0, $self->{timeout}) + $self->read_timeout($conn, \$chunk, $cl < CHUNKSIZE ? $cl : CHUNKSIZE, 0, $self->{timeout}) or return; } $buffer->print($chunk); @@ -175,7 +185,7 @@ } $env->{'psgi.input'} = $buffer->rewind; } else { - open my $input, "<", \$buf; + open my $input, "<", \""; $env->{'psgi.input'} = $input; } @@ -340,7 +350,8 @@ Some features in HTTP/1.1, notably chunked requests, responses and pipeline requests are B<NOT> supported, and it also does not support -HTTP/0.9. +HTTP/0.9. Requests with a C<Transfer-Encoding> header are rejected with +501 Not Implemented. See L<Starman> or uWSGI server if you want HTTP/1.1 and other features ready for a production use. diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/Plack-1.0054/lib/Plack/App/URLMap.pm new/Plack-1.0057/lib/Plack/App/URLMap.pm --- old/Plack-1.0054/lib/Plack/App/URLMap.pm 2026-05-05 08:38:14.000000000 +0200 +++ new/Plack-1.0057/lib/Plack/App/URLMap.pm 2026-10-24 11:01:28.000000000 +0200 @@ -45,7 +45,7 @@ my($http_host, $server_name) = @{$env}{qw( HTTP_HOST SERVER_NAME )}; if ($http_host and my $port = $env->{SERVER_PORT}) { - $http_host =~ s/:$port$//; + $http_host =~ s/:\Q$port\E$//; } for my $map (@{ $self->{_sorted_mapping} }) { diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/Plack-1.0054/lib/Plack/Handler/Apache2.pm new/Plack-1.0057/lib/Plack/Handler/Apache2.pm --- old/Plack-1.0054/lib/Plack/Handler/Apache2.pm 2026-05-05 08:38:14.000000000 +0200 +++ new/Plack-1.0057/lib/Plack/Handler/Apache2.pm 2026-10-24 11:01:28.000000000 +0200 @@ -225,6 +225,17 @@ standalone HTTP servers such as L<Starman> or L<Starlet> proxied with mod_proxy. +=head1 PATH_INFO AND ACCESS CONTROL + +Because mod_perl squeezes multiple slashes in C<PATH_INFO>, this +handler rebuilds C<PATH_INFO> from the unparsed request URI, +percent-decoded. Dot segments such as C</../> are B<not> removed, so +a request to C</admin/../public/x> is matched by Apache's +C<< <Location> >> blocks as C</public/x> while your application sees +C</admin/../public/x>. Do not rely on Apache's path-based access +control to protect parts of your application: enforce it in the +application (or in middleware) instead. + =head1 CREATING CUSTOM HANDLER If you want to create a custom handler that loads or creates PSGI diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/Plack-1.0054/lib/Plack/Handler/FCGI.pm new/Plack-1.0057/lib/Plack/Handler/FCGI.pm --- old/Plack-1.0054/lib/Plack/Handler/FCGI.pm 2026-05-05 08:38:14.000000000 +0200 +++ new/Plack-1.0057/lib/Plack/Handler/FCGI.pm 2026-10-24 11:01:28.000000000 +0200 @@ -389,6 +389,22 @@ See L<https://www.nginx.com/resources/wiki/start/topics/examples/fastcgiexample/> for more details. +=head2 PATH_INFO AND ACCESS CONTROL + +Some web servers squeeze multiple slashes in C<PATH_INFO>, so this +handler ignores the C<PATH_INFO> passed by the web server and rebuilds +it from C<REQUEST_URI>, percent-decoded and with C<SCRIPT_NAME> +stripped. Dot segments such as C</../> are B<not> removed. + +The web server, on the other hand, usually matches its C<location> or +C<< <Location> >> blocks against the normalized path. A request to +C</admin/../public/x> is matched by the web server as C</public/x>, +but your application sees C<PATH_INFO> C</admin/../public/x>. Do not +rely on path-based access control in the front web server to protect +parts of your application: enforce it in the application (or in +middleware) instead, or make sure the web server rejects request +URIs containing dot segments. + =head3 Apache mod_fastcgi After installing C<mod_fastcgi>, you should add the C<FastCgiExternalServer> diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/Plack-1.0054/lib/Plack/Middleware/ErrorDocument.pm new/Plack-1.0057/lib/Plack/Middleware/ErrorDocument.pm --- old/Plack-1.0054/lib/Plack/Middleware/ErrorDocument.pm 2026-05-05 08:38:14.000000000 +0200 +++ new/Plack-1.0057/lib/Plack/Middleware/ErrorDocument.pm 2026-10-24 11:01:28.000000000 +0200 @@ -28,6 +28,11 @@ } } + # the env is shared with outer middleware (e.g. AccessLog), + # so restore the original request after the subrequest + my @keys = qw(REQUEST_METHOD REQUEST_URI PATH_INFO QUERY_STRING CONTENT_LENGTH); + my %orig = map { exists $env->{$_} ? ($_ => $env->{$_}) : () } @keys; + # TODO: What if SCRIPT_NAME is not empty? $env->{REQUEST_METHOD} = 'GET'; $env->{REQUEST_URI} = $path; @@ -35,7 +40,13 @@ $env->{QUERY_STRING} = ''; delete $env->{CONTENT_LENGTH}; - my $sub_r = $self->app->($env); + my $sub_r = eval { $self->app->($env) }; + my $err = $@; + + for my $key (@keys) { + exists $orig{$key} ? ($env->{$key} = $orig{$key}) : delete $env->{$key}; + } + die $err if $err; if ($sub_r->[0] == 200) { $r->[1] = $sub_r->[1]; if (@$r == 3) { diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/Plack-1.0054/lib/Plack/Middleware/Recursive.pm new/Plack-1.0057/lib/Plack/Middleware/Recursive.pm --- old/Plack-1.0054/lib/Plack/Middleware/Recursive.pm 2026-05-05 08:38:14.000000000 +0200 +++ new/Plack-1.0057/lib/Plack/Middleware/Recursive.pm 2026-10-24 11:01:28.000000000 +0200 @@ -46,12 +46,13 @@ my $old_path_info = $env->{PATH_INFO}; + # the include callback is stored in $env, so don't let it keep $env alive + Scalar::Util::weaken($env); + return sub { my $new_path_info = shift; my($path, $query) = split /\?/, $new_path_info, 2; - Scalar::Util::weaken($env); - $env->{PATH_INFO} = $path; $env->{QUERY_STRING} = $query; $env->{REQUEST_METHOD} = 'GET'; diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/Plack-1.0054/lib/Plack/Request/Upload.pm new/Plack-1.0057/lib/Plack/Request/Upload.pm --- old/Plack-1.0054/lib/Plack/Request/Upload.pm 2026-05-05 08:38:14.000000000 +0200 +++ new/Plack-1.0057/lib/Plack/Request/Upload.pm 2026-10-24 11:01:28.000000000 +0200 @@ -35,6 +35,8 @@ $basename =~ s|\\|/|g; $basename = ( File::Spec::Unix->splitpath($basename) )[2]; $basename =~ s|[^\w\.-]+|_|g; + # never return an empty name or a directory reference + $basename = '_' if $basename =~ /^\.{0,2}\z/; $self->{basename} = $basename; } $self->{basename}; @@ -79,7 +81,13 @@ =item basename -Returns basename for "filename". +Returns basename for "filename". Characters other than word +characters, C<.> and C<-> are replaced with C<_>, and a name that +would be empty, C<.> or C<..> is returned as C<_>. + +Note that the returned value is still client-supplied: you should +make sure it doesn't overwrite existing files before using it as a +file name. =back diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/Plack-1.0054/lib/Plack/Request.pm new/Plack-1.0057/lib/Plack/Request.pm --- old/Plack-1.0054/lib/Plack/Request.pm 2026-05-05 08:38:14.000000000 +0200 +++ new/Plack-1.0057/lib/Plack/Request.pm 2026-10-24 11:01:28.000000000 +0200 @@ -2,7 +2,7 @@ use strict; use warnings; use 5.008_001; -our $VERSION = '1.0054'; +our $VERSION = '1.0057'; use HTTP::Headers::Fast; use Carp (); diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/Plack-1.0054/lib/Plack/Response.pm new/Plack-1.0057/lib/Plack/Response.pm --- old/Plack-1.0054/lib/Plack/Response.pm 2026-05-05 08:38:14.000000000 +0200 +++ new/Plack-1.0057/lib/Plack/Response.pm 2026-10-24 11:01:28.000000000 +0200 @@ -1,7 +1,7 @@ package Plack::Response; use strict; use warnings; -our $VERSION = '1.0054'; +our $VERSION = '1.0057'; use Plack::Util::Accessor qw(body status); use Carp (); diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/Plack-1.0054/lib/Plack.pm new/Plack-1.0057/lib/Plack.pm --- old/Plack-1.0054/lib/Plack.pm 2026-05-05 08:38:14.000000000 +0200 +++ new/Plack-1.0057/lib/Plack.pm 2026-10-24 11:01:28.000000000 +0200 @@ -3,7 +3,7 @@ use strict; use warnings; use 5.008_001; -our $VERSION = '1.0054'; +our $VERSION = '1.0057'; 1; __END__ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/Plack-1.0054/t/HTTP-Server-PSGI/framing.t new/Plack-1.0057/t/HTTP-Server-PSGI/framing.t --- old/Plack-1.0054/t/HTTP-Server-PSGI/framing.t 1970-01-01 01:00:00.000000000 +0100 +++ new/Plack-1.0057/t/HTTP-Server-PSGI/framing.t 2026-10-24 11:01:28.000000000 +0200 @@ -0,0 +1,140 @@ +use strict; +use warnings; + +use HTTP::Server::PSGI; +use POSIX (); +use Test::More; + +{ + package FakeSocket; + + # returns each segment in its own read, at most $len bytes at a time + sub new { + my($class, @segments) = @_; + bless { segments => [ @segments ], out => '' }, $class; + } + + sub sysread { + my $self = shift; + my(undef, $len, $off) = @_; + my $seg = \$self->{segments}[0]; + my $chunk = defined $$seg ? substr $$seg, 0, $len, '' : ''; + shift @{$self->{segments}} if defined $$seg && !length $$seg; + $_[0] = '' unless defined $_[0]; + substr($_[0], $off || 0) = $chunk; + return length $chunk; + } + + sub syswrite { + my($self, $buf, $len, $off) = @_; + $self->{out} .= substr $buf, $off || 0, $len; + return $len; + } +} + +my $server = HTTP::Server::PSGI->new(timeout => 5); + +sub run_request { + my $sock = FakeSocket->new(@_); + my $body; + $server->handle_connection({}, $sock, sub { + my $env = shift; + local $/; + my $input = $env->{'psgi.input'}; + $body = <$input>; + [ 200, [ 'Content-Type' => 'text/plain' ], [ 'OK' ] ]; + }); + my($status) = $sock->{out} =~ m{^HTTP/1\.\d (\d+)}; + return ($status, $body); +} + +{ + my($status, $body) = run_request( + "POST / HTTP/1.0\r\nContent-Length: 3\r\n\r\nabcGET /next HTTP/1.0\r\n\r\n" + ); + is $status, 200; + is $body, 'abc', 'body stops at Content-Length'; +} + +{ + my($status, $body) = run_request( + "POST / HTTP/1.0\r\nContent-Length: 5\r\n\r\nab", "c", "de", "GET / HTTP/1.0\r\n\r\n", + ); + is $status, 200; + is $body, 'abcde', 'body arriving in short reads is read up to Content-Length'; +} + +{ + my($status, $body) = run_request( + "POST / HTTP/1.0\r\nContent-Length: 10\r\n\r\nab", "c", + ); + is $status, undef, 'no response when the connection closes mid-body'; + is $body, undef, 'app is not called'; +} + +SKIP: { + skip 'needs fork', 2 if $^O eq 'MSWin32'; + # Content-Length beyond UV_MAX used to make substr() take all but the + # last byte of the buffer and then spin forever on the rest. Such values + # are now rejected as too long before the body is read. + for my $cl ('18446744073709551616', '9' x 400) { + my $pid = fork; + die "fork: $!" unless defined $pid; + if ($pid == 0) { + run_request("POST / HTTP/1.0\r\nContent-Length: $cl\r\n\r\nhello"); + POSIX::_exit(0); + } + my $done; + for (1..50) { + last if $done = waitpid($pid, POSIX::WNOHANG()) == $pid; + select undef, undef, undef, 0.1; + } + unless ($done) { + kill 'KILL', $pid; + waitpid $pid, 0; + } + ok $done, 'huge Content-Length (' . length($cl) . ' digits) does not loop'; + } +} + +for my $cl ('-5', '+5', '5abc', '0x5', '5, 9', '5, 5', '9' x 19, '0' x 18 . '5') { + my($status, $body) = run_request( + "POST / HTTP/1.0\r\nContent-Length: $cl\r\n\r\nabcdefghi" + ); + is $status, 400, "invalid Content-Length '$cl' is rejected"; + is $body, undef, 'app is not called'; +} + +{ + my($status, $body) = run_request( + "POST / HTTP/1.0\r\nContent-Length: 5\r\nContent-Length: 9\r\n\r\nabcdefghi" + ); + is $status, 400, 'duplicate Content-Length headers are rejected'; +} + +for my $cl ('', "Content-Length: 0\r\n") { + my($status, $body) = run_request( + "GET / HTTP/1.0\r\n$cl\r\ntrailing junk" + ); + is $status, 200; + is $body, '', 'no body without Content-Length' . ($cl ? ' (0)' : ''); +} + +for my $te ('chunked', 'gzip, chunked', 'identity') { + for my $cl ('', "Content-Length: 3\r\n") { + my($status, $body) = run_request( + "POST / HTTP/1.1\r\nTransfer-Encoding: $te\r\n$cl\r\n3\r\nabc\r\n0\r\n\r\n" + ); + is $status, 501, "Transfer-Encoding '$te' is rejected" . ($cl ? ' with Content-Length' : ''); + is $body, undef, 'app is not called'; + } +} + +for my $cl ('0', '5', '005', '0' x 17 . '5') { + my($status, $body) = run_request( + "POST / HTTP/1.0\r\nContent-Length: $cl\r\n\r\nabcde" + ); + is $status, 200, "valid Content-Length '$cl' is accepted"; +} + +done_testing; diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/Plack-1.0054/t/HTTP-Server-PSGI/read_size.t new/Plack-1.0057/t/HTTP-Server-PSGI/read_size.t --- old/Plack-1.0054/t/HTTP-Server-PSGI/read_size.t 1970-01-01 01:00:00.000000000 +0100 +++ new/Plack-1.0057/t/HTTP-Server-PSGI/read_size.t 2026-10-24 11:01:28.000000000 +0200 @@ -0,0 +1,52 @@ +use strict; +use warnings; + +use HTTP::Server::PSGI; +use Test::More; + +{ + package FakeSocket; + + # returns each segment in its own read, at most $len bytes at a time + sub new { + my($class, @segments) = @_; + bless { segments => [ @segments ], reads => [], out => '' }, $class; + } + + sub sysread { + my $self = shift; + my(undef, $len, $off) = @_; + push @{$self->{reads}}, $len; + my $seg = \$self->{segments}[0]; + my $chunk = defined $$seg ? substr $$seg, 0, $len, '' : ''; + shift @{$self->{segments}} if defined $$seg && !length $$seg; + $_[0] = '' unless defined $_[0]; + substr($_[0], $off || 0) = $chunk; + return length $chunk; + } + + sub syswrite { + my($self, $buf, $len, $off) = @_; + $self->{out} .= substr $buf, $off || 0, $len; + return $len; + } +} + +my $server = HTTP::Server::PSGI->new(timeout => 5); +my $app = sub { [ 200, [ 'Content-Type' => 'text/plain' ], [ 'OK' ] ] }; + +{ + my $sock = FakeSocket->new( + "POST / HTTP/1.0\r\nContent-Length: 100000000000\r\n\r\n", + 'x' x (200 * 1024), + ); + $server->handle_connection({}, $sock, $app); + + my(undef, @body_reads) = @{$sock->{reads}}; + cmp_ok scalar(@body_reads), '>', 1, 'body is read in several reads'; + my($max) = sort { $b <=> $a } @body_reads; + cmp_ok $max, '<=', HTTP::Server::PSGI::CHUNKSIZE, + 'huge Content-Length does not make a huge sysread'; +} + +done_testing; diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/Plack-1.0054/t/Plack-Middleware/error_document_restore_env.t new/Plack-1.0057/t/Plack-Middleware/error_document_restore_env.t --- old/Plack-1.0054/t/Plack-Middleware/error_document_restore_env.t 1970-01-01 01:00:00.000000000 +0100 +++ new/Plack-1.0057/t/Plack-Middleware/error_document_restore_env.t 2026-10-24 11:01:28.000000000 +0200 @@ -0,0 +1,45 @@ +use strict; +use warnings; +use FindBin; +use Test::More; +use HTTP::Request::Common; +use Plack::Test; +use Plack::Builder; + +my @lines; +my $inner_env; + +my $handler = builder { + enable "AccessLog", format => '%r %>s', logger => sub { push @lines, $_[0] }; + enable sub { + my $app = shift; + sub { $inner_env = $_[0]; $app->($_[0]) }; + }; + enable "ErrorDocument", 404 => "/errors/404.html", subrequest => 1; + enable "Static", path => qr{^/errors}, root => $FindBin::Bin; + + sub { + my $env = shift; + my $status = ($env->{PATH_INFO} =~ m!status/(\d+)!)[0] || 200; + [ $status, [ 'Content-Type' => 'text/plain' ], [ "Error: $status" ] ]; + }; +}; + +test_psgi app => $handler, client => sub { + my $cb = shift; + + my $res = $cb->(POST "http://localhost/status/404?q=1", [ foo => 'bar' ]); + is $res->code, 404; + like $res->content, qr/fancy 404/; + + is $inner_env->{REQUEST_METHOD}, 'POST'; + is $inner_env->{PATH_INFO}, '/status/404'; + is $inner_env->{QUERY_STRING}, 'q=1'; + is $inner_env->{REQUEST_URI}, '/status/404?q=1'; + ok $inner_env->{CONTENT_LENGTH}, 'CONTENT_LENGTH is restored'; + + is scalar @lines, 1; + like $lines[0], qr{^POST /status/404\?q=1 HTTP/1\.1 404}, 'access log records the original request'; +}; + +done_testing; diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/Plack-1.0054/t/Plack-Middleware/recursive/leak.t new/Plack-1.0057/t/Plack-Middleware/recursive/leak.t --- old/Plack-1.0054/t/Plack-Middleware/recursive/leak.t 1970-01-01 01:00:00.000000000 +0100 +++ new/Plack-1.0057/t/Plack-Middleware/recursive/leak.t 2026-10-24 11:01:28.000000000 +0200 @@ -0,0 +1,27 @@ +use strict; +use Test::More; +use Scalar::Util qw(weaken); + +use Plack::Middleware::Recursive; + +my $app = Plack::Middleware::Recursive->wrap(sub { + my $env = shift; + if ($env->{PATH_INFO} eq '/include') { + return $env->{'plack.recursive.include'}->("/included"); + } + return [ 200, [ 'Content-Type' => 'text/plain' ], [ "ok" ] ]; +}); + +for my $path ('/', '/include') { + my $weak; + { + my $env = { PATH_INFO => $path, REQUEST_METHOD => 'GET' }; + my $res = $app->($env); + is $res->[0], 200; + $weak = $env; + weaken $weak; + } + ok !defined $weak, "env is freed after request to $path"; +} + +done_testing; diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/Plack-1.0054/t/Plack-Middleware/urlmap_port_meta.t new/Plack-1.0057/t/Plack-Middleware/urlmap_port_meta.t --- old/Plack-1.0054/t/Plack-Middleware/urlmap_port_meta.t 1970-01-01 01:00:00.000000000 +0100 +++ new/Plack-1.0057/t/Plack-Middleware/urlmap_port_meta.t 2026-10-24 11:01:28.000000000 +0200 @@ -0,0 +1,35 @@ +use strict; +use Test::More; +use Plack::App::URLMap; + +my $make_app = sub { + my $name = shift; + sub { [ 200, [ 'Content-Type' => 'text/plain' ], [ $name ] ] }; +}; + +my $app = Plack::App::URLMap->new; +$app->map("http://example.com/" => $make_app->("host")); +$app->map("/" => $make_app->("default")); + +my $env = sub { + my($host, $port) = @_; + return { + HTTP_HOST => $host, + SERVER_NAME => 'localhost', + SERVER_PORT => $port, + PATH_INFO => '/', + SCRIPT_NAME => '', + }; +}; + +is $app->($env->('example.com:8080', 8080))->[2][0], 'host', 'port is stripped from Host'; + +# SERVER_PORT containing regex metacharacters must be matched literally +my $res = eval { $app->($env->('example.com:(', '(')) }; +is $@, '', 'regex metacharacters in SERVER_PORT do not die'; +is $res->[2][0], 'host', 'literal port is stripped from Host'; + +$res = eval { $app->($env->('example.com:80', '.*')) }; +is $res->[2][0], 'default', 'SERVER_PORT is not used as a pattern'; + +done_testing; diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/Plack-1.0054/t/Plack-Request/upload-basename.t new/Plack-1.0057/t/Plack-Request/upload-basename.t --- old/Plack-1.0054/t/Plack-Request/upload-basename.t 2026-05-05 08:38:14.000000000 +0200 +++ new/Plack-1.0057/t/Plack-Request/upload-basename.t 2026-10-24 11:01:28.000000000 +0200 @@ -1,9 +1,21 @@ use strict; use warnings; -use Test::More tests => 1; +use Test::More; use Plack::Request::Upload; my $upload = Plack::Request::Upload->new( filename => '/tmp/foo/bar/hoge.txt', ); is $upload->basename, 'hoge.txt'; + +for my $filename ('..', '.', '', 'x/', 'x/..', 'x\\..', '../..', '/') { + my $upload = Plack::Request::Upload->new(filename => $filename); + my $basename = $upload->basename; + ok length $basename, "basename for '$filename' is not empty"; + ok $basename ne '.' && $basename ne '..', "basename for '$filename' is not a dot directory"; +} + +is( Plack::Request::Upload->new(filename => '..foo')->basename, '..foo' ); +is( Plack::Request::Upload->new(filename => '.htaccess')->basename, '.htaccess' ); + +done_testing; ++++++ _scmsync.obsinfo ++++++ --- /var/tmp/diff_new_pack.Fi8ljl/_old 2026-10-26 14:17:28.735807435 +0100 +++ /var/tmp/diff_new_pack.Fi8ljl/_new 2026-10-26 14:17:28.738807560 +0100 @@ -1,6 +1,6 @@ -mtime: 1777966395 -commit: e965fa2ebab8c289f65dc58f15da74270e07bea67464a5b3169c4e91bdf0dc96 +mtime: 1793012414 +commit: 9538429a1e186f1adfaa027c9624b80c307516101ddd9f37e8d7ffbe76c27de0 url: https://src.opensuse.org/perl/perl-Plack -revision: e965fa2ebab8c289f65dc58f15da74270e07bea67464a5b3169c4e91bdf0dc96 +revision: 9538429a1e186f1adfaa027c9624b80c307516101ddd9f37e8d7ffbe76c27de0 projectscmsync: https://src.opensuse.org/perl/_ObsPrj ++++++ build.specials.obscpio ++++++ ++++++ build.specials.obscpio ++++++ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/.gitignore new/.gitignore --- old/.gitignore 1970-01-01 01:00:00.000000000 +0100 +++ new/.gitignore 2026-10-26 12:00:14.000000000 +0100 @@ -0,0 +1 @@ +.osc
