File Coverage

blib/lib/Mail/SpamAssassin/Plugin/HTMLEval.pm
Criterion Covered Total %
statement 36 114 31.5
branch 0 42 0.0
condition 1 27 3.7
subroutine 7 19 36.8
pod 1 13 7.6
total 45 215 20.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              
19             use strict;
20 22     22   159 use warnings;
  22         46  
  22         717  
21 22     22   131 # use bytes;
  22         62  
  22         691  
22             use re 'taint';
23 22     22   119  
  22         58  
  22         755  
24             use Mail::SpamAssassin::Plugin;
25 22     22   125 use Mail::SpamAssassin::Locales;
  22         41  
  22         596  
26 22     22   6550 use Mail::SpamAssassin::Util qw(untaint_var compile_regexp);
  22         50  
  22         686  
27 22     22   152  
  22         39  
  22         26422  
28             our @ISA = qw(Mail::SpamAssassin::Plugin);
29              
30             # constructor: register the eval rule
31             my $class = shift;
32             my $mailsaobject = shift;
33 63     63 1 235  
34 63         160 # some boilerplate...
35             $class = ref($class) || $class;
36             my $self = $class->SUPER::new($mailsaobject);
37 63   33     485 bless ($self, $class);
38 63         326  
39 63         179 # the important bit!
40             $self->register_eval_rule("html_tag_balance");
41             $self->register_eval_rule("html_image_only");
42 63         292 $self->register_eval_rule("html_image_ratio");
43 63         213 $self->register_eval_rule("html_charset_faraway");
44 63         213 $self->register_eval_rule("html_tag_exists");
45 63         199 $self->register_eval_rule("html_test");
46 63         275 $self->register_eval_rule("html_eval");
47 63         182 $self->register_eval_rule("html_text_match");
48 63         210 $self->register_eval_rule("html_title_subject_ratio");
49 63         191 $self->register_eval_rule("html_text_not_match");
50 63         195 $self->register_eval_rule("html_range");
51 63         180 $self->register_eval_rule("check_iframe_src");
52 63         180  
53 63         203 return $self;
54             }
55 63         513  
56             my ($self, $pms, undef, $rawtag, $rawexpr) = @_;
57              
58             return 0 if $rawtag !~ /^([a-zA-Z0-9]+)$/;
59 0     0 0   my $tag = $1;
60              
61 0 0         return 0 unless exists $pms->{html}{inside}{$tag};
62 0            
63             return 0 if $rawexpr !~ /^([\<\>\=\!\-\+ 0-9]+)$/;
64 0 0         my $expr = untaint_var($1);
65              
66 0 0         $pms->{html}{inside}{$tag} =~ /^([\<\>\=\!\-\+ 0-9]+)$/;
67 0           my $val = untaint_var($1);
68              
69 0           return eval "\$val $expr";
70 0           }
71              
72 0           my ($self, $pms, undef, $min, $max) = @_;
73              
74             return (exists $pms->{html}{inside}{img} &&
75             exists $pms->{html}{length} &&
76 0     0 0   $pms->{html}{length} > $min &&
77             $pms->{html}{length} <= $max);
78             }
79              
80             my ($self, $pms, undef, $min, $max) = @_;
81 0   0        
82             return 0 unless (exists $pms->{html}{non_space_len} &&
83             exists $pms->{html}{image_area} &&
84             $pms->{html}{image_area} > 0);
85 0     0 0   my $ratio = $pms->{html}{non_space_len} / $pms->{html}{image_area};
86             return ($ratio > $min && $ratio <= $max);
87             }
88              
89 0 0 0       my ($self, $pms) = @_;
      0        
90 0            
91 0   0       return 0 unless exists $pms->{html}{charsets};
92              
93             my @locales = Mail::SpamAssassin::Util::get_my_locales($pms->{conf}->{ok_locales});
94             return 0 if grep { $_ eq "all" } @locales;
95 0     0 0    
96             my $okay = 0;
97 0 0         my $bad = 0;
98             for my $c (split(' ', $pms->{html}{charsets})) {
99 0           if (Mail::SpamAssassin::Locales::is_charset_ok_for_locales($c, @locales)) {
100 0 0         $okay++;
  0            
101             }
102 0           else {
103 0           $bad++;
104 0           }
105 0 0         }
106 0           return ($bad && ($bad >= $okay));
107             }
108              
109 0           my ($self, $pms, undef, $tag) = @_;
110             return exists $pms->{html}{inside}{$tag};
111             }
112 0   0        
113             my ($self, $pms, undef, $test) = @_;
114             return $pms->{html}{$test};
115             }
116 0     0 0    
117 0           my ($self, $pms, undef, $test, $rawexpr) = @_;
118              
119             return 0 if $rawexpr !~ /^([\<\>\=\!\-\+ 0-9]+)$/;
120             my $expr = untaint_var($1);
121 0     0 0    
122 0           # workaround bug 3320: weird perl bug where additional, very explicit
123             # untainting into a new var is required.
124             my $tainted = $pms->{html}{$test};
125             return 0 unless defined($tainted);
126 0     0 0   my $val = $tainted;
127              
128 0 0         # just use the value in $val, don't copy it needlessly
129 0           return eval "\$val $expr";
130             }
131              
132             my ($self, $pms, undef, $text, $regexp) = @_;
133 0           my ($rec, $err) = compile_regexp($regexp, 0);
134 0 0         if (!$rec) {
135 0           warn "htmleval: html_text_match invalid regexp '$regexp': $err";
136             return 0;
137             }
138 0           foreach my $string (@{$pms->{html}{$text}}) {
139             next unless defined $string;
140             if ($string =~ $rec) {
141             return 1;
142 0     0 0   }
143 0           }
144 0 0         return 0;
145 0           }
146 0            
147             my ($self, $pms, undef, $ratio) = @_;
148 0            
  0            
149 0 0         my $subject = $pms->get('Subject');
150 0 0         if ($subject eq '') {
151 0           return 0;
152             }
153             my $max = 0;
154 0           for my $string (@{ $pms->{html}{title} }) {
155             if ($string) {
156             my $ratio = length($string) / length($subject);
157             $max = $ratio if $ratio > $max;
158 0     0 0   }
159             }
160 0           return $max > $ratio;
161 0 0         }
162 0            
163             my ($self, $pms, undef, $text, $regexp) = @_;
164 0           for my $string (@{ $pms->{html}{$text} }) {
165 0           if (defined $string && $string !~ /${regexp}/) {
  0            
166 0 0         return 1;
167 0           }
168 0 0         }
169             return 0;
170             }
171 0            
172             my ($self, $pms, undef, $test, $min, $max) = @_;
173              
174             return 0 unless exists $pms->{html}{$test};
175 0     0 0    
176 0           $test = $pms->{html}{$test};
  0            
177 0 0 0        
178 0           # not all perls understand what "inf" means, so we need to do
179             # non-numeric tests! urg!
180             if (!defined $max || $max eq "inf") {
181 0           return ($test eq "inf") ? 1 : ($test > $min);
182             }
183             elsif ($test eq "inf") {
184             # $max < inf, so $test == inf means $test > $max
185 0     0 0   return 0;
186             }
187 0 0         else {
188             # if we get here everything should be a number
189 0           return ($test > $min && $test <= $max);
190             }
191             }
192              
193 0 0 0       my ($self, $pms) = @_;
    0          
194 0 0          
195             foreach my $v ( values %{$pms->{html}->{uri_detail}} ) {
196             return 1 if $v->{types}->{iframe};
197             }
198 0            
199             return 0;
200             }
201              
202 0   0       1;