File Coverage

blib/lib/CGI/Untaint.pm
Criterion Covered Total %
statement 57 59 96.6
branch 30 34 88.2
condition n/a
subroutine 9 9 100.0
pod 3 3 100.0
total 99 105 94.2


line stmt bran cond sub pod time code
1             package CGI::Untaint;
2              
3             $VERSION = '1.26';
4              
5             =head1 NAME
6              
7             CGI::Untaint - process CGI input parameters
8              
9             =head1 SYNOPSIS
10              
11             use CGI::Untaint;
12              
13             my $q = new CGI;
14             my $handler = CGI::Untaint->new( $q->Vars );
15             my $handler2 = CGI::Untaint->new({
16             INCLUDE_PATH => 'My::Untaint',
17             }, $apr->parms);
18              
19             my $name = $handler->extract(-as_printable => 'name');
20             my $homepage = $handler->extract(-as_url => 'homepage');
21              
22             my $postcode = $handler->extract(-as_postcode => 'address6');
23              
24             # Create your own handler...
25              
26             package MyRecipes::CGI::Untaint::legal_age;
27             use base 'CGI::Untaint::integer';
28             sub is_valid {
29             shift->value > 21;
30             }
31              
32             package main;
33             my $age = $handler->extract(-as_legal_age => 'age');
34              
35             =head1 DESCRIPTION
36              
37             Dealing with large web based applications with multiple forms is a
38             minefield. It's often hard enough to ensure you validate all your
39             input at all, without having to worry about doing it in a consistent
40             manner. If any of the validation rules change, you often have to alter
41             them in many different places. And, if you want to operate taint-safe,
42             then you're just adding even more headaches.
43              
44             This module provides a simple, convenient, abstracted and extensible
45             manner for validating and untainting the input from web forms.
46              
47             You simply create a handler with a hash of your parameters (usually
48             $q->Vars), and then iterate over the fields you wish to extract,
49             performing whatever validations you choose. The resulting variable is
50             guaranteed not only to be valid, but also untainted.
51              
52             =cut
53              
54 7     7   325223 use strict;
  7         17  
  7         317  
55 7     7   45 use Carp;
  7         14  
  7         1084  
56 7     7   8709 use UNIVERSAL::require;
  7         13459  
  7         83  
