File Coverage

blib/lib/Ixchel/Actions/sagan_rules.pm
Criterion Covered Total %
statement 26 134 19.4
branch 0 40 0.0
condition 0 6 0.0
subroutine 9 14 64.2
pod 2 5 40.0
total 37 199 18.5


line stmt bran cond sub pod time code
1             package Ixchel::Actions::sagan_rules;
2              
3 1     1   102221 use 5.006;
  1         5  
4 1     1   6 use strict;
  1         3  
  1         41  
5 1     1   7 use warnings;
  1         2  
  1         80  
6 1     1   722 use File::Slurp;
  1         38190  
  1         64  
7 1     1   346 use YAML::XS qw(Dump Load);
  1         2570  
  1         58  
8 1     1   7 use List::Util qw(uniq);
  1         1  
  1         45  
9 1     1   363 use Ixchel::functions::file_get;
  1         5  
  1         116  
10 1     1   16 use File::Spec;
  1         1  
  1         37  
11 1     1   7 use base 'Ixchel::Actions::base';
  1         2  
  1         636  
12              
13             =head1 NAME
14              
15             Ixchel::Actions::sagan_rules - Generate the rules include for Sagan.
16              
17             =head1 VERSION
18              
19             Version 0.4.0
20              
21             =cut
22              
23             our $VERSION = '0.4.0';
24              
25             =head1 CLI SYNOPSIS
26              
27             ixchel -a sagan_rules [B<-w>] [B<-i> ]
28              
29             =head1 CODE SYNOPSIS
30              
31             use Data::Dumper;
32              
33             my $results=$ixchel->action(action=>'sagan_rules', opts=>{np=>1, w=>1, });
34              
35             print Dumper($results);
36              
37             =head1 DESCRIPTION
38              
39             Any item that does not match /\// or /\$/ has '$RULE_PATH/' prepended to it.
40              
41             If told to write it out, .sagan.config_base is used as the base directory to write
42             to with the file name being 'sagan-rules.yaml' or in the case of multi instance
43             "sagan-rules-$instance.yaml"
44              
45             =head1 FLAGS
46              
47             =head2 -w
48              
49             Write out the generated rule files.
50              
51             =head2 -i
52              
53             A instance to operate on.
54              
55             =head1 RESULT HASH REF
56              
57             .errors :: A array of errors encountered.
58             .status_text :: A string description of what was done and teh results.
59             .ok :: Set to zero if any of the above errored.
60              
61             =cut
62              
63       0 0   sub new_extra { }
64              
65             sub action_extra {
66 0     0 0   my $self = $_[0];
67              
68 0           my $url = $self->{config}{sagan}{rules};
69              
70 0           my $base_config_raw;
71 0           eval {
72 0           $self->status_add( status => 'Fetching ' . $url );
73 0           $base_config_raw = file_get( url => $url );
74             };
75 0 0         if ($@) {
76 0           $self->status_add( error => 1, status => 'Fetch Error... ' . $@ );
77 0           return undef;
78             }
79 0           $self->{base_config_raw} = $base_config_raw;
80              
81 0           my $base_config;
82 0           eval { $base_config = Load($base_config_raw); };
  0            
83 0 0         if ($@) {
84 0           $self->status_add( error => 1, status => 'Decoding YAML from "' . $url . '" failed... ' . $@ );
85 0           return undef;
86             }
87 0           my @base_config_split = split( /\n/, $base_config_raw );
88 0           $self->{base_config_split} = \@base_config_split;
89              
90             # make sure the base config looks sane
91 0 0         if ( !defined( $base_config->{'rules-files'} ) ) {
    0          
    0          
92 0           $self->status_add( error => 1, status => '.rules-files array is not present in the YAML from "' . $url . '"' );
93 0           return undef;
94             } elsif ( ref( $base_config->{'rules-files'} ) ne 'ARRAY' ) {
95 0           $self->status_add( error => 1, status => '.rules-files is not a array in the YAML from "' . $url . '"' );
96 0           return undef;
97             } elsif ( !defined( $base_config->{'rules-files'}[0] ) ) {
98 0           $self->status_add( error => 1, status => '.rules-files[0] is undef in the YAML from "' . $url . '"' );
99 0           return undef;
100             }
101              
102 0           my $rules = {};
103 0           foreach my $rule ( @{ $base_config->{'rules-files'} } ) {
  0            
104 0           $rules->{$rule} = 1;
105             }
106 0           $self->{rules} = $rules;
107              
108 0           my $config_base = $self->{config}{sagan}{config_base};
109              
110 0           $self->status_add( status => 'multi_instance = ' . $self->{config}{sagan}{multi_instance} );
111              
112 0 0         if ( $self->{config}{sagan}{multi_instance} ) {
113             #
114             #
115             #
116             # multi instance
117             #
118             #
119             #
120 0           my @instances;
121              
122 0 0         if ( defined( $self->{opts}{i} ) ) {
123 0           @instances = ( $self->{opts}{i} );
124             } else {
125 0           @instances = keys( %{ $self->{config}{sagan}{instances} } );
  0            
126             }
127 0           foreach my $instance (@instances) {
128 0           my $filled_in;
129 0           eval {
130 0           my $file = File::Spec->canonpath( $config_base . '/sagan-' . $instance . '-rules.yaml' );
131 0           $self->process_file( file => $file );
132             };
133 0 0         if ($@) {
134 0           $self->status_add( status => $@, error => 1 );
135             }
136              
137             } ## end foreach my $instance (@instances)
138             } else {
139             #
140             #
141             #
142             # single
143             #
144             #
145             #
146 0 0         if ( defined( $self->{opts}{i} ) ) {
147 0           $self->status_add(
148             error => 1,
149             status => '-i may not be used in single instance mode, .sagan.multi_instance=0 '
150             );
151 0           return undef;
152             }
153              
154 0           my $file = File::Spec->canonpath( $config_base . '/sagan-rules.yaml' );
155              
156 0           eval { $self->process_file( file => $file ); };
  0            
157 0 0         if ($@) {
158 0           $self->status_add( status => $@, error => 1 );
159             }
160             } ## end else [ if ( $self->{config}{sagan}{multi_instance...})]
161              
162 0           return undef;
163             } ## end sub action_extra
164              
165             sub short {
166 0     0 1   return 'Generate the rules include for Sagan.';
167             }
168              
169             sub opts_data {
170 0     0 1   return 'i=s
171             w
172             ';
173             }
174              
175             sub process_file {
176 0     0 0   my ( $self, %opts ) = @_;
177              
178 0           my $file = $opts{file};
179 0           my $filled_in;
180              
181 0 0         if ( !-f $file ) {
182 0           $filled_in = $self->{base_config_raw};
183 0           $self->status_add(
184             status => '-----[ ' . $file . ' ]-------------------------------------' . "\n" . $filled_in );
185             } else {
186             # figure out what rules we have
187 0           my $current_config;
188 0           eval {
189 0           my $current_config_raw = read_file($file);
190 0           $current_config = Load($current_config_raw);
191             };
192 0 0         if ($@) {
193 0           $self->status_add( status => $@, error => 1 );
194 0           return $self->{results};
195             }
196              
197             # get what rules are currently in use
198 0           my $current_rules = {};
199 0           foreach my $rule ( @{ $current_config->{'rules-files'} } ) {
  0            
200 0           $current_rules->{$rule} = 1;
201             }
202              
203             # get a list of custom rules
204 0           my $custom_rules = {};
205 0           foreach my $rule ( keys( %{$current_rules} ) ) {
  0            
206 0 0         if ( !defined( $self->{rules}->{$rule} ) ) {
207 0           $custom_rules->{$rule} = 1;
208             }
209             }
210 0           my @custom_rules_array = keys( %{$custom_rules} );
  0            
211              
212             # begin putting it back together
213 0           $filled_in = '';
214 0           my $start = 1;
215 0           foreach my $line ( @{ $self->{base_config_split} } ) {
  0            
216 0           my $ignore_line = 0;
217              
218 0 0 0       if ( $line =~ /^ *\#/ ) {
    0 0        
    0          
219 0           $ignore_line = 1;
220             } elsif ( !$start && $line =~ /^rules\-files\:/ ) {
221 0           $start = 1;
222 0           $ignore_line = 1;
223             } # post start ignore anything that is not a rule line
224             elsif ( $start && $line !~ /^\ \ \-\ \$RULE\_PATH/ ) {
225 0           $ignore_line = 1;
226             }
227              
228 0 0         if ($ignore_line) {
229 0           $filled_in = $filled_in . $line . "\n";
230             } else {
231             # get the rule name
232 0           my $rule = $line;
233 0           $rule =~ s/^\ \ \-\ //;
234              
235             # should never be there, but just in case perform some basic cleanup
236 0           $rule =~ s/ *\#.*//;
237 0           $rule =~ s/ *$//;
238              
239             # if it is not in the current rule set, comment it out
240 0 0         if ( !defined( $current_rules->{$rule} ) ) {
241 0           $filled_in = $filled_in . ' #- ' . $rule . "\n";
242             } else {
243 0           $filled_in = $filled_in . $line . "\n";
244             }
245             } ## end else [ if ($ignore_line) ]
246             } ## end foreach my $line ( @{ $self->{base_config_split...}})
247              
248 0 0         if ( defined( $custom_rules_array[0] ) ) {
249 0           $filled_in = $filled_in = "\n\n\n # rules found in the file but not in the source rules.yaml file\n";
250 0           foreach my $custom_rule (@custom_rules_array) {
251 0           $filled_in = $filled_in . ' - ' . $custom_rule . "\n";
252             }
253             }
254              
255             $self->status_add(
256 0           status => '-----[ ' . $file . ' ]-------------------------------------' . "\n" . $filled_in );
257              
258             } ## end else [ if ( !-f $file ) ]
259              
260 0 0         if ( $self->{opts}{w} ) {
261 0           $self->status_add( status => 'Writing out to "' . $file . '" ...' );
262 0           write_file( $file, $filled_in );
263             }
264              
265 0           return $filled_in;
266             } ## end sub process_file
267              
268             1;