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              
47             use Mail::SpamAssassin::Plugin;
48 20     20   137 use Mail::SpamAssassin::Util;
  20         42  
  20         639  
49 20     20   114 use strict;
  20         39  
  20         827  
50 20     20   113 use warnings;
  20         36  
  20         507  
51 20     20   98 # use bytes;
  20         33  
  20         718  
52             use re 'taint';
53 20     20   115  
  20         33  
  20         16268  
54             our @ISA = qw(Mail::SpamAssassin::Plugin);
55              
56             # constructor: register the eval rule
57             my $class = shift;
58             my $mailsaobject = shift;
59 61     61 1 217  
60 61         142 # some boilerplate...
61             $class = ref($class) || $class;
62             my $self = $class->SUPER::new($mailsaobject);
63 61   33     371 bless ($self, $class);
64 61         357  
65 61         168 $self->register_eval_rule("check_microsoft_executable");
66             $self->register_eval_rule("check_suspect_name");
67 61         245  
68 61         195 return $self;
69             }
70 61         468  
71             my ($self, $pms) = @_;
72              
73             _check_attachments(@_) unless exists $pms->{antivirus_microsoft_exe};
74 0     0 0    
75             return $pms->{antivirus_microsoft_exe};
76 0 0         }
77              
78 0           my ($self, $pms) = @_;
79              
80             _check_attachments(@_) unless exists $pms->{antivirus_suspect_name};
81              
82 0     0 0   return $pms->{antivirus_suspect_name};
83             }
84 0 0          
85             my ($self, $pms) = @_;
86 0            
87             $pms->{antivirus_microsoft_exe} = 0;
88             $pms->{antivirus_suspect_name} = 0;
89              
90 0     0     # MICROSOFT_EXECUTABLE triggered here
91             foreach my $p ($pms->{msg}->find_parts(qr/./, 1)) {
92 0           my ($ctype, $boundary, $charset, $name) =
93 0           Mail::SpamAssassin::Util::parse_content_type($p->get_header('content-type'));
94              
95             $name = lc($name || '');
96 0            
97 0           my $cte = lc($p->get_header('content-transfer-encoding') || '');
98             $ctype = lc $ctype;
99              
100 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)$/)
101             {
102 0   0       # file extension indicates an executable
103 0           $pms->{antivirus_microsoft_exe} = 1;
104             }
105 0 0 0       elsif ($cte =~ /base64/ && defined $p->raw()->[0] &&
    0 0        
    0 0        
106             $p->raw()->[0] =~ /^TV[opqr].A..[AB].[AQgw][A-H].A/)
107             {
108 0           # base64-encoded executable
109             $pms->{antivirus_microsoft_exe} = 1;
110             }
111             elsif ($ctype =~ /^text\b/) {
112             # uuencoded executable
113             for (@{$p->raw()}) {
114 0           if (/^M35[GHIJK].`..`..*````/) {
115             # uuencoded executable
116             $pms->{antivirus_microsoft_exe} = 1;
117             }
118 0           }
  0            
119 0 0         }
120              
121 0           # MIME_SUSPECT_NAME triggered here
122             if ($name && $ctype ne "application/octet-stream") {
123             $name =~ s/.*\.//;
124             $ctype =~ s@/(x-|vnd\.)@/@;
125              
126             if (
127 0 0 0       # text
128 0           (($name =~ /^(?:txt|[px]?html?|xml)$/) &&
129 0           ($ctype !~ m@^(?:text/(?:plain|[px]?html?|english|sgml|xml|enriched|richtext)|message/external-body)@)) ||
130              
131 0 0 0       # image
      0        
      0        
      0        
      0        
      0        
      0        
      0        
      0        
      0        
      0        
      0        
      0        
132             (($name =~ /^(?:jpe?g|tiff?|gif|png)$/) &&
133             ($ctype !~ m@^(?:image/|application/mac-binhex)@)) ||
134              
135             # vcard
136             (($name eq "vcf") && $ctype ne "text/vcard") ||
137              
138             # application
139             (($name =~ /^(?:bat|com|exe|pif|scr|swf|vbs)$/) &&
140             ($ctype !~ m@^application/@)) ||
141              
142             # msword
143             (($name eq "doc") && ($ctype !~ m@^application/.*word$@)) ||
144              
145             # powerpoint
146             (($name eq "ppt") &&
147             ($ctype !~ m@^application/.*(?:powerpoint|ppt)$@)) ||
148              
149             # excel
150             (($name eq "xls") && ($ctype !~ m@^application/.*excel$@))
151             )
152             {
153             $pms->{antivirus_suspect_name} = 1;
154             }
155             }
156             }
157             }
158 0            
159             1;