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   279899 use strict;
  4         38  
  4         119  
3 4     4   24 use warnings;
  4         6  
  4         2164  
4              
5             our $VERSION = 0.03;
6              
7             sub import {
8 4     4   38 my ($pkg, $cmd, @args) = @_;
9 4 50       5562 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 1505 my ($prefix) = @_;
22 2 100       7 $prefix = '' unless defined $prefix;
23 2         5 my $start_index = length($prefix);
24 2         13 my %options = map { lc(substr($_, $start_index)) => $ENV{$_} } grep index($_, $prefix) == 0, keys %ENV;
  5         21  
25 2         12 return %options;
26             }
27              
28             sub load_dotenv {
29 1     1 1 104 my ($filename) = @_;
30 1 50       5 $filename = '.env' unless defined $filename;
31 1 50       25 return unless -f $filename;
32              
33 1 50   1   30 open my $fh, '<:raw:encoding(UTF-8)', $filename
  1         6  
  1         2  
  1         5  
34             or die "unable to open env file '$filename': $!";
35              
36 1         12095 my @lines;
37 1         2 { local $!; @lines = <$fh> }
  1         24  
  1         41  
38 1         36 my %env;
39             # POSIX convention for env variable names:
40 1         8 my $varname_re = qr/[a-zA-Z_][a-zA-Z0-9_]+/;
41 1         7 foreach my $line (@lines) {
42             # code heavily inspired by Dotenv.pm (BooK++)
43 27 100       274 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       34 $v = '' unless defined $v;
58 16         72 $v =~ s/\s*\z//;
59              
60 16         29 my $interpolate_vars = 1; # unquoted strings interpolate variables.
61              
62             # drops quotes from quoted values, and interpolate if double quoted:
63 16 100       52 if ( $v =~ s/\A(['"])(.*)\1\z/$2/) {
64 3 100       10 if ($1 eq '"' ) {
65 1         18 $v =~ s/\\n/\n/g;
66 1         5 $v =~ s/\\//g;
67             }
68             else {
69 2         3 $interpolate_vars = 0;
70             }
71             }
72              
73 16 100       29 if ($interpolate_vars) {
74             # $env{$1} could point to a variable that doesn't exist.
75 4     4   33 no warnings 'uninitialized';
  4         7  
  4         1718  
76 14 100       70 $v =~ s{\$($varname_re)}{exists $ENV{$1} ? $ENV{$1} : $env{$1}}ge;
  7         35  
77             }
78 16         54 $env{$k} = $v;
79             }
80             }
81 1         30 %ENV = (%env, %ENV);
82 1         27 return;
83             }
84              
85             sub redacted_env {
86 1     1 1 97 my (%opts) = @_;
87 1 50       5 if (!$opts{rules}) {
88             $opts{rules} = [
89             {
90 1         11 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         2 my %redacted;
100             ENVKEY:
101 1         6 foreach my $k (keys %ENV) {
102 5         10 my $v = $ENV{$k};
103 5         6 foreach my $rule (@{ $opts{rules} }) {
  5         9  
104 7 100 100     59 if ( ($rule->{key} && $k =~ $rule->{key})
      66        
      66        
105             || ($rule->{value} && $v =~ $rule->{value})
106             ) {
107 3 50 33     21 if ( ($rule->{key} && $k =~ $rule->{key}) ) {
    0 0        
108             } elsif($rule->{value} && $v =~ $rule->{value}) {
109             }
110 3 50       8 next ENVKEY if $rule->{drop};
111 3         5 $v = $rule->{mask};
112 3         4 last;
113             }
114             }
115 5         12 $redacted{$k} = $v;
116             }
117 1         10 return %redacted;
118             }
119              
120             1;
121             __END__