@@ -27,7 +27,7 @@ sub setup {
27
27
return $c -> maybe::next ::method(@_ );
28
28
}
29
29
30
- sub prepare_parameters {
30
+ sub execute {
31
31
my $c = shift ;
32
32
33
33
$c -> maybe::next ::method(@_ );
@@ -47,33 +47,113 @@ sub prepare_parameters {
47
47
sub html_scrub {
48
48
my ($c , $conf ) = @_ ;
49
49
50
- param:
51
- for my $param (keys %{ $c -> request-> {parameters } }) {
52
- # while (my ($param, $value) = each %{ $c->request->{parameters} }) {
53
- my $value = \$c -> request-> {parameters }{$param };
54
- if (ref $$value && ref $$value ne ' ARRAY' ) {
55
- next param;
50
+ # If there's body_data - for e.g. a POSTed JSON body that was decoded -
51
+ # then we need to walk through it, scrubbing as appropriate
52
+ if (my $body_data = $c -> request-> body_data) {
53
+ $c -> _scrub_recurse($conf , $c -> request-> body_data);
54
+ }
55
+
56
+ # And if Catalyst::Controller::REST is in use so we have $req->data,
57
+ # then scrub that too
58
+ if ($c -> request-> can(' data' )) {
59
+ my $data = $c -> request-> data;
60
+ if ($data ) {
61
+ $c -> _scrub_recurse($conf , $c -> request-> data);
56
62
}
63
+ }
64
+
65
+ # Normal query/POST body parameters:
66
+ $c -> _scrub_recurse($conf , $c -> request-> parameters);
67
+
68
+ }
57
69
58
- # If we only want to operate on certain params, do that checking
59
- # now...
60
- if ($conf && $conf -> {ignore_params }) {
61
- my $ignore_params = $c -> config-> {scrubber }{ignore_params };
62
- if (ref $ignore_params ne ' ARRAY' ) {
63
- $ignore_params = [ $ignore_params ];
70
+ # Recursively scrub param values...
71
+ sub _scrub_recurse {
72
+ my ($c , $conf , $data ) = @_ ;
73
+
74
+ # If the thing we've got is a hashref, walk over its keys, checking
75
+ # whether we should ignore, otherwise, do the needful
76
+ if (ref $data eq ' HASH' ) {
77
+ for my $key (keys %$data ) {
78
+ if (!$c -> _should_scrub_param($conf , $key )) {
79
+ next ;
80
+ }
81
+
82
+ # OK, it's fine to fettle with this key - if its value is
83
+ # a ref, recurse, otherwise, scrub
84
+ if (my $ref = ref $data -> {$key }) {
85
+ $c -> _scrub_recurse($conf , $data -> {$key });
86
+ } else {
87
+ # Alright, non-ref value, so scrub it
88
+ # FIXME why did we have to have this ref-ref handling fun?
89
+ # $_ = $c->_scrubber->scrub($_) for (ref($$value) ? @{$$value} : $$value);
90
+ $data -> {$key } = $c -> _scrubber-> scrub($data -> {$key });
64
91
}
65
- for my $ignore_param (@$ignore_params ) {
66
- if (ref $ignore_param eq ' Regexp' ) {
67
- next param if $param =~ $ignore_param ;
68
- } else {
69
- next param if $param eq $ignore_param ;
70
- }
92
+ }
93
+ } elsif (ref $data eq ' ARRAY' ) {
94
+ # Simple - scrub all the values
95
+ $_ = $c -> _scrubber-> scrub($_ ) for @$data ;
96
+ for (@$data ) {
97
+ if (ref $_ ) {
98
+ $c -> _scrub_recurse($conf , $_ );
99
+ } else {
100
+ $_ = $c -> _scrubber-> scrub($_ );
71
101
}
72
- }
102
+ }
103
+ } elsif (ref $data eq ' CODE' ) {
104
+ $c -> log -> debug(" Can't scrub a coderef!" );
105
+ } else {
106
+ # This shouldn't happen, as we should always start with a ref,
107
+ # and non-ref hash/array values should have been handled above.
108
+ $c -> log -> debug(" Non-ref to scrub - should this happen?" );
109
+ }
110
+ }
73
111
74
- # If we're still here, we want to scrub this param's value.
75
- $_ = $c -> _scrubber-> scrub($_ ) for (ref ($$value ) ? @{$$value } : $$value );
112
+ sub _should_scrub_param {
113
+ my ($c , $conf , $param ) = @_ ;
114
+ # If we only want to operate on certain params, do that checking
115
+ # now...
116
+ if ($conf && $conf -> {ignore_params }) {
117
+ my $ignore_params = $c -> config-> {scrubber }{ignore_params };
118
+ if (ref $ignore_params ne ' ARRAY' ) {
119
+ $ignore_params = [ $ignore_params ];
120
+ }
121
+ for my $ignore_param (@$ignore_params ) {
122
+ if (ref $ignore_param eq ' Regexp' ) {
123
+ return if $param =~ $ignore_param ;
124
+ } else {
125
+ return if $param eq $ignore_param ;
126
+ }
127
+ }
76
128
}
129
+
130
+ # If we've not bailed above, we didn't match any ignore_params
131
+ # entries, or didn't have any, so we do want to scrub
132
+ return 1;
133
+ }
134
+
135
+
136
+ # Incredibly nasty monkey-patch to rewind filehandle before parsing - see
137
+ # https://github.com/perl-catalyst/catalyst-runtime/pull/186
138
+ # First, get the default handlers hashref:
139
+ my $default_data_handlers = Catalyst-> default_data_handlers();
140
+
141
+ # Wrap the coderef for application/json in one that rewinds the filehandle
142
+ # first:
143
+ my $orig_json_handler = $default_data_handlers -> {' application/json' };
144
+ $default_data_handlers -> {' application/json' } = sub {
145
+ $_ [0]-> seek (0,0); # rewind $fh arg
146
+ $orig_json_handler -> (@_ );
147
+ };
148
+
149
+
150
+ {
151
+ # and now replace the original default_data_handlers() with a version that
152
+ # returns our modified handlers
153
+ no warnings ' redefine' ;
154
+ *Catalyst::default_data_handlers = sub {
155
+ return $default_data_handlers ;
156
+ };
77
157
}
78
158
79
159
__PACKAGE__ -> meta-> make_immutable;
@@ -124,9 +204,11 @@ See SYNOPSIS for how to configure the plugin, both with its own configuration
124
204
passing on any options from L<HTML::Scrubber> to control exactly what
125
205
scrubbing happens.
126
206
127
- =item prepare_parameters
207
+ =item dispatch
128
208
129
- Sanitize HTML tags in all parameters (unless `ignore_params` exempts them).
209
+ Sanitize HTML tags in all parameters (unless `ignore_params` exempts them) -
210
+ this includes normal POST params, and serialised data (e.g. a POSTed JSON body)
211
+ accessed via `$c->req->body_data` or `$c->req->data`.
130
212
131
213
=back
132
214
0 commit comments