Files
2026-08-01 18:19:10 +02:00

155 lines
5.0 KiB
Perl

#!/usr/bin/perl
# Unit tests for Fail2Ban incremental-ban helpers.
use strict;
use warnings;
no warnings 'once';
use Test::More;
use File::Basename qw(dirname);
use File::Spec;
use Cwd qw(abs_path);
BEGIN {
# fail2ban-lib.pl imports WebminCore and initializes module globals at
# load time. Stub only those load-time dependencies so its pure helpers
# can be tested without a Webmin installation.
$INC{'WebminCore.pm'} = 1;
package WebminCore;
sub import
{
my $caller = caller;
no strict 'refs';
*{$caller.'::init_config'} = sub { };
*{$caller.'::get_module_acl'} = sub { return (); };
*{$caller.'::compare_version_numbers'} = sub {
my ($left, $right) = @_;
my @left = split(/\./, $left);
my @right = split(/\./, $right);
my $count = @left > @right ? scalar(@left) : scalar(@right);
for(my $i = 0; $i < $count; $i++) {
my $cmp = ($left[$i] || 0) <=> ($right[$i] || 0);
return $cmp if ($cmp);
}
return 0;
};
}
}
my $root = abs_path(File::Spec->catdir(dirname(__FILE__), '..'));
chdir($root) or die "chdir($root): $!";
do './fail2ban/fail2ban-lib.pl' or die $@ || $!;
subtest 'version support' => sub {
ok(!main::supports_bantime_increment('0.10.6'), '0.10 is unsupported');
ok(!main::supports_bantime_increment('0.11.0'), '0.11.0 is unsupported');
ok(main::supports_bantime_increment('0.11.1'), '0.11.1 is supported');
ok(main::supports_bantime_increment('0.11.2'), 'newer 0.11 is supported');
ok(main::supports_bantime_increment('1.0.0'), '1.0 is supported');
};
subtest 'boolean canonicalization' => sub {
is(main::canonical_fail2ban_boolean(undef), '', 'undefined stays unset');
is(main::canonical_fail2ban_boolean('YES'), 'true', 'yes becomes true');
is(main::canonical_fail2ban_boolean('on'), 'true', 'on becomes true');
is(main::canonical_fail2ban_boolean('0'), 'false', 'zero becomes false');
is(main::canonical_fail2ban_boolean('No'), 'false', 'no becomes false');
is(main::canonical_fail2ban_boolean('invalid'), 'invalid',
'unknown values remain visible for correction');
};
subtest 'positive duration validation' => sub {
foreach my $duration ('600', '30m', '1d 12h', '1d12h',
'2 weeks', '1month 2days', '0.5h') {
ok(main::valid_positive_fail2ban_duration($duration),
"accepts $duration");
}
foreach my $duration ('', '0', '0m', '-1', '1d+12h',
'1d;system("id")', 'forever', '1m 2',
"1\nm", "1\r\nm") {
ok(!main::valid_positive_fail2ban_duration($duration),
"rejects $duration");
}
};
subtest 'non-negative duration validation' => sub {
foreach my $duration ('0', '0m', '0 hours', '600', '30m', '1d 12h') {
ok(main::valid_nonnegative_fail2ban_duration($duration),
"accepts $duration");
}
foreach my $duration ('', '-1', '1d+12h', 'forever') {
ok(!main::valid_nonnegative_fail2ban_duration($duration),
"rejects $duration");
}
};
subtest 'growth factor validation' => sub {
foreach my $factor ('1', '2', '0.5', '.25', '24.0') {
ok(main::valid_bantime_factor($factor), "accepts $factor");
}
foreach my $factor ('', '0', '-1', '1+1', 'inf', '1.') {
ok(!main::valid_bantime_factor($factor), "rejects $factor");
}
};
subtest 'form validation' => sub {
my %valid = (
'bantime_increment' => '',
'bantime_factor_def' => 1,
'bantime_maxtime_def' => 1,
'bantime_overalljails' => '',
'bantime_rndtime_def' => 1,
);
is(main::validate_bantime_increment_inputs(\%valid), undef,
'all-default input is valid');
my %invalid = (%valid, 'bantime_increment' => 'maybe');
is(main::validate_bantime_increment_inputs(\%invalid),
'jail_ebantime_increment', 'invalid enable value is identified');
%invalid = (%valid, 'bantime_factor_def' => 0,
'bantime_factor' => '2 * 3');
is(main::validate_bantime_increment_inputs(\%invalid),
'jail_ebantime_factor', 'factor expressions are rejected');
%invalid = (%valid, 'bantime_maxtime_def' => 0,
'bantime_maxtime' => '0');
is(main::validate_bantime_increment_inputs(\%invalid),
'jail_ebantime_maxtime', 'zero maximum time is rejected');
my %zero_rndtime = (%valid, 'bantime_rndtime_def' => 0,
'bantime_rndtime' => '0');
is(main::validate_bantime_increment_inputs(\%zero_rndtime), undef,
'zero random time is accepted to disable inherited jitter');
};
subtest 'save mapping' => sub {
my @saved;
my $jail = { 'name' => 'sshd' };
my %input = (
'bantime_increment' => 'true',
'bantime_factor_def' => 1,
'bantime_maxtime_def' => 0,
'bantime_maxtime' => '5w',
'bantime_overalljails' => 'false',
'bantime_rndtime_def' => 1,
);
{
no warnings 'redefine';
local *main::save_directive = sub {
my ($name, $value, $section) = @_;
push(@saved, [ $name, $value, $section ]);
};
main::save_bantime_increment_options(\%input, $jail);
}
is_deeply(
[ map { [ $_->[0], $_->[1] ] } @saved ],
[ [ 'bantime.increment', 'true' ],
[ 'bantime.factor', undef ],
[ 'bantime.maxtime', '5w' ],
[ 'bantime.overalljails', 'false' ],
[ 'bantime.rndtime', undef ] ],
'form fields map through standard directive handling');
};
done_testing();