Data-Censor
view release on metacpan or search on metacpan
SYNOPSIS
# OO way, letting you specify your own list of sensitive-looking fields, and
# what they should be replaced by (all options here are optional)
my $censor = Data::Censor->new(
# Specify which fields to censor:
sensitive_fields => [ qw(card_number password) ],
# Specify text to replace their values with:
replacement => '(Sensitive data hidden)',
# Or specify callbacks for each field name which return the "censored"
# value - in this case, masking a card number (PAN) to show only the
# last four digits:
replacement_callbacks => {
card_number => sub {
my $pan = shift;
return "x" x (length($pan) - 4) . substr($pan, -4, 4);
},
},
);
# Censor the data in-place (changes the data structure, returns the number
# of keys censored)
my $censor_count = $censor->censor(\%data);
new (CONSTRUCTOR)
Accepts the following arguments:
sensitive_fields
Either an arrayref of sensitive fields, checked for equality, or a
regex to test against each key to see if it's considered sensitive.
replacement
The string to replace each value with. Any censoring callback
provided in `replacement_callbacks' which matches this key will take
precedence over this straightforward value.
replacement_callbacks
A hashref of key => sub {...}, where each key is a column name to
match, and the coderef takes the uncensored value and returns the
censored value, letting you for instance mask a card number but
leave the last 4 digits visible.
If you provide both `replacement' and `replacement_callbacks', any
callback defined which matches the key being considered takes
precedence. =back
METHODS
censor
Given a data structure (hashref), clones it and returns the cloned
version after censoring potentially sensitive data within.
clone_and_censor
Clones the provided hashref (using Clone - will die if not
lib/Data/Censor.pm view on Meta::CPAN
# OO way, letting you specify your own list of sensitive-looking fields, and
# what they should be replaced by (all options here are optional)
my $censor = Data::Censor->new(
# Specify which fields to censor:
sensitive_fields => [ qw(card_number password) ],
# Specify text to replace their values with:
replacement => '(Sensitive data hidden)',
# Or specify callbacks for each field name which return the "censored"
# value - in this case, masking a card number (PAN) to show only the
# last four digits:
replacement_callbacks => {
card_number => sub {
my $pan = shift;
return "x" x (length($pan) - 4) . substr($pan, -4, 4);
},
},
);
# Censor the data in-place (changes the data structure, returns the number
# of keys censored)
my $censor_count = $censor->censor(\%data);
lib/Data/Censor.pm view on Meta::CPAN
=over
=item sensitive_fields
Either an arrayref of sensitive fields, checked for equality, or a regex to test
against each key to see if it's considered sensitive.
=item replacement
The string to replace each value with. Any censoring callback provided in
C<replacement_callbacks> which matches this key will take precedence over this
straightforward value.
=item replacement_callbacks
A hashref of key => sub {...}, where each key is a column name to match, and the
coderef takes the uncensored value and returns the censored value, letting you
for instance mask a card number but leave the last 4 digits visible.
If you provide both C<replacement> and C<replacement_callbacks>, any callback
defined which matches the key being considered takes precedence.
=back
=cut
sub new {
my $class = shift;
my %args = @_;
lib/Data/Censor.pm view on Meta::CPAN
} else {
$self->{is_sensitive_field} = {
map { $_ => 1 } qw(
pass password old_password secret
private_key cardnum card_number pan
cvv cvv2 ccv
)
};
}
if ( is_hashref $args{replacement_callbacks} ) {
$self->{replacement_callbacks} = $args{replacement_callbacks};
}
if ( exists $args{replacement} ) {
$self->{replacement} = $args{replacement};
} else {
$self->{replacement} = 'Hidden (looks potentially sensitive)';
}
$self->{recurse_limit} = $args{recurse_limit} || 100;
return $self;
lib/Data/Censor.pm view on Meta::CPAN
unless $visited->{ $data->{$key} }++;
next;
}
next unless
( $self->{is_sensitive_field}
&& $self->{is_sensitive_field}{ lc $key } )
or ( $self->{censor_regex} && $key =~ $self->{censor_regex} );
# OK, censor this
if ( $self->{replacement_callbacks}{ lc $key } ) {
$data->{$key} = $self->{replacement_callbacks}{ lc $key }->(
$data->{$key}
);
$censored++;
} else {
$data->{$key} = $self->{replacement};
$censored++;
}
}
return $censored;
t/01-basic.t view on Meta::CPAN
my $count = $censor->censor($data);
my $hidden = 'Hidden (looks potentially sensitive)';
is($count, 3, "Two items censored with default config");
is($data->{password}, $hidden, 'password field censored');
is($data->{email}, 'davidp@preshweb.co.uk', 'email field not censored');
is($data->{card}{pan}, $hidden, 'pan field censored (recursion works)');
is($data->{card}{expiry}, '03/16', 'expiry field not censored');
# Test replacement callback
$censor = Data::Censor->new(
replacement_callbacks => {
pan => sub {
my $pan = shift;
return "x" x (length($pan) - 4)
. substr($pan, -4, 4);
},
},
);
$data = get_data();
$count = $censor->censor($data);
is($data->{password}, $hidden, "password censored normally");
( run in 2.186 seconds using v1.01-cache-2.11-cpan-9b1e4054eb1 )