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   27307 use 5.010001;
  40         186  
4 40     40   438 use strict;
  40         101  
  40         780  
5 40     40   231 use warnings;
  40         240  
  40         1174  
6              
7 40     40   253 use English qw(-no_match_vars);
  40         101  
  40         275  
8 40     40   15551 use Readonly;
  40         104  
  40         2230  
9              
10             use Perl::Critic::Exception::Configuration::Option::Policy::ParameterValue
11 40     40   281 qw{ throw_policy_value };
  40         273  
  40         1139  
12 40     40   2507 use Perl::Critic::Utils qw( :characters :severities :data_conversion );
  40         129  
  40         2158  
13              
14 40     40   12974 use parent 'Perl::Critic::Policy';
  40         134  
  40         265  
15              
16             our $VERSION = '1.150';
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 93     93 0 2545 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 74     74 1 322 sub default_severity { return $SEVERITY_HIGHEST }
95 74     74 1 303 sub default_themes { return qw( core bugs certrule ) }
96 36     36 1 106 sub applies_to { return 'PPI::Statement::Include' }
97              
98             #-----------------------------------------------------------------------------
99              
100             sub _parse_modules {
101 91     91   479 my ($self, $parameter, $config_string) = @_;
102              
103 91   66     641 my $module_specifications = $config_string // $parameter->get_default_string();
104              
105 91 100       409 return if not $module_specifications;
106 90 50       556 return if $module_specifications =~ m< \A \s* \z >xms;
107              
108 90         3382 while ( $module_specifications =~ s< $MODULES_REGEX ><>xms ) {
109 360         1490 my ($module, $regex_string, $description) = ($1, $2, $3);
110              
111 360         1145 $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 90 50       451 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 90         279 return;
130             }
131              
132             sub _parse_modules_file {
133 91     91   460 my ($self, $parameter, $config_string) = @_;
134              
135 91 50       407 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 360     360   1469 my ($self, %arguments) = @_;
187              
188 360   33     1047 my $description = $arguments{description} || $EMPTY;
189              
190 360 50       847 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 360   100     1130 $self->{_evil_modules} ||= {};
212 360         1163 $self->{_evil_modules}{ $arguments{module} } = $description;
213             }
214              
215 360         5640 return;
216             }
217              
218             #-----------------------------------------------------------------------------
219              
220             sub initialize_if_enabled {
221 72     72 1 308 my ($self, $config) = @_;
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 72   66     423 || exists $self->{_evil_modules_regexes};
228             }
229              
230             #-----------------------------------------------------------------------------
231              
232             sub violates {
233 63     63 1 141 my ( $self, $elem, undef ) = @_;
234              
235 63         157 my $module = $elem->module();
236 63 100       1307 return if not $module;
237              
238 61         147 my $evil_modules = $self->{_evil_modules};
239 61         158 my $evil_modules_regexes = $self->{_evil_modules_regexes};
240 61         85 my $description;
241              
242 61 50       134 if ( exists $evil_modules->{$module} ) {
243 0         0 $description = $evil_modules->{ $module };
244             }
245             else {
246             REGEX:
247 61         92 foreach my $regex ( @{$evil_modules_regexes} ) {
  61         132  
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       150 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         165 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 :