File Coverage

blib/lib/lib/filter.pm
Criterion Covered Total %
statement 11 137 8.0
branch 2 104 1.9
condition 0 22 0.0
subroutine 4 9 44.4
pod 0 1 0.0
total 17 273 6.2


line stmt bran cond sub pod time code
1             package lib::filter;
2              
3             our $DATE = '2016-08-23'; # DATE
4             our $VERSION = '0.26'; # VERSION
5              
6             # no need to avoid strict & warnings, because Config uses them
7 2     2   7 use strict;
  2         2  
  2         43  
8 2     2   6 use warnings;
  2         1  
  2         38  
9 2     2   4 use Config;
  2         2  
  2         164  
10              
11             # BEGIN snippet from Module::Path::More, with mods/simplification
12             my $SEPARATOR;
13             BEGIN {
14 2 50   2   21 if ($^O =~ /^(dos|os2)/i) {
    50          
15 0         0 $SEPARATOR = '\\';
16             } elsif ($^O =~ /^MacOS/i) {
17 0         0 $SEPARATOR = ':';
18             } else {
19 2         2548 $SEPARATOR = '/';
20             }
21             }
22             sub module_path {
23 0     0 0   my ($file, $inc) = @_;
24              
25 0           foreach my $dir (@$inc) {
26 0 0         next if !defined($dir);
27 0 0         next if ref($dir);
28 0           my $path = $dir . $SEPARATOR . $file;
29 0 0         return $path if -f $path;
30             }
31 0           undef;
32             }
33             # END snippet from Module::Path::More
34              
35             sub _open_handle {
36 0     0     my $path = shift;
37 0 0         open my($fh), "<", $path
38             or die "Can't open $path: $!";
39 0           $fh;
40             }
41              
42             my $hook;
43             my ($orig_inc, $orig_inc_sorted_by_len);
44              
45             sub import {
46 0     0     my ($class, %opts) = @_;
47              
48 0           my $dbgh = "[lib::filter]";
49              
50 0           for (keys %opts) {
51 0 0         die "Unknown option $_"
52             unless /\A(
53             debug|
54             allow_core|allow_noncore|
55             extra_inc|
56             allow|allow_list|allow_re|
57             allow_is_recursive|
58             disallow|disallow_list|disallow_re|
59             filter
60             )\z/x;
61             }
62              
63 0 0         $opts{allow_core} = 1 if !defined($opts{allow_core});
64 0 0         $opts{allow_noncore} = 1 if !defined($opts{allow_noncore});
65              
66 0 0 0       if ($opts{filter} && !ref($opts{filter})) {
67             # convenience, for when filter is specified from command-line (-M)
68 0           $opts{filter} = eval $opts{filter};
69 0 0         die "Error in filter code: $@" if $@;
70             }
71              
72 0 0         if ($opts{extra_inc}) {
73 0           unshift @INC, split(/:/, $opts{extra_inc});
74             }
75              
76 0 0         if (!$orig_inc) {
77 0           $orig_inc = [@INC];
78 0           $orig_inc_sorted_by_len = [sort {length($b) <=> length($a)} @INC];
  0            
79             }
80              
81 0           my $core_inc = [@Config{qw(privlibexp archlibexp)}];
82 0           my $noncore_inc = [grep {$_ ne $Config{privlibexp} &&
83 0 0         $_ ne $Config{archlibexp}} @$orig_inc];
84 0           my %allow;
85 0 0         if ($opts{allow}) {
86 0           for (split /\s*;\s*/, $opts{allow}) {
87 0           $allow{$_} = "allow";
88             }
89             }
90 0 0         if ($opts{allow_list}) {
91             open my($fh), "<", $opts{allow_list}
92 0 0         or die "Can't open allow_list file '$opts{allow_list}': $!";
93 0           while (my $line = <$fh>) {
94 0           $line =~ s/^\s+//;
95 0 0         $line =~ /^(\w+(?:::\w+)*)/ or next;
96 0   0       $allow{$1} ||= "allow_list";
97             }
98             }
99              
100 0           my %disallow;
101 0 0         if ($opts{disallow}) {
102 0           for (split /\s*;\s*/, $opts{disallow}) {
103 0           $disallow{$_} = "disallow";
104 0           (my $pm = "$_.pm") =~ s!::!/!g; delete $INC{$pm};
  0            
105             }
106             }
107 0 0         if ($opts{disallow_list}) {
108             open my($fh), "<", $opts{disallow_list}
109 0 0         or die "Can't open disallow_list file '$opts{disallow_list}': $!";
110 0           while (my $line = <$fh>) {
111 0           $line =~ s/^\s+//;
112 0 0         $line =~ /^(\w+(?:::\w+)*)/ or next;
113 0   0       $disallow{$1} ||= "disallow_list";
114 0           (my $pm = "$1.pm") =~ s!::!/!g; delete $INC{$pm};
  0            
115             }
116             }
117              
118             $hook = sub {
119 0     0     my ($self, $file) = @_;
120              
121 0           my @caller = caller(0);
122              
123 0 0         warn "$dbgh hook called for $file (from package $caller[0] file $caller[1])\n" if $opts{debug};
124              
125 0           my $path;
126             FILTER:
127             {
128 0           my $mod = $file; $mod =~ s/\.pm$//; $mod =~ s!/!::!g;
  0            
  0            
  0            
129 0           my $err_prefix = "Can't locate $file";
130 0 0         if ($opts{filter}) {
131 0           local $_ = $mod;
132 0 0         warn "$dbgh Checking against custom filter ...\n" if $opts{debug};
133 0 0         unless ($opts{filter}->($mod)) {
134 0           die "$err_prefix (module '$mod' is disallowed (filter))";
135             }
136             }
137 0 0 0       if ($opts{disallow_re} && $mod =~ /$opts{disallow_re}/) {
138 0           die "$err_prefix (module '$mod' is disallowed (disallow_re))";
139             }
140 0 0         if ($disallow{$mod}) {
141 0           die "$err_prefix (module '$mod' is disallowed ($disallow{$mod}))";
142             }
143 0 0 0       if ($opts{allow_re} && $mod =~ /$opts{allow_re}/) {
144 0 0         warn "$dbgh module $mod matches allow_re\n" if $opts{debug};
145 0           $path = module_path($file, $orig_inc);
146 0 0         last FILTER if $path;
147 0           die "$err_prefix (module '$mod' is allowed (allow_re) but can't locate $file in \@INC (\@INC contains: ".join(" ", @INC)."))";
148             }
149 0 0         if ($allow{$mod}) {
150 0 0         warn "$dbgh module $mod matches $allow{$mod}\n" if $opts{debug};
151 0           $path = module_path($file, $orig_inc);
152 0 0         last FILTER if $path;
153 0           die "$err_prefix (module '$mod' is allowed ($allow{$mod}) but can't locate $file in \@INC (\@INC contains: ".join(" ", @INC)."))";
154             }
155 0 0         if ($opts{allow_is_recursive}) {
156 0           my $caller_pkg_from_file;
157 0           for (@$orig_inc_sorted_by_len) {
158             #print "D:\$_=<$_> vs $caller[1]\n";
159 0 0         if (index($caller[1], $_) == 0) {
160 0           $caller_pkg_from_file = substr($caller[1], length($_)+1);
161             #print "D:caller_pkg_from_file=<$caller_pkg_from_file>\n";
162 0           $caller_pkg_from_file =~ s/\.pm\z//;
163 0           $caller_pkg_from_file =~ s/\Q$Config{archname}\E.//;
164 0           $caller_pkg_from_file =~ s![/\\]!::!g;
165             }
166             }
167 0           for my $caller_pkg (grep {defined} $caller[0], $caller_pkg_from_file) {
  0            
168 0           (my $pm = "$caller_pkg.pm") =~ s!::!/!g;
169 0 0         if (exists $INC{$pm}) {
170 0           $path = module_path($file, $orig_inc);
171 0 0         if ($path) {
172 0 0         warn "$dbgh module '$mod' allowed because it is require'd by $caller_pkg (allow_is_recursive=1)\n" if $opts{debug};
173 0           last FILTER;
174             }
175             }
176             }
177             }
178              
179 0           my $inc;
180 0 0 0       if ($opts{allow_noncore} && $opts{allow_core}) {
    0          
    0          
181 0           $inc = $orig_inc;
182             } elsif ($opts{allow_core}) {
183 0           $inc = $core_inc;
184             } elsif ($opts{allow_noncore}) {
185 0           $inc = $noncore_inc;
186             }
187 0 0         if ($inc) {
188 0 0         warn "$dbgh searching $file in (".join(", ", @$inc).")\n" if $opts{debug};
189 0           $path = module_path($file, $inc);
190             }
191 0 0         last FILTER if $path;
192             } # FILTER
193              
194 0 0         unless ($path) {
195 0 0         warn "$dbgh $file not found\n" if $opts{debug};
196 0           return;
197             }
198              
199 0 0         warn "$dbgh $file found at $path\n" if $opts{debug};
200 0           $INC{$file} = $path;
201 0           return _open_handle($path);
202 0           };
203              
204             @INC = (
205             $hook,
206             grep {
207 0           my $mod = $_;
  0            
208 0 0 0       if ("$mod" eq "$hook") {
    0 0        
    0          
209 0           0;
210 0           } elsif ($opts{allow_core} && grep {$mod eq $_} @$core_inc) {
211 0           1;
212 0           } elsif ($opts{allow_noncore} && grep {$mod eq $_} @$noncore_inc) {
213 0           1;
214             } else {
215 0           0;
216             }
217             } @$orig_inc,
218             );
219             #use DD; dd $orig_inc;
220             #use DD; dd \@INC;
221             }
222              
223             sub unimport {
224 0 0   0     return unless $hook;
225 0           @INC = grep { "$_" ne "$hook" } @INC;
  0            
226             }
227              
228             1;
229             # ABSTRACT: Allow/disallow loading modules
230              
231             __END__