57              
58             =head1 CONSTRUCTOR
59              
60             =head2 new
61              
62             my $handler = CGI::Untaint->new( $q->Vars );
63             my $handler2 = CGI::Untaint->new({
64             INCLUDE_PATH => 'My::Untaint',
65             }, $apr->parms);
66              
67             The simplest way to contruct an input handler is to pass a hash of
68             parameters (usually $q->Vars) to new(). Each parameter will then be able
69             to be extracted later by calling an extract() method on it.
70              
71             However, you may also pass a leading reference to a hash of configuration
72             variables.
73              
74             Currently the only such variable supported is 'INCLUDE_PATH', which
75             allows you to specify a local path in which to find extraction handlers.
76             See L.
77              
78             =cut
79              
80             sub new {
81 10     10 1 40255 my $class = shift;
82              
83             # want to cope with any of:
84             # (%vals), (\%vals), (\%config, %vals) or (\%config, \%vals)
85             # but %vals could also be an object ...
86 10         24 my ($vals, $config);
87              
88 10 100       57 if (@_ == 1) {
    100          
89              
90             # only one argument - must be either hashref or obj.
91 2 100       8 $vals = ref $_[0] eq "HASH" ? shift: { %{ +shift } }
  1         8  
92              
93             } elsif (@_ > 2) {
94              
95             # Conf + Hash or Hash
96 5 100       28 $config = shift if ref $_[0] eq "HASH";
97 5         33 $vals = {@_}
98              
99             } else {
100              
101             # Conf + Hashref or 1 key hash
102 3 100       20 ref $_[0] eq "HASH" ? ($config, $vals) = @_ : $vals = {@_};
103             }
104              
105 10         91 bless {
106             __config => $config,
107             __data => $vals,
108             } => $class;
109              
110             }
111              
112             =head1 METHODS
113              
114             =head2 extract
115              
116             my $homepage = $handler->extract(-as_url => 'homepage');
117             my $state = $handler->extract(-as_us_state => 'address4');
118             my $state = $handler->extract(-as_like_us_state => 'address4');
119              
120             Once you have constructed your Input Handler, you call the 'extract'
121             method on each piece of data with which you are concerned.
122              
123             The takes an -as_whatever flag to state what type of data you
124             require. This will check that the input value correctly matches the
125             required specification, and return an untainted value. It will then call
126             the is_valid() method, where applicable, to ensure that this doesn't
127             just _look_ like a valid value, but actually is one.
128              
129             If you want to skip this stage, then you can call -as_like_whatever
130             which will perform the untainting but not the validation.
131              
132             =cut
133              
134             sub extract {
135 29     29 1 7229 my $self = shift;
136 29         71 $self->{_ERR} = "";
137 29         47 my $val = eval { $self->_do_extract(@_) };
  29         94  
138 29 100       78 if ($@) {
139 12         39 chomp($self->{_ERR} = $@);
140 12         43 return;
141             }
142 17         92 return $val;
143             }
144              
145             sub _do_extract {
146 29     29   48 my $self = shift;
147              
148 29         84 my %param = @_;
149              
150             #----------------------------------------------------------------------
151             # Make sure we have a valid data handler
152             #----------------------------------------------------------------------
153 29         156 my @as = grep /^-as_/, keys %param;
154 29 50       94 croak "No data handler type specified" unless @as;
155 29 50       84 croak "Multiple data handler types specified" unless @as == 1;
156              
157 29         65 my $field = delete $param{ $as[0] };
158 29         73 my $skip_valid = $as[0] =~ s/^(-as_)like_/$1/;
159 29         104 my $module = $self->_load_module($as[0]);
160              
161             #----------------------------------------------------------------------
162             # Do we have a sensible value? Check the default untaint for this
163             # type of variable, unless one is passed.
164             #----------------------------------------------------------------------
165 29 100       142 defined(my $raw = $self->{__data}->{$field})
166             or die "No parameter for '$field'\n";
167              
168             # 'False' values get returned as themselves with no warnings.
169             # return $self->{__lastval} unless $self->{__lastval};
170              
171 24         136 my $handler = $module->_new($self, $raw);
172              
173 24         83 my $clean = eval { $handler->_untaint };
  24         101  
174 24 100       105 if ($@) { # Give sensible death message
175 6 50       67 die "$field ($raw) does not untaint with default pattern\n"
176             if $@ =~ /^Died at/;
177 0         0 die $@;
178             }
179              
180             #----------------------------------------------------------------------
181             # Are we doing a validation check?
182             #----------------------------------------------------------------------
183 18 100       46 unless ($skip_valid) {
184 17 50       114 if (my $ref = $handler->can('is_valid')) {
185 17 100       59 die "$field ($raw) does not pass the is_valid() check\n"
186             unless $handler->$ref();
187             }
188             }
189              
190 17         141 return $handler->untainted;
191             }
192              
193             =head2 error
194              
195             my $error = $handler->error;
196              
197             If the validation failed, this will return the reason why.
198              
199             =cut
200              
201 22     22 1 2149 sub error { $_[0]->{_ERR} }
202              
203             sub _load_module {
204 29     29   41 my $self = shift;
205 29         70 my $name = $self->_get_module_name(shift());
206              
207 29         109 foreach
208             my $prefix (grep defined, "CGI::Untaint", $self->{__config}{INCLUDE_PATH})
209             {
210 33         69 my $mod = "$prefix\::$name";
211 33 100       143 return $self->{__loaded}{$mod} if defined $self->{__loaded}{$mod};
212 18         24 eval {
213 18         154 $mod->require;
214 18 100       426 $mod->can('_untaint') or die;
215             };
216 18 100       105 return $self->{__loaded}{$mod} = $mod unless $@;
217             }
218 0         0 die "Can't find extraction handler for $name\n";
219             }
220              
221             # Convert the -as_whatever to a FQ module name
222             sub _get_module_name {
223 29     29   41 my $self = shift;
224 29         99 (my $handler = shift) =~ s/^-as_//;
225 29         71 return $handler;
226             }
227              
228             =head1 LOCAL EXTRACTION HANDLERS
229              
230             As well as as the handlers supplied with this module for extracting
231             data, you may also create your own. In general these should inherit from
232             'CGI::Untaint::object', and must provide an '_untaint_re' method which
233             returns a compiled regular expression, suitably bracketed such that $1
234             will return the untainted value required.
235              
236             e.g. if you often extract single digit variables, you could create
237              
238             package My::Untaint::digit;
239              
240             use base 'CGI::Untaint::object';
241              
242             sub _untaint_re { qr/^(\d)$/ }
243              
244             1;
245              
246             You should specify the path 'My::Untaint' in the INCLUDE_PATH
247             configuration option. (See new() above.)
248              
249             When extract() is called CGI::Untaint will also check to see if you have
250             an is_valid() method also, and if so will run this against the value
251             extracted from the regular expression (available as $self->value).
252              
253             If this returns a true value, then the extracted value will be returned,
254             otherwise we return undef.
255              
256             is_valid() can also modify the value being returned, by assigning
257             $self->value($new_value)
258              
259             e.g. in the above example, if you sometimes need to ensure that the
260             digit extracted is prime, you would supply:
261              
262             sub is_valid { (1 x shift->value) !~ /^1?$|^(11+?)\1+$/ };
263              
264             Now, when users call extract(), it will also check that the value
265             is valid(), i.e. prime:
266              
267             my $number = $handler->extract(-as_digit => 'value');
268              
269             A user wishing to skip the validation, but still ensure untainting can
270             call
271              
272             my $number = $handler->extract(-as_like_digit => 'value');
273              
274             =head2 Test::CGI::Untaint
275              
276             If you create your own local handlers, then you may wish to explore
277             L, available from the CPAN. This makes it very easy
278             to write tests for your handler. (Thanks to Profero Ltd.)
279              
280             =head1 AVAILABLE HANDLERS
281              
282             This package comes with the following simplistic handlers:
283              
284             printable - a printable string
285             integer - an integer
286             hex - a hexadecimal number (as a string)
287              
288             To really make this work for you you either need to write, or download
289             from CPAN, other handlers. Some of the handlers available on CPAN include:
290              
291             asin - an Amazon ID
292             boolean - boolean value
293             country - a country code or name
294             creditcard - a credit card number
295             date - a date (into a Date::Simple)
296             datetime - a date (into a DateTime)
297             email - an email address
298             hostname - a DNS host name
299             html - sanitized HTML
300             ipaddress - an IP address
301             isbn - an ISBN
302             uk_postcode - a UK Postcode
303             url - a URL
304             zipcode - a US zipcode
305              
306             =head1 BUGS
307              
308             None known yet.
309              
310             =head1 SEE ALSO
311              
312             L. L. L.
313              
314             =head1 AUTHOR
315              
316             Tony Bowden
317              
318             =head1 BUGS and QUERIES
319              
320             Please direct all correspondence regarding this module to:
321             bug-CGI-Untaint@rt.cpan.org
322              
323             =head1 COPYRIGHT and LICENSE
324              
325             Copyright (C) 2001-2005 Tony Bowden. All rights reserved.
326              
327             This module is free software; you can redistribute it and/or modify
328             it under the same terms as Perl itself.
329              
330             =cut
331              
332             1;