@@ -8,7 +8,7 @@ use Dancer2::Core::HTTP;
8
8
use Data::Dumper;
9
9
use Dancer2::FileUtils qw/ path open_file/ ;
10
10
use Sub::Quote;
11
- use Module::Runtime ' require_module' ;
11
+ use Module::Runtime qw/ require_module use_module / ;
12
12
use Ref::Util qw< is_hashref > ;
13
13
use Clone qw( clone) ;
14
14
@@ -48,6 +48,52 @@ has title => (
48
48
builder => ' _build_title' ,
49
49
);
50
50
51
+ has censor => (
52
+ is => ' ro' ,
53
+ isa => CodeRef,
54
+ lazy => 1,
55
+ default => sub {
56
+ my $self = shift ;
57
+
58
+ if ( my $custom = $self -> has_app && $self -> app-> setting(' error_censor' ) ) {
59
+
60
+ if ( is_hashref $custom ) {
61
+ die " only one key can be set for the 'error_censor' setting\n "
62
+ if 1 != keys %$custom ;
63
+
64
+ my ( $class , $args ) = %$custom ;
65
+
66
+ my $censor = use_module($class )-> new(%$args );
67
+
68
+ return sub {
69
+ $censor -> censor(@_ );
70
+ }
71
+ }
72
+
73
+ my $coderef = eval ' \&' .$custom ;
74
+
75
+ # it's already defined? Nice! We're done
76
+ return $coderef if $coderef ;
77
+
78
+ my $module = $custom =~ s / ::[^:]*?$// r ;
79
+
80
+ require_module($module );
81
+
82
+ return eval ' \&' .$custom ;
83
+ }
84
+
85
+ # reminder: update POD below if changing the config here
86
+ my $data_censor = use_module(' Data::Censor' )-> new(
87
+ sensitive_fields => qr / pass|card.?num|pan|secret/ i ,
88
+ replacement => " Hidden (looks potentially sensitive)" ,
89
+ );
90
+
91
+ return sub {
92
+ $data_censor -> censor(@_ );
93
+ };
94
+ }
95
+ );
96
+
51
97
sub _build_title {
52
98
my ($self ) = @_ ;
53
99
my $title = ' Error ' . $self -> status;
@@ -367,11 +413,11 @@ sub backtrace {
367
413
}
368
414
369
415
sub dumper {
370
- my $ obj = shift ;
416
+ my ( $self , $ obj) = @_ ;
371
417
372
418
# Take a copy of the data, so we can mask sensitive-looking stuff:
373
419
my $data = clone($obj );
374
- my $censored = _censor ( $data );
420
+ my $censored = $self -> censor -> ( $data );
375
421
376
422
# use Data::Dumper;
377
423
my $dd = Data::Dumper-> new( [ $data ] );
@@ -399,7 +445,7 @@ sub environment {
399
445
my $env = $self -> has_app && $self -> app-> has_request && $self -> app-> request-> env;
400
446
401
447
# Get a sanitised dump of the settings, session and environment
402
- $_ = $_ ? dumper($_ ) : ' <i>undefined</i>' for $settings , $session , $env ;
448
+ $_ = $_ ? $self -> dumper($_ ) : ' <i>undefined</i>' for $settings , $session , $env ;
403
449
404
450
return <<"END_HTML" ;
405
451
<div class="title">Stack</div><pre class="content">$stack </pre>
@@ -423,37 +469,6 @@ sub get_caller {
423
469
424
470
# private
425
471
426
- # Given a hashref, censor anything that looks sensitive. Returns number of
427
- # items which were "censored".
428
-
429
- sub _censor {
430
- my $hash = shift ;
431
- my $visited = shift || {};
432
-
433
- unless ( $hash && is_hashref($hash ) ) {
434
- carp " _censor given incorrect input: $hash " ;
435
- return ;
436
- }
437
-
438
- my $censored = 0;
439
- for my $key ( keys %$hash ) {
440
- if ( is_hashref( $hash -> {$key } ) ) {
441
- if (!$visited -> { $hash -> {$key } }) {
442
- # mark the new ref as visited
443
- $visited -> { $hash -> {$key } } = 1;
444
-
445
- $censored += _censor( $hash -> {$key }, $visited );
446
- }
447
- }
448
- elsif ( $key =~ / (pass|card?num|pan|secret)/i ) {
449
- $hash -> {$key } = " Hidden (looks potentially sensitive)" ;
450
- $censored ++;
451
- }
452
- }
453
-
454
- return $censored ;
455
- }
456
-
457
472
# Replaces the entities that are illegal in (X)HTML.
458
473
sub _html_encode {
459
474
my $value = shift ;
@@ -523,6 +538,60 @@ This is only an attribute getter, you'll have to set it at C<new>.
523
538
524
539
The message of the error page.
525
540
541
+ =attr censor
542
+
543
+ The function to use to censor error messages. By default it uses the C<censor > method of L<Data::Censor> "
544
+
545
+ # default censor function used by `error_censor`
546
+ # is equivalent to
547
+ sub MyApp::censor {
548
+ Data::Censor->new(
549
+ sensitive_fields => qr/pass|card.?num|pan|secret/i,
550
+ replacement => "Hidden (looks potentially sensitive)",
551
+ )->censor(@_);
552
+ }
553
+ setting error_censor => 'MyApp::censor';
554
+
555
+ It can be configured via the app setting C<error_censor > . If provided,
556
+ C<error_censor > has to be the fully qualified name of the censor
557
+ function. That function is expected to take in the data as a hashref,
558
+ modify it in place and return the number of items 'censored'.
559
+
560
+ For example, using L<Data::Censor> .
561
+
562
+ # in config.yml
563
+ error_censor: MyApp::Censor::censor
564
+
565
+ # in MyApp::Censor
566
+ package MyApp::Censor;
567
+
568
+ use Data::Censor;
569
+
570
+ my $data_censor = Data::Censor->new(
571
+ sensitive_fields => [ qw(card_number password hush) ],
572
+ replacement => '(Sensitive data hidden)',
573
+ );
574
+
575
+ sub censor { $data_censor->censor(@_) }
576
+
577
+ 1;
578
+
579
+
580
+ As a shortcut, C<error_censor > can also be the key/value combo of
581
+ a class and the arguments for its constructor. The created object
582
+ is expected to have a method C<censor > . For example, the use of
583
+ L<Data::Censor> above could also have been done via the config
584
+
585
+ error_censor:
586
+ Data::Censor:
587
+ sensitive_fields:
588
+ - card_number
589
+ - password
590
+ - hush
591
+ replacement: '(Sensitive data hidden)'
592
+
593
+
594
+
526
595
=method throw($response)
527
596
528
597
Populates the content of the response with the error's information.
0 commit comments