Fix RPM package install completion

This PR fixes package installation pages stopping after "Re-fetching available packages .." on RPM systems, before the System Status refresh, reboot check, and return link appeared.

The regression was introduced by https://github.com/webmin/webmin/commit/658f930, which wrapped RPM queries in `clean_environment` while the Package Updates installer had already cleaned the environment.

The inner clean overwrote the only saved environment, leaving `WEBMIN_CONFIG` unset and causing the System Status library to fail after the page headers had been sent.

Nested `clean_environment` calls now save separate snapshots, allowing each `reset_environment` call to restore the matching environment exactly while preserving Virtualmin's API script runner behavior.
This commit is contained in:
Ilia Ross
2026-10-17 01:27:12 +02:00
parent a864ec9a6f
commit fb70225187
2 changed files with 56 additions and 8 deletions

42
t/web-lib-environment.t Normal file
View File

@@ -0,0 +1,42 @@
#!/usr/bin/perl
# Tests nested environment cleanup and exact restoration.
use strict;
use warnings;
use Test::More;
use File::Basename qw(dirname);
use File::Spec;
my $root = File::Spec->rel2abs(File::Spec->catfile(dirname(__FILE__), '..'));
require File::Spec->catfile($root, 'web-lib-funcs.pl');
my %original = (
'PATH' => '/bin:/usr/bin',
'LANG' => 'de_DE.UTF-8',
'WEBMIN_CONFIG' => '/etc/webmin',
'HTTP_ACCEPT' => 'text/html',
);
{
local %ENV = %original;
no warnings 'once';
local @main::clean_environment_stack;
local %main::UNCLEAN_ENV;
main::clean_environment();
$ENV{'WEBMIN_CONFIG'} = '/nested/etc/webmin';
main::clean_environment();
main::clean_language();
main::reset_environment();
is($ENV{'WEBMIN_CONFIG'}, '/nested/etc/webmin',
'nested reset restores the outer cleaned environment');
ok(!exists($ENV{'HTTP_ACCEPT'}),
'nested reset does not restore the original request environment');
main::reset_environment();
is_deeply({ %ENV }, \%original,
'outer reset restores the original environment exactly');
}
done_testing();

View File

@@ -10285,10 +10285,14 @@ won't be passed to programs started by webmin. This is useful when calling
programs that check for CGI-related environment variables and modify their
behaviour, and to avoid passing sensitive variables to un-trusted programs.
Calls can be nested, with each reset_environment undoing the most recent call.
=cut
sub clean_environment
{
%UNCLEAN_ENV = %ENV;
# Save each environment separately so nested calls restore in order
push(@main::clean_environment_stack, { %ENV });
%UNCLEAN_ENV = %ENV if (@main::clean_environment_stack == 1);
foreach my $k (keys %ENV) {
if ($k =~ /^(HTTP|VIRTUALSERVER|QUOTA|USERADMIN)_/) {
delete($ENV{$k});
@@ -10310,17 +10314,17 @@ foreach my $e ('WEBMIN_CONFIG', 'SERVER_NAME', 'CONTENT_TYPE', 'REQUEST_URI',
=head2 reset_environment
Puts the environment back how it was before clean_environment was callled.
Puts the environment back how it was before the matching cleanup call.
A reset with no cleanup to match does nothing.
=cut
sub reset_environment
{
if (%UNCLEAN_ENV) {
foreach my $k (keys %UNCLEAN_ENV) {
$ENV{$k} = $UNCLEAN_ENV{$k};
}
undef(%UNCLEAN_ENV);
my $saved = pop(@main::clean_environment_stack);
if ($saved) {
%ENV = %$saved;
}
undef(%UNCLEAN_ENV) if (!@main::clean_environment_stack);
}
=head2 clean_language
@@ -10332,7 +10336,9 @@ reset_environment.
=cut
sub clean_language
{
if (!%UNCLEAN_ENV) {
# Share the snapshot from a preceding clean_environment call
if (!@main::clean_environment_stack) {
push(@main::clean_environment_stack, { %ENV });
%UNCLEAN_ENV = %ENV;
}
$ENV{'LANG'} = '';