package ossec::log::parse;
# ABSTRACT: Perl interface for parsing Ossec alert files
use strict;
use warnings;
use autodie;
use Carp;
use Scalar::Util qw/openhandle/;
our $VERSION = '0.1.0';
BEGIN {
my @accessors = qw/fh file/;
for my $accessor ( @accessors ) {
no strict 'refs';
*$accessor = sub {
my $self = shift;
return $self->{$accessor};
}
}
}
sub new {
my $class = shift;
my $arg = shift;
my $self = {};
if ( !defined($arg) ) {
$self->{diamond} = 1;
} elsif ( ref($arg) eq 'HASH' ) {
$self = $arg;
} elsif ( defined(openhandle($arg)) ) {
$self->{fh} = $arg;
} else {
$self->{file} = $arg;
}
bless $self, $class;
if ( defined($self->{file}) && !(defined($self->{fh})) ) {
unless ( -f $self->{file} ) {
carp("Could not open ".$self->{file});
return 0;
}
open( my $fh, "<", $self->{file} ) or croak("Cannot open ".$self->{file});
$self->{fh} = $fh;
}
if ( !defined($self->{fh}) && ( !defined($self->{diamond}) || !$self->{diamond} ) ) {
carp("No filename given in constructor. Aborting");
return 0;
}
return $self;
}
sub getAlert {
my $self = shift;
my $fh = $self->{fh};
my %alert;
my $position = 0;
while ( my $line = defined($fh) ? <$fh> : <> ) {
chomp($line);
if ($line =~ m/^\*\* Alert (\d+\.\d+):(.*)-(.*)/) {
$alert{'ts'} = $1;
$alert{'type'} = $2;
$alert{'group'} = $3;
# clean up variables
$alert{'type'} =~ s/^\s+|\s+$//g; # strip leading/trailing whitespace
$alert{'group'} =~ s/^\s+|\s+$//g;
$alert{'group'} =~ s/,$//g; # strip trailing comma
$position = 1;
next;
}
if ($position > 0) {
$position++;
if ($position == 2) {
if ($line =~ m!(\d+ \w+ \d+ \d+:\d+:\d+) \((.*)\) (\S+)->(.*)!) { # alert from remote agent
$alert{'ts.human'} = $1;
$alert{'agent.name'} = $2;
$alert{'agent.ip'} = $3;
$alert{'location'} = $4;
}
elsif ($line =~ m!(\d+ \w+ \d+ \d+:\d+:\d+) (\S+)->(.*)!) { # alert from local agent
$alert{'ts.human'} = $1;
$alert{'agent.name'} = $2;
$alert{'agent.ip'} = '-';
$alert{'location'} = $3;
}
$alert{'2'} = $line;
next;
}
elsif ($position == 3) {
if ($line =~ /^Rule: (\d+) \(level (\d+)\) -> '(.*)'$/) {
$alert{'rule.id'} = $1;
$alert{'rule.level'} = $2;
$alert{'rule.comment'} = $3;
# clean up variable
$alert{'rule.comment'} =~ s/\.$//; # remove any trailing period
}
$alert{'3'} = $line;
next;
}
elsif ($line !~ m/^$/ ) {
if ($alert{'full_log'} ) {
$alert{'full_log'} = "$alert{'full_log'}\n$line";
}
else {
$alert{'full_log'} = $line;
}
}
else {
$position = 0;
return \%alert;
}
}
}
}
1;