Merge branch 'master' into dev/download-target-check

This commit is contained in:
Ilia Ross
2026-07-14 00:58:07 +02:00
committed by GitHub
4 changed files with 115 additions and 21 deletions

49
t/web-lib-funcs-acl.t Normal file
View File

@@ -0,0 +1,49 @@
#!/usr/bin/perl
# Unit tests for ACL preservation helpers in web-lib-funcs.pl.
use strict;
use warnings;
use Test::More;
use File::Basename qw(dirname);
use File::Spec;
my $script = File::Spec->rel2abs(
File::Spec->catfile(dirname(__FILE__), '..', 'web-lib-funcs.pl'));
require $script;
subtest 'physical restore is enabled when supported' => sub {
no warnings 'redefine';
my $calls = 0;
local *main::backquote_command = sub {
$calls++;
return "setfacl 2.4.0 -- set file access control lists\n".
"Usage: setfacl [-bkndRLP] { -m|-M|-x|-X ... } file ...\n".
" setfacl [-P] --restore=file\n".
" -P, --physical physical walk, do not follow symbolic links\n".
" --restore=file restore ACLs (inverse of `getfacl -R')\n";
};
is(main::get_setfacl_restore_command('/usr/bin/setfacl'),
'/usr/bin/setfacl -P --restore=-',
'uses physical restore with setfacl 2.4.0 and later');
is(main::get_setfacl_restore_command('/usr/bin/setfacl'),
'/usr/bin/setfacl -P --restore=-',
'keeps using physical restore on later calls');
is($calls, 1, 'caches the capability check');
};
subtest 'older setfacl remains supported' => sub {
no warnings 'redefine';
local *main::backquote_command = sub {
return "setfacl 2.3.2 -- set file access control lists\n".
"Usage: setfacl [-bkndRLP] { -m|-M|-x|-X ... } file ...\n".
" -P, --physical physical walk, do not follow symbolic links\n".
" --restore=file restore ACLs (inverse of `getfacl -R')\n";
};
is(main::get_setfacl_restore_command('/usr/local/bin/setfacl'),
'/usr/local/bin/setfacl --restore=-',
'does not pass incompatible -P option to older versions');
};
done_testing();

View File

