File Coverage

blib/lib/Perl/Critic/Policy/Perlsecret.pm
Criterion Covered Total %
statement 104 104 100.0
branch 81 92 88.0
condition 43 60 71.6
subroutine 32 32 100.0
pod 4 6 66.6
total 264 294 89.8


line stmt bran cond sub pod time code
1             package Perl::Critic::Policy::Perlsecret;
2             # ABSTRACT: Prevent perlsecrets entering your codebase
3              
4 4     4   187560 use 5.006001;
  4         10  
5 4     4   13 use strict;
  4         4  
  4         60  
6 4     4   11 use warnings;
  4         9  
  4         85  
7              
8 4     4   1141 use parent 'Perl::Critic::Policy';
  4         646  
  4         17  
9              
10 4     4   395088 use Carp;
  4         7  
  4         163  
11 4     4   18 use Perl::Critic::Utils;
  4         4  
  4         40  
12 4     4   1907 use List::Util 'first';
  4         5  
  4         4562  
13              
14             our $VERSION = '0.0.11';
15              
16             Readonly::Scalar my $DESCRIPTION => 'Perlsecret risk.';
17             Readonly::Scalar my $EXPLANATION => 'Perlsecret detected.';
18              
19             # Eskimo Greeting skipped as only used in one liners
20             Readonly::Hash my %default_violations => (
21             'Venus' => \&_venus,
22             'Baby Cart' => \&_baby_cart,
23             'Bang Bang' => \&_bang_bang,
24             'Inchworm' => \&_inchworm,
25             'Inchworm on a Stick' => \&_inchworm_on_a_stick,
26             'Space Station' => \&_space_station,
27             'Goatse' => \&_goatse,
28             'Flaming X-Wing' => \&_flaming_x_wing,
29             'Kite' => \&_kite,
30             'Ornate Double Edged Sword' => \&_ornate_double_edged_sword,
31             'Flathead' => \&_flathead,
32             'Phillips' => \&_phillips,
33             'Torx' => \&_torx,
34             'Pozidriv' => \&_pozidriv,
35             'Winking Fat Comma' => \&_winking_fat_comma,
36             'Enterprise' => \&_enterprise,
37             'Key of Truth' => \&_key_of_truth,
38             'Abbott and Costello' => \&_abbott_and_costello,
39             );
40              
41             sub default_severity {
42 38     38 1 246 return $Perl::Critic::Utils::SEVERITY_HIGHEST;
43             }
44              
45             sub default_themes {
46 1     1 1 92670 return qw( perlsecret );
47             }
48              
49             sub applies_to {
50 23     23 1 117527 return qw(
51             PPI::Statement
52             );
53             }
54              
55             sub supported_parameters {
56             return (
57 25     25 0 792015 { name => 'allow_secrets',
58             description => q<A list of perlsecrets to allow.>,
59             default_string => '',
60             },
61              
62             { name => 'disallow_secrets',
63             description =>
64             q<A list of perlsecrets to disallow (default: all perlsecrets).>,
65             default_string =>
66             'Venus, Baby Cart, Bang Bang, Inchworm, Inchworm on a Stick, '
67             . 'Space Station, Goatse, Flaming X-Wing, Kite, '
68             . 'Ornate Double Edged Sword, Flathead, Phillips, Torx, '
69             . 'Pozidriv, Winking Fat Comma, Enterprise, Key of Truth, '
70             . 'Abbott and Costello',
71             },
72             );
73             }
74              
75             my $SPLIT_RE = qr/\s*,\s*/;
76              
77             sub read_config_list {
78 186     186 0 159 my ( $self, $str ) = @_;
79              
80             my @values = map {
81 186         1604 ( my $new = $_ ) =~ s/^\s+|\s+$//;
  1552         2952  
82 1552         1681 $new;
83             } split $SPLIT_RE, $str;
84              
85 186         491 return @values;
86             }
87              
88             sub violates {
89 93     93 1 4047 my ( $self, $element, $doc ) = @_;
90              
91 93         146 my @disallowed = $self->read_config_list( $self->{'_disallow_secrets'} );
92              
93             @disallowed
94 93 50       187 or @disallowed = keys %default_violations;
95              
96 93         130 my @allowed = $self->read_config_list( $self->{'_allow_secrets'} );
97              
98 93         76 my %violations;
99 93         96 foreach my $secret (@disallowed) {
100 1545 100       6103 if ( !exists $default_violations{$secret} ) {
101 1         192 croak("$secret is not a known secret");
102             }
103              
104 14     14   32 first { $secret eq $_ } @allowed
105 1544 100       7808 and next;
106              
107 1537         3109 $violations{$secret} = $default_violations{$secret};
108             }
109              
110 92         480 for my $policy ( keys %violations ) {
111 1219 100       5305 if ( $violations{$policy}->($element) ) {
112 38         1496 return $self->violation( $DESCRIPTION . " $policy ",
113             $EXPLANATION, $element );
114             }
115             }
116              
117 54         301 return; # No matches return i.e. no violations
118             }
119              
120             sub _venus {
121 67     67   105 for my $child ( $_[0]->children ) {
122 342 100       1136 next unless ref($child) eq 'PPI::Token::Operator';
123              
124 68 100       76 next unless $child eq '+';
125              
126 4 100       34 return 1 if $child->previous_sibling eq '0';
127 3 100       64 return 1 if $child->next_sibling eq '0';
128             }
129             }
130              
131             sub _baby_cart {
132 79     79   116 for my $child ( $_[0]->children ) {
133 440 100       685 if ( ref($child) eq 'PPI::Token::Cast' ) {
134 3 50       8 return 1 if $child->snext_sibling =~ m/\{\s*?\[/;
135             }
136 437 100       643 if ( ref($child) eq 'PPI::Token::Quote::Double' ) {
137 3 100       6 return 1 if $child =~ m/@\{\s*?\[/;
138             }
139              
140             }
141             }
142              
143             sub _bang_bang {
144 71     71   102 for my $child ( $_[0]->children ) {
145 356 100       1205 next unless ref($child) eq 'PPI::Token::Operator';
146 75 100 100     86 return 1 if $child eq '!' && $child->snext_sibling eq '!';
147             }
148             }
149              
150             sub _inchworm {
151 64     64   91 for my $child ( $_[0]->children ) {
152 326 100       1010 next unless ref($child) eq 'PPI::Token::Operator';
153 67 100       77 return 1 if $child eq '~~';
154 66 100 100     496 return 1 if $child eq '~' && $child->snext_sibling eq '~';
155             }
156             }
157              
158             sub _inchworm_on_a_stick {
159 66     66   99 for my $child ( $_[0]->children ) {
160 353 100       1100 next unless ref($child) eq 'PPI::Token::Operator';
161              
162 74 100 66     83 return 1 if $child eq '~' && $child->snext_sibling eq '-';
163 73 100 66     542 return 1 if $child eq '-' && $child->snext_sibling eq '~';
164             }
165             }
166              
167             sub _space_station {
168 68     68   95 for my $child ( $_[0]->children ) {
169 386 100       1315 next unless ref($child) eq 'PPI::Token::Operator';
170              
171 82 100 100     104 return 1
      66        
172             if $child eq '-'
173             && $child->snext_sibling eq '+'
174             && $child->snext_sibling->snext_sibling eq '-';
175             }
176             }
177              
178             sub _goatse {
179 65     65   103 for my $child ( $_[0]->children ) {
180 326 100       994 next unless ref($child) eq 'PPI::Structure::List';
181 15 100 100     28 return 1
182             if $child->sprevious_sibling eq '='
183             && $child->snext_sibling eq '=';
184             }
185             }
186              
187             sub _flaming_x_wing {
188 68     68   102 for my $child ( $_[0]->children ) {
189              
190 381 100       691 next unless ref($child) eq 'PPI::Token::QuoteLike::Readline';
191 2 50 33     8 return 1
192             if $child->sprevious_sibling eq '='
193             && $child->snext_sibling eq '=~';
194             }
195             }
196              
197             sub _kite {
198 67     67   102 for my $child ( $_[0]->children ) {
199 378 100       1236 next unless ref($child) eq 'PPI::Token::Operator';
200 87 100 66     96 return 1
201             if $child eq '~~'
202             && $child->snext_sibling eq '<>';
203             }
204             }
205              
206             sub _ornate_double_edged_sword {
207 70     70   98 for my $child ( $_[0]->children ) {
208 403 100       3490 next unless $child eq '<<m';
209 1 50 33     12 return 1
210             if $child->snext_sibling eq '=~'
211             && $child->snext_sibling->snext_sibling eq 'm>>';
212             }
213             }
214              
215             sub _flathead {
216 71     71   98 for my $child ( $_[0]->children ) {
217 401 100       3403 next unless $child eq '-=';
218 2 50       16 return 1 if $child->snext_sibling eq '!';
219             }
220             }
221              
222             sub _phillips {
223 68     68   95 for my $child ( $_[0]->children ) {
224 390 100       3367 next unless $child eq '+=';
225 1 50       8 return 1 if $child->snext_sibling eq '!';
226             }
227             }
228              
229             sub _torx {
230 72     72   104 for my $child ( $_[0]->children ) {
231 414 100       3562 next unless $child eq '*=';
232 1 50       9 return 1 if $child->snext_sibling eq '!';
233             }
234             }
235              
236             sub _pozidriv {
237 62     62   89 for my $child ( $_[0]->children ) {
238 316 100       2778 next unless $child eq 'x=';
239 1 50       9 return 1 if $child->snext_sibling eq '!';
240             }
241             }
242              
243             sub _winking_fat_comma {
244 68     68   104 for my $child ( $_[0]->children ) {
245             next
246 390 100 100     1724 unless ref($child) eq 'PPI::Token::Operator'
247             && $child eq ',';
248 16 100       139 return 1 if $child->snext_sibling eq '=>';
249             }
250             }
251              
252             sub _enterprise {
253 61     61   91 for my $child ( $_[0]->children ) {
254 313 100       1207 next unless $child->class eq 'PPI::Structure::List';
255 13 0 33     41 return 1
      33        
256             if $child->snext_sibling eq 'x'
257             && $child->snext_sibling->snext_sibling eq '!'
258             && $child->snext_sibling->snext_sibling->snext_sibling eq '!';
259             }
260             }
261              
262             sub _key_of_truth {
263 68     68   97 for my $child ( $_[0]->children ) {
264 406 100       1253 next unless $child->class eq 'PPI::Token::Number';
265 8 50 100     22 return 1
      66        
      66        
266             if $child eq '0'
267             && $child->snext_sibling eq '+'
268             && $child->snext_sibling->snext_sibling eq '!'
269             && $child->snext_sibling->snext_sibling->snext_sibling eq '!';
270             }
271             }
272              
273             sub _abbott_and_costello {
274 64     64   88 for my $child ( $_[0]->children ) {
275 344 100       1692 next unless ref($child) eq 'PPI::Token::Operator';
276              
277 70 100 100     79 return 1
      66        
      66        
      66        
278             if ( $child eq '||' || $child eq '//' )
279             && $child->snext_sibling->class eq 'PPI::Structure::List'
280             && ( $child->snext_sibling->content eq '()'
281             || $child->snext_sibling->content eq '( )' );
282              
283             }
284             }
285              
286             1;
287              
288             __END__
289              
290             =pod
291              
292             =encoding UTF-8
293              
294             =head1 NAME
295              
296             Perl::Critic::Policy::Perlsecret - Prevent perlsecrets entering your codebase
297              
298             =head1 VERSION
299              
300             version 0.0.11
301              
302             =head1 SYNOPSIS
303              
304             # in your .perlcriticrc
305             [Perlsecret]
306              
307             # overriding things
308             [Perlsecret]
309             allow_secrets = Bang Bang, Venus
310              
311             =head1 DESCRIPTION
312              
313             This policy checks for L<perlsecret> operators in your code and warns you
314             about them.
315              
316             You can override the secrets that are allowed or disallowed using the
317             parameters C<allow_secrets> and C<disallow_secrets>. The default is to
318             simply disallow everything.
319              
320             Notice the secrets are capitalized correctly ("Ornate Double-Bladed Sword",
321             not "Ornate double-bladed sword").
322              
323             [Perlsecret]
324             disallow_secrets = Flathead, Phillips, Pozidriv, Torx, Enterprise
325              
326             This provides the list to disallow.
327              
328             [Perlsecret]
329             allow_secrets = Bang Bang
330              
331             You can provide both, in which case it will start with the disallow list
332             you provided as the default and then allow everything in the allow list.
333             (There isn't much value to provide both of these.)
334              
335             =head1 NAME
336              
337             Perl::Critic::Policy::Perlsecret - Prevent perlsecrets entering your codebase
338              
339             =head1 VERSION
340              
341             version 0.0.11
342              
343             =head1 AUTHOR
344              
345             Lance Wicks <lancew@cpan.org>
346              
347             =head1 COPYRIGHT AND LICENSE
348              
349             This software is Copyright (c) 2017 by Lance Wicks.
350              
351             This is free software, licensed under:
352              
353             The GNU General Public License, Version 3, June 2007
354              
355             =cut