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   1542 use 5.006;
  2         8  
4 2     2   12 use strict;
  2         5  
  2         40  
5 2     2   11 use warnings;
  2         4  
  2         58  
6 2     2   1035 use Readonly;
  2         7949  
  2         115  
7              
8 2     2   1194 use Perl::Critic::Utils qw{ :booleans :severities };
  2         242779  
  2         41  
9              
10 2     2   1561 use base 'Perl::Critic::Policy';
  2         5  
  2         1270  
11              
12             our $VERSION = '2.01'; # 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              
19             #-----------------------------------------------------------------------------
20              
21 3     3 1 42 sub default_severity { return $SEVERITY_MEDIUM }
22 0     0 1 0 sub default_themes { return qw< security > }
23              
24             sub applies_to {
25 13     13 1 102277 return qw<
26             PPI::Token::Regexp::Match
27             PPI::Token::Regexp::Substitute
28             PPI::Token::QuoteLike::Regexp
29             PPI::Statement::Include
30             >;
31             }
32              
33             #-----------------------------------------------------------------------------
34              
35             sub violates {
36 19     19 1 826 my ( $self, $elem, $doc ) = @_;
37              
38 19 50       59 if ( $self->_pragma_enabled($elem) ) {
39 0         0 return; # ok!;
40             }
41              
42 19 100       65 my $re = $doc->ppix_regexp_from_element($elem)
43             or return;
44              
45 13 100       46200 if ( not $self->_allowed_modifier($re)) {
46 3         33 return $self->violation( $DESC, $EXPL, $elem );
47             }
48              
49 10         32 return; # ok!;
50             }
51              
52             sub _allowed_modifier {
53 13     13   40 my ( $self, $re ) = @_;
54              
55 13 100 100     50 if ( $re->modifier_asserted('a') and not $self->{_strict} ) {
56 4         98 return $TRUE;
57             }
58              
59 9 100       195 if ( $re->modifier_asserted('aa') ) {
60 6         102 return $TRUE;
61             }
62              
63 3         52 return $FALSE;
64             }
65              
66              
67             sub _correct_modifier {
68 6     6   398 my ( $self, $elem ) = @_;
69              
70 6 50 33     25 if ( $elem->arguments eq 'a' and not $self->{_strict} ) {
71 0         0 return $TRUE;
72             }
73              
74 6 50       246 if ( $elem->arguments eq 'aa' ) {
75 0         0 return $TRUE;
76             }
77              
78 6         192 return $FALSE;
79             }
80              
81             sub _pragma_enabled {
82 19     19   49 my ( $self, $elem ) = @_;
83              
84 19 50 66     127 if ( $elem->can('type')
      66        
      33        
85             and $elem->type() eq 'use'
86             and $elem->pragma() eq 're'
87             and $self->_correct_modifier($elem) )
88             {
89 0         0 return $TRUE;
90             }
91              
92 19         63 return $FALSE;
93             }
94              
95             sub initialize_if_enabled {
96 3     3 1 2005973 my ( $self, $config ) = @_;
97              
98 3   100     19 $self->{_strict} = $config->get('strict') || 0;
99              
100 3         47 return $TRUE;
101             }
102              
103             1;
104              
105             __END__
106              
107             =pod
108              
109             =head1 NAME
110              
111             Perl::Critic::Policy::RegularExpressions::RequireDefault - Always use the C</a> or C</aa> modifier with regular expressions.
112              
113             =head1 VERSION
114              
115             This documentation describes version 2.00
116              
117             =head1 AFFILIATION
118              
119             This policy has no affiliation
120              
121             =head1 DESCRIPTION
122              
123             This policy aims to help enforce Perl's protective measures against security vulnerabilities related to Unicode, such as:
124              
125             =over
126              
127             =item * Visual Spoofing
128              
129             =item * Character and String Transformation Vulnerabilities
130              
131             =back
132              
133             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.
134              
135             C</a> causes the sequences C<\d>, C<\s>, C<\w>, and the Posix character classes to match only in the ASCII range. Meaning:
136              
137             =over
138              
139             =item * C<\d> means the digits C<0> to C<9>
140              
141             my $ascii_letters =~ m/[A-Z]*/i; # not ok
142             my $ascii_letters =~ m/[A-Z]*/a; # ok
143             my $ascii_letters =~ m/[A-Z]*/aa; # ok
144              
145             =item * C<\s> means the five characters C<[ \f\n\r\t]>, and starting in Perl v5.18, also the vertical tab
146              
147             my $characters =~ m/[ \f\n\r\t]*/; # not ok
148             my $characters =~ m/[ \f\n\r\t]*/a; # ok
149             my $characters =~ m/[ \f\n\r\t]*/aa; # ok
150              
151             =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
152              
153             my $letters =~ m/[A-Za-z0-9_]*/; # not ok
154             my $letters =~ m/[A-Za-z0-9_]*/a; # ok
155             my $letters =~ m/[A-Za-z0-9_]*/aa; # ok
156              
157             =back
158              
159             The policy also supports the pragma:
160              
161             use re '/a';
162              
163             and:
164              
165             use re '/aa';
166              
167             Which mean it will not evaluate the regular expressions any further:
168              
169             use re '/a';
170             my $letters =~ m/[A-Za-z0-9_]*/; # ok
171              
172             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.
173              
174             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.
175              
176             =head1 CONFIGURATION AND ENVIRONMENT
177              
178             The policy has a single configuration parameter: C<strict>. The default is disabled (C<0>).
179              
180             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.
181              
182             Example configuration:
183              
184             [RegularExpressions::RequireDefault]
185             strict = 1
186              
187             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.
188              
189             =head1 INCOMPATIBILITIES
190              
191             This distribution holds no known incompatibilities at this time, please see L</DEPENDENCIES AND REQUIREMENTS> for details on version requirements.
192              
193             =head1 BUGS AND LIMITATIONS
194              
195             =over
196              
197             =item * The pragma handling does not take into consideration of a pragma is disabled.
198              
199             =item * The pragma handling does not take lexical scope into consideration properly and only detects the definition once
200              
201             =back
202              
203             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.
204              
205             =head1 BUG REPORTING
206              
207             Please report bugs via L<GitHub|https://github.com/jonasbn/perl-critic-policy-regularexpressions-requiredefault/issues>.
208              
209             =head1 TEST AND QUALITY
210              
211             This distribution aims to adhere to the Perl::Critic::Policy standards and Perl best practices and recommendations.
212              
213             =head1 DEPENDENCIES AND REQUIREMENTS
214              
215             This distribution requires:
216              
217             =over
218              
219             =item * Perl 5.6.0 syntactially for the actual implementation
220              
221             =item * L<Perl 5.14|https://metacpan.org/pod/release/JESSE/perl-5.14.0/pod/perl.pod> for developing the distribution, which relies on L<Dist::Zilla|http://dzil.org/>. The features on which this policy relies, where introduced in Perl 5.14, but this does not make for an actual requirement for the policy only the recommendations it imposes.
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>, part of Perl::Critic
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             )item * L<Perl Pragma Documentation|https://perldoc.perl.org/re.html>
264              
265             =back
266              
267             =head1 MOTIVATION
268              
269             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>
270              
271             | Perl folk: Looking for a PR challenge task? Check for \d in regexes
272             | that really should be [0-9] or should have the /a regex modifier.
273             | Perl is multinational by default! #TPCiSLC
274              
275             =head1 AUTHOR
276              
277             =over
278              
279             =item * jonasbn <jonasbn@cpan.org>
280              
281             =back
282              
283             =head1 ACKNOWLEDGEMENTS
284              
285             =over
286              
287             =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>
288              
289             =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
290              
291             =back
292              
293             =head1 LICENSE AND COPYRIGHT
294              
295             Perl::Critic::Policy::RegularExpressions::RequireDefault is (C) by jonasbn 2018-2019
296              
297             Perl::Critic::Policy::RegularExpressions::RequireDefault is released under the Artistic License 2.0
298              
299             Please see the LICENSE file included with the distribution of this module
300              
301             =cut