File Coverage

blib/lib/Perl/Critic/Policy/RegularExpressions/RequireDefault.pm
Criterion Covered Total %
statement 41 46 89.1
branch 12 16 75.0
condition 11 17 64.7
subroutine 13 14 92.8
pod 5 5 100.0
total 82 98 83.6


line stmt bran cond sub pod time code
1             package Perl::Critic::Policy::RegularExpressions::RequireDefault;
2              
3 2     2   1652 use 5.006001;
  2         9  
4 2     2   11 use strict;
  2         4  
  2         43  
5 2     2   14 use warnings;
  2         4  
  2         61  
6 2     2   1080 use Readonly;
  2         8034  
  2         118  
7              
8 2     2   1395 use Perl::Critic::Utils qw{ :severities };
  2         247105  
  2         42  
9              
10 2     2   1610 use base 'Perl::Critic::Policy';
  2         5  
  2         1255  
11              
12             our $VERSION = '2.00'; # VERSION: generated by DZP::OurPkgVersion
13              
14             #-----------------------------------------------------------------------------
15              
16             Readonly::Scalar my $DESC => q{Regular expression without "/a" or "/aa" flag};
17             Readonly::Scalar my $EXPL => q{Use regular expression "/a" or "/aa" flag};
18             Readonly::Scalar my $TRUE => 1;
19             Readonly::Scalar my $FALSE => 0;
20              
21             #-----------------------------------------------------------------------------
22              
23 3     3 1 47 sub default_severity { return $SEVERITY_MEDIUM }
24 0     0 1 0 sub default_themes { return qw< security > }
25              
26             sub applies_to {
27 13     13 1 106031 return qw<
28             PPI::Token::Regexp::Match
29             PPI::Token::Regexp::Substitute
30             PPI::Token::QuoteLike::Regexp
31             PPI::Statement::Include
32             >;
33             }
34              
35             #-----------------------------------------------------------------------------
36              
37             sub violates {
38 19     19 1 871 my ( $self, $elem, $doc ) = @_;
39              
40 19 50       58 if ( $self->_pragma_enabled($elem) ) {
41 0         0 return; # ok!;
42             }
43              
44 19 100       67 my $re = $doc->ppix_regexp_from_element($elem)
45             or return;
46              
47 13 100       47831 if ( not $self->_allowed_modifier($re)) {
48 3         19 return $self->violation( $DESC, $EXPL, $elem );
49             }
50              
51 10         34 return; # ok!;
52             }
53              
54             sub _allowed_modifier {
55 13     13   40 my ( $self, $re ) = @_;
56              
57 13 100 100     51 if ( $re->modifier_asserted('a') and not $self->{_strict} ) {
58 4         102 return $TRUE;
59             }
60              
61 9 100       229 if ( $re->modifier_asserted('aa') ) {
62 6         116 return $TRUE;
63             }
64              
65 3         56 return $FALSE;
66             }
67              
68              
69             sub _correct_modifier {
70 6     6   404 my ( $self, $elem ) = @_;
71              
72 6 50 33     23 if ( $elem->arguments eq 'a' and not $self->{_strict} ) {
73 0         0 return $TRUE;
74             }
75              
76 6 50       238 if ( $elem->arguments eq 'aa' ) {
77 0         0 return $TRUE;
78             }
79              
80 6         211 return $FALSE;
81             }
82              
83             sub _pragma_enabled {
84 19     19   44 my ( $self, $elem ) = @_;
85              
86 19 50 66     135 if ( $elem->can('type')
      66        
      33        
87             and $elem->type() eq 'use'
88             and $elem->pragma() eq 're'
89             and $self->_correct_modifier($elem) )
90             {
91 0         0 return $TRUE;
92             }
93              
94 19         64 return $FALSE;
95             }
96              
97             sub initialize_if_enabled {
98 3     3 1 2062198 my ( $self, $config ) = @_;
99              
100 3   100     16 $self->{_strict} = $config->get('strict') || 0;
101              
102 3         48 return $TRUE;
103             }
104              
105             1;
106              
107             __END__
108              
109             =pod
110              
111             =head1 NAME
112              
113             Perl::Critic::Policy::RegularExpressions::RequireDefault - Always use the C</a> or C</aa> modifier with regular expressions.
114              
115             =head1 VERSION
116              
117             This documentation describes version 2.00
118              
119             =head1 AFFILIATION
120              
121             This policy has no affiliation
122              
123             =head1 DESCRIPTION
124              
125             This policy aims to help enforce Perl's protective measures against security vulnerabilities related to Unicode, such as:
126              
127             =over
128              
129             =item * Visual Spoofing
130              
131             =item * Character and String Transformation Vulnerabilities
132              
133             =back
134              
135             The C</a> and C</aa> modifiers standing for ASCII-restrict or ASCII-safe, provides protection for applications that do not need to be exposed to all of Unicode and possible security issues with Unicode.
136              
137             C</a> causes the sequences C<\d>, C<\s>, C<\w>, and the Posix character classes to match only in the ASCII range. Meaning:
138              
139             =over
140              
141             =item * C<\d> means the digits C<0> to C<9>
142              
143             my $ascii_letters =~ m/[A-Z]*/i; # not ok
144             my $ascii_letters =~ m/[A-Z]*/a; # ok
145             my $ascii_letters =~ m/[A-Z]*/aa; # ok
146              
147             =item * C<\s> means the five characters C<[ \f\n\r\t]>, and starting in Perl v5.18, also the vertical tab
148              
149             my $characters =~ m/[ \f\n\r\t]*/; # not ok
150             my $characters =~ m/[ \f\n\r\t]*/a; # ok
151             my $characters =~ m/[ \f\n\r\t]*/aa; # ok
152              
153             =item * C<\w> means the 63 characters C<[A-Za-z0-9_]> and all the Posix classes such as C<[[:print:]]> match only the appropriate ASCII-range characters
154              
155             my $letters =~ m/[A-Za-z0-9_]*/; # not ok
156             my $letters =~ m/[A-Za-z0-9_]*/a; # ok
157             my $letters =~ m/[A-Za-z0-9_]*/aa; # ok
158              
159             =back
160              
161             The policy also supports the pragma:
162              
163             use re 'a';
164              
165             and:
166              
167             use re 'aa';
168              
169             Which mean it will not evaluate the regular expressions any further:
170              
171             use re 'a';
172             my $letters =~ m/[A-Za-z0-9_]*/; # ok
173              
174             Do note that the C</a> and C</aa> modifiers require Perl 5.14, so by using the recommended modifiers you indirectly introduct a requirement for Perl 5.14.
175              
176             This policy is inspired by L<Perl::Critic::Policy::RegularExpressions::RequireExtendedFormatting|https://metacpan.org/pod/Perl::Critic::Policy::RegularExpressions::RequireExtendedFormatting> and many implementation details was lifted from this particular distribution.
177              
178             =head1 CONFIGURATION AND ENVIRONMENT
179              
180             The policy has a single configuration parameter: C<strict>. The default is disabled (C<0>).
181              
182             The policy, if enabled, allow for both C<'a'> and C<'aa'>, if strict however is enabled, C<'a'> will trigger a violation and C<'aa'> will not.
183              
184             Example configuration:
185              
186             [RegularExpressions::RequireDefault]
187             strict = 1
188              
189             Do note that the policy also evaluates if the pragmas are enabled, meaning: C<use re 'a';> will trigger a violation and C<use re 'a';> will not if the policy is configured for strict evaluation.
190              
191             =head1 INCOMPATIBILITIES
192              
193             This distribution holds no known incompatibilities at this time, please see L</DEPENDENCIES AND REQUIREMENTS> for details on version requirements.
194              
195             =head1 BUGS AND LIMITATIONS
196              
197             =over
198              
199             =item * The pragma handling does not take into consideration of a pragma is disabled.
200              
201             =item * The pragma handling does not take lexical scope into consideration properly and only detects the definition once
202              
203             =back
204              
205             This distribution holds no other known limitations or bugs at this time, please refer to the L<the issue listing on GitHub|https://github.com/jonasbn/perl-critic-policy-regularexpressions-requiredefault/issues> for more up to date information.
206              
207             =head1 BUG REPORTING
208              
209             Please report bugs via L<GitHub|https://github.com/jonasbn/perl-critic-policy-regularexpressions-requiredefault/issues>.
210              
211             =head1 TEST AND QUALITY
212              
213             This distribution aims to adhere to the Perl::Critic::Policy standards and Perl best practices and recommendations.
214              
215             =head1 DEPENDENCIES AND REQUIREMENTS
216              
217             This distribution requires:
218              
219             =over
220              
221             =item * L<Perl 5.14|https://metacpan.org/pod/release/JESSE/perl-5.14.0/pod/perl.pod>, released 2011-05-14
222              
223             =item * L<Carp|https://metacpan.org/pod/Carp>, in core since Perl 5.
224              
225             =item * L<Readonly|https://metacpan.org/pod/Readonly>
226              
227             =item * L<Perl::Critic::Policy|https://metacpan.org/pod/Perl::Critic::Policy>
228              
229             =item * L<Perl::Critic::Utils|https://metacpan.org/pod/Perl::Critic::Utils>
230              
231             =back
232              
233             Please see the listing in the file: F<cpanfile>, included with the distribution for a complete listing and description for configuration, test and development.
234              
235             =head1 TODO
236              
237             Ideas and suggestions for improvements and new features are listed in GitHub and are marked as C<enhancement>.
238              
239             =over
240              
241             =item * Please see L<the issue listing on GitHub|https://github.com/jonasbn/perl-critic-policy-regularexpressions-requiredefault/issues>
242              
243             =back
244              
245             =head1 SEE ALSO
246              
247             =over
248              
249             =item * L<Perl regular expression documentation: perlre|https://perldoc.perl.org/perlre.html>
250              
251             =item * L<Perl delta file describing introduction of modifiers in Perl 5.14|https://perldoc.pl/perl5140delta#%2Fd%2C-%2Fl%2C-%2Fu%2C-and-%2Fa-modifiers>
252              
253             =item * L<Unicode Security Issues FAQ|http://www.unicode.org/faq/security.html>
254              
255             =item * L<Unicode Security Guide|http://websec.github.io/unicode-security-guide/>
256              
257             =item * L<Presentation: "Unicode Transformations: Finding Elusive Vulnerabilities" by Chris Weber for OWASP AppSecDC November 2009|https://www.owasp.org/images/5/5a/Unicode_Transformations_Finding_Elusive_Vulnerabilities-Chris_Weber.pdf|>
258              
259             =item * L<Perl::Critic|https://metacpan.org/pod/Perl::Critic>
260              
261             =item * L<Perl::Critic::Policy::RegularExpressions::RequireExtendedFormatting|https://metacpan.org/pod/Perl::Critic::Policy::RegularExpressions::RequireExtendedFormatting>
262              
263             =back
264              
265             =head1 MOTIVATION
266              
267             The motivation for this Perl::Critic policy came from a L<tweet|https://twitter.com/jmaslak/status/1008896883169751040> by L<@joel|https://twitter.com/jmaslak>
268              
269             | Perl folk: Looking for a PR challenge task? Check for \d in regexes
270             | that really should be [0-9] or should have the /a regex modifier.
271             | Perl is multinational by default! #TPCiSLC
272              
273             =head1 AUTHOR
274              
275             =over
276              
277             =item * jonasbn <jonasbn@cpan.org>
278              
279             =back
280              
281             =head1 ACKNOWLEDGEMENTS
282              
283             =over
284              
285             =item * L<Joelle Maslak (@joel)|https://twitter.com/jmaslak> / L<JMASLAK|https://metacpan.org/author/JMASLAK> for the initial idea, see link to original tweet under L</MOTIVATION>
286              
287             =item * L<Dan Book (@Grinnz)|https://github.com/Grinnz> / L<DBOOK|https://metacpan.org/author/DBOOK|> for information on Pragma and requirement for Perl 5.14, when using the modifiers handled and mentioned by this policy
288              
289             =back
290              
291             =head1 LICENSE AND COPYRIGHT
292              
293             Perl::Critic::Policy::RegularExpressions::RequireDefault is (C) by jonasbn 2018-2019
294              
295             Perl::Critic::Policy::RegularExpressions::RequireDefault is released under the Artistic License 2.0
296              
297             Please see the LICENSE file included with the distribution of this module
298              
299             =cut