]> git.proxmox.com Git - proxmox-spamassassin.git/blob - upstream/lib/Mail/SpamAssassin/Plugin/PhishTag.pm
buildsys: drop upstream tarball and add extracted sources
[proxmox-spamassassin.git] / upstream / lib / Mail / SpamAssassin / Plugin / PhishTag.pm
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
20 package Mail::SpamAssassin::Plugin::PhishTag;
21
22 use strict;
23 use warnings;
24 use Errno qw(EBADF);
25 use Mail::SpamAssassin;
26 use Mail::SpamAssassin::Logger;
27
28 our @ISA = qw(Mail::SpamAssassin::Plugin);
29
30 sub new{
31 my ($class, $mailsa)=@_;
32 $class=ref($class) ||$class;
33 my $self = $class->SUPER::new($mailsa);
34 bless($self,$class);
35 $self->set_config($mailsa->{conf});
36 return $self;
37 }
38
39 sub set_config{
40 my($self, $conf) = @_;
41 my @cmds;
42
43 push (@cmds, {
44 setting => 'trigger_target',
45 type => $Mail::SpamAssassin::Conf::CONF_TYPE_HASH_KEY_VALUE,
46 is_admin => 1,
47 });
48
49 push (@cmds, {
50 setting => 'trigger_config',
51 type => $Mail::SpamAssassin::Conf::CONF_TYPE_STRING,
52 is_admin => 1,
53 default => '',
54 });
55
56 push (@cmds, {
57 setting => 'trigger_ratio',
58 type => $Mail::SpamAssassin::Conf::CONF_TYPE_NUMERIC,
59 is_admin => 1,
60 default => 0,
61 });
62
63 $conf->{parser}->register_commands(\@cmds);
64 }
65
66 #prepare the plugin
67 sub check_start{
68 my ($self, $params) = @_;
69 my $pms = $params->{permsgstatus};
70
71 #initialize the PHISHTAG data structure for
72 #saving configuration information
73 $pms->{PHISHTAG} = {};
74 $pms->{PHISHTAG}->{triggers}={};
75 $pms->{PHISHTAG}->{targets}=[];
76
77 #read the configuration info
78 $self->read_configfile($params);
79 $self->read_settings($params);
80 }
81
82 sub read_settings{
83 my ($self, $params) = @_;
84 my $pms = $params->{permsgstatus};
85
86 my $triggers= $pms->{PHISHTAG}->{triggers};
87 my $targets= $pms->{PHISHTAG}->{targets};
88 while (my ($tname,$ttarget)=each %{$pms->{conf}->{trigger_target}}){
89 push @$targets, [$ttarget, $tname];
90 $$triggers{$tname}=0;
91 }
92 }
93
94
95 sub read_configfile{
96 my ($self, $params) = @_;
97 my $pms = $params->{permsgstatus};
98
99 #nothing interesting here if there is not a configuration file
100 return if($pms->{conf}->{trigger_config} !~/\S/);
101
102 my $triggers= $pms->{PHISHTAG}->{triggers};
103 my $targets= $pms->{PHISHTAG}->{targets};
104
105 my $target;
106 local *F;
107 open(F, '<', $pms->{conf}->{trigger_config});
108 for ($!=0; <F>; $!=0) {
109 #each entry is separated by blank lines
110 undef($target) if(!/\S/);
111
112 #lines that start with pound are comments
113 next if(/^\s*\#/);
114
115 #an entry starts with a URL line prefixed with the word "target"
116 if(/^target\s+(\S+)/){
117 $target=[$1];
118 push @$targets,$target;
119 }
120 #add the test to the list of listened triggers
121 #and to the triggers of the last target
122 elsif(defined $target){
123 s/\s+//g;
124 $$triggers{$_}=0;
125 push @$target, $_;
126 }
127 }
128 defined $_ || $!==0 or
129 $!==EBADF ? dbg("PHISHTAG: error reading config file: $!")
130 : die "error reading config file: $!";
131 close(F) or die "error closing config file: $!";
132 }
133
134 sub hit_rule {
135 my ($self, $params) = @_;
136 my $pms = $params->{permsgstatus};
137 my $rulename = $params->{rulename};
138
139 #mark the rule as hit
140 if(defined($pms->{PHISHTAG}->{triggers}->{$rulename})){
141 $pms->{PHISHTAG}->{triggers}->{$rulename}=1;
142 dbg("PHISHTAG: $rulename has been caught\n");
143 }
144 }
145
146 sub check_post_learn {
147 my ($self, $params) = @_;
148 my $pms = $params->{permsgstatus};
149
150 #find out which targets have fulfilled their requirements
151 my $triggers= $pms->{PHISHTAG}->{triggers};
152 my $targets= $pms->{PHISHTAG}->{targets};
153 my @filled;
154 foreach my $target(@$targets){
155 my $uri= $$target[0];
156 my $fulfilled=1;
157 #all the triggers of a target have to exist for it to be fulfilled
158 foreach my $i(1..$#$target){
159 if(! $triggers->{$$target[$i]}){
160 $fulfilled=0;
161 last;
162 }
163 }
164 if($fulfilled){
165 push @filled, $uri;
166 dbg("PHISHTAG: Fulfilled $uri\n");
167 }
168 }
169
170 if(scalar(@filled) &&
171 $pms->{conf}->{trigger_ratio} > rand(100)){
172 $pms->{PHISHTAG}->{letgo}=0;
173 $pms->{PHISHTAG}->{uri}=$filled[int(rand(scalar(@filled)))];
174
175 dbg("PHISHTAG: Decided to keep this email and point to ".
176 $pms->{PHISHTAG}->{uri});
177 #make sure that SpamAssassin does not remove this email
178 $pms->got_hit("PHISHTAG_TOSS",
179 "BODY: ",
180 score => -100);
181 }
182 else{
183 dbg("PHISHTAG: Will let this email to SpamAssassin's discretion\n");
184 $pms->{PHISHTAG}->{letgo}=1;
185 }
186
187
188 #nothing interesting here, if we will not rewrite the email
189 if($pms->{PHISHTAG}->{letgo}){
190 return;
191 }
192
193 my $pristine_body=\$pms->{msg}->{pristine_body};
194 #dbg("PRISTINE>>\n".$$pristine_body);
195
196 my $uris = $pms->get_uri_detail_list();
197 #rewrite the url
198 while (my($uri, $info) = each %{$uris}) {
199 if(defined ($info->{types}->{a})){
200 $$pristine_body=~s/$uri/$pms->{PHISHTAG}->{uri}/mg;
201 }
202 }
203 dbg("PRISTINE>>\n".$$pristine_body);
204 }
205
206 1;
207 __END__
208
209 =head1 NAME
210
211 PhishTag - SpamAssassin plugin for redirecting links in incoming emails.
212
213 =head1 SYNOPSIS
214
215 loadplugin Mail::SpamAssassin::Plugin::PhishTag
216
217 trigger_ratio 0.1
218 trigger_target RULE_NAME http://www.antiphishing.org/consumer_recs.html
219
220 =head1 DESCRIPTION
221
222 PhishTag enables administrators to rewrite links in emails that trigger certain
223 tests, preferably anti-phishing blacklist tests. The plugin will inhibit the
224 blocking of a portion of the emails that trigger the test by SpamAssassin, and
225 let them pass to the users' inbox after the rewrite. It is useful in providing
226 training to email users about company policies and general email usage.
227
228 =head1 OPTIONS
229
230 The following options can be set by modifying the configuration file.
231
232 =over 4
233
234 =item * trigger_ratio percentage_value
235
236 Sets the probability in percentage that a positive test will trigger the
237 email rewrite, e.g. 0.1 will rewrite on the average 1 in 1000 emails that
238 match the trigger.
239
240 =item * trigger_target RULE_NAME http_url
241
242 The name of the test which would trigger the email rewrite; all the URLs
243 will be replaced by http_url.
244
245 =back
246
247 =head1 DOWNLOAD
248
249 The source of this plugin is available at:
250 http://umut.topkara.org/PhishTag/PhishTag.pm
251 a sample configuration file is also available:
252 http://umut.topkara.org/PhishTag/PhishTag.cf
253
254 =head1 SEE ALSO
255
256 Check the list of tests performed by SpamAssassin to modify the
257 configuration file to match your needs from
258 https://spamassassin.apache.org/tests.html
259
260 =head1 AUTHOR
261
262 Umut Topkara, 2008, E<lt>umut@topkara.orgE<gt>
263 http://umut.topkara.org
264
265 =head1 COPYRIGHT AND LICENSE
266
267 This plugin is free software; you can redistribute it and/or modify
268 it under the same terms as SpamAssassin itself, either version 3.2.4
269 or, at your option, any later version of SpamAssassin you may have
270 available.
271
272
273 =cut