File Coverage

lib/HTML/Form/XSS.pm
Criterion Covered Total %
statement 30 75 40.0
branch 2 10 20.0
condition 0 3 0.0
subroutine 8 15 53.3
pod 2 4 50.0
total 42 107 39.2


line stmt bran cond sub pod time code
1             package HTML::Form::XSS;
2              
3             =pod
4              
5             =head1 NAME
6              
7             HTML::Form::XSS - Test HTML forms for cross site scripting vulnerabilities.
8              
9             =head1 SYNOPSIS
10              
11             use HTML::Form::XSS;
12             use WWW::Mechanize;
13             my $mech = WWW::Mechanize->new();
14             my $checker = HTML::Form::XSS->new($mech, config => '../root/config.xml');
15             $mech->get("http://www.site.com/pagewithform.html");
16             my @forms = $mech->forms();
17             foreach my $form (@forms){
18             my @results = $checker->do_audit($form);
19             foreach my $result (@results){
20             if($result->vulnerable()){
21             my $example = $result->example();
22             print "Example of vulnerable URL: $example\n";
23             last;
24             }
25             }
26             }
27              
28             =head1 DESCRIPTION
29              
30             Provides a simple way to test HTML forms for cross site scripting (XSS)
31             vulnerabilities.
32              
33             Checks to perform are given in a XML config file with the results of each
34             test returned.
35              
36             =head1 METHODS
37              
38             =cut
39              
40 2     2   130820 use strict;
  2         3  
  2         43  
41 2     2   6 use warnings;
  2         2  
  2         42  
42 2     2   1283 use XML::Simple;
  2         11718  
  2         10  
43 2     2   679 use HTML::Form::XSS::Result;
  2         4  
  2         43  
44 2     2   9 use parent qw(HTML::XSSLint); #we use this module as a base
  2         1  
  2         9  
45             our $VERSION = 1.00;
46             my $BROWSER = 'Mozilla/5.0 (compatible, MSIE 11, Windows NT 6.3; Trident/7.0; rv:11.0) like Gecko'; #emulate MS IE
47             ###################################
48              
49             =pod
50              
51             =head2 new()
52              
53             my $mech = WWW::Mechanize->new();
54             my $checker = HTML::Form::XSS->new($mech, config => '../root/config.xml');
55              
56             Creates a new HTML::Form::XSS object using two required parameters. Firstly a
57             or compatible object, secondly the path to the XML config file.
58              
59             Please see the example config.xml included in this distribution for details.
60              
61             =cut
62              
63             ###################################
64             sub new{
65 1     1 1 10232 my($class, $mech, %params) = @_;
66 1 50       4 if($mech){ #we need this someday
67 1 50       4 if(defined($params{'config'})){ #how can we setup without this
68             my $self = {
69             '_mech' => $mech,
70 1         4 '_configFile' => $params{'config'}
71             };
72 1         2 bless $self, $class;
73 1         4 $self->_loadConfig();
74 1         6 return $self;
75             }
76             else{
77 0         0 die("No Config file option given");
78             }
79             }
80             else{
81 0         0 die("No WWW::Mechanize compatible object given");
82             }
83 0         0 return undef;
84             }
85             ###################################
86             sub make_params { #passing a check value here, so we can do many checks
87 0     0 0 0 my($self, $check, @inputs) = @_;
88 0         0 my %params;
89 0         0 foreach my $input (@inputs){
90 0 0 0     0 if(defined($input->name()) && length($input->name())){
91 0         0 my $value = $self->random_string();
92 0         0 $params{$input->name()} = $check . $value;
93             }
94             }
95 0         0 return \%params;
96             }
97             ###################################
98              
99             =pod
100              
101             =head2 do_audit()
102              
103             my @results = $checker->do_audit($form);
104              
105             Using the provided object the form is tested for all the
106             XSS attacks in the XML config file.
107              
108             An array of objects are returned, one for
109             each check.
110              
111             =cut
112              
113             #######################################################
114             sub do_audit { #we do many checks here not just one
115 0     0 1 0 my($self, $form) = @_;
116 0         0 my @results;
117 0         0 print "Checking...\n";
118 0         0 foreach my $check ($self->_getChecks()){
119 0         0 my $params = $self->make_params($check, $form->inputs);
120 0         0 my $request = $self->fillin_and_click($form, $params);
121 0         0 $request->header('User-Agent' => $BROWSER);
122 0         0 my $response = $self->request($request);
123 0         0 print "Status: " . $response->code() . "\n";
124 0 0       0 $response->is_success or die("Can't fetch " . $form->action);
125 0         0 my @names = $self->compare($response->content, $params);
126 0         0 my $result = HTML::Form::XSS::Result->new( #using are modified result class
127             form => $form,
128             names => \@names,
129             check => $check
130             );
131 0         0 push(@results, $result);
132             }
133 0         0 print "\n";
134 0         0 return @results;
135             }
136             ###################################
137             sub compare{ #we need to make the patterns regex safe
138 0     0 0 0 my($self, $html, $params) = @_;
139 0         0 my @names;
140 0         0 foreach my $param (keys(%{$params})){
  0         0  
141 0         0 my $pattern = $self->_makeRegexpSafe($params->{$param});
142 0 0       0 if($html =~ m/$pattern/){
143 0         0 push(@names, $param);
144             }
145             }
146 0         0 return @names;
147             }
148             ###################################
149             #
150             #private methods
151             #
152             ###################################
153             sub _getChecks{
154 0     0   0 my $self = shift;
155 0         0 my $config = $self->_getConfig();
156 0         0 my $checks = $config->{'checks'}->{'check'};
157 0         0 return @{$checks};
  0         0  
158             }
159             ###################################
160             sub _getConfigFile{
161 1     1   2 my $self = shift;
162 1         7 return $self->{'_configFile'};
163             }
164             ###################################
165             sub _getConfig{
166 0     0   0 my $self = shift;
167 0         0 return $self->{'_config'};
168             }
169             ###################################
170             sub _loadConfig{
171 1     1   2 my $self = shift;
172 1         4 my $file = $self->_getConfigFile();
173 1         8 my $simple = XML::Simple->new();
174 1         57 my $ref = $simple->XMLin($file);
175 1         55808 $self->{'_config'} = $ref;
176 1         12 return 1;
177             }
178             ###################################
179             sub _makeRegexpSafe{
180 0     0     my($self, $pattern) = @_;
181 0           $pattern =~ s/([\(\)])/\\$1/g; #add back slashes where required
182 0           return $pattern;
183             }
184             ###################################
185             sub _getMech{
186 0     0     my $self = shift;
187 0           return $self->{'_mech'};
188             }
189             ###################################
190              
191             =pod
192              
193             =head1 SEE ALSO
194              
195             L,
196             L,
197             L
198              
199             =head1 AUTHOR
200              
201             MacGyveR
202              
203             Development questions, bug reports, and patches are welcome to the above address
204              
205             =head1 COPYRIGHT
206              
207             Copyright (c) 2016 MacGyveR. All rights reserved.
208              
209             This library is free software; you can redistribute it and/or modify it under the same terms as Perl itself.
210              
211             =cut
212              
213             ####################################################
214             return 1;