CGI-ACL
view release on metacpan or search on metacpan
t/extended_tests.t view on Meta::CPAN
# _verified_rdns has installed "local $SIG{ALRM} = sub { die 'DNS timeout' }"
# so the signal triggers that handler, which die()s inside the eval,
# setting $@. The code then executes "return if $@ || !$hostname".
my $guard = mock_scoped 'CGI::ACL::_rdns_forward' => sub {
kill 'ALRM', $$; # fire the alarm now â handler will die
return (); # never reached
};
diag "triggering SIGALRM inside _verified_rdns eval" if $ENV{TEST_VERBOSE};
# 127.0.0.1 normally has a PTR record; gethostbyaddr should succeed,
# so the code enters _rdns_forward before timing out.
my $result = CGI::ACL::_verified_rdns($config{LOCAL_IP});
is($result, undef, 'SIGALRM timeout causes _verified_rdns to return undef');
};
subtest '_verified_rdns(): SIGALRM path: alarm is cancelled after eval (no leak)' => sub {
plan skip_all => 'SIGALRM not available on Windows' if $^O eq 'MSWin32';
# Verify the outer alarm(0) cancels any lingering alarm after the eval.
# If the alarm were not cancelled we could get a spurious SIGALRM later.
my $guard = mock_scoped 'CGI::ACL::_rdns_forward' => sub {
kill 'ALRM', $$;
return ();
};
# Install a safety net: if alarm fires outside _verified_rdns it means
# the alarm was NOT properly cancelled â that would be a bug.
my $leaked = 0;
local $SIG{ALRM} = sub { $leaked = 1 };
CGI::ACL::_verified_rdns($config{LOCAL_IP});
# Allow one event loop tick for any leaked alarm to fire
select(undef, undef, undef, 0.05);
ok(!$leaked, 'no alarm leak after _verified_rdns returns');
};
# âââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââ
# _verified_rdns() !$hostname BRANCH (line 922)
# Purpose: when gethostbyaddr returns undef (no PTR record) the code
# executes "return if $@ || !$hostname". The !$hostname sub-path.
# âââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââ
subtest '_verified_rdns(): IP with no PTR record returns undef (!$hostname path)' => sub {
plan skip_all => 'SIGALRM not available on Windows' if $^O eq 'MSWin32';
# RFC 5737 TEST-NET addresses normally have no PTR record.
# gethostbyaddr should return undef, exercising the !$hostname branch.
diag "calling _verified_rdns on no-PTR IP $config{RFC5737_IP}" if $ENV{TEST_VERBOSE};
my $result = CGI::ACL::_verified_rdns($config{RFC5737_IP});
is($result, undef, 'no-PTR address returns undef via !$hostname branch');
};
# âââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââ
# WINDOWS PLATFORM PATH (unreachable on non-Windows)
# Lines 923-929 in lib/CGI/ACL.pm contain the Windows synchronous code path
# (no alarm, direct gethostbyaddr + _rdns_forward). These lines execute only
# when $^O eq 'MSWin32'. On macOS/Linux they are DEAD CODE and cannot be
# covered without mocking $^O itself, which is a Readonly built-in.
#
# COMMENTED OUT â shown here for review. Do not enable unless running on
# Windows where the coverage will naturally be collected by the normal DNS
# tests.
#
# subtest '_verified_rdns(): Windows synchronous path' => sub {
# plan skip_all => 'only on Windows' unless $^O eq 'MSWin32';
# # On Windows, $^O is 'MSWin32', so gethostbyaddr and _rdns_forward
# # are called without an alarm. Lines 925-928 execute here.
# my $guard = mock_scoped 'CGI::ACL::_rdns_forward'
# => sub { ($config{LOCAL_IP}) };
# my $result = CGI::ACL::_verified_rdns($config{LOCAL_IP});
# ok(defined $result, 'Windows path returns hostname');
# };
# âââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââ
# âââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââ
# _verified_rdns() FORWARD-CONFIRMATION CONDITION (line 932)
# Purpose: the ternary "$hostname && grep { $_ eq $canonical } @forward_ips"
# must return $hostname when the forward list contains the canonical IP.
# âââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââ
subtest '_verified_rdns(): forward IP matches canonical â returns hostname' => sub {
plan skip_all => 'SIGALRM not available on Windows' if $^O eq 'MSWin32';
# 127.0.0.1 normally has a PTR; mock _rdns_forward to confirm it.
my $guard = mock_scoped 'CGI::ACL::_rdns_forward'
=> sub { ($config{LOCAL_IP}) };
diag "forward-confirm: _rdns_forward returns LOCAL_IP" if $ENV{TEST_VERBOSE};
my $result = CGI::ACL::_verified_rdns($config{LOCAL_IP});
ok(defined($result), 'matching forward IP returns a hostname');
like($result, qr/\S/, 'returned hostname is non-empty');
};
subtest '_verified_rdns(): forward IP list empty â returns undef' => sub {
plan skip_all => 'SIGALRM not available on Windows' if $^O eq 'MSWin32';
# Mock _rdns_forward to return an empty list; grep can never match,
# so the ternary returns undef even though $hostname is defined.
my $guard = mock_scoped 'CGI::ACL::_rdns_forward' => sub { () };
diag "forward-confirm: _rdns_forward returns empty list" if $ENV{TEST_VERBOSE};
my $result = CGI::ACL::_verified_rdns($config{LOCAL_IP});
is($result, undef, 'empty forward list causes verification failure â undef');
};
# âââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââ
# all_denied(): COMBINED RESTRICTION PATHS
# Purpose: exercise paths that chain multiple restriction types together,
# giving Devel::Cover full LCSAJ coverage of the multi-branch decision tree.
# âââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââ
subtest 'all_denied(): allow_ip + deny_country â IP match bypasses country check' => sub {
# Allowed IP is exempt from country restrictions.
# Path: allowed_ips match â return 0 (skip country check).
my $acl = CGI::ACL->new()
->allow_ip($config{RFC5737_IP})
->deny_country($config{WILDCARD});
diag "allow_ip overrides deny_country(*) for allowed IP" if $ENV{TEST_VERBOSE};
( run in 0.718 second using v1.01-cache-2.11-cpan-9581c071862 )