File Coverage

blib/lib/HTML/Scrubber/StripScripts.pm
Criterion Covered Total %
statement 35 35 100.0
branch 14 14 100.0
condition 3 3 100.0
subroutine 5 5 100.0
pod 1 1 100.0
total 58 58 100.0


line stmt bran cond sub pod time code
1             package HTML::Scrubber::StripScripts;
2 7     7   27877 use strict;
  7         14  
  7         301  
3              
4 7     7   44 use vars qw($VERSION);
  7         13  
  7         558  
5             $VERSION = '0.02';
6              
7             =head1 NAME
8              
9             HTML::Scrubber::StripScripts - strip scripting from HTML
10              
11             =head1 SYNOPSIS
12              
13             use HTML::Scrubber::StripScripts;
14              
15             my $hss = HTML::Scrubber::StripScripts->new(
16             Allow_src => 1,
17             Allow_href => 1,
18             Allow_a_mailto => 1,
19             Whole_document => 1,
20             Block_tags => ['hr'],
21             );
22              
23             my $clean_html = $hss->scrub($dirty_html);
24              
25             =head1 DESCRIPTION
26              
27             This module provides a preworked configuration for L,
28             configuring it to leave as much non-scripting markup in place as
29             possible while being certain to eliminate all scripting constructs.
30             This allows web applications to display HTML originating from an
31             untrusted source without introducing XSS (cross site scripting)
32             vulnerabilities.
33              
34             =head1 CONSTRUCTORS
35              
36             =over
37              
38             =item new ( CONFIG )
39              
40             Returns a new C object, configured with a filtering
41             policy based on a whitelist of XSS-free tags and attributes. If
42             present, the CONFIG parameter must be a hashref. The following keys
43             are recognized (unrecognized keys will be silently ignored).
44              
45             =over
46              
47             =item C
48              
49             By default, the scrubber won't be configured to allow constructs
50             that cause the browser to fetch things automatically, such as C
51             attributes in C tags. If this option is present and true then
52             those constructs will be allowed.
53              
54             =item C
55              
56             By default, the scrubber won't be configured to allow constructs
57             that cause the browser to fetch things if the user clicks on
58             something, such as the C attribute in C tags. Set this
59             option to a true value to allow this type of construct.
60              
61             =item C
62              
63             By default, the scrubber won't be configured to allow C
64             URLs in C attributes in C tags. Set this option to a true
65             value to allow them. Ignored unless C is true.
66              
67             =item C
68              
69             By default, the scrubber will be configured to deal with a snippet
70             of HTML to be placed inside another document after scrubbing, and
71             won't allow C and C tags and so on.
72              
73             Set this option to a true value if an entire HTML document is being
74             scrubbed.
75              
76             =item C
77              
78             If present, this must be an array ref holding a list of lower case
79             tag names. These tags will be removed from the allowed list.
80              
81             For example, a guestbook CGI that uses C
tags to separate posts
82             might wish to disallow the C
tag in posts, even though C
83             presents no XSS hazard.
84              
85             =back
86              
87             =cut
88              
89             require 5.005; # for qr//
90 7     7   6590 use HTML::Scrubber;
  7         71891  
  7         251  
91              
92 7     7   70 use vars qw(%re);
  7         15  
  7         21681  
