CGI-Info
view release on metacpan or search on metacpan
t/cgi_security.t view on Meta::CPAN
reset_env();
$ENV{GATEWAY_INTERFACE} = 'CGI/1.1';
$ENV{REQUEST_METHOD} = 'GET';
$ENV{REMOTE_ADDR} = $REMOTE;
$ENV{HTTP_USER_AGENT} = $SQL_UA_INJECTION;
$ENV{QUERY_STRING} = 'q=hello';
my $info = CGI::Info->new();
my $is_robot = $info->is_robot();
is($info->status(), $STATUS_FORBIDDEN,
'SQL-injected User-Agent triggers 403 via is_robot()');
ok($is_robot, 'is_robot() returns true for SQL-injected UA');
};
subtest 'SQL UA: ORDER BY in User-Agent blocked' => sub {
reset_env();
$ENV{REMOTE_ADDR} = $REMOTE;
$ENV{HTTP_USER_AGENT} = 'Fakebot/1.0 ORDER BY 1--';
$ENV{REQUEST_METHOD} = 'GET';
$ENV{QUERY_STRING} = 'x=1';
my $info = CGI::Info->new();
$info->is_robot();
is($info->status(), $STATUS_FORBIDDEN,
'User-Agent with ORDER BY triggers 403');
};
subtest 'SQL UA: AND N=N in User-Agent blocked' => sub {
reset_env();
$ENV{REMOTE_ADDR} = $REMOTE;
$ENV{HTTP_USER_AGENT} = 'TestBot AND 1=1';
$ENV{REQUEST_METHOD} = 'GET';
$ENV{QUERY_STRING} = 'x=1';
my $info = CGI::Info->new();
$info->is_robot();
is($info->status(), $STATUS_FORBIDDEN,
'User-Agent with AND N=N triggers 403');
};
# ---------------------------------------------------------------------------
# Section 5: XSS injection via GET query string
# Known-working and known-bypass patterns.
# ---------------------------------------------------------------------------
subtest 'XSS: <script> tag blocked in GET' => sub {
my $info = make_get("q=$XSS_SCRIPT_TAG");
$info->params();
is($info->status(), $STATUS_FORBIDDEN,
'<script>alert(1)</script> triggers 403');
};
subtest 'XSS: <img onerror> blocked in GET' => sub {
my $info = make_get("q=$XSS_IMG_ONERROR");
$info->params();
is($info->status(), $STATUS_FORBIDDEN,
'<img onerror=...> triggers 403');
};
subtest 'XSS: URL-encoded <script> blocked in GET' => sub {
# %3Cscript%3E decoded to <script> by URL-decode pass, then caught.
my $info = make_get("q=$XSS_URL_ENCODED");
$info->params();
is($info->status(), $STATUS_FORBIDDEN,
'URL-encoded <script> tag triggers 403');
};
subtest 'XSS: <svg onload=> blocked in GET' => sub {
my $info = make_get("q=$XSS_SVG_ONLOAD");
$info->params();
is($info->status(), $STATUS_FORBIDDEN,
'<svg onload=alert(1)> triggers 403');
};
subtest 'XSS: multi-line <img> tag blocked' => sub {
# Fixed: replaced [^\n]+ with .+ and added /s flag so the dot matches
# newlines. "<img\nsrc=x\nonerror=alert(1)>" is now caught.
my $payload = $XSS_IMG_MULTILINE;
my $info = make_get("q=$payload");
my $p = $info->params();
is($info->status(), $STATUS_FORBIDDEN,
'multi-line <img\\nsrc=x onerror=...> is blocked with 403');
ok(!defined $p, 'params() returns undef for multi-line XSS');
};
subtest 'XSS: javascript: URI blocked' => sub {
# Fixed: added /\bjavascript\s*:/i check before the mustleak/traversal
# checks. A "javascript:" URI triggers XSS in href/src even without <>.
my $info = make_get("url=$XSS_JAVASCRIPT_URI");
my $p = $info->params();
is($info->status(), $STATUS_FORBIDDEN,
'javascript: URI is blocked with 403');
};
subtest 'XSS: double-URL-encoded script tag blocked' => sub {
# Fixed: added a second URL-decode pass so %252F -> %2F -> / and
# %253C -> %3C -> < are both normalised before WAF checks run.
my $info = make_get("q=$XSS_DOUBLE_ENCODED");
my $p = $info->params();
is($info->status(), $STATUS_FORBIDDEN,
'double-encoded <script> (%253C) is blocked with 403');
};
# ---------------------------------------------------------------------------
# Section 6: Path traversal via GET query string
# ---------------------------------------------------------------------------
subtest 'Traversal: classic ../ blocked in GET' => sub {
my $info = make_get("file=$TRAV_CLASSIC");
$info->params();
is($info->status(), $STATUS_FORBIDDEN,
'../../../etc/passwd triggers 403');
};
subtest 'Traversal: URL-encoded ..%2F blocked in GET' => sub {
# ..%2F is decoded to ../ in a single pass, then caught.
my $info = make_get("file=$TRAV_URL_ENCODED");
$info->params();
is($info->status(), $STATUS_FORBIDDEN,
'..%2Fetc%2Fpasswd triggers 403');
};
subtest 'Traversal: double-encoded ..%252F blocked' => sub {
# Fixed: second URL-decode pass normalises %252F -> %2F -> / so
# the resulting ../ is caught by the traversal check.
my $info = make_get("file=$TRAV_DOUBLE_ENCODED");
my $p = $info->params();
is($info->status(), $STATUS_FORBIDDEN,
'double-encoded ..%252F is blocked with 403');
ok(!defined $p, 'params() returns undef for double-encoded traversal');
};
subtest 'Traversal: null-byte poisoning stripped then caught' => sub {
# NUL bytes are stripped before the traversal check, so
# "../etc/passwd\0.jpg" becomes "../etc/passwd.jpg" and is still caught.
my $payload = '../etc/passwd%00.jpg';
my $info = make_get("file=$payload");
$info->params();
is($info->status(), $STATUS_FORBIDDEN,
'NUL-poisoned path traversal (%00) is still blocked with 403');
};
subtest 'Traversal: Windows backslash path not blocked (forward-slash only) [DESIGN]' => sub {
# The traversal regex checks /\.\.\// (forward slash only).
# ..\..\..\windows\... on Windows is not caught.
# Note: CGI::Info targets Unix so this may be acceptable.
my $info = make_get("file=$TRAV_WINDOWS");
my $p = $info->params();
TODO: {
local $TODO = 'Traversal check is forward-slash only; Windows backslash paths not blocked';
is($info->status(), $STATUS_FORBIDDEN,
'Windows-style ..\\..\\..\\path should be blocked with 403');
}
};
# ---------------------------------------------------------------------------
# Section 7: HTTP_REFERER injection
# Referrer is used to classify robots but also to block spam crawlers.
# ---------------------------------------------------------------------------
subtest 'Referer: closing parenthesis in referer triggers robot classification' => sub {
# Any referer containing ")" is treated as a spam/robot referrer.
reset_env();
$ENV{GATEWAY_INTERFACE} = 'CGI/1.1';
$ENV{REQUEST_METHOD} = 'GET';
$ENV{REMOTE_ADDR} = $REMOTE;
$ENV{HTTP_USER_AGENT} = $BENIGN_UA;
$ENV{HTTP_REFERER} = 'http://example.com/foo(bar)';
my $info = CGI::Info->new();
ok($info->is_robot(), 'referer with ) is classified as robot');
};
subtest 'Referer: backslash normalised before matching (no crash)' => sub {
# Stray backslashes in the referer are normalised to _ before comparison.
# Verify this does not produce an exception or regex failure.
( run in 2.013 seconds using v1.01-cache-2.11-cpan-ad19def0cd9 )