#!/usr/bin/perl -w
use strict;
use lib ('./blib','./lib','../blib','../lib');
use CGI::Minimal;
my $do_tests = [1..8];
my $test_subs = {
1 => { -code => \&test_x_www, -desc => 'decode application/x-www-form-urlencoded ' },
2 => { -code => \&test_sgml_form, -desc => 'decode application/sgml-form-urlencoded ' },
3 => { -code => \&test_repeated_params, -desc => 'decode repeated parameter options ' },
4 => { -code => \&test_raw_buffer, -desc => 'raw buffer ' },
5 => { -code => \&test_no_params, -desc => 'no parameters ' },
6 => { -code => \&test_truncation, -desc => 'detect form truncation ' },
7 => { -code => \&test_multipart_form, -desc => 'decode multipart/form-data ' },
8 => { -code => \&test_post_form, -desc => 'decode ordinary POST form data ' },
};
# 3 => { -code => \&test_bad_form, -desc => 'detect bad calls ' },
run_tests($test_subs,$do_tests);
exit;
###########################################################################################
##############################################################
# Test raw buffer handling #
##############################################################
sub test_raw_buffer {
$ENV{'QUERY_STRING'} = 'hello=first;hello=second;hello=third;hello=fourth';
$ENV{'CONTENT_LENGTH'} = length($ENV{'QUERY_STRING'});
$ENV{'CONTENT_TYPE'} = 'application/sgml-form-urlencoded';
$ENV{'GATEWAY_INTERFACE'} = 'CGI/1.1';
$ENV{'REQUEST_METHOD'} = 'GET';
############################
# raw buffer tests
{
CGI::Minimal::reset_globals;
CGI::Minimal::allow_hybrid_post_get(1);
my $raw_buffer = CGI::Minimal::raw();
if (defined $raw_buffer) {
return 'failed: reset globals failed to reset raw buffer';
}
my $cgi = CGI::Minimal->new;
$raw_buffer = CGI::Minimal::raw();
unless (defined $raw_buffer) {
return 'failed: raw buffer was undefined when it should not have been'
}
}
# Success is an empty string (no error message ;) )
return '';
}
##############################################################
# Test decoding of forms with no parameters #
##############################################################
sub test_no_params {
###########################
# no parameters
$ENV{'QUERY_STRING'} = '';
$ENV{'CONTENT_LENGTH'} = length($ENV{'QUERY_STRING'});
$ENV{'CONTENT_TYPE'} = 'application/sgml-form-urlencoded';
$ENV{'GATEWAY_INTERFACE'} = 'CGI/1.1';
$ENV{'REQUEST_METHOD'} = 'GET';
{
CGI::Minimal::reset_globals;
CGI::Minimal::allow_hybrid_post_get(1);
my $cgi = CGI::Minimal->new;
my @params = $cgi->param;
if (0 != @params) {
return 'failed: Unexpected param keys found: ' . join(',',@params);
}
}
# Success is an empty string (no error message ;) )
return '';
}
##############################################################
# Test decoding of forms with multiple values for parameters #
##############################################################
sub test_repeated_params {
###########################
# repeated parameter names
$ENV{'QUERY_STRING'} = 'hello=first;hello=second;hello=third;hello=fourth';
$ENV{'CONTENT_LENGTH'} = length($ENV{'QUERY_STRING'});
$ENV{'CONTENT_TYPE'} = 'application/sgml-form-urlencoded';
$ENV{'GATEWAY_INTERFACE'} = 'CGI/1.1';
$ENV{'REQUEST_METHOD'} = 'GET';
{
CGI::Minimal::reset_globals;
CGI::Minimal::allow_hybrid_post_get(1);
my $cgi = CGI::Minimal->new;
my $string_pairs = { 'hello' => ['first', 'second', 'third', 'fourth'], };
my @form_keys = keys %$string_pairs;
my @param_keys = $cgi->param;
if ($#form_keys != $#param_keys) {
return 'failed : Expected 1 parameter name from SGML form, found ' . ($#param_keys + 1);
}
my %form_keys_hash = map {$_ => $string_pairs->{$_} } @form_keys;
foreach my $key_item (@param_keys) {
if (! defined $form_keys_hash{$key_item}) {
return 'failed : Parameter names did not match';
}
my @item_values = $cgi->param($key_item);
my $n_found_items = $#item_values + 1;
my @expected_items = @{$form_keys_hash{$key_item}};
my $n_expected_items = $#expected_items + 1;
if ($n_found_items != $n_expected_items) {
return 'failed: Expected $n_expected_items parameter values, found $n_found_items';
}
for (my $count = 0; $count < $n_expected_items; $count++) {
unless ($item_values[$count] eq $expected_items[$count]) {
return 'failed: Parameter lists mis-match (' . join(',',@item_values) . ') != (' . join(',',@expected_items) . ')';
}
}
my $first_element = $cgi->param($key_item);
unless ($first_element eq $expected_items[0]) {
return 'failed: multiple item param failed to return first element in scalar context';
}
}
}
# Success is an empty string (no error message ;) )
return '';
}
######################################################
# Test SGML form decoding #
######################################################
sub test_sgml_form {
$ENV{'QUERY_STRING'} = 'hello=testing;hello2=SGML+encoded+FORM;nullparm=;=nullkey;submit+button=submit';
$ENV{'CONTENT_LENGTH'} = length($ENV{'QUERY_STRING'});
$ENV{'CONTENT_TYPE'} = 'application/sgml-form-urlencoded';
$ENV{'GATEWAY_INTERFACE'} = 'CGI/1.1';
$ENV{'REQUEST_METHOD'} = 'GET';
CGI::Minimal::reset_globals;
CGI::Minimal::allow_hybrid_post_get(1);
my $cgi = CGI::Minimal->new;
my $string_pairs = { 'hello' => 'testing',
'hello2' => 'SGML encoded FORM',
'nullparm' => '',
'' => 'nullkey',
'submit button' => 'submit',
};
my @form_keys = keys %$string_pairs;
my @param_keys = $cgi->param;
if ($#form_keys != $#param_keys) {
my $n_expected_parms = $#form_keys + 1;
return "failed : Expected $n_expected_parms parameters SGML form, found " . ($#param_keys + 1);
}
my %form_keys_hash = map {$_ => $string_pairs->{$_} } @form_keys;
foreach my $key_item (@param_keys) {
if (! defined $form_keys_hash{$key_item}) {
return 'failed : Parameter names did not match';
}
my $item_value = $cgi->param($key_item);
if ($form_keys_hash{$key_item} ne $item_value) {
return 'failed : Parameter values did not match';
}
}
# Unused parameter
my $value = $cgi->param('no-such-parameter');
if (defined $value) {
return "failed: Got a value besides 'undef' for an undeclared parameter query";
}
# Success is an empty string (no error message ;) )
return '';
}
######################################################
# Test bad form decoding #
######################################################
sub test_bad_form {
$ENV{'QUERY_STRING'} = 'hello=testing&hello2=standard+encoded+FORM&submit+button=submit';
$ENV{'CONTENT_LENGTH'} = length($ENV{'QUERY_STRING'});
$ENV{'CONTENT_TYPE'} = 'application/x-www-form-urlencoded';
$ENV{'GATEWAY_INTERFACE'} = 'CGI/1.1';
$ENV{'REQUEST_METHOD'} = 'TRACE';
eval {
CGI::Minimal::reset_globals;
CGI::Minimal::allow_hybrid_post_get(1);
my $cgi = CGI::Minimal->new;
};
unless ($@) {
return 'failed: Failed to catch unsupported request method';
}
# Success is an empty string (no error message ;) )
return '';
}
######################################################
# Test simple form decoding #
######################################################
sub test_x_www {
$ENV{'QUERY_STRING'} = 'hello=testing&hello2=standard%20encoded+FORM&hello%31=1&hello3=&&=test&submit+button=submit';
$ENV{'CONTENT_LENGTH'} = length($ENV{'QUERY_STRING'});
$ENV{'GATEWAY_INTERFACE'} = 'CGI/1.1';
$ENV{'CONTENT_TYPE'} = 'application/x-www-form-urlencoded';
foreach my $request_method ('GET','HEAD') {
$ENV{'REQUEST_METHOD'} = $request_method;
CGI::Minimal::reset_globals;
CGI::Minimal::allow_hybrid_post_get(1);
my $cgi = CGI::Minimal->new;
my $string_pairs = { 'hello' => 'testing',
'hello2' => 'standard encoded FORM',
'hello3' => '',
'hello1' => '1',
'' => 'test',
'' => '',
'submit button' => 'submit',
};
my @form_keys = keys %$string_pairs;
my $expected_keys = $#form_keys + 1;
my @param_keys = $cgi->param;
if ($#form_keys != $#param_keys) {
return "failed : Expected $expected_keys parameters in x-www-form-urlencoded, found " . ($#param_keys + 1);
}
my %form_keys_hash = map {$_ => $string_pairs->{$_} } @form_keys;
foreach my $key_item (@param_keys) {
if (! defined $form_keys_hash{$key_item}) {
return 'failed : Parameter names did not match';
}
my $item_value = $cgi->param($key_item);
if ($form_keys_hash{$key_item} ne $item_value) {
return 'failed : Parameter values did not match';
}
}
}
{
local $^W;
$ENV{'QUERY_STRING'} = undef;
$ENV{'CONTENT_LENGTH'} = 0;
$ENV{'GATEWAY_INTERFACE'} = 'CGI/1.1';
$ENV{'CONTENT_TYPE'} = 'application/x-www-form-urlencoded';
CGI::Minimal::reset_globals;
CGI::Minimal::allow_hybrid_post_get(1);
my $cgi = CGI::Minimal->new;
my @parms = $cgi->param;
if ($#parms > -1) {
return 'failed: should have been no parms from undef QUERY_STRING - but is was not';
}
}
# Success is an empty string (no error message ;) )
return '';
}
######################################################
# Test hybrid POST/GET form decoding #
######################################################
sub test_post_form {
local $^W;
my $data = 'hello2=standard%20encoded+FORM&hello%31=1&hello3=&&=test&submit+button=submit';
$ENV{'CONTENT_LENGTH'} = length($data);
$ENV{'GATEWAY_INTERFACE'} = 'CGI/1.1';
$ENV{'REQUEST_METHOD'} = 'POST';
$ENV{'QUERY_STRING'} = 'hello=testing';
foreach my $mode ('normal','max_size','zero_size') {
foreach my $content_type ('application/x-www-form-urlencoded', undef) {
$ENV{'CONTENT_TYPE'} = $content_type;
my $test_file = "test-data.$$.data";
open (TESTFILE,">$test_file") || return ("failed : could not open test file $test_file for writing: $!");
binmode (TESTFILE);
print TESTFILE $data;
close (TESTFILE);
# "Bad evil naughty Zoot"
CGI::Minimal::reset_globals;
CGI::Minimal::allow_hybrid_post_get(1);
if ($mode eq 'max_size') {
CGI::Minimal::max_read_size(10);
} elsif ($mode eq 'zero_size') {
CGI::Minimal::max_read_size(0);
}
open (STDIN,$test_file) || return ("failed : could not open test file $test_file for reading: $!");
my $cgi = CGI::Minimal->new;
close (STDIN);
unlink $test_file;
if (($mode eq 'max_size') or ($mode eq 'zero_size')) {
unless ($cgi->truncated) {
return 'failed: max read size not honored';
}
next;
}
my $string_pairs = { 'hello' => 'testing',
'hello2' => 'standard encoded FORM',
'hello3' => '',
'hello1' => '1',
'' => 'test',
'' => '',
'submit button' => 'submit',
};
my @form_keys = keys %$string_pairs;
my $expected_keys = $#form_keys + 1;
my @param_keys = $cgi->param;
if ($#form_keys != $#param_keys) {
return "failed : Expected $expected_keys parameters in x-www-form-urlencoded, found " . ($#param_keys + 1);
}
my %form_keys_hash = map {$_ => $string_pairs->{$_} } @form_keys;
foreach my $key_item (@param_keys) {
if (! defined $form_keys_hash{$key_item}) {
return 'failed : Parameter names did not match';
}
my $item_value = $cgi->param($key_item);
if ($form_keys_hash{$key_item} ne $item_value) {
return 'failed : Parameter values did not match';
}
}
}
}
return '';
}
######################################################
# Test multiparm hybrid form decoding #
######################################################
sub test_multipart_form {
my ($mode) = @_;
$mode = '' unless (defined $mode);
local $^W;
$ENV{'QUERY_STRING'} = 'hello=testing&otherthing=alpha';
my $basic_boundary = 'lkjsdlkjsd';
my @boundaries_list = ();
my $boundary_test_code = {};
for (my $count = 0; $count < 128; $count ++) {
next if ((10 == $count) or (13 == $count) or (26 == $count)); # Skip CR, LF and EOF (Ctrl-Z) characters for testing
my $test_boundary = chr($count) . $basic_boundary;
push (@boundaries_list,$test_boundary);
$boundary_test_code->{$test_boundary} = $count;
}
foreach my $boundary (@boundaries_list) {
my $data = multipart_data($boundary);
$ENV{'CONTENT_LENGTH'} = length($data);
if ($mode eq 'truncate') { $ENV{'CONTENT_LENGTH'} = length($data) + 1; }
$ENV{'CONTENT_TYPE'} = "multipart/form-data; boundary=---------------------------$boundary";
$ENV{'GATEWAY_INTERFACE'} = 'CGI/1.1';
$ENV{'REQUEST_METHOD'} = 'POST';
my $test_file = "test-data.$$.data";
open (TESTFILE,">$test_file") || return ("failed : could not open test file $test_file for writing: $!");
binmode (TESTFILE);
print TESTFILE $data;
close (TESTFILE);
# "Bad evil naughty Zoot"
CGI::Minimal::reset_globals;
CGI::Minimal::allow_hybrid_post_get(1);
open (STDIN,$test_file) || return ("failed : could not open test file $test_file for reading: $!");
my $cgi = CGI::Minimal->new;
close (STDIN);
unlink $test_file;
if ($mode eq 'truncate') {
unless ($cgi->truncated) { return 'failed: did not detect truncated form'; }
} else {
if ($cgi->truncated) { return "failed: form falsely appeared truncated for boundary char " . $boundary_test_code->{$boundary}; }
}
my $string_pairs = { 'hello' => 'also testing',
'hello2' => 'testing2',
'hello3' => '-----------------------------20lkjsdlkjsd',
'submit button' => 'submit',
'otherthing' => 'alpha',
};
my %mime_types = (
'hello' => 'application/xml',
'hello2' => 'text/html',
'hello3' => 'text/html',
'submit button' => 'text/plain',
'otherthing' => 'text/plain',
);
my %filenames = (
'hello' => 'hello1.xml',
'hello2' => 'example',
'hello3' => 'example3',
'submit button' => '',
'otherthing' => '',
);
{
my @form_keys = keys %$string_pairs;
my @param_keys = $cgi->param;
my $expected_n = $#form_keys + 1;
if ($#form_keys != $#param_keys) {
return "failed : Expected $expected_n parameters in multipart form, found "
. ($#param_keys + 1)
. ". testing codepoint " . $boundary_test_code->{$boundary}
. " "
. " for boundary $boundary $data";
}
my %form_keys_hash = map {$_ => $string_pairs->{$_} } @form_keys;
foreach my $key_item (@param_keys) {
if (! defined $form_keys_hash{$key_item}) {
return 'failed : Parameter names did not match';
}
my $item_value = $cgi->param($key_item);
if ($form_keys_hash{$key_item} ne $item_value) {
return "failed : Parameter values for '$key_item' did not match (expected '$form_keys_hash{$key_item}', got '$item_value')";
}
my $item_mime_type = $cgi->param_mime($key_item);
unless ($item_mime_type eq $mime_types{$key_item}) {
return "failed : Parameter MIME types did not match (expeced '$mime_types{$key_item}', got '$item_mime_type'";
}
my $item_filename = $cgi->param_filename($key_item);
unless ($item_filename eq $filenames{$key_item}) {
return 'failed : Parameter filenames did not match';
}
}
}
{
my @form_keys = keys %$string_pairs;
my @param_keys = $cgi->param_mime;
my $n_expected = $#form_keys + 1;
if ($#form_keys != $#param_keys) {
return "failed : Expected $n_expected parameters in mime params for multipart form, found "
. ($#param_keys + 1)
. ". testing codepoint " . $boundary_test_code->{$boundary}
. " "
. " for boundary $boundary $data";
}
my %form_keys_hash = map {$_ => $string_pairs->{$_} } @form_keys;
foreach my $key_item (@param_keys) {
if (! defined $form_keys_hash{$key_item}) {
return 'failed : MIME Parameter names did not match';
}
}
}
{
my @form_keys = keys %$string_pairs;
my @param_keys = $cgi->param_filename;
my $n_expected = $#form_keys + 1;
if ($#form_keys != $#param_keys) {
return "failed : Expected $n_expected parameters in filename params for multipart form, found "
. ($#param_keys + 1)
. ". testing codepoint " . $boundary_test_code->{$boundary}
. " "
. " for boundary $boundary $data";
}
my %form_keys_hash = map {$_ => $string_pairs->{$_} } @form_keys;
foreach my $key_item (@param_keys) {
if (! defined $form_keys_hash{$key_item}) {
return 'failed : filename Parameter names did not match';
}
}
}
my @multihello_mimes = $cgi->param_mime('hello');
if (1 != $#multihello_mimes) {
return 'failed: unexpected number of parameter MIME types for repeated values';
}
my @multihello2_mimes = $cgi->param_mime('hello2');
if (0 != $#multihello2_mimes) {
return 'failed: unexpected number of parameter MIME types for single value';
}
my @multihello_filenames = $cgi->param_filename('hello');
if (1 != $#multihello_filenames) {
return 'failed: unexpected number of parameter filenames for repeated values';
}
my @multihello2_filenames = $cgi->param_filename('hello2');
if (0 != $#multihello2_filenames) {
return 'failed: unexpected number of parameter filenames for single value';
}
eval {
$cgi->param_mime('one','two');
};
unless ($@) {
return 'failed: failed to catch invalid number of param_mime parameters';
}
my @null_parms = $cgi->param_mime('one');
unless (-1 == $#null_parms) {
return 'failed: failed to handle undefined mime parameter request correctly in array context';
}
my $null_parm = $cgi->param_mime('one');
if (defined $null_parm) {
return 'failed: failed to handle undefined mime parameter request correctly in scalar context';
}
@null_parms = $cgi->param_filename('one');
unless (-1 == $#null_parms) {
return 'failed: failed to handle undefined filename parameter request correctly in array context';
}
$null_parm = $cgi->param_filename('one');
if (defined $null_parm) {
return 'failed: failed to handle undefined filename parameter request correctly in scalar context';
}
eval {
$cgi->param_filename('one','two');
};
unless ($@) {
return 'failed: failed to catch invalid number of param_filename parameters';
}
}
# Success is an empty string (no error message ;) )
return '';
}
######################################################
# tests for detection of truncated forms #
######################################################
sub test_truncation { test_multipart_form('truncate'); }
######################################################
# multipart test data #
######################################################
sub multipart_data {
my ($boundary) = @_;
my $data =<<"EOD";
-----------------------------$boundary
Content-Disposition: form-data; name="hello"; filename="hello1.xml"
Content-Type: application/xml
also testing
-----------------------------$boundary
Content-Disposition: form-data; name="hello2"; filename="example"
Content-Type: text/html
testing2
-----------------------------$boundary
Content-Disposition: form-data; name="hello3"; filename="example3"
Content-Type: text/html
-----------------------------20lkjsdlkjsd
-----------------------------$boundary
Content-Disposition: form-data; name="submit button"
submit
-----------------------------$boundary--
EOD
$data =~ s/\012/\015\012/gs;
return $data;
}
###########################################################################################
sub run_tests {
my ($test_subs,$do_tests) = @_;
print @$do_tests[0],'..',@$do_tests[$#$do_tests],"\n";
print STDERR "\n";
my $n_failures = 0;
foreach my $test (@$do_tests) {
my $sub = $test_subs->{$test}->{-code};
my $desc = $test_subs->{$test}->{-desc};
my $failure = '';
eval { $failure = &$sub; };
if ($@) {
$failure = $@;
}
if ($failure ne '') {
chomp $failure;
print "not ok $test\n";
print STDERR " $desc - $failure\n";
$n_failures++;
} else {
print "ok $test\n";
print STDERR " $desc - ok\n";
}
}
print "END\n";
}