93             %re = (
94             size => qr#^[+-]?\d+(?:\./d+)?[%*]?$#,
95             color => qr#^(?:\w{2,20}|\#[\da-fA-F]{6})$#,
96             word => qw#^\w*$#,
97             wordlist => qr#(?:[\w\-\, ]{1,200})$#,
98             text => qr#^[^\0]*$#,
99             url => qr# (?:^ (?:https?|ftp) :// ) | (?:^ [\w\.,/-]+ $) #ix,
100             a_mailto => qr# (?:^ (?:https?|ftp) :// ) | (?:^ [\w\.,/-]+ $) | (?:^ mailto: [\w\-\.\+\=\*]+\@[\w\-\.]+ $) #ix,
101             );
102              
103             sub new {
104 7     7 1 5319 my ($pkg, %cfg) = @_;
105              
106 7         17 my (@cite, @href, @src, @background);
107 7 100       63 @cite = ( cite => $re{'url'} ) if $cfg{Allow_href};
108 7 100       30 @href = ( href => $re{'url'} ) if $cfg{Allow_href};
109 7 100       50 @src = ( src => $re{'url'} ) if $cfg{Allow_src};
110 7 100       28 @background = ( background => $re{'url'} ) if $cfg{Allow_src};
111              
112 7         15 my %empty = ();
113              
114 7         38 my %font_attr = (
115             'size' => $re{'size'},
116             'face' => $re{'wordlist'},
117             'color' => $re{'color'},
118             );
119              
120 7         27 my %insdel_attr = (
121             @cite,
122             'datetime' => $re{'text'},
123             );
124              
125 7         24 my %texta_attr = (
126             'align' => $re{'word'},
127             );
128              
129 7         31 my %cellha_attr = (
130             'align' => $re{'word'},
131             'char' => $re{'word'},
132             'charoff' => $re{'size'},
133             );
134              
135 7         21 my %cellva_attr = (
136             'valign' => $re{'word'},
137             );
138              
139 7         36 my %cellhv_attr = ( %cellha_attr, %cellva_attr );
140              
141 7         48 my %col_attr = (
142             %cellhv_attr,
143             'width' => $re{'size'},
144             'span' => $re{'number'},
145             );
146              
147 7         116 my %thtd_attr = (
148             'abbr' => $re{'text'},
149             'axis' => $re{'text'},
150             'headers' => $re{'text'},
151             'scope' => $re{'word'},
152             'rowspan' => $re{'size'},
153             'colspan' => $re{'size'},
154             %cellhv_attr,
155             'nowrap' => $re{'word'},
156             'bgcolor' => $re{'color'},
157             'width' => $re{'size'},
158             'height' => $re{'size'},
159             'bordercolor' => $re{'color'},
160             'bordercolorlight' => $re{'color'},
161             'bordercolordark' => $re{'color'},
162             );
163              
164 7 100       567 my %rules = (
165             'br' => { 'clear' => $re{'word'} },
166             'em' => \%empty,
167             'strong' => \%empty,
168             'dfn' => \%empty,
169             'code' => \%empty,
170             'samp' => \%empty,
171             'kbd' => \%empty,
172             'var' => \%empty,
173             'cite' => \%empty,
174             'abbr' => \%empty,
175             'acronym' => \%empty,
176             'q' => { @cite },
177             'blockquote' => { @cite },
178             'sub' => \%empty,
179             'sup' => \%empty,
180             'tt' => \%empty,
181             'i' => \%empty,
182             'b' => \%empty,
183             'big' => \%empty,
184             'small' => \%empty,
185             'u' => \%empty,
186             's' => \%empty,
187             'strike' => \%empty,
188             'font' => \%font_attr,
189             'table' => { 'frame' => $re{'word'},
190             'rules' => $re{'word'},
191             %texta_attr,
192             'bgcolor' => $re{'color'},
193             @background,
194             'width' => $re{'size'},
195             'height' => $re{'size'},
196             'cellspacing' => $re{'size'},
197             'cellpadding' => $re{'size'},
198             'border' => $re{'size'},
199             'bordercolor' => $re{'color'},
200             'bordercolorlight' => $re{'color'},
201             'bordercolordark' => $re{'color'},
202             'summary' => $re{'text'},
203             },
204             'caption' => { 'align' => $re{'word'} },
205             'colgroup' => \%col_attr,
206             'col' => \%col_attr,
207             'thead' => \%cellhv_attr,
208             'tfoot' => \%cellhv_attr,
209             'tbody' => \%cellhv_attr,
210             'tr' => { bgcolor => $re{'color'},
211             %cellhv_attr,
212             },
213             'th' => \%thtd_attr,
214             'td' => \%thtd_attr,
215             'ins' => \%insdel_attr,
216             'del' => \%insdel_attr,
217             'a' => { @href },
218             'h1' => \%texta_attr,
219             'h2' => \%texta_attr,
220             'h3' => \%texta_attr,
221             'h4' => \%texta_attr,
222             'h5' => \%texta_attr,
223             'h6' => \%texta_attr,
224             'p' => \%texta_attr,
225             'div' => \%texta_attr,
226             'span' => \%texta_attr,
227             'ul' => { 'type' => $re{'word'},
228             'compact' => $re{'word'},
229             },
230             'ol' => { 'type' => $re{'text'},
231             'compact' => $re{'word'},
232             'start' => $re{'size'},
233             },
234             'li' => { 'type' => $re{'text'},
235             'value' => $re{'size'},
236             },
237             'dl' => { 'compact' => $re{'word'} },
238             'dt' => \%empty,
239             'dd' => \%empty,
240             'address' => \%empty,
241             'hr' => { %texta_attr,
242             'width' => $re{'size'},
243             'size ' => $re{'size'},
244             'noshade' => $re{'word'},
245             },
246             'pre' => { 'width' => $re{'size'} },
247             'center' => \%empty,
248             'nobr' => \%empty,
249             'img' => { @src,
250             'alt' => $re{'text'},
251             'width' => $re{'size'},
252             'height' => $re{'size'},
253             'border' => $re{'size'},
254             'hspace' => $re{'size'},
255             'vspace' => $re{'size'},
256             'align' => $re{'word'},
257             },
258             ( $cfg{Whole_document} ?
259             ( 'body' => { 'bgcolor' => $re{'color'},
260             @background,
261             'link' => $re{'color'},
262             'vlink' => $re{'color'},
263             'alink' => $re{'color'},
264             'text' => $re{'color'},
265             },
266             'head' => {},
267             'title' => {},
268             'html' => {},
269             ) : ()
270             ),
271             );
272              
273 7 100 100     65 if ( $cfg{Allow_href} and $cfg{Allow_a_mailto} ) {
274 1         4 $rules{'a'}{'href'} = $re{'a_mailto'};
275             }
276              
277 7 100       29 if ( $cfg{Block_tags} ) {
278 1         2 foreach my $block (@{ $cfg{Block_tags} }) {
  1         3  
279 3         10 delete $rules{$block};
280             }
281             }
282              
283 7         171 return HTML::Scrubber->new(
284             rules => [%rules],
285             comment => 0,
286             process => 0,
287             );
288             }
289              
290             =head1 BUGS
291              
292             =over
293              
294             =item
295              
296             All scripting is safely removed, but no attempt is made to ensure that
297             there is a matching end tag for each start tag. That could be a problem
298             if the scrubbed HTML is to be inserted into a larger HTML document, since
299             C tags and so on could be maliciously left open.
300              
301             If that's a big problem for you, consider using the more heavyweight
302             (and probably much slower) L module instead.
303              
304             =back
305              
306             =head1 SEE ALSO
307              
308             L, L
309              
310             =head1 AUTHOR
311              
312             Nick Cleaton Enick@cleaton.netE
313              
314             =head1 COPYRIGHT
315              
316             Copyright (C) 2003 Nick Cleaton. All Rights Reserved.
317              
318             This module is free software; you can redistribute it and/or modify it
319             under the same terms as Perl itself.
320              
321             =cut
322              
323             1;
324