File Coverage

blib/lib/Test/CGI/Untaint.pm
Criterion Covered Total %
statement 126 145 86.9
branch 50 70 71.4
condition 16 31 51.6
subroutine 15 15 100.0
pod 5 6 83.3
total 212 267 79.4


line stmt bran cond sub pod time code
1             package Test::CGI::Untaint;
2              
3             # turn on perl's safety features
4 5     5   149214 use strict;
  5         12  
  5         215  
5             #use warnings;
6 5     5   41 use Carp qw(croak);
  5         12  
  5         304  
7              
8             # use test builder
9 5     5   26 use Test::Builder;
  5         14  
  5         530  
10             my $Test = Test::Builder->new();
11              
12             # the stuff to test
13 5     5   11927 use CGI;
  5         121486  
  5         41  
14 5     5   5575 use CGI::Untaint;
  5         18369  
  5         69  
15              
16             # export the test functions
17 5     5   354 use Exporter;
  5         11  
  5         228  
18 5     5   28 use vars qw(@ISA @EXPORT @EXPORT_OK %EXPORT_TAGS $config_vars $VERSION @Data_Stack);
  5         9  
  5         11500  
19             @ISA = qw( Exporter );
20             @EXPORT = qw( is_extractable unextractable
21             is_extractable_deeply is_extractable_isa );
22             @EXPORT_OK = qw( config_vars );
23             %EXPORT_TAGS = ("all" => [ @EXPORT, @EXPORT_OK ]);
24              
25             # set the version
26             $VERSION = "1.10";
27              
28             =head1 NAME
29              
30             Test::CGI::Untaint - Test CGI::Untaint Local Extraction Handlers
31              
32             =head1 SYNOPSIS
33              
34             use Test::More tests => 2;
35             use Test::CGI::Untaint;
36              
37             # see that 'red' is extracted from 'Red'
38             is_extractable("Red","red","validcolor");
39              
40             # see that validcolor fails
41             unextractable("tree","validcolor");
42              
43             =head1 DESCRIPTION
44              
45             The B module can be extended with "Local Extraction
46             Handlers" that can be used define new ways of untainting data.
47              
48             This module is designed to test these data extraction modules. It
49             does this with the following methods:
50              
51             =over 4
52              
53             =item is_extractable
54              
55             Tests that first value passed has the second value passed extracted
56             from it when the local extraction handler named in the third argument
57             is called. An optional name for the test may be passed in the
58             forth argument. For example:
59              
60             # check that "Buffy" is extracted from "Buffy Summers" with
61             # the CGI::Untaint::slayer local extraction handler
62             is_extractable("Buffy Summers","Buffy", "slayer");
63              
64             =cut
65              
66             sub is_extractable
67             {
68             # extract the params, have a default test name
69 6     6 1 5804 my ($data, $wanted, $func, $name) = @_;
70              
71             # debug info
72             # { no warnings;
73             # print STDERR "data is '$data'\n";
74             # print STDERR "wanted is '$wanted'\n";
75             # print STDERR "func is '$func'\n";
76             # print STDERR "name is '$name'\n";
77             # }
78              
79             # default name
80 6   66     36 $name ||= "'$data' extractable as $func";
81              
82             # create a CGI::Untaint object
83 6         16 my $untaint = CGI::Untaint->new(config_vars(),
84             data => $data);
85              
86 6         97 my $result = $untaint->extract("-as_$func" => "data");
87              
88             # check if there was an error
89 6 100       16327 if ($untaint->error)
90             {
91 2         28 $Test->ok(0,$name);
92 2         1278 $Test->diag($untaint->error);
93 2         153 return 0;
94             }
95              
96             # check that the extracted value is equal
97             $Test->is_eq(
98 4         30 $result,
99             $wanted,
100             $name
101             );
102             }
103              
104             =item unextractable
105              
106             Checks that nothing is extracted from the first argument passed with
107             the local extraction handler named in the second argument. For
108             example:
109              
110             # check that nothing is extracted from "Willow Rosenberg"
111             # with the CGI::Untaint::slayer local extraction handler
112             unextractable("Willow Rosenberg", "slayer");
113              
114             The third argument may optionally contain a name for the test.
115              
116             =cut
117              
118             sub unextractable
119             {
120             # extract the params, have a default test name
121 4     4 1 3784 my ($data, $func, $name) = @_;
122              
123             # work out what it's called
124 4   66     21 $name ||= "'$data' unextractable as $func";
125              
126             # create a CGI::Untaint object
127 4         10 my $untaint = CGI::Untaint->new(config_vars(),
128             data => $data);
129              
130             # try extracting it
131 4         57 my $result = $untaint->extract("-as_$func" => "data");
132 4 100       487 unless($Test->ok($untaint->error, $name))
133             {
134 2         991 $Test->diag("expected data to be unextractable, but got:");
135 2 50       126 if (defined($result))
136 2         7 { $Test->diag(" '$result'") }
137             else
138 0         0 { $Test->diag(" undef") }
139             }
140 4         667 return !$result;
141             }
142              
143             =item is_extractable_deeply
144              
145             Tests that first value passed has the second value passed extracted
146             from it when the local extraction handler named in the third argument
147             is called B. Where C does a simple string
148             equality test, this does a proper deep check like C in
149             B. This is most useful when your class returns a big
150             old data structure from is_valid rather than a simple scalar.
151              
152             =cut
153              
154             sub is_extractable_deeply
155             {
156             # extract the params, have a default test name
157 2     2 1 1056 my ($data, $wanted, $func, $name) = @_;
158              
159             # default name
160 2   33     14 $name ||= "'$data' deeply extractable as $func";
161              
162             # create a CGI::Untaint object
163 2         6 my $untaint = CGI::Untaint->new(config_vars(),
164             data => $data);
165              
166 2         33 my $result = $untaint->extract("-as_$func" => "data");
167              
168             # check if there was an error
169 2 50       350 if ($untaint->error)
170             {
171 0         0 $Test->ok(0,$name);
172 0         0 $Test->diag($untaint->error);
173 0         0 return 0;
174             }
175              
176             # The code for the rest of this function is borrowed from
177             # Test::More.
178              
179             # variable to store the success or failure
180 2         11 my $ok;
181              
182             # hang on, are these things both not refs?
183 2 50 33     13 if( !ref $result || !ref $wanted ) {
184 0         0 $ok = $Test->is_eq($result, $wanted, $name);
185             }
186              
187             else
188             {
189             # do the deep check
190 2         5 local @Data_Stack = ();
191 2 100       6 if (_deep_check($result, $wanted))
192             {
193             # yey! it worked
194 1         6 $ok = $Test->ok(1, $name);
195             }
196             else
197             {
198             # no it didn't, darn!
199 1         7 $ok = $Test->ok(0, $name);
200 1         851 $ok = $Test->diag(_format_stack(@Data_Stack));
201             }
202             }
203              
204             # return the value
205 2         349 return $ok;
206             }
207              
208             =item is_extractable_isa
209              
210             Tests that the first value pass extracts something that is, or is
211             a subclass of, the class passed in the second argument when the
212             extraction handler .
213              
214             =cut
215              
216             sub is_extractable_isa
217             {
218 4     4 1 3527 my ($data, $class, $func, $name) = @_;
219              
220             # default name
221 4   33     25 $name ||= "'$data' extractable as a '$class'";
222              
223             # create a CGI::Untaint object
224 4         8 my $untaint = CGI::Untaint->new(config_vars(),
225             data => $data);
226              
227 4         64 my $object = $untaint->extract("-as_$func" => "data");
228              
229             # check if there was an error
230 4 50       677 if ($untaint->error)
231             {
232 0         0 $Test->ok(0,$name);
233 0         0 $Test->diag($untaint->error);
234 0         0 return 0;
235             }
236              
237             # the code for the rest of this function is stolen pretty much
238             # wholeheartedly from Test::More. It's been reformatted to my
239             # style and I've added lots of comments.
240              
241 4         19 my $diag;
242              
243             # check if the object is defined
244 4 50       17 if (!defined $object)
    100          
245 0         0 { $diag = "the extracted object isn't defined"; }
246              
247             # check if the object is a ref
248             elsif (!ref $object)
249 1         3 { $diag = "the extracted object isn't a reference"; }
250              
251             # check if we can call isa on it
252             else
253             {
254             # We can't use UNIVERSAL::isa because we want to honor isa() overrides
255 3         10 local($@, $!); # eval sometimes resets $!
256              
257             # try calling isa
258 3         6 my $rslt = eval { $object->isa($class) };
  3         30  
259              
260             # did we get an error?
261 3 100       21 if ($@)
    100          
262             {
263             # see if it's a error due to the thing being a ref rather than the
264             # thing being an object
265 1 50       6 if ($@ =~ /^Can't call method "isa" on unblessed reference/)
266             {
267             # hmm looks like it''s just a plain old ref. Use UNIVERSAL::isa
268             # to check we get the same thing
269 1 50       6 if (!UNIVERSAL::isa($object, $class))
270             {
271 1         3 my $ref = ref $object;
272 1         5 $diag = "the extracted object isn't a '$class' it's a '$ref'";
273             }
274             }
275             else
276             {
277             # We got a error thrown from the code when we called the isa
278             # method? That's screwed up! PANIC!
279 0         0 die <
280             WHOA! I tried to call ->isa on the extacted object and got some weird
281             error. This should never happen. Please contact the author immediately.
282             Here's the error.
283             $@
284             WHOA
285             }
286             }
287              
288             # did we get false back? That means it's a real object, but it
289             # just isn't a subclass of what we thought it should be.
290             elsif( !$rslt ) {
291 1         3 my $ref = ref $object;
292 1         4 $diag = "the extracted object isn't a '$class' it's a '$ref'";
293             }
294             }
295              
296             # did we have a 'problem'?
297 4         5 my $ok;
298 4 100       8 if($diag)
299             {
300             # print a failure
301 3         10 $ok = $Test->ok( 0, $name );
302             # print out the debug info
303 3         1370 $Test->diag(" $diag\n");
304             }
305             else
306 1         9 { $ok = $Test->ok( 1, $name ); }
307              
308             # return true unless we printed out a failure
309 4         619 return $ok;
310             }
311              
312             =back
313              
314             And that's that all there is to it, apart from the one function that
315             can be used to configure the test suite. It's not exported by default
316             (though you may optionally import it if you want.)
317              
318             =over 4
319              
320             =item config_vars
321              
322             The config_vars function is a get/set function that can be used to set
323             the hashref that will be passed to the creation of the CGI::Untaint
324             object used for testing. For example, if you need to instruct
325             CGI::Untaint to use a custom prefix for your local extraction
326             handlers, you can do so like so:
327              
328             use Test::CGI::Untaint qw(:all);
329             config_vars({ INCLUDE_PATH => "Profero" });
330              
331             =cut
332              
333             sub config_vars
334             {
335             # setting?
336 17 100   17 1 61 if (@_)
337             {
338 1 50       5 croak "Argument to 'config_vars' must be a hashref"
339             unless ref $_[0] eq "HASH";
340 1         2 $config_vars = shift;
341             }
342              
343             # return the current value or a default value
344 17   100     152 return $config_vars || {};
345             }
346              
347             =back
348              
349             =head1 BUGS
350              
351             None known.
352              
353             Bugs (and requests for new features) can be reported to the open
354             source development team at Profero though the CPAN RT system:
355             L
356              
357             =head1 AUTHOR
358              
359             Written By Mark Fowler Emark@twoshortplanks.comE.
360              
361             Copyright Profero 2003
362              
363             This program is free software; you can redistribute it
364             and/or modify it under the same terms as Perl itself.
365              
366             =head1 SEE ALSO
367              
368             L, L
369              
370             =cut
371              
372              
373             # code below this point is DIRECTLY cargo culted from Test::More
374             # without changing anything
375              
376             my $DNE = bless [], 'Does::Not::Exist';
377              
378             sub eq_array {
379 4     4 0 7 my($a1, $a2) = @_;
380 4 50       9 return 1 if $a1 eq $a2;
381              
382 4         15 my $ok = 1;
383 4 100       10 my $max = $#$a1 > $#$a2 ? $#$a1 : $#$a2;
384 4         7 for (0..$max) {
385 7 50       14 my $e1 = $_ > $#$a1 ? $DNE : $a1->[$_];
386 7 100       25 my $e2 = $_ > $#$a2 ? $DNE : $a2->[$_];
387              
388 7         24 push @Data_Stack, { type => 'ARRAY', idx => $_, vals => [$e1, $e2] };
389 7         16 $ok = _deep_check($e1,$e2);
390 7 100       15 pop @Data_Stack if $ok;
391              
392 7 100       21 last unless $ok;
393             }
394 4         10 return $ok;
395             }
396              
397             sub _deep_check {
398 9     9   12 my($e1, $e2) = @_;
399 9         7 my $ok = 0;
400              
401 9         10 my $eq;
402             {
403             # Quiet uninitialized value warnings when comparing undefs.
404 9         9 local $^W = 0;
  9         20  
405              
406 9 100       17 if( $e1 eq $e2 ) {
407 4         8 $ok = 1;
408             }
409             else {
410 5 100 66     74 if( UNIVERSAL::isa($e1, 'ARRAY') and
    50 33        
    50 33        
    50 33        
411             UNIVERSAL::isa($e2, 'ARRAY') )
412             {
413 4         9 $ok = eq_array($e1, $e2);
414             }
415             elsif( UNIVERSAL::isa($e1, 'HASH') and
416             UNIVERSAL::isa($e2, 'HASH') )
417             {
418 0         0 $ok = eq_hash($e1, $e2);
419             }
420             elsif( UNIVERSAL::isa($e1, 'REF') and
421             UNIVERSAL::isa($e2, 'REF') )
422             {
423 0         0 push @Data_Stack, { type => 'REF', vals => [$e1, $e2] };
424 0         0 $ok = _deep_check($$e1, $$e2);
425 0 0       0 pop @Data_Stack if $ok;
426             }
427             elsif( UNIVERSAL::isa($e1, 'SCALAR') and
428             UNIVERSAL::isa($e2, 'SCALAR') )
429             {
430 0         0 push @Data_Stack, { type => 'REF', vals => [$e1, $e2] };
431 0         0 $ok = _deep_check($$e1, $$e2);
432             }
433             else {
434 1         4 push @Data_Stack, { vals => [$e1, $e2] };
435 1         4 $ok = 0;
436             }
437             }
438             }
439              
440 9         39 return $ok;
441             }
442              
443             sub _format_stack {
444 1     1   3 my(@Stack) = @_;
445              
446 1         1 my $var = '$FOO';
447 1         2 my $did_arrow = 0;
448 1         3 foreach my $entry (@Stack) {
449 3   100     11 my $type = $entry->{type} || '';
450 3         5 my $idx = $entry->{'idx'};
451 3 50       21 if( $type eq 'HASH' ) {
    100          
    50          
452 0 0       0 $var .= "->" unless $did_arrow++;
453 0         0 $var .= "{$idx}";
454             }
455             elsif( $type eq 'ARRAY' ) {
456 2 100       7 $var .= "->" unless $did_arrow++;
457 2         6 $var .= "[$idx]";
458             }
459             elsif( $type eq 'REF' ) {
460 0         0 $var = "\${$var}";
461             }
462             }
463              
464 1         2 my @vals = @{$Stack[-1]{vals}}[0,1];
  1         4  
465 1         3 my @vars = ();
466 1         4 ($vars[0] = $var) =~ s/\$FOO/ \$got/;
467 1         4 ($vars[1] = $var) =~ s/\$FOO/\$expected/;
468              
469 1         2 my $out = "Structures begin differing at:\n";
470 1         3 foreach my $idx (0..$#vals) {
471 2         2 my $val = $vals[$idx];
472 2 100       11 $vals[$idx] = !defined $val ? 'undef' :
    50          
473             $val eq $DNE ? "Does not exist"
474             : "'$val'";
475             }
476              
477 1         5 $out .= "$vars[0] = $vals[0]\n";
478 1         3 $out .= "$vars[1] = $vals[1]\n";
479              
480 1         6 $out =~ s/^/ /msg;
481 1         6 return $out;
482             }
483              
484             1;