File Coverage

blib/lib/Mail/SpamAssassin/Plugin/AntiVirus.pm
Criterion Covered Total %
statement 23 49 46.9
branch 0 16 0.0
condition 1 58 1.7
subroutine 6 9 66.6
pod 1 3 33.3
total 31 135 22.9


line stmt bran cond sub pod time code
1             # <@LICENSE>
2             # Licensed to the Apache Software Foundation (ASF) under one or more
3             # contributor license agreements. See the NOTICE file distributed with
4             # this work for additional information regarding copyright ownership.
5             # The ASF licenses this file to you under the Apache License, Version 2.0
6             # (the "License"); you may not use this file except in compliance with
7             # the License. You may obtain a copy of the License at:
8             #
9             # http://www.apache.org/licenses/LICENSE-2.0
10             #
11             # Unless required by applicable law or agreed to in writing, software
12             # distributed under the License is distributed on an "AS IS" BASIS,
13             # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14             # See the License for the specific language governing permissions and
15             # limitations under the License.
16             # </@LICENSE>
17              
18             =head1 NAME
19              
20             AntiVirus - simple anti-virus tests
21              
22             =head1 SYNOPSIS
23              
24             loadplugin Mail::SpamAssassin::Plugin::AntiVirus
25              
26             body MICROSOFT_EXECUTABLE eval:check_microsoft_executable()
27             body MIME_SUSPECT_NAME eval:check_suspect_name()
28              
29             =head1 DESCRIPTION
30              
31             The MICROSOFT_EXECUTABLE rule works by checking for 3 possibilities in
32             the message in any application/* or text/* part in the message:
33              
34             =over 4
35              
36             =item - in text parts, look for a uuencoded executable start string
37              
38             =item - in application parts, look for filenames ending in an executable extension
39              
40             =item - in application parts, look for a base64 encoded executable start string
41              
42             =back
43              
44             =cut
45              
46             package Mail::SpamAssassin::Plugin::AntiVirus;
47              
48 19     19   141 use Mail::SpamAssassin::Plugin;
  19         45  
  19         727  
49 19     19   122 use Mail::SpamAssassin::Util;
  19         40  
  19         7065  
50 19     19   126 use strict;
  19         37  
  19         451  
51 19     19   118 use warnings;
  19         39  
  19         645  
52             # use bytes;
53 19     19   133 use re 'taint';
  19         53  
  19         17844  
54              
55             our @ISA = qw(Mail::SpamAssassin::Plugin);
56              
57             # constructor: register the eval rule
58             sub new {
59 60     60 1 211 my $class = shift;
60 60         150 my $mailsaobject = shift;
61              
62             # some boilerplate...
63 60   33     425 $class = ref($class) || $class;
64 60         363 my $self = $class->SUPER::new($mailsaobject);
65 60         209 bless ($self, $class);
66              
67 60         352 $self->register_eval_rule("check_microsoft_executable");
68 60         267 $self->register_eval_rule("check_suspect_name");
69              
70 60         526 return $self;
71             }
72              
73             sub check_microsoft_executable {
74 0     0 0   my ($self, $pms) = @_;
75              
76 0 0         _check_attachments(@_) unless exists $pms->{antivirus_microsoft_exe};
77              
78 0           return $pms->{antivirus_microsoft_exe};
79             }
80              
81             sub check_suspect_name {
82 0     0 0   my ($self, $pms) = @_;
83              
84 0 0         _check_attachments(@_) unless exists $pms->{antivirus_suspect_name};
85              
86 0           return $pms->{antivirus_suspect_name};
87             }
88              
89             sub _check_attachments {
90 0     0     my ($self, $pms) = @_;
91              
92 0           $pms->{antivirus_microsoft_exe} = 0;
93 0           $pms->{antivirus_suspect_name} = 0;
94              
95             # MICROSOFT_EXECUTABLE triggered here
96 0           foreach my $p ($pms->{msg}->find_parts(qr/./, 1)) {
97 0           my ($ctype, $boundary, $charset, $name) =
98             Mail::SpamAssassin::Util::parse_content_type($p->get_header('content-type'));
99              
100 0   0       $name = lc($name || '');
101              
102 0   0       my $cte = lc($p->get_header('content-transfer-encoding') || '');
103 0           $ctype = lc $ctype;
104              
105 0 0 0       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)$/)
    0 0        
    0 0        
106             {
107             # file extension indicates an executable
108 0           $pms->{antivirus_microsoft_exe} = 1;
109             }
110             elsif ($cte =~ /base64/ && defined $p->raw()->[0] &&
111             $p->raw()->[0] =~ /^TV[opqr].A..[AB].[AQgw][A-H].A/)
112             {
113             # base64-encoded executable
114 0           $pms->{antivirus_microsoft_exe} = 1;
115             }
116             elsif ($ctype =~ /^text\b/) {
117             # uuencoded executable
118 0           for (@{$p->raw()}) {
  0            
119 0 0         if (/^M35[GHIJK].`..`..*````/) {
120             # uuencoded executable
121 0           $pms->{antivirus_microsoft_exe} = 1;
122             }
123             }
124             }
125              
126             # MIME_SUSPECT_NAME triggered here
127 0 0 0       if ($name && $ctype ne "application/octet-stream") {
128 0           $name =~ s/.*\.//;
129 0           $ctype =~ s@/(x-|vnd\.)@/@;
130              
131 0 0 0       if (
      0        
      0        
      0        
      0        
      0        
      0        
      0        
      0        
      0        
      0        
      0        
      0        
132             # text
133             (($name =~ /^(?:txt|[px]?html?|xml)$/) &&
134             ($ctype !~ m@^(?:text/(?:plain|[px]?html?|english|sgml|xml|enriched|richtext)|message/external-body)@)) ||
135              
136             # image
137             (($name =~ /^(?:jpe?g|tiff?|gif|png)$/) &&
138             ($ctype !~ m@^(?:image/|application/mac-binhex)@)) ||
139              
140             # vcard
141             (($name eq "vcf") && $ctype ne "text/vcard") ||
142              
143             # application
144             (($name =~ /^(?:bat|com|exe|pif|scr|swf|vbs)$/) &&
145             ($ctype !~ m@^application/@)) ||
146              
147             # msword
148             (($name eq "doc") && ($ctype !~ m@^application/.*word$@)) ||
149              
150             # powerpoint
151             (($name eq "ppt") &&
152             ($ctype !~ m@^application/.*(?:powerpoint|ppt)$@)) ||
153              
154             # excel
155             (($name eq "xls") && ($ctype !~ m@^application/.*excel$@))
156             )
157             {
158 0           $pms->{antivirus_suspect_name} = 1;
159             }
160             }
161             }
162             }
163              
164             1;