File Coverage

blib/lib/Perl/Critic/Policy/Modules/ProhibitEvilModules.pm
Criterion Covered Total %
statement 57 87 65.5
branch 10 30 33.3
condition 7 16 43.7
subroutine 17 18 94.4
pod 5 6 83.3
total 96 157 61.1


line stmt bran cond sub pod time code
1             package Perl::Critic::Policy::Modules::ProhibitEvilModules;
2              
3 40     40   26522 use 5.010001;
  40         160  
4 40     40   165 use strict;
  40         115  
  40         791  
5 40     40   137 use warnings;
  40         93  
  40         1757  
6              
7 40     40   167 use English qw(-no_match_vars);
  40         66  
  40         302  
8 40     40   14619 use Readonly;
  40         71  
  40         1915  
9              
10             use Perl::Critic::Exception::Configuration::Option::Policy::ParameterValue
11 40     40   1473 qw{ throw_policy_value };
  40         268  
  40         2660  
12 40     40   220 use Perl::Critic::Utils qw( :characters :severities :data_conversion );
  40         272  
  40         1785  
13              
14 40     40   10636 use parent 'Perl::Critic::Policy';
  40         80  
  40         242  
15              
16             our $VERSION = '1.156';
17              
18             #-----------------------------------------------------------------------------
19              
20             Readonly::Scalar my $EXPL => q{Find an alternative module};
21              
22             Readonly::Scalar my $MODULE_NAME_REGEX =>
23             qr<
24             \b
25             [[:alpha:]_]
26             (?:
27             (?: \w | :: )*
28             \w
29             )?
30             \b
31             >xms;
32             Readonly::Scalar my $REGULAR_EXPRESSION_REGEX => qr< [/] ( [^/]+ ) [/] >xms;
33             Readonly::Scalar my $DESCRIPTION_REGEX => qr< [{] ( [^}]+ ) [}] >xms;
34              
35             # It's kind of unfortunate that I had to put capturing parentheses in the
36             # component regexes above, because they're not visible here and so make
37             # figuring out the positions of captures hard. Too bad we can't make the
38             # minimum perl version 5.10. :]
39             Readonly::Scalar my $MODULES_REGEX =>
40             qr<
41             \A
42             \s*
43             (?:
44             ( $MODULE_NAME_REGEX )
45             | $REGULAR_EXPRESSION_REGEX
46             )
47             (?: \s* $DESCRIPTION_REGEX )?
48             \s*
49             >xms;
50              
51             Readonly::Scalar my $MODULES_FILE_LINE_REGEX =>
52             qr<
53             \A
54             \s*
55             (?:
56             ( $MODULE_NAME_REGEX )
57             | $REGULAR_EXPRESSION_REGEX
58             )
59             \s*
60             ( \S (?: .* \S )? )?
61             \s*
62             \z
63             >xms;
64              
65             Readonly::Scalar my $DEFAULT_MODULES =>
66             join
67             $SPACE,
68             map { "$_ {Found use of $_. This module is deprecated by the Perl 5 Porters.}" }
69             qw< Class::ISA Pod::Plainer Shell Switch >;
70              
71             # Indexes in the arrays of regexes for the "modules" option.
72             Readonly::Scalar my $INDEX_REGEX => 0;
73             Readonly::Scalar my $INDEX_DESCRIPTION => 1;
74              
75             #-----------------------------------------------------------------------------
76              
77             sub supported_parameters {
78             return (
79             {
80 94     94 0 1556 name => 'modules',
81             description => 'The names of or patterns for modules to forbid.',
82             default_string => $DEFAULT_MODULES,
83             parser => \&_parse_modules,
84             },
85             {
86             name => 'modules_file',
87             description => 'A file containing names of or patterns for modules to forbid.',
88             default_string => $EMPTY,
89             parser => \&_parse_modules_file,
90             },
91             );
92             }
93              
94 75     75 1 262 sub default_severity { return $SEVERITY_HIGHEST }
95 74     74 1 221 sub default_themes { return qw( core bugs certrule ) }
96 37     37 1 84 sub applies_to { return 'PPI::Statement::Include' }
97              
98             #-----------------------------------------------------------------------------
99              
100             sub _parse_modules {
101 92     92   284 my ($self, $parameter, $config_string) = @_;
102              
103 92   66     544 my $module_specifications = $config_string // $parameter->get_default_string();
104              
105 92 100       304 return if not $module_specifications;
106 91 50       470 return if $module_specifications =~ m< \A \s* \z >xms;
107              
108 91         7283 while ( $module_specifications =~ s< $MODULES_REGEX ><>xms ) {
109 364         1092 my ($module, $regex_string, $description) = ($1, $2, $3);
110              
111 364         944 $self->_handle_module_specification(
112             module => $module,
113             regex_string => $regex_string,
114             description => $description,
115             option_name => 'modules',
116             option_value => $config_string,
117             );
118             }
119              
120 91 50       316 if ($module_specifications) {
121 0         0 throw_policy_value
122             policy => $self->get_short_name(),
123             option_name => 'modules',
124             option_value => $config_string,
125             message_suffix =>
126             qq{contains unparseable data: "$module_specifications"};
127             }
128              
129 91         270 return;
130             }
131              
132             sub _parse_modules_file {
133 92     92   247 my ($self, undef, $config_string) = @_;
134              
135 92 50       276 return if not $config_string;
136 0 0       0 return if $config_string =~ m< \A \s* \z >xms;
137              
138 0 0       0 open my $handle, '<', $config_string
139             or throw_policy_value
140             policy => $self->get_short_name(),
141             option_name => 'modules_file',
142             option_value => $config_string,
143             message_suffix =>
144             qq<refers to a file that could not be opened: $OS_ERROR>;
145 0         0 while ( my $line = <$handle> ) {
146 0         0 $self->_handle_module_specification_on_line($line, $config_string);
147             }
148 0 0       0 close $handle or warn qq<Could not close "$config_string": $OS_ERROR\n>;
149              
150 0         0 return;
151             }
152              
153             sub _handle_module_specification_on_line {
154 0     0   0 my ($self, $line, $config_string) = @_;
155              
156 0         0 $line =~ s< [#] .* \z ><>xms;
157 0         0 $line =~ s< \s+ \z ><>xms;
158 0         0 $line =~ s< \A \s+ ><>xms;
159              
160 0 0       0 return if not $line;
161              
162 0 0       0 if ( $line =~ s< $MODULES_FILE_LINE_REGEX ><>xms ) {
163 0         0 my ($module, $regex_string, $description) = ($1, $2, $3);
164              
165 0         0 $self->_handle_module_specification(
166             module => $module,
167             regex_string => $regex_string,
168             description => $description,
169             option_name => 'modules_file',
170             option_value => $config_string,
171             );
172             }
173             else {
174 0         0 throw_policy_value
175             policy => $self->get_short_name(),
176             option_name => 'modules_file',
177             option_value => $config_string,
178             message_suffix =>
179             qq{contains unparseable data: "$line"};
180             }
181              
182 0         0 return;
183             }
184              
185             sub _handle_module_specification {
186 364     364   1290 my ($self, %arguments) = @_;
187              
188 364   33     781 my $description = $arguments{description} || $EMPTY;
189              
190 364 50       785 if ( my $regex_string = $arguments{regex_string} ) {
191             # These are module name patterns (e.g. /Acme/)
192 0         0 my $actual_regex;
193              
194 0         0 eval { $actual_regex = qr/$regex_string/; 1 } ## no critic (ExtendedFormatting, LineBoundaryMatching, DotMatchAnything)
  0         0  
195             or throw_policy_value
196             policy => $self->get_short_name(),
197             option_name => $arguments{option_name},
198             option_value => $arguments{option_value},
199 0 0       0 message_suffix =>
200             qq{contains an invalid regular expression: "$regex_string"};
201              
202             # Can't use a hash due to stringification, so this is an AoA.
203 0   0     0 $self->{_evil_modules_regexes} ||= [];
204              
205             push
206 0         0 @{ $self->{_evil_modules_regexes} },
  0         0  
207             [ $actual_regex, $description ];
208             }
209             else {
210             # These are literal module names (e.g. Acme::Foo)
211 364   100     951 $self->{_evil_modules} ||= {};
212 364         817 $self->{_evil_modules}{ $arguments{module} } = $description;
213             }
214              
215 364         2316 return;
216             }
217              
218             #-----------------------------------------------------------------------------
219              
220             sub initialize_if_enabled {
221 73     73 1 253 my ($self, undef) = @_;
222              
223             # Disable if no modules are specified; there's no point in running if
224             # there aren't any.
225             return
226             exists $self->{_evil_modules}
227 73   66     367 || exists $self->{_evil_modules_regexes};
228             }
229              
230             #-----------------------------------------------------------------------------
231              
232             sub violates {
233 63     63 1 123 my ( $self, $elem, undef ) = @_;
234              
235 63         126 my $module = $elem->module();
236 63 100       1059 return if not $module;
237              
238 61         123 my $evil_modules = $self->{_evil_modules};
239 61         83 my $evil_modules_regexes = $self->{_evil_modules_regexes};
240 61         72 my $description;
241              
242 61 50       125 if ( exists $evil_modules->{$module} ) {
243 0         0 $description = $evil_modules->{ $module };
244             }
245             else {
246             REGEX:
247 61         94 foreach my $regex ( @{$evil_modules_regexes} ) {
  61         110  
248 0 0       0 if ( $module =~ $regex->[$INDEX_REGEX] ) {
249 0         0 $description = $regex->[$INDEX_DESCRIPTION];
250 0         0 last REGEX;
251             }
252             }
253             }
254              
255 61 50       118 if (defined $description) {
256 0   0     0 $description ||= qq<Prohibited module "$module" used>;
257              
258 0         0 return $self->violation( $description, $EXPL, $elem );
259             }
260              
261 61         138 return; # ok!
262             }
263              
264             1;
265              
266             __END__
267              
268             #-----------------------------------------------------------------------------
269              
270             =pod
271              
272             =head1 NAME
273              
274             Perl::Critic::Policy::Modules::ProhibitEvilModules - Ban modules that aren't blessed by your shop.
275              
276              
277             =head1 AFFILIATION
278              
279             This Policy is part of the core L<Perl::Critic|Perl::Critic>
280             distribution.
281              
282              
283             =head1 DESCRIPTION
284              
285             Use this policy if you wish to prohibit the use of specific modules.
286             These may be modules that you feel are deprecated, buggy, unsupported,
287             insecure, or just don't like.
288              
289              
290             =head1 CONFIGURATION
291              
292             The set of prohibited modules is configurable via the C<modules> and
293             C<modules_file> options.
294              
295             The value of C<modules> should be a string of space-delimited, fully
296             qualified module names and/or regular expressions. An example of
297             prohibiting two specific modules in a F<.perlcriticrc> file:
298              
299             [Modules::ProhibitEvilModules]
300             modules = Getopt::Std Autoload
301              
302             Regular expressions are identified by values beginning and ending with
303             slashes. Any module with a name that matches C<m/pattern/> will be
304             forbidden. For example:
305              
306             [Modules::ProhibitEvilModules]
307             modules = /Acme::/
308              
309             would cause all modules that match C<m/Acme::/> to be forbidden.
310              
311             In addition, you can override the default message ("Prohibited module
312             "I<module>" used") with your own, in order to give suggestions for
313             alternative action. To do so, put your message in curly braces after
314             the module name or regular expression. Like this:
315              
316             [Modules::ProhibitEvilModules]
317             modules = Fatal {Found use of Fatal. Use autodie instead} /Acme::/ {We don't use joke modules}
318              
319             Similarly, the C<modules_file> option gives the name of a file
320             containing specifications for prohibited modules. Only one module
321             specification is allowed per line and comments start with an octothorp
322             and run to end of line; no curly braces are necessary for delimiting
323             messages:
324              
325             Evil # Prohibit the "Evil" module and use the default message.
326              
327             # Prohibit the "Fatal" module and give a replacement message.
328             Fatal Found use of Fatal. Use autodie instead.
329              
330             # Use a regular expression.
331             /Acme::/ We don't use joke modules.
332              
333             By default, the modules that have been deprecated by the Perl 5 Porters are
334             reported; at the time of writing these are L<Class::ISA|Class::ISA>,
335             L<Pod::Plainer|Pod::Plainer>, L<Shell|Shell>, and L<Switch|Switch>.
336             Specifying a value for the C<modules> option will override this.
337              
338              
339             =head1 NOTES
340              
341             Note that this policy doesn't apply to pragmas.
342              
343              
344             =head1 AUTHOR
345              
346             Jeffrey Ryan Thalhammer <jeff@imaginative-software.com>
347              
348              
349             =head1 COPYRIGHT
350              
351             Copyright (c) 2005-2023 Imaginative Software Systems
352              
353             This program is free software; you can redistribute it and/or modify
354             it under the same terms as Perl itself. The full text of this license
355             can be found in the LICENSE file included with this module.
356              
357             =cut
358              
359             # Local Variables:
360             # mode: cperl
361             # cperl-indent-level: 4
362             # fill-column: 78
363             # indent-tabs-mode: nil
364             # c-indentation-style: bsd
365             # End:
366             # ex: set ts=8 sts=4 sw=4 tw=78 ft=perl expandtab shiftround :