File Coverage

blib/lib/ENV/Util.pm
Criterion Covered Total %
statement 61 66 92.4
branch 23 34 67.6
condition 8 15 53.3
subroutine 8 8 100.0
pod 3 3 100.0
total 103 126 81.7


line stmt bran cond sub pod time code
1             package ENV::Util;
2 4     4   500611 use strict;
  4         7  
  4         163  
3 4     4   22 use warnings;
  4         34  
  4         3003  
4              
5             our $VERSION = 0.04;
6              
7             sub import {
8 4     4   53 my ($pkg, $cmd, @args) = @_;
9 4 50       7497 return if !defined $cmd;
10 0 0       0 if ($cmd eq '-load_dotenv') {
11 0         0 load_dotenv(@args)
12             }
13             else {
14 0         0 local($!, $^E);
15 0         0 my ($pkg, $file, $line) = caller(1);
16 0         0 die "invalid import action for $pkg in $file line $line.";
17             }
18             }
19              
20             sub prefix2hash {
21 2     2 1 133622 my ($prefix) = @_;
22 2 100       6 $prefix = '' unless defined $prefix;
23 2         3 my $start_index = length($prefix);
24 2         8 my %options = map { lc(substr($_, $start_index)) => $ENV{$_} } grep index($_, $prefix) == 0, keys %ENV;
  5         17  
25 2         9 return %options;
26             }
27              
28             sub load_dotenv {
29 1     1 1 237298 my ($filename) = @_;
30 1 50       6 $filename = '.env' unless defined $filename;
31 1 50       38 return unless -f $filename;
32              
33 1 50   1   100 open my $fh, '<:raw:encoding(UTF-8)', $filename
  1         920  
  1         49  
  1         7  
34             or die "unable to open env file '$filename': $!";
35              
36 1         1402 my @lines;
37 1         6 { local $!; @lines = <$fh> }
  1         12  
  1         40  
38 1         38 my %env;
39             # POSIX convention for env variable names:
40 1         6 my $varname_re = qr/[a-zA-Z_][a-zA-Z0-9_]+/;
41 1         4 foreach my $line (@lines) {
42             # code heavily inspired by Dotenv.pm (BooK++)
43 27 100       506 if (my ($k, $v) = $line =~ m{
44             \A\s*
45             # 'export' (bash), 'set'/'setenv' ([t]csh) are optional keywords:
46             (?: (?:export|set|setenv) \s+ )?
47             ( $varname_re )
48             (?: \s* (?:=|\s+) \s* ) # separator is '=' or spaces
49             (
50             '[^']*(?:\\'|[^']*)*' # single quoted value
51             |"[^"]*(?:\\"|[^"]*)*" # or double quoted value
52             | [^\#\r\n]+ # or unquoted value
53             )?
54             \s* (?: \# .* )? # inline comment
55             \z}sx
56             ) {
57 16 100       38 $v = '' unless defined $v;
58 16         83 $v =~ s/\s*\z//;
59              
60 16         32 my $interpolate_vars = 1; # unquoted strings interpolate variables.
61              
62             # drops quotes from quoted values, and interpolate if double quoted:
63 16 100       53 if ( $v =~ s/\A(['"])(.*)\1\z/$2/) {
64 3 100       12 if ($1 eq '"' ) {
65 1         7 $v =~ s/\\n/\n/g;
66 1         4 $v =~ s/\\//g;
67             }
68             else {
69 2         5 $interpolate_vars = 0;
70             }
71             }
72              
73 16 100       35 if ($interpolate_vars) {
74             # $env{$1} could point to a variable that doesn't exist.
75 4     4   37 no warnings 'uninitialized';
  4         11  
  4         2345  
76 14 100       94 $v =~ s{\$($varname_re)}{exists $ENV{$1} ? $ENV{$1} : $env{$1}}ge;
  7         37  
77             }
78 16         52 $env{$k} = $v;
79             }
80             }
81 1         66 %ENV = (%env, %ENV);
82 1         37 return;
83             }
84              
85             sub redacted_env {
86 1     1 1 225104 my (%opts) = @_;
87 1 50       7 if (!$opts{rules}) {
88             $opts{rules} = [
89             {
90 1         13 key => qr(USER|ID|NAME|MAIL|ACC|TOKEN|PASS|PW|SECRET|KEY|ACCESS|PIN|SSN|CARD|IP),
91             mask => '',
92             },
93             {
94             value => qr(\@|:|=),
95             mask => '',
96             },
97             ]
98             }
99 1         3 my %redacted;
100             ENVKEY:
101 1         5 foreach my $k (keys %ENV) {
102 5         13 my $v = $ENV{$k};
103 5         8 foreach my $rule (@{ $opts{rules} }) {
  5         12  
104 7 100 100     71 if ( ($rule->{key} && $k =~ $rule->{key})
      66        
      66        
105             || ($rule->{value} && $v =~ $rule->{value})
106             ) {
107 3 50 33     27 if ( ($rule->{key} && $k =~ $rule->{key}) ) {
    0 0        
108             } elsif($rule->{value} && $v =~ $rule->{value}) {
109             }
110 3 50       11 next ENVKEY if $rule->{drop};
111 3         35 $v = $rule->{mask};
112 3         6 last;
113             }
114             }
115 5         14 $redacted{$k} = $v;
116             }
117 1         14 return %redacted;
118             }
119              
120             1;
121             __END__