File Coverage

blib/lib/Iodef/Pb/Simple.pm
Criterion Covered Total %
statement 9 11 81.8
branch n/a
condition n/a
subroutine 4 4 100.0
pod n/a
total 13 15 86.6


line stmt bran cond sub pod time code
1             package Iodef::Pb::Simple;
2              
3 1     1   50676 use 5.008008;
  1         3  
4 1     1   3 use strict;
  1         1  
  1         14  
5 1     1   3 use warnings;
  1         4  
  1         108  
6              
7             require Exporter;
8              
9             our $VERSION = '0.17_01';
10             $VERSION = eval $VERSION; # see L<perlmodstyle>
11              
12             our @ISA = qw(Exporter);
13              
14             # Items to export into callers namespace by default. Note: do not export
15             # names by default without a very good reason. Use EXPORT_OK instead.
16             # Do not simply export all your public functions/methods/constants.
17              
18             # This allows declaration use Iodef::Pb::Simple ':all';
19             # If you do not need this, moving things directly into @EXPORT or @EXPORT_OK
20             # will save memory.
21             our %EXPORT_TAGS = ( 'all' => [ qw(
22             iodef_descriptions iodef_assessments iodef_confidence iodef_impacts iodef_impacts_first
23             iodef_additional_data iodef_systems iodef_addresses iodef_events_additional_data iodef_services
24             iodef_systems_additional_data iodef_normalize_restriction iodef_guid iodef_uuid iodef_malware
25             iodef_bgp iodef_contacts iodef_contacts_cc iodef_normalize_purpose
26             uuid_random uuid_ns is_uuid
27             ) ] );
28              
29             our @EXPORT_OK = ( @{ $EXPORT_TAGS{'all'} } );
30              
31             our @EXPORT = qw(
32            
33             );
34              
35 1     1   428 use Iodef::Pb;
  0            
  0            