@@ -110,6 +110,14 @@ subtest 'encode_xml_value type selection' => sub {
# Plain strings.
like(encode_xml_value('hello'), qr{^<string>hello</string>\s*$}, 'word -> string');
like(encode_xml_value(''), qr{^<string></string>\s*$}, 'empty string -> empty <string>');
my @warnings;
my $undef_xml;
{
local $SIG{__WARN__} = sub { push(@warnings, @_); };
$undef_xml = encode_xml_value(undef);
}
like($undef_xml, qr{^<string></string>\s*$}, 'undef -> empty <string>');
is_deeply(\@warnings, [], 'undef emits no warnings');
# A value with control characters cannot live in a <string> (the
# printable-range regex fails), so it must fall through to base64.
@@ -189,6 +197,19 @@ subtest 'parse_xml_value' => sub {
is(parse_xml_value(value_tree('<boolean>1</boolean>')),1, 'boolean parsed');
is(parse_xml_value(value_tree('<double>2.5</double>')),'2.5', 'double parsed');
is(parse_xml_value(value_tree('<string>hi</string>')), 'hi', 'string parsed');
my @warnings;
{
local $SIG{__WARN__} = sub { push(@warnings, @_); };
is(parse_xml_value(value_tree('<string></string>')), '',
'empty string parsed');
is(parse_xml_value(value_tree('')), '', 'implicit empty string parsed');
is(parse_xml_value(value_tree('<base64></base64>')), '',
'empty base64 parsed');
my $empty_name = parse_xml_value(value_tree(
'<struct><member><name></name><value><string>x</string></value></member></struct>'));
is_deeply($empty_name, { '' => 'x' }, 'empty struct member name parsed');
}
is_deeply(\@warnings, [], 'empty XML values emit no warnings');
# base64 is decoded back to its raw bytes.
my $b64 = encode_base64("ab\x00cd");

View File

@@ -10359,16 +10359,17 @@ out progress of an HTTP request.
=cut
sub progress_callback
{
my ($mode, $arg) = @_;
if (defined(&theme_progress_callback)) {
# Call the theme override
return &theme_progress_callback(@_);
}
if ($_[0] == 2) {
if ($mode == 2) {
# Got size
print $progress_callback_prefix;
if ($_[1]) {
$progress_size = $_[1];
$progress_step = int($_[1] / 10);
if ($arg) {
$progress_size = $arg;
$progress_step = int($arg / 10);
print &text('progress_size2',
&html_escape($progress_callback_url),
&nice_size($progress_size)),"<br>\n";
@@ -10380,42 +10381,42 @@ if ($_[0] == 2) {
}
$last_progress_time = $last_progress_size = undef;
}
elsif ($_[0] == 3) {
elsif ($mode == 3) {
# Got data update
my $sp = $progress_callback_prefix.("&nbsp;" x 5);
if ($progress_size) {
# And we have a size to compare against
my $st = int(($_[1] * 10) / $progress_size);
my $st = int(($arg * 10) / $progress_size);
my $time_now = time();
if ($st != $progress_step ||
$time_now - $last_progress_time > 60) {
# Show progress every 10% or 60 seconds
print $sp,&text('progress_datan', &nice_size($_[1]),
int($_[1]*100/$progress_size)),"<br>\n";
print $sp,&text('progress_datan', &nice_size($arg),
int($arg*100/$progress_size)),"<br>\n";
$last_progress_time = $time_now;
}
$progress_step = $st;
}
else {
# No total size .. so only show in 1M jumps
if ($_[1] > $last_progress_size+1024*1024) {
if ($arg > $last_progress_size+1024*1024) {
print $sp,&text('progress_data2n',
&nice_size($_[1])),"<br>\n";
$last_progress_size = $_[1];
&nice_size($arg)),"<br>\n";
$last_progress_size = $arg;
}
}
}
elsif ($_[0] == 4) {
elsif ($mode == 4) {
# All done downloading
print $progress_callback_prefix,&text('progress_done'),"<br>\n";
}
elsif ($_[0] == 5) {
elsif ($mode == 5) {
# Got new location after redirect
$progress_callback_url = $_[1];
$progress_callback_url = $arg;
}
elsif ($_[0] == 6) {
elsif ($mode == 6) {
# URL is in cache
$progress_callback_url = $_[1];
$progress_callback_url = $arg;
print &text('progress_incache',
&html_escape($progress_callback_url)),"<br>\n";
}
@@ -11629,7 +11630,8 @@ elsif (defined($main::open_tempfiles{$_[0]})) {
}
if ($file_acls) {
# Set original ACLs
open(my $pipe, '|-', "$setfacl --restore=-");
my $restore_command = &get_setfacl_restore_command($setfacl);
open(my $pipe, '|-', $restore_command);
print($pipe $file_acls);
close($pipe);
}
@@ -11694,6 +11696,27 @@ if (!defined($main::selinux_enabled_cache)) {
return $main::selinux_enabled_cache;
}
=head2 get_setfacl_restore_command(setfacl-command)
Returns a command for restoring ACLs from standard input. Uses physical
restore when supported by setfacl, while remaining compatible with versions
older than 2.4.0 which reject -P together with --restore.
=cut
sub get_setfacl_restore_command
{
my ($setfacl) = @_;
state %physical_restore_cache;
if (!exists($physical_restore_cache{$setfacl})) {
my $help = &backquote_command("$setfacl --help 2>&1 </dev/null");
$physical_restore_cache{$setfacl} =
$help =~ /\[-P\]\s+--restore(?:=|\b)/ ? 1 : 0;
}
my $physical = $physical_restore_cache{$setfacl} ? "-P " : "";
return "$setfacl ${physical}--restore=-";
}
=head2 get_clear_file_attributes(file)
Finds file attributes that may prevent writing, clears them and returns them

View File

@@ -18,7 +18,7 @@ my ($base64) = &find_xmls("base64", $value, 1);
my ($struct) = &find_xmls("struct", $value, 1);
my ($array) = &find_xmls("array", $value, 1);
if ($scalar) {
return $scalar->[1]->[2];
return $scalar->[1]->[2] // "";
}
elsif ($date) {
# Need to decode date
@@ -26,7 +26,7 @@ elsif ($date) {
}
elsif ($base64) {
# Convert to binary
return &decode_base64($base64->[1]->[2]);
return &decode_base64($base64->[1]->[2] // "");
}
elsif ($struct) {
# Parse member names and values
@@ -35,7 +35,7 @@ elsif ($struct) {
my ($name) = &find_xmls("name", $member, 1);
my ($value) = &find_xmls("value", $member, 1);
my $perlv = &parse_xml_value($value);
$rv{$name->[1]->[2]} = $perlv;
$rv{$name->[1]->[2] // ""} = $perlv;
}
return \%rv;
}
@@ -51,7 +51,7 @@ elsif ($array) {
}
else {
# Fallback - just a string directly in the value
return $value->[1]->[2];
return $value->[1]->[2] // "";
}
}
@@ -60,6 +60,7 @@ else {
sub encode_xml_value
{
my ($perlv) = @_;
$perlv = "" if (!defined($perlv));
if (ref($perlv) eq "ARRAY") {
# Convert to array XML format
my $xmlrv = "<array>\n<data>\n";