CGI-Info
view release on metacpan or search on metacpan
t/40-more.t view on Meta::CPAN
restore_env();
};
# Test XSS injection detection
subtest 'XSS injection detection' => sub {
setup_mock_env(
GATEWAY_INTERFACE => 'CGI/1.1',
REQUEST_METHOD => 'GET',
QUERY_STRING => 'comment=<script>alert("xss")</script>'
);
# Mock STDIN data so that we don't hang on reading
$CGI::Info::stdin_data = 'username=test&password=secret';
my $info = CGI::Info->new();
my $params = $info->params();
is($info->{status}, 403, 'XSS injection blocked');
ok(!defined($params), 'No parameters returned for XSS injection');
restore_env();
$CGI::Info::stdin_data = undef;
};
# Test directory traversal detection
subtest 'Directory traversal detection' => sub {
setup_mock_env(
GATEWAY_INTERFACE => 'CGI/1.1',
REQUEST_METHOD => 'GET',
QUERY_STRING => 'file=../../../etc/passwd'
);
my $info = CGI::Info->new();
my $params = $info->params();
is($info->{status}, 403, 'Directory traversal blocked');
ok(!defined($params), 'No parameters returned for directory traversal');
restore_env();
};
# Test User-Agent SQL injection detection
subtest 'User-Agent SQL injection detection' => sub {
setup_mock_env(
GATEWAY_INTERFACE => 'CGI/1.1',
REQUEST_METHOD => 'GET',
QUERY_STRING => 'q=test',
HTTP_USER_AGENT => "Mozilla' AND 1=1 ORDER BY 1--"
);
my $info = CGI::Info->new();
my $params = $info->params();
is($info->{status}, 403, 'User-Agent SQL injection blocked');
ok(!defined($params), 'No parameters returned for malicious User-Agent');
restore_env();
};
# Test file upload validation
subtest 'File upload validation' => sub {
my $temp_dir = tempdir(CLEANUP => 1);
setup_mock_env(
GATEWAY_INTERFACE => 'CGI/1.1',
REQUEST_METHOD => 'POST',
CONTENT_TYPE => 'multipart/form-data; boundary=test123',
CONTENT_LENGTH => '100'
);
my $info = CGI::Info->new();
# Test without upload_dir
$CGI::Info::stdin_data = '{}';
my $params = $info->params();
ok(!defined($params), 'Upload rejected without upload_dir');
# Test with invalid upload_dir
$params = $info->params(upload_dir => '/invalid/path');
is($info->{status}, 500, 'Invalid upload directory rejected');
# Test with valid upload_dir in temp area
$params = $info->params(upload_dir => $temp_dir);
# Should pass validation (actual multipart parsing would need more setup)
restore_env();
$CGI::Info::stdin_data = undef;
};
# Test parameter caching
subtest 'Parameter caching' => sub {
delete $ENV{'GATEWAY_INTERFACE'};
delete $ENV{'REQUEST_METHOD'};
delete $ENV{'QUERY_STRING'};
my $info = CGI::Info->new();
local @ARGV = ('cached=value');
my $params1 = $info->params();
my $params2 = $info->params();
is($params1, $params2, 'Parameters are cached on repeat calls');
is($params1->{cached}, 'value', 'Cached parameters retain values');
};
# Test param() method
subtest 'param() method' => sub {
my $info = CGI::Info->new();
local @ARGV = ('name=John', 'age=30');
is($info->param('name'), 'John', 'Single parameter retrieval');
is($info->param('age'), '30', 'Numeric parameter as string');
is($info->param('missing'), undef, 'Missing parameter returns undef');
# Test param() without arguments (should call params())
my $all_params = $info->param();
is_deeply($all_params, {name => 'John', age => '30'}, 'param() without args returns all');
};
# Test param() with allow list
subtest 'param() with allow list' => sub {
my $info = CGI::Info->new(carp_on_warn => 1);
local @ARGV = ('allowed=yes', 'forbidden=no');
# Set up allow list
$info->params(allow => { allowed => undef });
is($info->param('allowed'), 'yes', 'Allowed parameter accessible via param()');
is($info->param('forbidden'), undef, 'Forbidden parameter returns undef');
my @warn_msgs = map { $_->{message} }
grep { $_->{level} eq 'warn' } @{$info->messages() // []};
like(join("\n", @warn_msgs), qr/forbidden.*isn't in the allow list/, 'Warning recorded for forbidden access');
};
# Test edge cases and error conditions
subtest 'Edge cases and error conditions' => sub {
my $info = CGI::Info->new();
# Test empty parameters
local @ARGV = ();
my $params = $info->params();
ok(!defined($params), 'Empty parameters return undef');
# Test malformed key=value pairs
local @ARGV = ('=value', 'key=', 'malformed');
$params = $info->params();
ok(!exists($params->{''}), 'Empty key ignored');
is($params->{key}, undef, 'Empty value handled correctly');
ok(!exists($params->{malformed}), 'Malformed pair without = ignored');
};
# Test URL decoding
subtest 'URL decoding' => sub {
my $info = CGI::Info->new();
local @ARGV = ('name=John%20Doe', 'email=test%40example.com', 'plus=a+b');
my $params = $info->params();
is($params->{name}, 'John Doe', 'Space decoding from %20');
is($params->{email}, 'test@example.com', 'At symbol decoding from %40');
is($params->{plus}, 'a b', 'Plus to space conversion');
};
# Test duplicate parameter handling
subtest 'Duplicate parameter handling' => sub {
my $info = CGI::Info->new();
# Simulate duplicate parameters (normally from query string)
local @ARGV = ('tag=red', 'tag=blue', 'tag=green');
my $params = $info->params();
# Should combine with commas
is($params->{tag}, 'red,blue,green', 'Duplicate parameters combined with commas');
};
# Test content length validation
subtest 'Content length validation' => sub {
setup_mock_env(
GATEWAY_INTERFACE => 'CGI/1.1',
REQUEST_METHOD => 'POST',
CONTENT_TYPE => 'application/x-www-form-urlencoded'
);
my $info = CGI::Info->new();
# Test missing content length
my $params = $info->params();
is($info->{status}, 411, 'Missing content length returns 411');
# Test invalid content length
$ENV{CONTENT_LENGTH} = 'invalid';
$params = $info->params();
is($info->{status}, 411, 'Invalid content length returns 411');
# Test oversized content
$info = new_ok('CGI::Info');
$info->{max_upload_size} = 100;
$ENV{CONTENT_LENGTH} = '1000';
$params = $info->params();
is($info->{status}, 413, 'Oversized content returns 413');
# Boundary test
$info = new_ok('CGI::Info');
$info->{max_upload_size} = 0;
$ENV{CONTENT_LENGTH} = '1';
$params = $info->params();
is($info->{status}, 413, 'Oversized content returns 413, when max_upload_size set to 0');
$info = CGI::Info->new(max_upload_size => 0);
$params = $info->params();
is($info->{status}, 413, 'Oversized content returns 413, when max_upload_size set to 0 in new');
restore_env();
};
# Test HTTP method validation
subtest 'HTTP method validation' => sub {
# Test OPTIONS method
setup_mock_env(
GATEWAY_INTERFACE => 'CGI/1.1',
REQUEST_METHOD => 'OPTIONS'
);
my $info = CGI::Info->new();
my $params = $info->params();
is($info->{status}, 405, 'OPTIONS method returns 405');
# Test DELETE method
$ENV{REQUEST_METHOD} = 'DELETE';
$params = $info->params();
is($info->{status}, 405, 'DELETE method returns 405');
# Test unsupported method
$ENV{REQUEST_METHOD} = 'PATCH';
$params = $info->params();
is($info->{status}, 501, 'Unsupported method returns 501');
restore_env();
};
# Test testing flags
subtest 'Testing flags' => sub {
local %ENV;
delete $ENV{'GATEWAY_INTERFACE'};
delete $ENV{'REQUEST_METHOD'};
delete $ENV{'QUERY_STRING'};
my $info = CGI::Info->new();
# Test robot flag
local @ARGV = ('--robot', 'param=value');
my $params = $info->params();
ok($info->{is_robot}, 'Robot flag sets is_robot');
is($params->{param}, 'value', 'Parameters parsed after flag');
# Test mobile flag
$info = CGI::Info->new();
local @ARGV = ('--mobile', 'device=phone');
$params = $info->params();
ok($info->{is_mobile}, 'Mobile flag sets is_mobile');
# Test search engine flag
$info = CGI::Info->new();
local @ARGV = ('--search-engine', 'bot=google');
$params = $info->params();
ok($info->{is_search_engine}, 'Search engine flag sets is_search_engine');
# Test tablet flag
$info = CGI::Info->new();
( run in 1.093 second using v1.01-cache-2.11-cpan-b16cb0d3907 )