36             use Module::Pluggable require => 1;
37             use OSSP::uuid;
38              
39             my @plugins = __PACKAGE__->plugins();
40              
41             sub is_uuid {
42             my $arg = shift;
43             return undef unless($arg && $arg =~ /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/);
44             return(1);
45             }
46              
47             sub uuid_random {
48             my $uuid = OSSP::uuid->new();
49             $uuid->make('v4');
50             my $str = $uuid->export('str');
51             undef $uuid;
52             return($str);
53             }
54              
55             sub uuid_ns {
56             my $source = shift;
57             my $uuid = OSSP::uuid->new();
58             my $uuid_ns = OSSP::uuid->new();
59             $uuid_ns->load('ns::URL');
60             $uuid->make("v3",$uuid_ns,$source);
61             my $str = $uuid->export('str');
62             undef $uuid;
63             return($str);
64             }
65              
66             sub iodef_normalize_restriction {
67             my $restriction = shift || return;
68            
69             if($restriction =~ /^\d$/){
70             return 'private' if($restriction == RestrictionType::restriction_type_private());
71             return 'public' if($restriction == RestrictionType::restriction_type_public());
72             return 'need-to-know' if($restriction == RestrictionType::restriction_type_need_to_know());
73             return 'default' if($restriction == RestrictionType::restriction_type_default());
74             return;
75             } else {
76             for(lc($restriction)){
77             if(/^private$/){
78             $restriction = RestrictionType::restriction_type_private(),
79             last;
80             }
81             if(/^public$/){
82             $restriction = RestrictionType::restriction_type_public(),
83             last;
84             }
85             if(/^need-to-know$/){
86             $restriction = RestrictionType::restriction_type_need_to_know(),
87             last;
88             }
89             if(/^default$/){
90             $restriction = RestrictionType::restriction_type_default(),
91             last;
92             }
93             }
94             }
95             return $restriction;
96             }
97              
98             sub iodef_normalize_purpose {
99             my $thing = shift || return;
100            
101             if($thing =~ /^\d$/){
102             return 'other' if($thing == IncidentType::IncidentPurpose::Incident_purpose_other());
103             return 'mitigation' if($thing == IncidentType::IncidentPurpose::Incident_purpose_mitigation());
104             return 'traceback' if($thing == IncidentType::IncidentPurpose::Incident_purpose_traceback());
105             return 'reporting' if($thing == IncidentType::IncidentPurpose::Incident_purpose_reporting());
106             return;
107             } else {
108             for(lc($thing)){
109             if(/^other/){
110             $thing = IncidentType::IncidentPurpose::Incident_purpose_other(),
111             last;
112             }
113             if(/^mitigation$/){
114             $thing = IncidentType::IncidentPurpose::Incident_purpose_mitigation(),
115             last;
116             }
117             if(/^traceback$/){
118             $thing = IncidentType::IncidentPurpose::Incident_purpose_traceback(),
119             last;
120             }
121             if(/^reporting$/){
122             $thing = IncidentType::IncidentPurpose::Incident_purpose_reporting(),
123             last;
124             }
125             }
126             }
127             return $thing;
128             }
129              
130              
131             sub iodef_descriptions {
132             my $iodef = shift;
133            
134             my @array;
135             foreach my $i (@{$iodef->get_Incident()}){
136             my $desc = $i->get_Description();
137             $desc = [$desc] unless(ref($desc) eq 'ARRAY');
138             push(@array,@$desc);
139             }
140             return(\@array);
141             }
142              
143             sub iodef_contacts {
144             my $iodef = shift;
145            
146             my @array;
147             foreach my $i (@{$iodef->get_Incident()}){
148             my $c = $i->get_Contact();
149             next unless($c);
150             $c = [$c] unless(ref($c) eq 'ARRAY');
151             push(@array,@$c);
152             }
153             return(\@array);
154             }
155              
156             sub iodef_contacts_cc {
157             my $i = shift;
158            
159             return unless($i->get_Contact());
160             my $contacts = (ref($i->get_Contact()) eq 'ContactType') ? [$i->get_Contact()] : $i->get_Contact();
161            
162             my @array;
163             foreach my $c (@$contacts){
164             next unless($c->get_type() == ContactType::ContactRole::Contact_role_cc());
165             push(@array,$c);
166             }
167             return unless(@array);
168             return(\@array);
169             }
170            
171            
172              
173             sub iodef_confidence {
174             my $iodef = shift;
175            
176             if(ref($iodef) eq 'IODEFDocumentType'){
177             $iodef = $iodef->get_Incident();
178             }
179            
180             $iodef = [$iodef] unless(ref($iodef) eq 'ARRAY');
181            
182             my $ret = @{$iodef}[0]->get_Assessment();
183             my @array;
184             foreach my $a (@$ret){
185             push(@array,$a->get_Confidence());
186             }
187             return(\@array);
188             }
189              
190             sub iodef_assessments {
191             my $iodef = shift;
192            
193             return [] unless(ref($iodef) eq 'IncidentType');
194             return $iodef->get_Assessment();
195             }
196              
197             sub iodef_impacts {
198             my $iodef = shift;
199            
200             if(ref($iodef) eq 'IODEFDocumentType'){
201             $iodef = $iodef->get_Incident();
202             };
203             $iodef = [$iodef] unless(ref($iodef) eq 'ARRAY');
204            
205             my $array;
206             foreach my $i (@$iodef){
207             my @local = @{$i->get_Assessment()};
208             push(@$array, map { @{$_->get_Impact()} } @local);
209             }
210             return $array;
211             }
212              
213             sub iodef_impacts_first {
214             my $iodef = shift;
215              
216             return [] unless(ref($iodef) eq 'IncidentType');
217              
218             my $impacts = iodef_impacts($iodef);
219             my $impact = @{$impacts}[0];
220             return($impact);
221             }
222              
223             sub iodef_address_type {
224             my $type = shift;
225            
226             # TODO -- return contstant for net-addr-ipv4, etc.
227            
228             return $type;
229             }
230              
231             sub iodef_addresses {
232             my $iodef = shift;
233             my $type = shift;
234            
235             return unless($iodef);
236            
237             $type = iodef_address_type($type) if($type);
238            
239             if(ref($iodef) eq 'IODEFDocumentType'){
240             $iodef = $iodef->get_Incident();
241             }
242            
243             $iodef = [$iodef] unless(ref($iodef) eq 'ARRAY');
244            
245             my @array;
246             foreach my $i (@$iodef){
247             next unless($i->get_EventData());
248             foreach my $e (@{$i->get_EventData()}){
249             my @flows = (ref($e->get_Flow()) eq 'ARRAY') ? @{$e->get_Flow()} : $e->get_Flow();
250             foreach my $f (@flows){
251             my @systems = (ref($f->get_System()) eq 'ARRAY') ? @{$f->get_System()} : $f->get_System();
252             foreach my $s (@systems){
253             my @nodes = (ref($s->get_Node()) eq 'ARRAY') ? @{$s->get_Node()} : $s->get_Node();
254             foreach my $n (@nodes){
255             my $addresses = $n->get_Address();
256             $addresses = [$addresses] if(ref($addresses) eq 'AddressType');
257             push(@array,@$addresses);
258             }
259             }
260             }
261             }
262             }
263             return(\@array);
264             }
265              
266             sub iodef_bgp {
267             my $iodef = shift;
268            
269             return unless($iodef);
270            
271             if(ref($iodef) eq 'IODEFDocumentType'){
272             $iodef = $iodef->get_Incident();
273             }
274            
275             $iodef = [$iodef] unless(ref($iodef) eq 'ARRAY');
276            
277             my @array;
278             foreach my $i (@$iodef){
279             next unless($i->get_EventData());
280             foreach my $e (@{$i->get_EventData()}){
281             my @flows = (ref($e->get_Flow()) eq 'ARRAY') ? @{$e->get_Flow()} : $e->get_Flow();
282             foreach my $f (@flows){
283             my @systems = (ref($f->get_System()) eq 'ARRAY') ? @{$f->get_System()} : $f->get_System();
284             foreach my $s (@systems){
285             my $x = _additional_data($s);
286             next unless($x);
287             my $hash;
288             foreach (@$x){
289             next unless(lc($_->get_meaning()) =~ /^(asn|asn_desc|prefix|rir|cc)$/);
290             $hash->{$_->get_meaning()} = $_->get_content();
291             }
292             push(@array,$hash);
293             }
294             }
295             }
296             }
297             return unless(@array);
298             return(\@array);
299             }
300              
301             sub iodef_systems {
302             my $iodef = shift;
303            
304             my @array;
305             return unless($iodef->get_EventData());
306             foreach my $e (@{$iodef->get_EventData()}){
307             my @flows = (ref($e->get_Flow()) eq 'ARRAY') ? @{$e->get_Flow()} : $e->get_Flow();
308             foreach my $f (@flows){
309             my @systems = (ref($f->get_System()) eq 'ARRAY') ? @{$f->get_System()} : $f->get_System();
310             push(@array,@systems);
311             }
312             }
313             return(\@array);
314             }
315              
316             sub _additional_data {
317             my $array = shift;
318            
319             $array = [$array] unless(ref($array) eq 'ARRAY');
320             my $return_array;
321             foreach my $e (@$array){
322             next unless($e && $e->get_AdditionalData());
323             my @additional_data = (ref($e->get_AdditionalData()) eq 'ARRAY') ? @{$e->get_AdditionalData()} : $e->get_AdditionalData();
324             push(@$return_array,@additional_data);
325             }
326             return($return_array);
327             }
328              
329             sub iodef_additional_data {
330             my $iodef = shift;
331              
332             if(ref($iodef) eq 'IODEFDocumentType'){
333             $iodef = $iodef->get_Incident();
334             }
335             $iodef = [$iodef] unless(ref($iodef) eq 'ARRAY');
336            
337             my $array = _additional_data($iodef);
338             return($array);
339             }
340              
341             sub iodef_malware {
342             my $iodef = shift;
343              
344             my $ad = iodef_additional_data($iodef);
345             return unless($#{$ad});
346            
347             my $array = [];
348             foreach (@$ad){
349             next unless($_->get_meaning() =~ /^malware hash$/);
350             push(@$array,$_);
351             }
352             return unless($#{$array} > -1);
353             return $array;
354             }
355            
356              
357             sub iodef_events_additional_data {
358             my $iodef = shift;
359            
360             my $array = [];
361             foreach my $i (@{$iodef->get_Incident()}){
362             next unless($i->get_EventData());
363             if(my $ret = _additional_data($i->get_EventData())){
364             push(@$array,@$ret);
365             }
366             }
367            
368             return($array);
369             }
370              
371             sub iodef_systems_additional_data {
372             my $iodef = shift;
373            
374             return unless(ref($iodef) eq 'IncidentType');
375            
376             my $array;
377             foreach my $e (@{$iodef->get_EventData()}){
378             my @flows = (ref($e->get_Flow()) eq 'ARRAY') ? @{$e->get_Flow()} : $e->get_Flow();
379             foreach my $f (@flows){
380             next unless($f->get_System());
381             my @systems = (ref($f->get_System()) eq 'ARRAY') ? @{$f->get_System()} : $f->get_System();
382             if(my $ret = _additional_data($f->get_System())){
383             push(@$array,@$ret);
384             }
385             }
386             }
387             return($array);
388             }
389              
390             sub iodef_guid {
391             my $iodef = shift;
392            
393             return unless(ref($iodef) eq 'IncidentType');
394            
395             my $ad = $iodef->get_AdditionalData();
396             foreach (@$ad){
397             next unless($_->get_meaning() =~ /^guid/);
398             ## TODO -- return array of guids?
399             return $_->get_content();
400             }
401             }
402              
403             sub iodef_uuid {
404             my $iodef = shift;
405             return $iodef->get_IncidentID->get_content();
406             }
407              
408             sub new {
409             my $class = shift;
410             my $args = shift;
411              
412             $args->{'description'} = 'unknown' unless($args->{'description'});
413             $args->{'lang'} = 'EN' unless($args->{'lang'});
414             $args->{'timezone'} = 'UTC' unless($args->{'timezone'});
415              
416             unless(ref($args->{'description'}) eq 'MLStringType'){
417             $args->{'description'} = MLStringType->new({
418             lang => $args->{'lang'},
419             content => $args->{'description'},
420             });
421             }
422              
423             my $pb = IODEFDocumentType->new({
424             lang => $args->{'lang'},
425             # IODEF version
426             version => '1.00',
427             # PB version
428             formatid => '0.01',
429             Incident => [
430             IncidentType->new({
431             Description => [ $args->{'description'} ],
432             }),
433             ],
434             });
435             foreach(@plugins){
436             $_->process($args,$pb);
437             }
438            
439             #die ::Dumper($pb);
440             #####
441             $pb = $class->new_carboncopy($pb,$args);
442              
443             return $pb;
444             }
445              
446             # i dunno that this belongs here, should be higher up the stack maybe
447             # putting here for now till we figure out what to do with it
448             sub new_carboncopy {
449             my $class = shift;
450             my $pb = shift;
451             my $args = shift;
452            
453             return $pb unless($args->{'carboncopy'});
454             return $pb if($args->{'carboncopy_nosplit'} && !$args->{'carboncopy_nosplit'});
455            
456             # setup pointer to the original
457             my $orig_obj = @{$pb->get_Incident()}[0];
458            
459             my %local_args = %$args;
460             my $restriction = $args->{'carboncopy_restriction'} || 'private';
461             $local_args{'restriction'} = $restriction;
462             $local_args{'alternativeid_restriction'} = $restriction;
463            
464             $restriction = iodef_normalize_restriction($restriction) unless($restriction =~ /^\d+$/);
465            
466             my $current_id = @{$pb->get_Incident()}[0]->get_IncidentID();
467             # we need to change the restriction for the altid
468             $current_id = IncidentIDType->new({
469             content => $current_id->get_content(),
470             name => $current_id->get_name(),
471             instance => $current_id->get_instance(),
472             restriction => $restriction,
473             });
474            
475             # setup the array;
476             $pb = [ $pb ];
477              
478             my @cc = split(/,/,$args->{'carboncopy'});
479            
480             delete($local_args{'carboncopy'});
481            
482             my @new_ids;
483             foreach (@cc){
484             $local_args{'guid'} = uuid_ns($_);
485            
486             # create new incident to be pushed into an altid
487             my $new_id = IncidentIDType->new({
488             content => uuid_random(),
489             restriction => $restriction,
490             name => $orig_obj->get_IncidentID->get_name(),
491             });
492            
493             $local_args{'IncidentID'} = $new_id;
494             $local_args{'AlternativeID'} = AlternativeIDType->new({
495             restriction => $restriction,
496             IncidentID => [$current_id],
497             });
498             push(@new_ids, $new_id);
499              
500             # create the new incident
501             my $x = $class->new(\%local_args);
502             push(@$pb,$x);
503             }
504            
505             my $orig_altid = $orig_obj->get_AlternativeID();
506             if($orig_altid){
507             push(@{$orig_altid->{'IncidentID'}},@new_ids);
508             } else {
509             $orig_altid = AlternativeIDType->new({
510             IncidentID => \@new_ids,
511             restriction => $restriction,
512             });
513             }
514            
515             return $pb;
516             }
517              
518             1;
519             __END__
520             # Below is stub documentation for your module. You'd better edit it!
521              
522             =head1 NAME
523              
524             Iodef::Pb::Simple - Perl extension providing high level API access to Iodef::Pb. It takes simple key-pair hashes and maps them to the appropriate IODEF classes using a Module::Pluggable framework of plugins.
525              
526             =head1 SYNOPSIS
527              
528             use Iodef::Pb::Simple;
529             use Data::Dumper;
530              
531             my $x = Iodef::Pb::Simple->new({
532             contact => 'Wes Young',
533             address => 'example.com',
534             #rdata => '1.2.3.4',
535             id => '1234',
536             address => '1.1.1.1',
537             prefix => '1.1.1.0/24',
538             asn => 'AS1234',
539             cc => 'US',
540             assessment => 'botnet',
541             confidence => '50',
542             restriction => 'private',
543             method => 'http://www.virustotal.com/analisis/02da4d701931b1b00703419a34313d41938e5bd22d336186e65ea1b8a6bfbf1d-1280410372',
544             });
545              
546             my $str = $x->encode();
547             warn Dumper($x);
548             warn Dumper(IODEFDocumentType->decode($str));
549              
550             =head1 DESCRIPTION
551              
552             This library provides high level access to the Iodef::Pb API. It allows for the rapid generation of simple IODEF messages.
553              
554             Once the buffer's are encoded, they can easily be transported via REST, ZeroMQ, Crossroads.io or any other messaging framework (or the google protocol RPC bits themselves). To store these in a database, you can easily base64 the data-structure and save as text.
555              
556             =head2 EXPORT
557              
558             None by default. Object Oriented.
559              
560             =head1 SEE ALSO
561              
562             http://github.com/collectiveintel/iodef-pb-simple-perl
563             http://collectiveintel.org
564              
565             =head1 AUTHOR
566              
567             Wes Young, E<lt>wes@barely3am.comE<gt>
568              
569             =head1 COPYRIGHT AND LICENSE
570              
571             Copyright (C) 2012 by Wes Young <wesyoung.me>
572             Copyright (C) 2012 the REN-ISAC <ren-isac.net>
573             Copyright (C) 2012 the trustee's of Indiana University <iu.edu>
574              
575             This library is free software; you can redistribute it and/or modify
576             it under the same terms as Perl itself, either Perl version 5.10.1 or,
577             at your option, any later version of Perl 5 you may have available.
578              
579             =cut