=head1 NAME
AntiVirus - simple anti-virus tests
=head1 SYNOPSIS
loadplugin Mail::SpamAssassin::Plugin::AntiVirus
body MICROSOFT_EXECUTABLE eval:check_microsoft_executable()
body MIME_SUSPECT_NAME eval:check_suspect_name()
=head1 DESCRIPTION
The MICROSOFT_EXECUTABLE rule works by checking for 3 possibilities in
the message in any application/* or text/* part in the message:
=over 4
=item - in text parts, look for a uuencoded executable start string
=item - in application parts, look for filenames ending in an executable extension
=item - in application parts, look for a base64 encoded executable start string
=back
=cut
package Mail::SpamAssassin::Plugin::AntiVirus;
use Mail::SpamAssassin::Plugin;
use Mail::SpamAssassin::Util;
use strict;
use warnings;
use bytes;
use vars qw(@ISA);
@ISA = qw(Mail::SpamAssassin::Plugin);
sub new {
my $class = shift;
my $mailsaobject = shift;
$class = ref($class) || $class;
my $self = $class->SUPER::new($mailsaobject);
bless ($self, $class);
$self->register_eval_rule("check_microsoft_executable");
$self->register_eval_rule("check_suspect_name");
return $self;
}
sub check_microsoft_executable {
my ($self, $pms) = @_;
_check_attachments(@_) unless exists $pms->{antivirus_microsoft_exe};
return $pms->{antivirus_microsoft_exe};
}
sub check_suspect_name {
my ($self, $pms) = @_;
_check_attachments(@_) unless exists $pms->{antivirus_suspect_name};
return $pms->{antivirus_suspect_name};
}
sub _check_attachments {
my ($self, $pms) = @_;
$pms->{antivirus_microsoft_exe} = 0;
$pms->{antivirus_suspect_name} = 0;
foreach my $p ($pms->{msg}->find_parts(qr/./, 1)) {
my ($ctype, $boundary, $charset, $name) =
Mail::SpamAssassin::Util::parse_content_type($p->get_header('content-type'));
$name = lc $name || '';
my $cte = lc $p->get_header('content-transfer-encoding') || '';
$ctype = lc $ctype;
if ($name && $name =~ /\.(?:ade|adp|asx|bas|bat|chm|cmd|com|cpl|crt|dll|exe|hlp|hta|inf|ins|isp|js|jse|lnk|mda|mdb|mde|mdt|mdw|mdz|msc|msi|msp|mst|nws|ops|pcd|pif|prf|reg|scf|scr\??|sct|shb|shs|shm|swf|url|vb|vbe|vbs|vbx|vxd|wsc|wsf|wsh)$/)
{
$pms->{antivirus_microsoft_exe} = 1;
}
elsif ($cte =~ /base64/ && defined $p->raw()->[0] &&
$p->raw()->[0] =~ /^TV[opqr].A..[AB].[AQgw][A-H].A/)
{
$pms->{antivirus_microsoft_exe} = 1;
}
elsif ($ctype =~ /^text\b/) {
for (@{$p->raw()}) {
if (/^M35[GHIJK].`..`..*````/) {
$pms->{antivirus_microsoft_exe} = 1;
}
}
}
if ($name && $ctype ne "application/octet-stream") {
$name =~ s/.*\.//;
$ctype =~ s@/(x-|vnd\.)@/@;
if (
(($name =~ /^(?:txt|[px]?html?|xml)$/) &&
($ctype !~ m@^(?:text/(?:plain|[px]?html?|english|sgml|xml|enriched|richtext)|message/external-body)@)) ||
(($name =~ /^(?:jpe?g|tiff?|gif|png)$/) &&
($ctype !~ m@^(?:image/|application/mac-binhex)@)) ||
(($name eq "vcf") && $ctype ne "text/vcard") ||
(($name =~ /^(?:bat|com|exe|pif|scr|swf|vbs)$/) &&
($ctype !~ m@^application/@)) ||
(($name eq "doc") && ($ctype !~ m@^application/.*word$@)) ||
(($name eq "ppt") &&
($ctype !~ m@^application/.*(?:powerpoint|ppt)$@)) ||
(($name eq "xls") && ($ctype !~ m@^application/.*excel$@))
)
{
$pms->{antivirus_suspect_name} = 1;
}
}
}
}
1;