@@ -27,7 +27,7 @@ sub setup {
2727 return $c -> maybe::next ::method(@_ );
2828}
2929
30- sub prepare_parameters {
30+ sub execute {
3131 my $c = shift ;
3232
3333 $c -> maybe::next ::method(@_ );
@@ -47,33 +47,113 @@ sub prepare_parameters {
4747sub html_scrub {
4848 my ($c , $conf ) = @_ ;
4949
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);
5662 }
63+ }
64+
65+ # Normal query/POST body parameters:
66+ $c -> _scrub_recurse($conf , $c -> request-> parameters);
67+
68+ }
5769
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 });
6491 }
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($_ );
71101 }
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+ }
73111
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+ }
76128 }
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+ };
77157}
78158
79159__PACKAGE__ -> meta-> make_immutable;
@@ -124,9 +204,11 @@ See SYNOPSIS for how to configure the plugin, both with its own configuration
124204passing on any options from L<HTML::Scrubber> to control exactly what
125205scrubbing happens.
126206
127- =item prepare_parameters
207+ =item dispatch
128208
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`.
130212
131213=back
132214
0 commit comments