You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@spamassassin.apache.org by km...@apache.org on 2018/12/14 21:05:01 UTC
svn commit: r1848969 [1/2] - in /spamassassin/branches/3.4: ./
lib/Mail/SpamAssassin/ lib/Mail/SpamAssassin/Conf/
lib/Mail/SpamAssassin/Plugin/ t/
Author: kmcgrail
Date: Fri Dec 14 21:05:01 2018
New Revision: 1848969
URL: http://svn.apache.org/viewvc?rev=1848969&view=rev
Log:
Work on improving evaluation rules and preparing for 3.4.3
Modified:
spamassassin/branches/3.4/MANIFEST
spamassassin/branches/3.4/UPGRADE
spamassassin/branches/3.4/lib/Mail/SpamAssassin/Conf.pm
spamassassin/branches/3.4/lib/Mail/SpamAssassin/Conf/Parser.pm
spamassassin/branches/3.4/lib/Mail/SpamAssassin/Constants.pm
spamassassin/branches/3.4/lib/Mail/SpamAssassin/Dns.pm
spamassassin/branches/3.4/lib/Mail/SpamAssassin/Logger.pm
spamassassin/branches/3.4/lib/Mail/SpamAssassin/Message.pm
spamassassin/branches/3.4/lib/Mail/SpamAssassin/PerMsgStatus.pm
spamassassin/branches/3.4/lib/Mail/SpamAssassin/Plugin/Bayes.pm
spamassassin/branches/3.4/lib/Mail/SpamAssassin/Plugin/BodyRuleBaseExtractor.pm
spamassassin/branches/3.4/lib/Mail/SpamAssassin/Plugin/Check.pm
spamassassin/branches/3.4/lib/Mail/SpamAssassin/Plugin/HTMLEval.pm
spamassassin/branches/3.4/lib/Mail/SpamAssassin/Plugin/MIMEHeader.pm
spamassassin/branches/3.4/lib/Mail/SpamAssassin/Plugin/P595Body.pm
spamassassin/branches/3.4/lib/Mail/SpamAssassin/Plugin/PDFInfo.pm
spamassassin/branches/3.4/lib/Mail/SpamAssassin/Plugin/ReplaceTags.pm
spamassassin/branches/3.4/lib/Mail/SpamAssassin/Plugin/Rule2XSBody.pm
spamassassin/branches/3.4/lib/Mail/SpamAssassin/Plugin/URIDetail.pm
spamassassin/branches/3.4/lib/Mail/SpamAssassin/PluginHandler.pm
spamassassin/branches/3.4/lib/Mail/SpamAssassin/Util.pm
spamassassin/branches/3.4/t/dnsbl.t
spamassassin/branches/3.4/t/if_can.t
spamassassin/branches/3.4/t/mimeheader.t
spamassassin/branches/3.4/t/regexp_valid.t
spamassassin/branches/3.4/t/stop_always_matching_regexps.t
Modified: spamassassin/branches/3.4/MANIFEST
URL: http://svn.apache.org/viewvc/spamassassin/branches/3.4/MANIFEST?rev=1848969&r1=1848968&r2=1848969&view=diff
==============================================================================
--- spamassassin/branches/3.4/MANIFEST (original)
+++ spamassassin/branches/3.4/MANIFEST Fri Dec 14 21:05:01 2018
@@ -224,6 +224,7 @@ sql/userpref_mysql.sql
sql/userpref_pg.sql
sql/txrep_mysql.sql
sql/txrep_pg.sql
+sql/txrep_sqlite.sql
t/README
t/SATest.pl
t/SATest.pm
Modified: spamassassin/branches/3.4/UPGRADE
URL: http://svn.apache.org/viewvc/spamassassin/branches/3.4/UPGRADE?rev=1848969&r1=1848968&r2=1848969&view=diff
==============================================================================
--- spamassassin/branches/3.4/UPGRADE (original)
+++ spamassassin/branches/3.4/UPGRADE Fri Dec 14 21:05:01 2018
@@ -21,6 +21,11 @@ Note for Users Upgrading to SpamAssassin
to run sa-update" unless it can find list from config files.
+- Deprecated functions: Parser::is_delimited_regexp_valid(),
+ Parser::is_regexp_valid(), Util::regexp_remove_delimiters(),
+ Util::make_qr(). These all are combined into new Util::compile_regexp().
+
+
Note for Users Upgrading to SpamAssassin 3.4.2
----------------------------------------------
Modified: spamassassin/branches/3.4/lib/Mail/SpamAssassin/Conf.pm
URL: http://svn.apache.org/viewvc/spamassassin/branches/3.4/lib/Mail/SpamAssassin/Conf.pm?rev=1848969&r1=1848968&r2=1848969&view=diff
==============================================================================
--- spamassassin/branches/3.4/lib/Mail/SpamAssassin/Conf.pm (original)
+++ spamassassin/branches/3.4/lib/Mail/SpamAssassin/Conf.pm Fri Dec 14 21:05:01 2018
@@ -82,12 +82,11 @@ use warnings;
# use bytes;
use re 'taint';
-use Mail::SpamAssassin::Util;
use Mail::SpamAssassin::NetSet;
use Mail::SpamAssassin::Constants qw(:sa :ip);
use Mail::SpamAssassin::Conf::Parser;
use Mail::SpamAssassin::Logger;
-use Mail::SpamAssassin::Util qw(untaint_var);
+use Mail::SpamAssassin::Util qw(untaint_var compile_regexp);
use File::Spec;
our @ISA = qw();
@@ -2733,24 +2732,23 @@ Example: http://chkpt.zdnet.com/chkpt/wh
push (@cmds, {
setting => 'redirector_pattern',
is_priv => 1,
+ default => [],
+ type => $CONF_TYPE_STRINGLIST,
code => sub {
my ($self, $key, $value, $line) = @_;
+
+ $value =~ s/^\s+//;
if ($value eq '') {
return $MISSING_REQUIRED_VALUE;
}
- elsif (!$self->{parser}->is_delimited_regexp_valid("redirector_pattern", $value)) {
+
+ my ($rec, $err) = compile_regexp($value, 1);
+ if (!$rec) {
+ dbg("config: invalid redirector_pattern '$value': $err");
return $INVALID_VALUE;
}
- # convert to qr// while including modifiers
- local ($1,$2,$3);
- $value =~ /^m?(\W)(.*)(?:\1|>|}|\)|\])(.*?)$/;
- my $pattern = $2;
- $pattern = "(?".$3.")".$pattern if $3;
- $pattern = qr/$pattern/;
-
- push @{$self->{main}->{conf}->{redirector_patterns}}, $pattern;
- # dbg("config: adding redirector regex: " . $value);
+ push @{$self->{main}->{conf}->{redirector_patterns}}, $rec;
}
});
@@ -2983,11 +2981,9 @@ why the IP is listed, typically a hyperl
Create a sub-test for 'set'. If you want to look up a multi-meaning zone
like relays.osirusoft.com, you can then query the results from that zone
using the zone ID from the original query. The sub-test may either be an
-IPv4 dotted address for RBLs that return multiple A records or a
+IPv4 dotted address for RBLs that return multiple A records, or a
non-negative decimal number to specify a bitmask for RBLs that return a
-single A record containing a bitmask of results, a SenderBase test
-beginning with "sb:", or (if none of the preceding options seem to fit) a
-regular expression.
+single A record containing a bitmask of results, or a regular expression.
Note: the set name must be exactly the same for as the main query rule,
including selections like '-notfirsthop' appearing at the end of the set
@@ -3001,11 +2997,17 @@ name.
is_priv => 1,
code => sub {
my ($self, $key, $value, $line) = @_;
- local ($1,$2);
- if ($value =~ /^(\S+)\s+(?:rbl)?eval:(.*)$/) {
- my ($rulename, $fn) = ($1, $2);
- dbg("config: header eval rule name is $rulename function is $fn");
- if ($fn !~ /^\w+(\(.*\))?$/) {
+ local($1);
+ if ($value !~ s/^(\S+)\s+//) {
+ return $INVALID_VALUE;
+ }
+ my $rulename = $1;
+ if ($value eq '') {
+ return $MISSING_REQUIRED_VALUE;
+ }
+ if ($value =~ /^(?:rbl)?eval:(.*)$/) {
+ my $fn = $1;
+ if ($fn !~ /^\w+\(.*\)$/) {
return $INVALID_VALUE;
}
if ($fn =~ /^check_(?:rbl|dns)/) {
@@ -3015,25 +3017,9 @@ name.
$self->{parser}->add_test ($rulename, $fn, $TYPE_HEAD_EVALS);
}
}
- elsif ($value =~ /^(\S+)\s+exists:(.*)$/) {
- my ($rulename, $header_name) = ($1, $2);
- # RFC 5322 section 3.6.8, ftext printable US-ASCII ch not including ":"
- if ($header_name !~ /\S/) {
- return $MISSING_REQUIRED_VALUE;
- # } elsif ($header_name !~ /^([!-9;-\176]+)$/) {
- } elsif ($header_name !~ /^([^: \t]+)$/) { # be generous
- return $INVALID_HEADER_FIELD_NAME;
- }
- $self->{parser}->add_test ($rulename, "defined($header_name)",
- $TYPE_HEAD_TESTS);
- $self->{descriptions}->{$rulename} = "Found a $header_name header";
- }
else {
- my @values = split(/\s+/, $value, 2);
- if (@values != 2) {
- return $MISSING_REQUIRED_VALUE;
- }
- $self->{parser}->add_test (@values, $TYPE_HEAD_TESTS);
+ # Detailed parsing in add_test
+ $self->{parser}->add_test ($rulename, $value, $TYPE_HEAD_TESTS);
}
}
});
@@ -3063,22 +3049,22 @@ Define a body eval test. See above.
is_priv => 1,
code => sub {
my ($self, $key, $value, $line) = @_;
- local ($1,$2);
- if ($value =~ /^(\S+)\s+eval:(.*)$/) {
- my ($rulename, $fn) = ($1, $2);
- dbg("config: body eval rule name is $rulename function is $fn");
-
- if ($fn !~ /^\w+(\(.*\))?$/) {
+ local($1);
+ if ($value !~ s/^(\S+)\s+//) {
+ return $INVALID_VALUE;
+ }
+ my $rulename = $1;
+ if ($value eq '') {
+ return $MISSING_REQUIRED_VALUE;
+ }
+ if ($value =~ /^eval:(.*)$/) {
+ my $fn = $1;
+ if ($fn !~ /^\w+\(.*\)$/) {
return $INVALID_VALUE;
}
$self->{parser}->add_test ($rulename, $fn, $TYPE_BODY_EVALS);
- }
- else {
- my @values = split(/\s+/, $value, 2);
- if (@values != 2) {
- return $MISSING_REQUIRED_VALUE;
- }
- $self->{parser}->add_test (@values, $TYPE_BODY_TESTS);
+ } else {
+ $self->{parser}->add_test ($rulename, $value, $TYPE_BODY_TESTS);
}
}
});
@@ -3107,11 +3093,15 @@ points of the URI, and will also be fast
is_priv => 1,
code => sub {
my ($self, $key, $value, $line) = @_;
- my @values = split(/\s+/, $value, 2);
- if (@values != 2) {
+ local($1);
+ if ($value !~ s/^(\S+)\s+//) {
+ return $INVALID_VALUE;
+ }
+ my $rulename = $1;
+ if ($value eq '') {
return $MISSING_REQUIRED_VALUE;
}
- $self->{parser}->add_test (@values, $TYPE_URI_TESTS);
+ $self->{parser}->add_test ($rulename, $value, $TYPE_URI_TESTS);
}
});
@@ -3138,15 +3128,22 @@ Define a raw-body eval test. See above.
is_priv => 1,
code => sub {
my ($self, $key, $value, $line) = @_;
- local ($1,$2);
- if ($value =~ /^(\S+)\s+eval:(.*)$/) {
- $self->{parser}->add_test ($1, $2, $TYPE_RAWBODY_EVALS);
+ local($1);
+ if ($value !~ s/^(\S+)\s+//) {
+ return $INVALID_VALUE;
+ }
+ my $rulename = $1;
+ if ($value eq '') {
+ return $MISSING_REQUIRED_VALUE;
+ }
+ if ($value =~ /^eval:(.*)$/) {
+ my $fn = $1;
+ if ($fn !~ /^\w+\(.*\)$/) {
+ return $INVALID_VALUE;
+ }
+ $self->{parser}->add_test ($rulename, $fn, $TYPE_RAWBODY_EVALS);
} else {
- my @values = split(/\s+/, $value, 2);
- if (@values != 2) {
- return $MISSING_REQUIRED_VALUE;
- }
- $self->{parser}->add_test (@values, $TYPE_RAWBODY_TESTS);
+ $self->{parser}->add_test ($rulename, $value, $TYPE_RAWBODY_TESTS);
}
}
});
@@ -3172,15 +3169,22 @@ Define a full message eval test. See ab
is_priv => 1,
code => sub {
my ($self, $key, $value, $line) = @_;
- local ($1,$2);
- if ($value =~ /^(\S+)\s+eval:(.*)$/) {
- $self->{parser}->add_test ($1, $2, $TYPE_FULL_EVALS);
+ local($1);
+ if ($value !~ s/^(\S+)\s+//) {
+ return $INVALID_VALUE;
+ }
+ my $rulename = $1;
+ if ($value eq '') {
+ return $MISSING_REQUIRED_VALUE;
+ }
+ if ($value =~ /^eval:(.*)$/) {
+ my $fn = $1;
+ if ($fn !~ /^\w+\(.*\)$/) {
+ return $INVALID_VALUE;
+ }
+ $self->{parser}->add_test ($rulename, $fn, $TYPE_FULL_EVALS);
} else {
- my @values = split(/\s+/, $value, 2);
- if (@values != 2) {
- return $MISSING_REQUIRED_VALUE;
- }
- $self->{parser}->add_test (@values, $TYPE_FULL_TESTS);
+ $self->{parser}->add_test ($rulename, $value, $TYPE_FULL_TESTS);
}
}
});
@@ -3225,15 +3229,19 @@ ignore these for scoring.
is_priv => 1,
code => sub {
my ($self, $key, $value, $line) = @_;
- my @values = split(/\s+/, $value, 2);
- if (@values != 2) {
+ local($1);
+ if ($value !~ s/^(\S+)\s+//) {
+ return $INVALID_VALUE;
+ }
+ my $rulename = $1;
+ if ($value eq '') {
return $MISSING_REQUIRED_VALUE;
}
- if ($values[1] =~ /\*\s*\*/) {
+ if ($value =~ /\*\s*\*/) {
info("config: found invalid '**' or '* *' operator in meta command");
return $INVALID_VALUE;
}
- $self->{parser}->add_test (@values, $TYPE_META_TESTS);
+ $self->{parser}->add_test ($rulename, $value, $TYPE_META_TESTS);
}
});
@@ -3933,12 +3941,15 @@ from SQL or LDAP, instead of passing the
type => $CONF_TYPE_BOOL,
});
-=item loadplugin PluginModuleName [/path/module.pm]
+=item loadplugin [Mail::SpamAssassin::Plugin::]ModuleName [/path/module.pm]
-Load a SpamAssassin plugin module. The C<PluginModuleName> is the perl module
+Load a SpamAssassin plugin module. The C<ModuleName> is the perl module
name, used to create the plugin object itself.
-C</path/to/module.pm> is the file to load, containing the module's perl code;
+Module naming is strict, name must only contain alphanumeric characters or
+underscores. File must have .pm extension.
+
+C</path/module.pm> is the file to load, containing the module's perl code;
if it's specified as a relative path, it's considered to be relative to the
current configuration file. If it is omitted, the module will be loaded
using perl's search path (the C<@INC> array).
@@ -3957,20 +3968,16 @@ See C<Mail::SpamAssassin::Plugin> for mo
}
my ($package, $path);
local ($1,$2);
- if ($value =~ /^(\S+)\s+(\S+)$/) {
+ if ($value =~ /^((?:\w+::){0,10}\w+)(?:\s+(\S+\.pm))?$/i) {
($package, $path) = ($1, $2);
- } elsif ($value =~ /^\S+$/) {
- ($package, $path) = ($value, undef);
} else {
return $INVALID_VALUE;
}
- # is blindly untainting safe? it is no worse than before
- $_ = untaint_var($_) for ($package,$path);
$self->load_plugin ($package, $path);
}
});
-=item tryplugin PluginModuleName [/path/module.pm]
+=item tryplugin ModuleName [/path/module.pm]
Same as C<loadplugin>, but silently ignored if the .pm file cannot be found in
the filesystem.
@@ -3987,15 +3994,11 @@ the filesystem.
}
my ($package, $path);
local ($1,$2);
- if ($value =~ /^(\S+)\s+(\S+)$/) {
+ if ($value =~ /^((?:\w+::){0,10}\w+)(?:\s+(\S+\.pm))?$/i) {
($package, $path) = ($1, $2);
- } elsif ($value =~ /^\S+$/) {
- ($package, $path) = ($value, undef);
} else {
return $INVALID_VALUE;
}
- # is blindly untainting safe? it is no worse than before
- $_ = untaint_var($_) for ($package,$path);
$self->load_plugin ($package, $path, 1);
}
});
@@ -4777,12 +4780,7 @@ sub maybe_body_only {
sub load_plugin {
my ($self, $package, $path, $silent) = @_;
- if ($path) {
- $path = $self->{parser}->fix_path_relative_to_current_file($path);
- }
- # it wouldn't hurt to do some checking on validity of $package
- # and $path before untainting them
- $self->{main}->{plugins}->load_plugin(untaint_var($package), $path, $silent);
+ $self->{main}->{plugins}->load_plugin($package, $path, $silent);
}
sub load_plugin_succeeded {
@@ -4963,6 +4961,7 @@ sub feature_bug6558_free { 1 }
sub feature_edns { 1 } # supports 'dns_options edns' config option
sub feature_dns_query_restriction { 1 } # supported config option
sub feature_registryboundaries { 1 } # replaces deprecated registrarboundaries
+sub feature_compile_regexp { 1 } # Util::compile_regexp
sub perl_min_version_5010000 { return $] >= 5.010000 } # perl version check ("perl_version" not neatly backwards-compatible)
###########################################################################
Modified: spamassassin/branches/3.4/lib/Mail/SpamAssassin/Conf/Parser.pm
URL: http://svn.apache.org/viewvc/spamassassin/branches/3.4/lib/Mail/SpamAssassin/Conf/Parser.pm?rev=1848969&r1=1848968&r2=1848969&view=diff
==============================================================================
--- spamassassin/branches/3.4/lib/Mail/SpamAssassin/Conf/Parser.pm (original)
+++ spamassassin/branches/3.4/lib/Mail/SpamAssassin/Conf/Parser.pm Fri Dec 14 21:05:01 2018
@@ -137,7 +137,7 @@ package Mail::SpamAssassin::Conf::Parser
use Mail::SpamAssassin::Conf;
use Mail::SpamAssassin::Constants qw(:sa);
use Mail::SpamAssassin::Logger;
-use Mail::SpamAssassin::Util qw(untaint_var);
+use Mail::SpamAssassin::Util qw(untaint_var compile_regexp);
use Mail::SpamAssassin::NetSet;
use strict;
@@ -147,6 +147,9 @@ use re 'taint';
our @ISA = qw();
+my $RULENAME_RE = RULENAME_RE;
+my $ARITH_EXPRESSION_LEXER = ARITH_EXPRESSION_LEXER;
+
###########################################################################
sub new {
@@ -506,13 +509,12 @@ sub handle_conditional {
my ($self, $key, $value, $if_stack_ref, $skip_parsing_ref) = @_;
my $conf = $self->{conf};
- my $lexer = ARITH_EXPRESSION_LEXER;
- my @tokens = ($value =~ m/($lexer)/og);
+ my @tokens = ($value =~ /($ARITH_EXPRESSION_LEXER)/og);
my $eval = '';
my $bad = 0;
foreach my $token (@tokens) {
- if ($token =~ /^(?:\W+|[+-]?\d+(?:\.\d+)?)$/) {
+ if ($token =~ /^(?:\W{1,5}|[+-]?\d+(?:\.\d+)?)$/) {
# using tainted subr. argument may taint the whole expression, avoid
my $u = untaint_var($token);
$eval .= $u . " ";
@@ -536,17 +538,25 @@ sub handle_conditional {
$eval .= $]." ";
}
elsif ($token =~ /^\w[\w\:]+$/) { # class name
- my $u = untaint_var($token);
- $eval .= '"' . $u . '" ';
+ # Strictly controlled form:
+ if ($token =~ /^(?:\w+::){0,10}\w+$/) {
+ my $u = untaint_var($token);
+ $eval .= "'$u'";
+ } else {
+ warn "config: illegal name '$token' in 'if $value'\n";
+ $bad++;
+ last;
+ }
}
else {
$bad++;
warn "config: unparseable chars in 'if $value': '$token'\n";
+ last;
}
}
if ($bad) {
- $self->lint_warn("bad 'if' line, in \"$self->{currentfile}\"", undef);
+ $self->lint_warn("config: bad 'if' line, in \"$self->{currentfile}\"", undef);
return -1;
}
@@ -572,7 +582,7 @@ sub cond_clause_plugin_loaded {
sub cond_clause_can {
my ($self, $method) = @_;
- if ($self->{currentfile} =~ q!/user_prefs$! ) {
+ if ($self->{currentfile} =~ q!\buser_prefs$! ) {
warn "config: 'if can $method' not available in user_prefs";
return 0
}
@@ -589,7 +599,7 @@ sub cond_clause_can_or_has {
local($1,$2);
if (!defined $method) {
- $self->lint_warn("bad 'if' line, no argument to $fn_name(), ".
+ $self->lint_warn("config: bad 'if' line, no argument to $fn_name(), ".
"in \"$self->{currentfile}\"", undef);
} elsif ($method =~ /^(.*)::([^:]+)$/) {
no strict "refs";
@@ -597,7 +607,7 @@ sub cond_clause_can_or_has {
return 1 if $module->can($meth) &&
( $fn_name eq 'has' || &{$method}() );
} else {
- $self->lint_warn("bad 'if' line, cannot find '::' in $fn_name($method), ".
+ $self->lint_warn("config: bad 'if' line, cannot find '::' in $fn_name($method), ".
"in \"$self->{currentfile}\"", undef);
}
return;
@@ -876,39 +886,40 @@ sub finish_parsing {
# eval type handling
if (($type & 1) == 1) {
- if (my ($function, $args) = ($text =~ m/(.*?)\s*\((.*?)\)\s*$/)) {
- my ($packed, $argsref) =
- $self->pack_eval_method($function, $args, $name, $text);
-
- if (!$packed) {
- # we've already warned about this
+ if (my ($function, $args) = ($text =~ /^(\w+)\((.*?)\)$/)) {
+ my $argsref = $self->pack_eval_args($args);
+ if (!defined $argsref) {
+ $self->lint_warn("syntax error for eval function $name: $text");
+ next;
}
elsif ($type == $Mail::SpamAssassin::Conf::TYPE_BODY_EVALS) {
- $conf->{body_evals}->{$priority}->{$name} = $packed;
+ $conf->{body_evals}->{$priority}->{$name} = [ $function, [@$argsref] ];
}
elsif ($type == $Mail::SpamAssassin::Conf::TYPE_HEAD_EVALS) {
- $conf->{head_evals}->{$priority}->{$name} = $packed;
+ $conf->{head_evals}->{$priority}->{$name} = [ $function, [@$argsref] ];
}
elsif ($type == $Mail::SpamAssassin::Conf::TYPE_RBL_EVALS) {
# We don't do priorities for $Mail::SpamAssassin::Conf::TYPE_RBL_EVALS
# we also use the arrayref instead of the packed string
- $conf->{rbl_evals}->{$name} = [ $function, @$argsref ];
+ $conf->{rbl_evals}->{$name} = [ $function, [@$argsref] ];
}
elsif ($type == $Mail::SpamAssassin::Conf::TYPE_RAWBODY_EVALS) {
- $conf->{rawbody_evals}->{$priority}->{$name} = $packed;
+ $conf->{rawbody_evals}->{$priority}->{$name} = [ $function, [@$argsref] ];
}
elsif ($type == $Mail::SpamAssassin::Conf::TYPE_FULL_EVALS) {
- $conf->{full_evals}->{$priority}->{$name} = $packed;
+ $conf->{full_evals}->{$priority}->{$name} = [ $function, [@$argsref] ];
}
#elsif ($type == $Mail::SpamAssassin::Conf::TYPE_URI_EVALS) {
- # $conf->{uri_evals}->{$priority}->{$name} = $packed;
+ # $conf->{uri_evals}->{$priority}->{$name} = [ $function, [@$argsref] ];
#}
else {
$self->lint_warn("unknown type $type for $name: $text", $name);
+ next;
}
}
else {
$self->lint_warn("syntax error for eval function $name: $text", $name);
+ next;
}
}
# non-eval tests
@@ -935,6 +946,7 @@ sub finish_parsing {
}
else {
$self->lint_warn("unknown type $type for $name: $text", $name);
+ next;
}
}
}
@@ -986,8 +998,7 @@ sub _meta_deps_recurse {
return unless $rule;
# Lex the rule into tokens using a rather simple RE method ...
- my $lexer = ARITH_EXPRESSION_LEXER;
- my @tokens = ($rule =~ m/$lexer/og);
+ my @tokens = ($rule =~ /($ARITH_EXPRESSION_LEXER)/og);
# Go through each token in the meta rule
my $conf_tests = $conf->{tests};
@@ -1086,40 +1097,36 @@ sub find_dup_rules {
}
}
+# Deprecated function
sub pack_eval_method {
- my ($self, $function, $args, $name, $text) = @_;
+ warn "deprecated function pack_eval_method() used\n";
+ return ('',undef);
+}
+sub pack_eval_args {
+ my ($self, $args) = @_;
+
+ return [] if $args =~ /^\s+$/;
+
+ # bug 4419: Parse quoted strings, unquoted alphanumerics/floats,
+ # unquoted IPv4 and IPv6 addresses, and unquoted common domain names.
+ # s// is used so that we can determine whether or not we successfully
+ # parsed ALL arguments.
my @args;
- if (defined $args) {
- # bug 4419: Parse quoted strings, unquoted alphanumerics/floats,
- # unquoted IPv4 and IPv6 addresses, and unquoted common domain names.
- # s// is used so that we can determine whether or not we successfully
- # parsed ALL arguments.
- local($1,$2,$3);
- while ($args =~ s/^\s* (?: (['"]) (.*?) \1 | ( [\d\.:A-Za-z-]+? ) )
- \s* (?: , \s* | $ )//x) {
- if (defined $2) {
- push @args, $2;
- }
- else {
- push @args, $3;
- }
- }
+ local($1,$2,$3);
+ while ($args =~ s/^\s* (?: (['"]) (.*?) \1 | ( [\d\.:A-Za-z-]+? ) )
+ \s* (?: , \s* | $ )//x) {
+ # DO NOT UNTAINT THESE ARGS
+ # The eval function that handles these should do that as necessary,
+ # we have no idea what acceptable arguments look like here.
+ push @args, defined $2 ? $2 : $3;
}
if ($args ne '') {
- $self->lint_warn("syntax error (unparsable argument: $args) for eval function: $name: $text", $name);
- return;
+ return undef;
}
- my $argstr = $function;
- $argstr =~ s/\s+//gs;
-
- if (@args > 0) {
- $argstr .= ',' . join(', ',
- map { my $s = $_; $s =~ s/\#/[HASH]/gs; 'q#' . $s . '#' } @args);
- }
- return ($argstr, \@args);
+ return \@args;
}
###########################################################################
@@ -1181,7 +1188,7 @@ sub add_test {
my $conf = $self->{conf};
# Don't allow invalid names ...
- if ($name !~ /^[_[:alpha:]]\w*$/) {
+ if ($name !~ /^${RULENAME_RE}$/) {
$self->lint_warn("config: error: rule '$name' has invalid characters ".
"(not Alphanumeric + Underscore + starting with a non-digit)\n", $name);
return;
@@ -1204,29 +1211,68 @@ sub add_test {
}
}
+ # parameter to compile_regexp()
+ my $ignore_amre =
+ $self->{conf}->{lint_rules} ||
+ $self->{conf}->{ignore_always_matching_regexps};
+
# all of these rule types are regexps
if ($type == $Mail::SpamAssassin::Conf::TYPE_BODY_TESTS ||
$type == $Mail::SpamAssassin::Conf::TYPE_FULL_TESTS ||
$type == $Mail::SpamAssassin::Conf::TYPE_RAWBODY_TESTS ||
$type == $Mail::SpamAssassin::Conf::TYPE_URI_TESTS)
{
- return unless $self->is_delimited_regexp_valid($name, $text);
+ my ($rec, $err) = compile_regexp($text, 1, $ignore_amre);
+ if (!$rec) {
+ $self->lint_warn("config: invalid regexp for $name '$text': $err", $name);
+ return;
+ }
+ $conf->{test_qrs}->{$name} = $rec;
}
- if ($type == $Mail::SpamAssassin::Conf::TYPE_HEAD_TESTS)
+ elsif ($type == $Mail::SpamAssassin::Conf::TYPE_HEAD_TESTS)
{
+ local($1,$2,$3);
# RFC 5322 section 3.6.8, ftext printable US-ASCII chars not including ":"
# no re "strict"; # since perl 5.21.8: Ranges of ASCII printables...
- if ($text =~ /^!?defined\([!-9;-\176]+\)$/) {
- # fine, implements 'exists:'
+ if ($text =~ /^exists:(.*)/) {
+ my $hdr = $1;
+ # never evaled, so can be quite generous with the name
+ # check :addr etc header options
+ if ($hdr !~ /^[^:\s]+:?$/) {
+ $self->lint_warn("config: invalid head test $name header: $hdr");
+ return;
+ }
+ $hdr =~ s/:$//;
+ $conf->{test_opt_header}->{$name} = $hdr;
+ $conf->{test_opt_exists}->{$name} = 1;
} else {
- my ($pat) = ($text =~ /^\s*\S+\s*(?:\=|\!)\~\s*(\S.*?\S)\s*$/);
- if ($pat) { $pat =~ s/\s+\[if-unset:\s+(.+)\]\s*$//; }
- return unless $self->is_delimited_regexp_valid($name, $pat);
+ if ($text !~ /^([^:\s]+(?:\:|(?:\:[a-z]+){1,2})?)\s*([=!]~)\s*(.+)$/) {
+ $self->lint_warn("config: invalid head test $name: $text");
+ return;
+ }
+ my ($hdr, $op, $pat) = ($1, $2, $3);
+ $hdr =~ s/:$//;
+ if ($pat =~ s/\s+\[if-unset:\s+(.+)\]$//) {
+ $conf->{test_opt_unset}->{$name} = $1;
+ }
+ my ($rec, $err) = compile_regexp($pat, 1, $ignore_amre);
+ if (!$rec) {
+ $self->lint_warn("config: invalid regexp for $name '$pat': $err", $name);
+ return;
+ }
+ $conf->{test_qrs}->{$name} = $rec;
+ $conf->{test_opt_header}->{$name} = $hdr;
+ $conf->{test_opt_neg}->{$name} = 1 if $op eq '!~';
}
}
elsif ($type == $Mail::SpamAssassin::Conf::TYPE_META_TESTS)
{
- return unless $self->is_meta_valid($name, $text);
+ if ($self->is_meta_valid($name, $text)) {
+ # Untaint now once and not repeatedly later
+ $text = untaint_var($text);
+ } else {
+ return;
+ }
}
$conf->{tests}->{$name} = $text;
@@ -1291,39 +1337,36 @@ sub is_meta_valid {
# $meta is a degenerate translation of the rule, replacing all variables (i.e. rule names) with 0.
my $meta = '';
- $rule = untaint_var($rule); # must be careful below
- # Bug #7557 code injection
- if ( $rule =~ /\S(::|->)\S/ ) {
- warn("is_meta_valid: Bogus rule $name: $rule") ;
+
+ # Paranoid check (Bug #7557)
+ if ($rule =~ /(?:\:\:|->)/) {
+ warn("config: invalid meta $name rule: $rule") ;
return 0;
}
# Lex the rule into tokens using a rather simple RE method ...
- my $lexer = ARITH_EXPRESSION_LEXER;
- my @tokens = ($rule =~ m/$lexer/og);
- if (length($name) == 1) {
- for (@tokens) {
- print "$name $_\n " or die "Error writing token: $!";
- }
- }
+ my @tokens = ($rule =~ /($ARITH_EXPRESSION_LEXER)/og);
+
# Go through each token in the meta rule
foreach my $token (@tokens) {
# If the token is a syntactically legal rule name, make it zero
- if ($token =~ /^[_[:alpha:]]\w+\z/s) {
+ if ($token =~ /^${RULENAME_RE}\z/s) {
$meta .= "0 ";
}
# if it is a (decimal) number or a string of 1 or 2 punctuation
# characters (i.e. operators) tack it onto the degenerate rule
- elsif ( $token =~ /^(\d+(?:\.\d+)?|[[:punct:]]{1,2})\z/s ) {
+ elsif ($token =~ /^(\d+(?:\.\d+)?|[[:punct:]]{1,2})\z/s) {
$meta .= "$token ";
}
- # WTF is it? Just warn, for now. Bug #7557
+ # Skip anything unknown (Bug #7557)
else {
- $self->lint_warn("config: Strange rule $name token: $token", $name);
- $meta .= "$token ";
+ $self->lint_warn("config: invalid meta $name token: $token", $name);
+ return 0;
}
}
- my $evalstr = 'my $x = ' . $meta . '; 1;';
+
+ $meta = untaint_var($meta); # was carefully checked
+ my $evalstr = 'my $x = '.$meta.'; 1;';
if (eval $evalstr) {
return 1;
}
@@ -1334,94 +1377,21 @@ sub is_meta_valid {
return 0;
}
+# Deprecated functions, leave just in case..
sub is_delimited_regexp_valid {
- my ($self, $name, $re) = @_;
-
- if (!$re || $re !~ /^\s*m?(\W).*(?:\1|>|}|\)|\])[a-z]*\s*$/) {
- $re ||= '';
- $self->lint_warn("config: invalid regexp for rule $name: $re: missing or invalid delimiters\n", $name);
- return 0;
- }
- return $self->is_regexp_valid($name, $re);
+ my ($self, $rule, $re) = @_;
+ warn "deprecated is_delimited_regexp_valid() called, use compile_regexp()\n";
+ my ($rec, $err) = compile_regexp($re, 1, 1);
+ return $rec;
}
-
sub is_regexp_valid {
- my ($self, $name, $re) = @_;
-
- # OK, try to remove any normal perl-style regexp delimiters at
- # the start and end, and modifiers at the end if present,
- # so we can validate those too.
- my $origre = $re;
- my $safere = $re;
- my $mods = '';
- local ($1,$2);
- if ($re =~ s/^m\{//) {
- $re =~ s/\}([a-z]*)\z//; $mods = $1;
- }
- elsif ($re =~ s/^m\(//) {
- $re =~ s/\)([a-z]*)\z//; $mods = $1;
- }
- elsif ($re =~ s/^m<//) {
- $re =~ s/>([a-z]*)\z//; $mods = $1;
- }
- elsif ($re =~ s/^m(\W)//) {
- $re =~ s/\Q$1\E([a-z]*)\z//; $mods = $1;
- }
- elsif ($re =~ s{^/(.*)/([a-z]*)\z}{$1}) {
- $mods = $2;
- }
- else {
- $safere = "m#".$re."#";
- }
-
- if ($self->{conf}->{lint_rules} ||
- $self->{conf}->{ignore_always_matching_regexps})
- {
- my $msg = $self->is_always_matching_regexp($name, $re);
-
- if (defined $msg) {
- if ($self->{conf}->{lint_rules}) {
- $self->lint_warn($msg, $name);
- } else {
- warn $msg;
- return 0;
- }
- }
- }
-
- # now prepend the modifiers, in order to check if they're valid
- if ($mods) {
- $re = "(?" . $mods . ")" . $re;
- }
-
- # note: this MUST use m/...${re}.../ in some form or another, ie.
- # interpolation of the $re variable into a code regexp, in order to test the
- # security of the regexp. simply using ("" =~ $re) will NOT do that, and
- # will therefore open a hole!
- { # no re "strict"; # since perl 5.21.8: Ranges of ASCII printables...
- if (eval { ("" =~ m{$re}); 1; }) { return 1 }
- }
- my $err = $@ ne '' ? $@ : "errno=$!"; chomp $err;
- $err =~ s/ at .*? line \d.*$//;
- $self->lint_warn("config: invalid regexp for rule $name: $origre: $err\n", $name);
- return 0;
+ my ($self, $rule, $re) = @_;
+ warn "deprecated is_regexp_valid() called, use compile_regexp()\n";
+ my ($rec, $err) = compile_regexp($re, 1, 1);
+ return $rec;
}
-
-# check the pattern for some basic errors, and warn if found
sub is_always_matching_regexp {
- my ($self, $name, $re) = @_;
-
- if ($re =~ /(?<!\\)\|\|/) {
- return "config: regexp for rule $name always matches due to '||'";
- }
- elsif ($re =~ /^\|/) {
- return "config: regexp for rule $name always matches due to " .
- "pattern starting with '|'";
- }
- elsif ($re =~ /\|(?<!\\\|)$/) {
- return "config: regexp for rule $name always matches due to " .
- "pattern ending with '|'";
- }
+ warn "deprecated is_always_matching_regexp() called\n";
return;
}
Modified: spamassassin/branches/3.4/lib/Mail/SpamAssassin/Constants.pm
URL: http://svn.apache.org/viewvc/spamassassin/branches/3.4/lib/Mail/SpamAssassin/Constants.pm?rev=1848969&r1=1848968&r2=1848969&view=diff
==============================================================================
--- spamassassin/branches/3.4/lib/Mail/SpamAssassin/Constants.pm (original)
+++ spamassassin/branches/3.4/lib/Mail/SpamAssassin/Constants.pm Fri Dec 14 21:05:01 2018
@@ -32,7 +32,7 @@ our(@BAYES_VARS, @IP_VARS, @SA_VARS, %EX
# NOTE: Unless you need these to be available at BEGIN time, you're better with this out of a BEGIN block with a simple our statement.
BEGIN {
- @IP_VARS = qw(
+ @IP_VARS = qw(
IP_IN_RESERVED_RANGE IP_PRIVATE LOCALHOST IPV4_ADDRESS IP_ADDRESS
);
@BAYES_VARS = qw(
@@ -43,7 +43,7 @@ BEGIN {
HARVEST_DNSBL_PRIORITY MBX_SEPARATOR
MAX_BODY_LINE_LENGTH MAX_HEADER_KEY_LENGTH MAX_HEADER_VALUE_LENGTH
MAX_HEADER_LENGTH ARITH_EXPRESSION_LEXER AI_TIME_UNKNOWN
- CHARSETS_LIKELY_TO_FP_AS_CAPS MAX_URI_LENGTH
+ CHARSETS_LIKELY_TO_FP_AS_CAPS MAX_URI_LENGTH RULENAME_RE
);
%EXPORT_TAGS = (
@@ -402,4 +402,7 @@ use constant CHARSETS_LIKELY_TO_FP_AS_CA
koi|jp|jis|euc|gb|big5|isoir|cp1251|windows-1251|georgianps|pt154|tis
)[-_a-z0-9]*}ix;
+# Allowed rulename format
+use constant RULENAME_RE => qr([_a-zA-Z][_a-zA-Z0-9]{0,127});
+
1;
Modified: spamassassin/branches/3.4/lib/Mail/SpamAssassin/Dns.pm
URL: http://svn.apache.org/viewvc/spamassassin/branches/3.4/lib/Mail/SpamAssassin/Dns.pm?rev=1848969&r1=1848968&r2=1848969&view=diff
==============================================================================
--- spamassassin/branches/3.4/lib/Mail/SpamAssassin/Dns.pm (original)
+++ spamassassin/branches/3.4/lib/Mail/SpamAssassin/Dns.pm Fri Dec 14 21:05:01 2018
@@ -139,6 +139,12 @@ sub do_rbl_lookup {
# TODO: these are constant so they should only be added once at startup
sub register_rbl_subtest {
my ($self, $rule, $set, $subtest) = @_;
+
+ if ($subtest =~ /^sb:/) {
+ warn("dns: ignored $rule, SenderBase rules are deprecated\n");
+ return 0;
+ }
+
$self->{dnspost}->{$set}->{$subtest} = $rule;
}
@@ -307,30 +313,6 @@ sub process_dnsbl_set {
# test for exact equality, not a regexp (an IPv4 address)
$self->dnsbl_hit($rule, $question, $answer) if $subtest eq $rdatastr;
}
- # senderbase
- elsif ($subtest =~ s/^sb://) {
- # SB rules are not available to users
- if ($self->{conf}->{user_defined_rules}->{$rule}) {
- dbg("dns: skipping rule '$rule': not supported when user-defined");
- next;
- }
-
- $rdatastr =~ s/^\d+-//;
- my %sb = ($rdatastr =~ m/(?:^|\|)(\d+)=([^|]+)/g);
- my $undef = 0;
- while ($subtest =~ m/\bS(\d+)\b/g) {
- if (!defined $sb{$1}) {
- $undef = 1;
- last;
- }
- $subtest =~ s/\bS(\d+)\b/\$sb{$1}/;
- }
-
- # untaint. (bug 3325)
- $subtest = untaint_var($subtest);
-
- $self->got_hit($rule, "SenderBase: ", ruletype => "dnsbl") if !$undef && eval $subtest;
- }
# bitmask
elsif ($subtest =~ /^\d+$/) {
# Bug 6803: response should be within 127.0.0.0/8, ignore otherwise
Modified: spamassassin/branches/3.4/lib/Mail/SpamAssassin/Logger.pm
URL: http://svn.apache.org/viewvc/spamassassin/branches/3.4/lib/Mail/SpamAssassin/Logger.pm?rev=1848969&r1=1848968&r2=1848969&view=diff
==============================================================================
--- spamassassin/branches/3.4/lib/Mail/SpamAssassin/Logger.pm (original)
+++ spamassassin/branches/3.4/lib/Mail/SpamAssassin/Logger.pm Fri Dec 14 21:05:01 2018
@@ -265,6 +265,8 @@ sub add {
my $name = lc($params{method});
my $class = ucfirst($name);
+ return 0 if $class !~ /^\w+$/; # be paranoid
+
eval 'use Mail::SpamAssassin::Logger::'.$class.'; 1'
or do {
my $eval_stat = $@ ne '' ? $@ : "errno=$!"; chomp $eval_stat;
Modified: spamassassin/branches/3.4/lib/Mail/SpamAssassin/Message.pm
URL: http://svn.apache.org/viewvc/spamassassin/branches/3.4/lib/Mail/SpamAssassin/Message.pm?rev=1848969&r1=1848968&r2=1848969&view=diff
==============================================================================
--- spamassassin/branches/3.4/lib/Mail/SpamAssassin/Message.pm (original)
+++ spamassassin/branches/3.4/lib/Mail/SpamAssassin/Message.pm Fri Dec 14 21:05:01 2018
@@ -188,14 +188,34 @@ sub new {
@message = split(/^/m, $message, -1);
}
- # Pull off mbox and mbx separators
- # also deal with null messages
+ # Deal with null message
if (!@message) {
# bug 4884:
# if we get here, it means that the input was null, so fake the message
# content as a single newline...
@message = ("\n");
- } elsif ($message[0] =~ /^From\s+(?!:)/) {
+ }
+
+ # Bug 7648:
+ # Make sure the message is tainted. When linting, @testmsg is not, so this
+ # handles that. Perhaps 3rd party tools could call this with untainted
+ # messages? Tainting the message is important because it prevents certain
+ # exploits later.
+ if (Mail::SpamAssassin::Util::am_running_in_taint_mode() &&
+ grep { !Scalar::Util::tainted($_) } @message) {
+ local($_);
+ # To preserve newlines, no joining and splitting here, process each line
+ # directly as is.
+ foreach (@message) {
+ $_ = Mail::SpamAssassin::Util::taint_var($_);
+ }
+ if (grep { !Scalar::Util::tainted($_) } @message) {
+ die "Mail::SpamAssassin::Message failed to enforce message taintness";
+ }
+ }
+
+ # Pull off mbox and mbx separators
+ if ($message[0] =~ /^From\s+(?!:)/) {
# careful not to confuse with obsolete syntax which allowed WSP before ':'
# mbox formated mailbox
$self->{'mbox_sep'} = shift @message;
Modified: spamassassin/branches/3.4/lib/Mail/SpamAssassin/PerMsgStatus.pm
URL: http://svn.apache.org/viewvc/spamassassin/branches/3.4/lib/Mail/SpamAssassin/PerMsgStatus.pm?rev=1848969&r1=1848968&r2=1848969&view=diff
==============================================================================
--- spamassassin/branches/3.4/lib/Mail/SpamAssassin/PerMsgStatus.pm (original)
+++ spamassassin/branches/3.4/lib/Mail/SpamAssassin/PerMsgStatus.pm Fri Dec 14 21:05:01 2018
@@ -269,7 +269,6 @@ sub new {
'master_deadline' => $msg->{master_deadline}, # dflt inherited from msg
'deadline_exceeded' => 0, # time limit exceeded, skipping further tests
};
- #$self->{main}->{use_rule_subs} = 1;
dbg("check: pms new, time limit in %.3f s",
$self->{master_deadline} - time) if $self->{master_deadline};
Modified: spamassassin/branches/3.4/lib/Mail/SpamAssassin/Plugin/Bayes.pm
URL: http://svn.apache.org/viewvc/spamassassin/branches/3.4/lib/Mail/SpamAssassin/Plugin/Bayes.pm?rev=1848969&r1=1848968&r2=1848969&view=diff
==============================================================================
--- spamassassin/branches/3.4/lib/Mail/SpamAssassin/Plugin/Bayes.pm (original)
+++ spamassassin/branches/3.4/lib/Mail/SpamAssassin/Plugin/Bayes.pm Fri Dec 14 21:05:01 2018
@@ -1645,8 +1645,14 @@ sub learner_new {
my ($self) = @_;
my $store;
- my $module = untaint_var($self->{conf}->{bayes_store_module});
- $module = 'Mail::SpamAssassin::BayesStore::DBM' if !$module;
+ my $module = $self->{conf}->{bayes_store_module};
+ if (!$module) {
+ $module = 'Mail::SpamAssassin::BayesStore::DBM';
+ } elsif ($module =~ /^([_A-Za-z0-9:]+)$/) {
+ $module = untaint_var($module);
+ } else {
+ die "bayes: invalid module: $module\n";
+ }
dbg("bayes: learner_new self=%s, bayes_store_module=%s", $self,$module);
undef $self->{store}; # DESTROYs previous object, if any
Modified: spamassassin/branches/3.4/lib/Mail/SpamAssassin/Plugin/BodyRuleBaseExtractor.pm
URL: http://svn.apache.org/viewvc/spamassassin/branches/3.4/lib/Mail/SpamAssassin/Plugin/BodyRuleBaseExtractor.pm?rev=1848969&r1=1848968&r2=1848969&view=diff
==============================================================================
--- spamassassin/branches/3.4/lib/Mail/SpamAssassin/Plugin/BodyRuleBaseExtractor.pm (original)
+++ spamassassin/branches/3.4/lib/Mail/SpamAssassin/Plugin/BodyRuleBaseExtractor.pm Fri Dec 14 21:05:01 2018
@@ -29,7 +29,7 @@ package Mail::SpamAssassin::Plugin::Body
use Mail::SpamAssassin::Plugin;
use Mail::SpamAssassin::Logger;
-use Mail::SpamAssassin::Util qw(untaint_var);
+use Mail::SpamAssassin::Util qw(untaint_var qr_to_string);
use Mail::SpamAssassin::Util::Progress;
use Errno qw(ENOENT EACCES EEXIST);
@@ -152,8 +152,12 @@ NEXT_RULE:
foreach my $name (keys %{$rules}) {
$self->{show_progress} and $progress and $progress->update(++$count);
- my $rule = $rules->{$name};
- my $cachekey = join "#", $name, $rule;
+ #my $rule = $rules->{$name};
+ my $rule = qr_to_string($conf->{test_qrs}->{$name});
+ if (!defined $rule) {
+ die "zoom: error: regexp for $rule not found\n";
+ }
+ my $cachekey = $name.'#'.$rule;
my $cent = $cached->{rule_bases}->{$cachekey};
if (defined $cent) {
@@ -177,7 +181,7 @@ NEXT_RULE:
}
# ignore ReplaceTags rules
- my $is_a_replacetags_rule = $conf->{rules_to_replace}->{$name};
+ my $is_a_replacetags_rule = $conf->{replace_rules}->{$name};
my ($minlen, $lossy, @bases);
if (!$is_a_replacetags_rule) {
@@ -407,11 +411,14 @@ sub simplify_and_qr_regexp {
my $rule = shift;
my $main = $self->{main};
- $rule = Mail::SpamAssassin::Util::regexp_remove_delimiters($rule);
- # remove the regexp modifiers, keep for later
+
my $mods = '';
- while ($rule =~ s/^\(\?([a-z]*)\)//) { $mods .= $1; }
+
+ # remove the regexp modifiers, keep for later
+ while ($rule =~ s/^\(\?([a-z]*)\)//) {
+ $mods .= $1;
+ }
# modifier removal
while ($rule =~ s/^\(\?-([a-z]*)\)//) {
@@ -685,7 +692,7 @@ sub extract_hints {
$add_candidate->();
if (!$longestexact) {
- die "no long-enough string found in $rawrule";
+ die "no long-enough string found in $rawrule\n";
# all unrolled versions must have a long string, otherwise
# we cannot reliably match all variants of the rule
} else {
Modified: spamassassin/branches/3.4/lib/Mail/SpamAssassin/Plugin/Check.pm
URL: http://svn.apache.org/viewvc/spamassassin/branches/3.4/lib/Mail/SpamAssassin/Plugin/Check.pm?rev=1848969&r1=1848968&r2=1848969&view=diff
==============================================================================
--- spamassassin/branches/3.4/lib/Mail/SpamAssassin/Plugin/Check.pm (original)
+++ spamassassin/branches/3.4/lib/Mail/SpamAssassin/Plugin/Check.pm Fri Dec 14 21:05:01 2018
@@ -28,6 +28,9 @@ use Mail::SpamAssassin::Constants qw(:sa
our @ISA = qw(Mail::SpamAssassin::Plugin);
+my $ARITH_EXPRESSION_LEXER = ARITH_EXPRESSION_LEXER;
+my $RULENAME_RE = RULENAME_RE;
+
# methods defined by the compiled ruleset; deleted in finish_tests()
our @TEMPORARY_METHODS;
@@ -263,11 +266,15 @@ sub run_rbl_eval_tests {
%{$pms->{test_log_msgs}} = (); # clear test state
- my ($function, @args) = @{$test};
+ my $function = $test->[0];
+ if (!exists $pms->{conf}->{eval_plugins}->{$function}) {
+ warn("rules: unknown eval '$function' for $rulename, ignoring RBL eval\n");
+ return 0;
+ }
my $result;
eval {
- $result = $pms->$function($rulename, @args); 1;
+ $result = $pms->$function($rulename, @{$test->[1]}); 1;
} or do {
my $eval_stat = $@ ne '' ? $@ : "errno=$!"; chomp $eval_stat;
die "rules: $eval_stat\n" if $eval_stat =~ /__alarm__ignore__/;
@@ -334,6 +341,7 @@ sub run_generic_tests {
$self->push_evalstr_prefix($pms, '
# start_rules_plugin_code '.$ruletype.' '.$priority.'
my $scoresptr = $self->{conf}->{scores};
+ my $qrptr = $self->{conf}->{test_qrs};
');
if (defined $opts{pre_loop_body}) {
$opts{pre_loop_body}->($self, $pms, $conf, %nopts);
@@ -529,11 +537,9 @@ sub do_meta_tests {
loop_body => sub
{
my ($self, $pms, $conf, $rulename, $rule, %opts) = @_;
- $rule = untaint_var($rule); # presumably checked
# Lex the rule into tokens using a rather simple RE method ...
- my $lexer = ARITH_EXPRESSION_LEXER;
- my @tokens = ($rule =~ m/$lexer/og);
+ my @tokens = ($rule =~ /$ARITH_EXPRESSION_LEXER/og);
# Set the rule blank to start
$meta{$rulename} = "";
@@ -544,15 +550,12 @@ sub do_meta_tests {
# Go through each token in the meta rule
foreach my $token (@tokens) {
- # Numbers can't be rule names
- if ($token =~ tr{A-Za-z0-9_}{}c || substr($token,0,1) =~ tr{A-Za-z_}{}c) {
- $meta{$rulename} .= "$token ";
- }
- else { # token is a rule name
+ # ... rulename?
+ if ($token =~ /^${RULENAME_RE}\z/) {
# the " || 0" formulation is to avoid "use of uninitialized value"
# warnings; this is better than adding a 0 to a hash for every
# rule referred to in a meta...
- $meta{$rulename} .= "(\$h->{'$token'} || 0) ";
+ $meta{$rulename} .= "(\$h->{'$token'}||0) ";
if (!exists $conf->{scores}->{$token}) {
dbg("rules: meta test $rulename has undefined dependency '$token'");
@@ -571,6 +574,9 @@ sub do_meta_tests {
# If the token is another meta rule, add it as a dependency
push (@{ $rule_deps{$rulename} }, $token)
if (exists $conf->{meta_tests}->{$opts{priority}}->{$token});
+ } else {
+ # ... number or operator
+ $meta{$rulename} .= "$token ";
}
}
},
@@ -666,66 +672,30 @@ sub do_head_tests {
args => [ ],
loop_body => sub
{
- my ($self, $pms, $conf, $rulename, $rule, %opts) = @_;
- my $def;
- $rule = untaint_var($rule); # presumably checked
- my ($hdrname, $op, $op_infix, $pat);
- if ($rule =~ /^\s* (\S+) \s* ([=!]~) \s* (\S .*? \S) \s*$/x) {
- ($hdrname, $op, $pat) = ($1,$2,$3); # e.g.: Subject =~ /patt/
- $op_infix = 1;
- if (!defined $pat) {
- warn "rules: invalid rule: $rulename\n";
- $pms->{rule_errors}++;
- next;
- }
- if ($pat =~ s/\s+\[if-unset:\s+(.+)\]\s*$//) { $def = $1 }
- } elsif ($rule =~ /^\s* (\S+) \s* \( \s* (\S+) \s* \) \s*$/x) {
- # implements exists:name_of_header (and similar function or prefix ops)
- ($hdrname, $op) = ($2,$1); # e.g.: !defined(Subject)
+ my ($self, $pms, $conf, $rulename, $pat, %opts) = @_;
+ my ($op, $op_infix);
+ my $hdrname = $conf->{test_opt_header}->{$rulename};
+ if (exists $conf->{test_opt_exists}->{$rulename}) {
$op_infix = 0;
- } else {
- warn "rules: unrecognized rule: $rulename\n";
- $pms->{rule_errors}++;
- next;
+ if (exists $conf->{test_opt_neg}->{$rulename}) {
+ $op = '!defined';
+ } else {
+ $op = 'defined';
+ }
+ }
+ else {
+ $op_infix = 1;
+ $op = $conf->{test_opt_neg}->{$rulename} ? '!~' : '=~';
}
+ my $def = $conf->{test_opt_unset}->{$rulename};
push(@{ $ordered{$hdrname . (!defined $def ? '' : "\t".$def)} },
$rulename);
- next if ($opts{doing_user_rules} &&
+ return if ($opts{doing_user_rules} &&
!$self->is_user_rule_sub($rulename.'_head_test'));
- # caller can set this member of the Mail::SpamAssassin object to
- # override this; useful for profiling rule runtimes, although I think
- # the HitFreqsRuleTiming.pm plugin is probably better nowadays anyway
- if ($self->{main}->{use_rule_subs}) {
- my $matching_string_unavailable = 0;
- my $expr;
- if ($op =~ /^!?[A-Za-z_]+$/) { # function or its negation
- $expr = $op . '($text)';
- $matching_string_unavailable = 1;
- } else { # infix operator
- $expr = '$text ' . $op . ' ' . $pat;
- if ($op eq '=~' || $op eq '!~') {
- $expr .= 'g';
- } else {
- $matching_string_unavailable = 1;
- }
- }
- $self->add_temporary_method ($rulename.'_head_test', '{
- my($self,$text) = @_;
- '.$self->hash_line_for_rule($pms, $rulename).'
- while ('.$expr.') {
- $self->got_hit(q{'.$rulename.'}, "", ruletype => "header");
- '. $self->hit_rule_plugin_code($pms, $rulename, "header", "last",
- $matching_string_unavailable) . '
- }
- }');
- }
- else {
- # store for use below
- $testcode{$rulename} = [$op_infix, $op, $pat];
- }
+ $testcode{$rulename} = [$op_infix, $op, $pat];
},
pre_loop_body => sub
{
@@ -746,15 +716,6 @@ sub do_head_tests {
(!defined($def) ? 'undef' : 'q{'.$def.'}') . ');
');
foreach my $rulename (@{$v}) {
- if ($self->{main}->{use_rule_subs}) {
- $self->add_evalstr($pms, '
- if ($scoresptr->{q{'.$rulename.'}}) {
- '.$rulename.'_head_test($self, $hval);
- '.$self->ran_rule_plugin_code($rulename, "header").'
- }
- ');
- }
- else {
my $tc_ref = $testcode{$rulename};
my ($op_infix, $op, $pat);
($op_infix, $op, $pat) = @$tc_ref if defined $tc_ref;
@@ -772,9 +733,7 @@ sub do_head_tests {
$matching_string_unavailable = 1;
}
else { # infix operator
- if (! ($op eq '=~' || $op eq '!~') ) { # not a pattern matching op.
- $matching_string_unavailable = 1;
- } elsif ( ($conf->{tflags}->{$rulename}||'') =~ /\bmultiple\b/ ) {
+ if (($conf->{tflags}->{$rulename}||'') =~ /\bmultiple\b/) {
$posline = 'pos $hval = 0; $hits = 0;';
$ifwhile = 'while';
$hitdone = 'last';
@@ -783,7 +742,11 @@ sub do_head_tests {
$max = untaint_var($max);
$whlimit = ' && $hits++ < '.$max if $max;
}
- $expr = '$hval ' . $op . ' ' . $pat . $matchg;
+ if ($matchg) {
+ $expr = '$hval '.$op.' /$qrptr->{q{'.$rulename.'}}/g';
+ } else {
+ $expr = '$hval '.$op.' $qrptr->{q{'.$rulename.'}}';
+ }
}
$self->add_evalstr($pms, '
@@ -798,7 +761,6 @@ sub do_head_tests {
'.$self->ran_rule_plugin_code($rulename, "header").'
}
');
- }
}
$self->pop_evalstr_prefix();
}
@@ -820,7 +782,6 @@ sub do_body_tests {
loop_body => sub
{
my ($self, $pms, $conf, $rulename, $pat, %opts) = @_;
- $pat = untaint_var($pat); # presumably checked
my $sub = '';
if (would_log('dbg', 'rules-all') == 2) {
$sub .= '
@@ -838,7 +799,7 @@ sub do_body_tests {
body_'.$loopid.': foreach my $l (@_) {
pos $l = 0;
'.$self->hash_line_for_rule($pms, $rulename).'
- while ($l =~ '.$pat.'g'. ($max? ' && $hits++ < '.$max:'') .') {
+ while ($l =~ /$qrptr->{q{'.$rulename.'}}/g'. ($max? ' && $hits++ < '.$max:'') .') {
$self->got_hit(q{'.$rulename.'}, "BODY: ", ruletype => "body");
'. $self->hit_rule_plugin_code($pms, $rulename, 'body',
"last body_".$loopid) . '
@@ -853,7 +814,7 @@ sub do_body_tests {
$sub .= '
foreach my $l (@_) {
'.$self->hash_line_for_rule($pms, $rulename).'
- if ($l =~ '.$pat.') {
+ if ($l =~ $qrptr->{q{'.$rulename.'}}) {
$self->got_hit(q{'.$rulename.'}, "BODY: ", ruletype => "body");
'. $self->hit_rule_plugin_code($pms, $rulename, "body", "last") .'
}
@@ -861,30 +822,15 @@ sub do_body_tests {
';
}
- if ($self->{main}->{use_rule_subs}) {
- $self->add_evalstr($pms, '
- if ($scoresptr->{q{'.$rulename.'}}) {
- '.$rulename.'_body_test($self,@_);
- '.$self->ran_rule_plugin_code($rulename, "body").'
- }
- ');
- }
- else {
- $self->add_evalstr($pms, '
- if ($scoresptr->{q{'.$rulename.'}}) {
- '.$sub.'
- '.$self->ran_rule_plugin_code($rulename, "body").'
- }
- ');
- }
+ $self->add_evalstr($pms, '
+ if ($scoresptr->{q{'.$rulename.'}}) {
+ '.$sub.'
+ '.$self->ran_rule_plugin_code($rulename, "body").'
+ }
+ ');
- next if ($opts{doing_user_rules} &&
+ return if ($opts{doing_user_rules} &&
!$self->is_user_rule_sub($rulename.'_body_test'));
-
- if ($self->{main}->{use_rule_subs}) {
- $self->add_temporary_method ($rulename.'_body_test',
- '{ my $self = shift; '.$sub.' }');
- }
}
);
}
@@ -902,7 +848,6 @@ sub do_uri_tests {
loop_body => sub
{
my ($self, $pms, $conf, $rulename, $pat, %opts) = @_;
- $pat = untaint_var($pat); # presumably checked
my $sub = '';
if (would_log('dbg', 'rules-all') == 2) {
$sub .= '
@@ -918,7 +863,7 @@ sub do_uri_tests {
uri_'.$loopid.': foreach my $l (@_) {
pos $l = 0;
'.$self->hash_line_for_rule($pms, $rulename).'
- while ($l =~ '.$pat.'g'. ($max? ' && $hits++ < '.$max:'') .') {
+ while ($l =~ /$qrptr->{q{'.$rulename.'}}/g'. ($max? ' && $hits++ < '.$max:'') .') {
$self->got_hit(q{'.$rulename.'}, "URI: ", ruletype => "uri");
'. $self->hit_rule_plugin_code($pms, $rulename, "uri",
"last uri_".$loopid) . '
@@ -930,7 +875,7 @@ sub do_uri_tests {
$sub .= '
foreach my $l (@_) {
'.$self->hash_line_for_rule($pms, $rulename).'
- if ($l =~ '.$pat.') {
+ if ($l =~ $qrptr->{q{'.$rulename.'}}) {
$self->got_hit(q{'.$rulename.'}, "URI: ", ruletype => "uri");
'. $self->hit_rule_plugin_code($pms, $rulename, "uri", "last") .'
}
@@ -938,30 +883,15 @@ sub do_uri_tests {
';
}
- if ($self->{main}->{use_rule_subs}) {
- $self->add_evalstr($pms, '
- if ($scoresptr->{q{'.$rulename.'}}) {
- '.$rulename.'_uri_test($self, @_);
- '.$self->ran_rule_plugin_code($rulename, "uri").'
- }
- ');
- }
- else {
- $self->add_evalstr($pms, '
- if ($scoresptr->{q{'.$rulename.'}}) {
- '.$sub.'
- '.$self->ran_rule_plugin_code($rulename, "uri").'
- }
- ');
- }
+ $self->add_evalstr($pms, '
+ if ($scoresptr->{q{'.$rulename.'}}) {
+ '.$sub.'
+ '.$self->ran_rule_plugin_code($rulename, "uri").'
+ }
+ ');
- next if ($opts{doing_user_rules} &&
+ return if ($opts{doing_user_rules} &&
!$self->is_user_rule_sub($rulename.'_uri_test'));
-
- if ($self->{main}->{use_rule_subs}) {
- $self->add_temporary_method ($rulename.'_uri_test',
- '{ my $self = shift; '.$sub.' }');
- }
}
);
}
@@ -979,7 +909,6 @@ sub do_rawbody_tests {
loop_body => sub
{
my ($self, $pms, $conf, $rulename, $pat, %opts) = @_;
- $pat = untaint_var($pat); # presumably checked
my $sub = '';
if (would_log('dbg', 'rules-all') == 2) {
$sub .= '
@@ -997,7 +926,7 @@ sub do_rawbody_tests {
rawbody_'.$loopid.': foreach my $l (@_) {
pos $l = 0;
'.$self->hash_line_for_rule($pms, $rulename).'
- while ($l =~ '.$pat.'g'. ($max? ' && $hits++ < '.$max:'') .') {
+ while ($l =~ /$qrptr->{q{'.$rulename.'}}/g'. ($max? ' && $hits++ < '.$max:'') .') {
$self->got_hit(q{'.$rulename.'}, "RAW: ", ruletype => "rawbody");
'. $self->hit_rule_plugin_code($pms, $rulename, "rawbody",
"last rawbody_".$loopid) . '
@@ -1010,7 +939,7 @@ sub do_rawbody_tests {
$sub .= '
foreach my $l (@_) {
'.$self->hash_line_for_rule($pms, $rulename).'
- if ($l =~ '.$pat.') {
+ if ($l =~ $qrptr->{q{'.$rulename.'}}) {
$self->got_hit(q{'.$rulename.'}, "RAW: ", ruletype => "rawbody");
'. $self->hit_rule_plugin_code($pms, $rulename, "rawbody", "last") . '
}
@@ -1018,30 +947,15 @@ sub do_rawbody_tests {
';
}
- if ($self->{main}->{use_rule_subs}) {
- $self->add_evalstr($pms, '
- if ($scoresptr->{q{'.$rulename.'}}) {
- '.$rulename.'_rawbody_test($self, @_);
- '.$self->ran_rule_plugin_code($rulename, "rawbody").'
- }
- ');
- }
- else {
- $self->add_evalstr($pms, '
- if ($scoresptr->{q{'.$rulename.'}}) {
- '.$sub.'
- '.$self->ran_rule_plugin_code($rulename, "rawbody").'
- }
- ');
- }
+ $self->add_evalstr($pms, '
+ if ($scoresptr->{q{'.$rulename.'}}) {
+ '.$sub.'
+ '.$self->ran_rule_plugin_code($rulename, "rawbody").'
+ }
+ ');
- next if ($opts{doing_user_rules} &&
+ return if ($opts{doing_user_rules} &&
!$self->is_user_rule_sub($rulename.'_rawbody_test'));
-
- if ($self->{main}->{use_rule_subs}) {
- $self->add_temporary_method ($rulename.'_rawbody_test',
- '{ my $self = shift; '.$sub.' }');
- }
}
);
}
@@ -1066,7 +980,6 @@ sub do_full_tests {
loop_body => sub
{
my ($self, $pms, $conf, $rulename, $pat, %opts) = @_;
- $pat = untaint_var($pat); # presumably checked
my ($max) = ($pms->{conf}->{tflags}->{$rulename}||'') =~ /\bmaxhits=(\d+)\b/;
$max = untaint_var($max);
$self->add_evalstr($pms, '
@@ -1075,7 +988,7 @@ sub do_full_tests {
'.$self->hash_line_for_rule($pms, $rulename).'
dbg("rules-all: running full rule %s", q{'.$rulename.'});
$hits = 0;
- while ($$fullmsgref =~ '.$pat.'g'. ($max? ' && $hits++ < '.$max:'') .') {
+ while ($$fullmsgref =~ /$qrptr->{q{'.$rulename.'}}/g'. ($max? ' && $hits++ < '.$max:'') .') {
$self->got_hit(q{'.$rulename.'}, "FULL: ", ruletype => "full");
'. $self->hit_rule_plugin_code($pms, $rulename, "full", "last") . '
}
@@ -1093,7 +1006,7 @@ sub do_head_eval_tests {
return unless (defined($pms->{conf}->{head_evals}->{$priority}));
dbg("rules: running head_eval tests; score so far=".$pms->{score});
$self->run_eval_tests ($pms, $Mail::SpamAssassin::Conf::TYPE_HEAD_EVALS,
- $pms->{conf}->{head_evals}->{$priority}, '', $priority);
+ 'head_evals', '', $priority);
}
sub do_body_eval_tests {
@@ -1101,8 +1014,7 @@ sub do_body_eval_tests {
return unless (defined($pms->{conf}->{body_evals}->{$priority}));
dbg("rules: running body_eval tests; score so far=".$pms->{score});
$self->run_eval_tests ($pms, $Mail::SpamAssassin::Conf::TYPE_BODY_EVALS,
- $pms->{conf}->{body_evals}->{$priority}, 'BODY: ',
- $priority, $bodystring);
+ 'body_evals', 'BODY: ', $priority, $bodystring);
}
sub do_rawbody_eval_tests {
@@ -1110,8 +1022,7 @@ sub do_rawbody_eval_tests {
return unless (defined($pms->{conf}->{rawbody_evals}->{$priority}));
dbg("rules: running rawbody_eval tests; score so far=".$pms->{score});
$self->run_eval_tests ($pms, $Mail::SpamAssassin::Conf::TYPE_RAWBODY_EVALS,
- $pms->{conf}->{rawbody_evals}->{$priority}, 'RAW: ',
- $priority, $bodystring);
+ 'rawbody_evals', 'RAW: ', $priority, $bodystring);
}
sub do_full_eval_tests {
@@ -1119,12 +1030,11 @@ sub do_full_eval_tests {
return unless (defined($pms->{conf}->{full_evals}->{$priority}));
dbg("rules: running full_eval tests; score so far=".$pms->{score});
$self->run_eval_tests($pms, $Mail::SpamAssassin::Conf::TYPE_FULL_EVALS,
- $pms->{conf}->{full_evals}->{$priority}, '',
- $priority, $fullmsgref);
+ 'full_evals', '', $priority, $fullmsgref);
}
sub run_eval_tests {
- my ($self, $pms, $testtype, $evalhash, $prepend2desc, $priority, @extraevalargs) = @_;
+ my ($self, $pms, $testtype, $evalname, $prepend2desc, $priority, @extraevalargs) = @_;
my $master_deadline = $pms->{master_deadline};
if ($pms->{deadline_exceeded}) {
@@ -1159,7 +1069,7 @@ sub run_eval_tests {
&& !$doing_user_rules)
{
my $method = "${package_name}::${methodname}";
- # dbg("rules: run_eval_tests - calling previously compiled %s", $method);
+ #dbg("rules: run_eval_tests - calling previously compiled %s", $method);
my $t = Mail::SpamAssassin::Timeout->new({ deadline => $master_deadline });
my $err = $t->run(sub {
no strict "refs";
@@ -1173,24 +1083,23 @@ sub run_eval_tests {
}
# look these up once in advance to save repeated lookups in loop below
+ my $evalhash = $conf->{$evalname}->{$priority};
my $tflagsref = $conf->{tflags};
+ my $scoresref = $conf->{scores};
my $eval_pluginsref = $conf->{eval_plugins};
my $have_start_rules = $self->{main}->have_plugin("start_rules");
my $have_ran_rule = $self->{main}->have_plugin("ran_rule");
# the buffer for the evaluated code
- my $evalstr = q{ };
- $evalstr .= q{ my $function; };
-
+ my $evalstr = '';
+
# conditionally include the dbg in the eval str
- my $dbgstr = q{ };
+ my $dbgstr = '';
if (would_log('dbg')) {
- $dbgstr = q{
- dbg("rules: ran eval rule $rulename ======> got hit ($result)");
- };
+ $dbgstr = 'dbg("rules: ran eval rule $rulename ======> got hit ($result)");';
}
- while (my ($rulename, $test) = each %{$evalhash}) {
+ while (my ($rulename, $test) = each %{$evalhash}) {
if ($tflagsref->{$rulename}) {
# If the rule is a net rule, and we are in a non-net scoreset, skip it.
if ($tflagsref->{$rulename} =~ /\bnet\b/) {
@@ -1201,34 +1110,35 @@ sub run_eval_tests {
next if (($scoreset & 2) == 0);
}
}
+
+ # skip if score zeroed
+ next if !$scoresref->{$rulename};
- $test = untaint_var($test); # presumably checked
- my ($function, $argstr) = ($test,'');
- if ($test =~ s/^([^,]+)(,.*)$//gs) {
- ($function, $argstr) = ($1,$2);
+ my $function = untaint_var($test->[0]); # was validated with \w+
+ if (!$function) {
+ warn "rules: error: no eval function defined for $rulename";
+ next;
}
- if (!$function) {
- warn "rules: error: no function defined for $rulename";
+ if (!exists $conf->{eval_plugins}->{$function}) {
+ warn("rules: error: unknown eval '$function' for $rulename\n");
next;
}
-
+
$evalstr .= '
- if ($scoresptr->{q#'.$rulename.'#}) {
+ {
$rulename = q#'.$rulename.'#;
%{$self->{test_log_msgs}} = ();
- ';
+';
# only need to set current_rule_name for plugin evals
if ($eval_pluginsref->{$function}) {
# let plugins get the name of the rule that is currently being run,
# and ensure their eval functions exist
$evalstr .= '
-
- $self->{current_rule_name} = $rulename;
- $self->register_plugin_eval_glue(q#'.$function.'#);
-
- ';
+ $self->{current_rule_name} = $rulename;
+ $self->register_plugin_eval_glue(q#'.$function.'#);
+';
}
# this stuff is quite slow, and totally superfluous if
@@ -1236,47 +1146,41 @@ sub run_eval_tests {
if ($have_start_rules) {
# XXX - should we use helper function here?
$evalstr .= '
-
$self->{main}->call_plugins("start_rules", {
permsgstatus => $self,
ruletype => "eval",
priority => '.$priority.'
});
- ';
+';
}
-
- $evalstr .= '
+ $evalstr .= '
eval {
- $result = $self->' . $function . ' (@extraevalargs '. $argstr .' ); 1;
+ $result = $self->'.$function.'(@extraevalargs, @{$testptr->{q#'.$rulename.'#}->[1]}); 1;
} or do {
$result = 0;
die "rules: $@\n" if $@ =~ /__alarm__ignore__/;
$self->handle_eval_rule_errors($rulename);
};
-
- ';
+';
if ($have_ran_rule) {
# XXX - should we use helper function here?
$evalstr .= '
-
$self->{main}->call_plugins("ran_rule", {
permsgstatus => $self, ruletype => "eval", rulename => $rulename
});
-
- ';
+';
}
$evalstr .= '
-
if ($result) {
$self->got_hit($rulename, $prepend2desc, ruletype => "eval", value => $result);
'.$dbgstr.'
}
}
- ';
+';
}
# don't free the eval ruleset here -- we need it in the compiled code!
@@ -1288,16 +1192,15 @@ sub run_eval_tests {
{
package $package_name;
- sub ${methodname} {
- my (\$self, \@extraevalargs) = \@_;
-
- my \$scoresptr = \$self->{conf}->{scores};
- my \$prepend2desc = q#$prepend2desc#;
- my \$rulename;
- my \$result;
+ sub ${methodname} {
+ my (\$self, \@extraevalargs) = \@_;
- $evalstr
- }
+ my \$testptr = \$self->{conf}->{$evalname}->{$priority};
+ my \$prepend2desc = q#$prepend2desc#;
+ my \$rulename;
+ my \$result;
+ $evalstr
+ }
1;
}
Modified: spamassassin/branches/3.4/lib/Mail/SpamAssassin/Plugin/HTMLEval.pm
URL: http://svn.apache.org/viewvc/spamassassin/branches/3.4/lib/Mail/SpamAssassin/Plugin/HTMLEval.pm?rev=1848969&r1=1848968&r2=1848969&view=diff
==============================================================================
--- spamassassin/branches/3.4/lib/Mail/SpamAssassin/Plugin/HTMLEval.pm (original)
+++ spamassassin/branches/3.4/lib/Mail/SpamAssassin/Plugin/HTMLEval.pm Fri Dec 14 21:05:01 2018
@@ -24,7 +24,7 @@ use re 'taint';
use Mail::SpamAssassin::Plugin;
use Mail::SpamAssassin::Locales;
-use Mail::SpamAssassin::Util qw(untaint_var);
+use Mail::SpamAssassin::Util qw(untaint_var compile_regexp);
our @ISA = qw(Mail::SpamAssassin::Plugin);
@@ -57,13 +57,18 @@ sub new {
sub html_tag_balance {
my ($self, $pms, undef, $rawtag, $rawexpr) = @_;
- $rawtag =~ /^([a-zA-Z0-9]+)$/; my $tag = $1;
- $rawexpr =~ /^([\<\>\=\!\-\+ 0-9]+)$/; my $expr = $1;
+
+ return 0 if $rawtag !~ /^([a-zA-Z0-9]+)$/;
+ my $tag = $1;
return 0 unless exists $pms->{html}{inside}{$tag};
+ return 0 if $rawexpr !~ /^([\<\>\=\!\-\+ 0-9]+)$/;
+ my $expr = untaint_var($1);
+
$pms->{html}{inside}{$tag} =~ /^([\<\>\=\!\-\+ 0-9]+)$/;
- my $val = $1;
+ my $val = untaint_var($1);
+
return eval "\$val $expr";
}
@@ -119,14 +124,14 @@ sub html_test {
sub html_eval {
my ($self, $pms, undef, $test, $rawexpr) = @_;
- my $expr;
- if ($rawexpr =~ /^[\<\>\=\!\-\+ 0-9]+$/) {
- $expr = untaint_var($rawexpr);
- }
+
+ return 0 if $rawexpr !~ /^([\<\>\=\!\-\+ 0-9]+)$/;
+ my $expr = untaint_var($1);
+
# workaround bug 3320: wierd perl bug where additional, very explicit
# untainting into a new var is required.
my $tainted = $pms->{html}{$test};
- return unless defined($tainted);
+ return 0 unless defined($tainted);
my $val = $tainted;
# just use the value in $val, don't copy it needlessly
@@ -135,8 +140,14 @@ sub html_eval {
sub html_text_match {
my ($self, $pms, undef, $text, $regexp) = @_;
- for my $string (@{ $pms->{html}{$text} }) {
- if (defined $string && $string =~ /${regexp}/) {
+ my ($rec, $err) = compile_regexp($regexp, 0);
+ if (!$rec) {
+ warn "htmleval: html_text_match invalid regexp '$regexp': $err";
+ return 0;
+ }
+ foreach my $string (@{$pms->{html}{$text}}) {
+ next unless defined $string;
+ if ($string =~ $rec) {
return 1;
}
}
Modified: spamassassin/branches/3.4/lib/Mail/SpamAssassin/Plugin/MIMEHeader.pm
URL: http://svn.apache.org/viewvc/spamassassin/branches/3.4/lib/Mail/SpamAssassin/Plugin/MIMEHeader.pm?rev=1848969&r1=1848968&r2=1848969&view=diff
==============================================================================
--- spamassassin/branches/3.4/lib/Mail/SpamAssassin/Plugin/MIMEHeader.pm (original)
+++ spamassassin/branches/3.4/lib/Mail/SpamAssassin/Plugin/MIMEHeader.pm Fri Dec 14 21:05:01 2018
@@ -65,12 +65,15 @@ use re 'taint';
use Mail::SpamAssassin::Plugin;
use Mail::SpamAssassin::Conf;
use Mail::SpamAssassin::Logger;
-use Mail::SpamAssassin::Util qw(untaint_var);
+use Mail::SpamAssassin::Util qw(untaint_var compile_regexp);
+use Mail::SpamAssassin::Constants qw(:sa);
our @ISA = qw(Mail::SpamAssassin::Plugin);
our @TEMPORARY_METHODS;
+my $RULENAME_RE = RULENAME_RE;
+
# ---------------------------------------------------------------------------
# constructor
@@ -101,27 +104,37 @@ sub set_config {
is_priv => 1,
code => sub {
my ($self, $key, $value, $line) = @_;
- local ($1,$2,$3,$4);
- if ($value !~ /^(\S+)\s+(\S+)\s*([\=\!]\~)\s*(.+)$/) {
+ local ($1,$2,$3);
+ if ($value !~ s/^(${RULENAME_RE})\s+//) {
return $Mail::SpamAssassin::Conf::INVALID_VALUE;
}
-
- # provide stricter syntax for rule name!?
my $rulename = untaint_var($1);
- my $hdrname = $2;
- my $negated = ($3 eq '!~') ? 1 : 0;
- my $pattern = $4;
-
- return unless $self->{parser}->is_delimited_regexp_valid($rulename, $pattern);
-
- $pattern = Mail::SpamAssassin::Util::make_qr($pattern);
- return $Mail::SpamAssassin::Conf::INVALID_VALUE unless $pattern;
+ if ($value eq '') {
+ return $Mail::SpamAssassin::Conf::MISSING_REQUIRED_VALUE;
+ }
+ # Take :raw to hdrname!
+ if ($value !~ /^([^:\s]+(?:\:(?:raw)?)?)\s*([=!]~)\s*(.+)$/) {
+ return $Mail::SpamAssassin::Conf::INVALID_VALUE;
+ }
+ my $hdrname = $1;
+ my $negated = $2 eq '!~' ? 1 : 0;
+ my $pattern = $3;
+ $hdrname =~ s/:$//;
+ my $if_unset = '';
+ if ($pattern =~ s/\s+\[if-unset:\s+(.+)\]$//) {
+ $if_unset = $1;
+ }
+ my ($rec, $err) = compile_regexp($pattern, 1);
+ if (!$rec) {
+ info("mimeheader: invalid regexp for $rulename '$pattern': $err");
+ return $Mail::SpamAssassin::Conf::INVALID_VALUE;
+ }
$self->{mimeheader_tests}->{$rulename} = {
hdr => $hdrname,
negated => $negated,
- if_unset => '', # TODO!
- pattern => $pattern
+ if_unset => $if_unset,
+ pattern => $rec
};
# now here's a hack; generate a fake eval rule function to
@@ -129,7 +142,6 @@ sub set_config {
# TODO: we should have a more elegant way for new rule types to
# be defined
my $evalfn = "_mimeheader_eval_$rulename";
- $evalfn =~ s/[^a-zA-Z0-9_]/_/gs;
# don't redefine the subroutine if it already exists!
# this causes lots of annoying warnings and such during things like
@@ -139,6 +151,7 @@ sub set_config {
$self->{parser}->add_test($rulename, $evalfn."()",
$Mail::SpamAssassin::Conf::TYPE_BODY_EVALS);
+ # evalfn/rulename safe, sanitized by $RULENAME_RE
my $evalcode = '
sub Mail::SpamAssassin::Plugin::MIMEHeader::'.$evalfn.' {
$_[0]->eval_hook_called($_[1], q{'.$rulename.'});
@@ -175,7 +188,7 @@ sub eval_hook_called {
my $getraw;
- if ($hdr =~ s/:raw$//i) {
+ if ($hdr =~ s/:raw$//) {
$getraw = 1;
} else {
$getraw = 0;
@@ -188,9 +201,9 @@ sub eval_hook_called {
} else {
$val = $p->get_header($hdr);
}
- $val ||= $if_unset;
+ $val = $if_unset if !defined $val;
- if ($val =~ ${pattern}) {
+ if ($val =~ $pattern) {
return ($negated ? 0 : 1);
}
}
Modified: spamassassin/branches/3.4/lib/Mail/SpamAssassin/Plugin/P595Body.pm
URL: http://svn.apache.org/viewvc/spamassassin/branches/3.4/lib/Mail/SpamAssassin/Plugin/P595Body.pm?rev=1848969&r1=1848968&r2=1848969&view=diff
==============================================================================
--- spamassassin/branches/3.4/lib/Mail/SpamAssassin/Plugin/P595Body.pm (original)
+++ spamassassin/branches/3.4/lib/Mail/SpamAssassin/Plugin/P595Body.pm Fri Dec 14 21:05:01 2018
@@ -70,10 +70,12 @@ sub setup_test_set_pri {
my $alternates = [];
while (my ($rule, $pat) = each %{$conf->{body_tests}->{$pri}}) {
- $pat = Mail::SpamAssassin::Util::regexp_remove_delimiters($pat);
-
# ignore rules marked for ReplaceTags work!
- next if ($conf->{rules_to_replace}->{$rule});
+ next if ($conf->{replace_rules}->{$rule});
+
+ #$pat = Mail::SpamAssassin::Util::regexp_remove_delimiters($pat);
+ $pat = qr_to_string($conf->{test_qrs}->{$rule});
+ next unless !$pat;
# use the REGMARK feature:
# see http://taint.org/2006/11/16/154546a.html#comment-1011
Modified: spamassassin/branches/3.4/lib/Mail/SpamAssassin/Plugin/PDFInfo.pm
URL: http://svn.apache.org/viewvc/spamassassin/branches/3.4/lib/Mail/SpamAssassin/Plugin/PDFInfo.pm?rev=1848969&r1=1848968&r2=1848969&view=diff
==============================================================================
--- spamassassin/branches/3.4/lib/Mail/SpamAssassin/Plugin/PDFInfo.pm (original)
+++ spamassassin/branches/3.4/lib/Mail/SpamAssassin/Plugin/PDFInfo.pm Fri Dec 14 21:05:01 2018
@@ -142,7 +142,7 @@ package Mail::SpamAssassin::Plugin::PDFI
use Mail::SpamAssassin::Plugin;
use Mail::SpamAssassin::Logger;
-use Mail::SpamAssassin::Util;
+use Mail::SpamAssassin::Util qw(compile_regexp);
use strict;
use warnings;
# use bytes;
@@ -471,16 +471,15 @@ sub pdf_name_regex {
return 0 if (exists $pms->{'pdfinfo'}->{'no_parts'});
return 0 unless (exists $pms->{'pdfinfo'}->{"names_pdf"});
+ my ($rec, $err) = compile_regexp($re, 2);
+ if (!$rec) {
+ info("pdfinfo: invalid regexp '$re': $err");
+ return 0;
+ }
+
my $hit = 0;
foreach my $name (keys %{$pms->{'pdfinfo'}->{"names_pdf"}}) {
- eval {
- my $regex = Mail::SpamAssassin::Util::make_qr($re);
- if ( $name =~ m/$regex/ ) {
- $hit = 1;
- }
- };
- dbg("pdfinfo: error in regex $re - $@") if $@;
- if ($hit) {
+ if ($name =~ $rec) {
dbg("pdfinfo: pdf_name_regex hit on $name");
return 1;
}
@@ -722,15 +721,13 @@ sub pdf_match_details {
my $check_value = $pms->{pdfinfo}->{details}->{$detail};
return unless $check_value;
- my $hit = 0;
- eval {
- my $re = Mail::SpamAssassin::Util::make_qr($regex);
- if ( $check_value =~ m/$re/ ) {
- $hit = 1;
- }
- };
- dbg("pdfinfo: error in regex $regex - $@") if $@;
- if ($hit) {
+ my ($rec, $err) = compile_regexp($regex, 2);
+ if (!$rec) {
+ info("pdfinfo: invalid regexp '$regex': $err");
+ return 0;
+ }
+
+ if ($check_value =~ $rec) {
dbg("pdfinfo: pdf_match_details $detail $regex matches $check_value");
return 1;
}
Modified: spamassassin/branches/3.4/lib/Mail/SpamAssassin/Plugin/ReplaceTags.pm
URL: http://svn.apache.org/viewvc/spamassassin/branches/3.4/lib/Mail/SpamAssassin/Plugin/ReplaceTags.pm?rev=1848969&r1=1848968&r2=1848969&view=diff
==============================================================================
--- spamassassin/branches/3.4/lib/Mail/SpamAssassin/Plugin/ReplaceTags.pm (original)
+++ spamassassin/branches/3.4/lib/Mail/SpamAssassin/Plugin/ReplaceTags.pm Fri Dec 14 21:05:01 2018
@@ -52,6 +52,7 @@ package Mail::SpamAssassin::Plugin::Repl
use Mail::SpamAssassin;
use Mail::SpamAssassin::Plugin;
use Mail::SpamAssassin::Logger;
+use Mail::SpamAssassin::Util qw(compile_regexp qr_to_string);
use strict;
use warnings;
@@ -73,6 +74,16 @@ sub new {
return $self;
}
+sub finish_parsing_start {
+ my ($self, $opts) = @_;
+
+ # keeps track of replaced rules
+ # don't have $pms in finish_parsing_end() so init this..
+ $self->{replace_rules_done} = {};
+
+ return 1;
+}
+
sub finish_parsing_end {
my ($self, $opts) = @_;
@@ -82,94 +93,96 @@ sub finish_parsing_end {
my $start = $conf->{replace_start};
my $end = $conf->{replace_end};
- # this is the version-specific code
- for my $type (qw|body_tests rawbody_tests head_tests full_tests uri_tests|) {
- for my $priority (keys %{$conf->{$type}}) {
- while (my ($rule, $re) = each %{$conf->{$type}->{$priority}}) {
- # skip if not listed by replace_rules
- next unless $conf->{rules_to_replace}{$rule};
-
- if (would_log('dbg', 'replacetags') > 1) {
- dbg("replacetags: replacing $rule: $re");
- }
-
- my $passes = 0;
- my $doagain;
+ foreach my $rule (keys %{$conf->{replace_rules}}) {
+ # process rules only once, mark to replace_rules_done,
+ # do NOT delete $conf->{replace_rules}, it's used by BodyRuleExtractor
+ next if exists $self->{replace_rules_done}->{$rule};
+ $self->{replace_rules_done}->{$rule} = 1;
+
+ if (!exists $conf->{test_qrs}->{$rule}) {
+ dbg("replacetags: replace requested for non-existing rule: $rule\n");
+ next;
+ }
- do {
- my $pre_name;
- my $post_name;
- my $inter_name;
- $doagain = 0;
-
- # get modifier tags
- if ($re =~ s/${start}pre (.+?)${end}//) {
- $pre_name = $1;
- }
- if ($re =~ s/${start}post (.+?)${end}//) {
- $post_name = $1;
- }
- if ($re =~ s/${start}inter (.+?)${end}//) {
- $inter_name = $1;
- }
+ my $re = qr_to_string($conf->{test_qrs}->{$rule});
+ next unless defined $re;
+ my $origre = $re;
+
+ my $passes = 0;
+ my $doagain;
+
+ do {
+ my $pre_name;
+ my $post_name;
+ my $inter_name;
+ $doagain = 0;
+
+ # get modifier tags
+ if ($re =~ s/${start}pre (.+?)${end}//) {
+ $pre_name = $1;
+ }
+ if ($re =~ s/${start}post (.+?)${end}//) {
+ $post_name = $1;
+ }
+ if ($re =~ s/${start}inter (.+?)${end}//) {
+ $inter_name = $1;
+ }
- # this will produce an array of tags to be replaced
- # for two adjacent tags, an element of "" will be between the two
- my @re = split(/(<[^<>]+>)/, $re);
-
- if ($pre_name) {
- my $pre = $conf->{replace_pre}->{$pre_name};
- if ($pre) {
- s{($start.+?$end)}{$pre$1} for @re;
- }
- }
- if ($post_name) {
- my $post = $conf->{replace_post}->{$post_name};
- if ($post) {
- s{($start.+?$end)}{$1$post}g for @re;
- }
- }
- if ($inter_name) {
- my $inter = $conf->{replace_inter}->{$inter_name};
- if ($inter) {
- s{^$}{$inter} for @re;
- }
- }
- for (my $i = 0; $i < @re; $i++) {
- if ($re[$i] =~ m|$start(.+?)$end|g) {
- my $tag_name = $1;
- # if the tag exists, replace it with the corresponding phrase
- if ($tag_name) {
- my $replacement = $conf->{replace_tag}->{$tag_name};
- if ($replacement) {
- $re[$i] =~ s|$start$tag_name$end|$replacement|g;
- $doagain = 1 if !$doagain && $replacement =~ /<[^>]+>/;
- }
- }
+ # this will produce an array of tags to be replaced
+ # for two adjacent tags, an element of "" will be between the two
+ my @re = split(/(<[^<>]+>)/, $re);
+
+ if ($pre_name) {
+ my $pre = $conf->{replace_pre}->{$pre_name};
+ if ($pre) {
+ s{($start.+?$end)}{$pre$1} for @re;
+ }
+ }
+ if ($post_name) {
+ my $post = $conf->{replace_post}->{$post_name};
+ if ($post) {
+ s{($start.+?$end)}{$1$post}g for @re;
+ }
+ }
+ if ($inter_name) {
+ my $inter = $conf->{replace_inter}->{$inter_name};
+ if ($inter) {
+ s{^$}{$inter} for @re;
+ }
+ }
+ for (my $i = 0; $i < @re; $i++) {
+ if ($re[$i] =~ m|$start(.+?)$end|g) {
+ my $tag_name = $1;
+ # if the tag exists, replace it with the corresponding phrase
+ if ($tag_name) {
+ my $replacement = $conf->{replace_tag}->{$tag_name};
+ if ($replacement) {
+ $re[$i] =~ s|$start$tag_name$end|$replacement|g;
+ $doagain = 1 if !$doagain && $replacement =~ /<[^>]+>/;
}
}
+ }
+ }
- $re = join('', @re);
-
- # do the actual replacement
- $conf->{$type}->{$priority}->{$rule} = $re;
+ $re = join('', @re);
- if (would_log('dbg', 'replacetags') > 1) {
- dbg("replacetags: replaced $rule: $re");
- }
+ $passes++;
+ } while $doagain && $passes <= 5;
- $passes++;
- } while $doagain && $passes <= 5;
+ if ($re ne $origre) {
+ # do the actual replacement
+ my ($rec, $err) = compile_regexp($re, 0);
+ if (!$rec) {
+ info("replacetags: regexp compilation failed '$re': $err");
+ next;
}
+ $conf->{test_qrs}->{$rule} = $rec;
+ #dbg("replacetags: replaced $rule: '$origre' => '$re'");
+ dbg("replacetags: replaced $rule");
+ } else {
+ dbg("replacetags: nothing was replaced in $rule");
}
}
-
- # free this up, if possible
- if (!$conf->{allow_user_rules}) {
- delete $conf->{rules_to_replace};
- }
-
- dbg("replacetags: done replacing tags");
}
sub user_conf_parsing_end {
@@ -250,6 +263,7 @@ body, header, uri, full, rawbody tests a
push(@cmds, {
setting => 'replace_rules',
is_priv => 1,
+ default => {},
type => $Mail::SpamAssassin::Conf::CONF_TYPE_HASH_KEY_VALUE,
code => sub {
my ($self, $key, $value, $line) = @_;
@@ -259,8 +273,8 @@ body, header, uri, full, rawbody tests a
unless ($value =~ /\S+/) {
return $Mail::SpamAssassin::Conf::INVALID_VALUE;
}
- foreach my $rule (split(' ', $value)) {
- $conf->{rules_to_replace}->{$rule} = 1;
+ foreach my $rule (split(/\s+/, $value)) {
+ $self->{replace_rules}->{$rule} = 1;
}
}
});
Modified: spamassassin/branches/3.4/lib/Mail/SpamAssassin/Plugin/Rule2XSBody.pm
URL: http://svn.apache.org/viewvc/spamassassin/branches/3.4/lib/Mail/SpamAssassin/Plugin/Rule2XSBody.pm?rev=1848969&r1=1848968&r2=1848969&view=diff
==============================================================================
--- spamassassin/branches/3.4/lib/Mail/SpamAssassin/Plugin/Rule2XSBody.pm (original)
+++ spamassassin/branches/3.4/lib/Mail/SpamAssassin/Plugin/Rule2XSBody.pm Fri Dec 14 21:05:01 2018
@@ -38,6 +38,7 @@ package Mail::SpamAssassin::Plugin::Rule
use Mail::SpamAssassin::Plugin;
use Mail::SpamAssassin::Logger;
use Mail::SpamAssassin::Plugin::OneLineBodyRuleType;
+use Mail::SpamAssassin::Util qw(qr_to_string);
use strict;
use warnings;
@@ -120,24 +121,25 @@ sub setup_test_set_pri {
my $found = 0;
foreach my $name (keys %{$rules}) {
- my $rule = $rules->{$name};
+ #my $rule = $rules->{$name};
+ my $rule = qr_to_string($conf->{test_qrs}->{$name});
my $comprule = $hasrules->{$longname{$name} || ''};
$rule =~ s/\#/\[hash\]/gs;
- if (!$comprule) {
+ if (!$comprule) {
# this is pretty common, based on rule complexity; don't warn
# dbg "zoom: skipping rule $name, not in compiled ruleset";
next;
}
if ($comprule ne $rule) {
- dbg "zoom: skipping rule $name, code differs in compiled ruleset";
+ dbg "zoom: skipping rule $name, code differs in compiled ruleset '$comprule' '$rule'";
next;
}
# ignore rules marked for ReplaceTags work!
# TODO: we should be able to order the 'finish_parsing_end'
# plugin calls to do this.
- if ($conf->{rules_to_replace}->{$name}) {
+ if ($conf->{replace_rules}->{$name}) {
dbg "zoom: skipping rule $name, ReplaceTags";
next;
}
Modified: spamassassin/branches/3.4/lib/Mail/SpamAssassin/Plugin/URIDetail.pm
URL: http://svn.apache.org/viewvc/spamassassin/branches/3.4/lib/Mail/SpamAssassin/Plugin/URIDetail.pm?rev=1848969&r1=1848968&r2=1848969&view=diff
==============================================================================
--- spamassassin/branches/3.4/lib/Mail/SpamAssassin/Plugin/URIDetail.pm (original)
+++ spamassassin/branches/3.4/lib/Mail/SpamAssassin/Plugin/URIDetail.pm Fri Dec 14 21:05:01 2018
@@ -68,7 +68,7 @@ Regular expressions should be delimited
package Mail::SpamAssassin::Plugin::URIDetail;
use Mail::SpamAssassin::Plugin;
use Mail::SpamAssassin::Logger;
-use Mail::SpamAssassin::Util qw(untaint_var);
+use Mail::SpamAssassin::Util qw(untaint_var compile_regexp);
use strict;
use warnings;
@@ -122,22 +122,23 @@ sub set_config {
if ($target !~ /^(?:raw|type|cleaned|text|domain)$/) {
return $Mail::SpamAssassin::Conf::INVALID_VALUE;
}
- if ($conf->{parser}->is_delimited_regexp_valid($name, $pattern)) {
- $pattern = $pluginobj->make_qr($pattern);
- }
- else {
- return $Mail::SpamAssassin::Conf::INVALID_VALUE;
+
+ my ($rec, $err) = compile_regexp($pattern, 1);
+ if (!$rec) {
+ dbg("config: uri_detail invalid regexp '$pattern': $err");
+ return $Mail::SpamAssassin::Conf::INVALID_VALUE;
}
- dbg("config: uri_detail adding ($target $op /$pattern/) to $name");
+ dbg("config: uri_detail adding ($target $op /$rec/) to $name");
$conf->{parser}->{conf}->{uri_detail}->{$name}->{$target} =
- [$op, $pattern];
+ [$op, $rec];
$added_criteria = 1;
}
if ($added_criteria) {
dbg("config: uri_detail added $name\n");
- $conf->{parser}->add_test($name, 'check_uri_detail()', $Mail::SpamAssassin::Conf::TYPE_BODY_EVALS);
+ $conf->{parser}->add_test($name, 'check_uri_detail()',
+ $Mail::SpamAssassin::Conf::TYPE_BODY_EVALS);
}
else {
warn "config: failed to add invalid rule $name";
@@ -163,8 +164,8 @@ sub check_uri_detail {
if (exists $rule->{raw}) {
my($op,$patt) = @{$rule->{raw}};
- if ( ($op eq '=~' && $raw =~ /$patt/) ||
- ($op eq '!~' && $raw !~ /$patt/) ) {
+ if ( ($op eq '=~' && $raw =~ $patt) ||
+ ($op eq '!~' && $raw !~ $patt) ) {
dbg("uri: raw matched: '%s' %s /%s/", $raw,$op,$patt);
} else {
next;
@@ -176,8 +177,8 @@ sub check_uri_detail {
my($op,$patt) = @{$rule->{type}};
my $match;
for my $text (keys %{ $info->{types} }) {
- if ( ($op eq '=~' && $text =~ /$patt/) ||
- ($op eq '!~' && $text !~ /$patt/) ) { $match = $text; last }
+ if ( ($op eq '=~' && $text =~ $patt) ||
+ ($op eq '!~' && $text !~ $patt) ) { $match = $text; last }
}
next unless defined $match;
dbg("uri: type matched: '%s' %s /%s/", $match,$op,$patt);
@@ -188,8 +189,8 @@ sub check_uri_detail {
my($op,$patt) = @{$rule->{cleaned}};
my $match;
for my $text (@{ $info->{cleaned} }) {
- if ( ($op eq '=~' && $text =~ /$patt/) ||
- ($op eq '!~' && $text !~ /$patt/) ) { $match = $text; last }
+ if ( ($op eq '=~' && $text =~ $patt) ||
+ ($op eq '!~' && $text !~ $patt) ) { $match = $text; last }
}
next unless defined $match;
dbg("uri: cleaned matched: '%s' %s /%s/", $match,$op,$patt);
@@ -200,8 +201,8 @@ sub check_uri_detail {
my($op,$patt) = @{$rule->{text}};
my $match;
for my $text (@{ $info->{anchor_text} }) {
- if ( ($op eq '=~' && $text =~ /$patt/) ||
- ($op eq '!~' && $text !~ /$patt/) ) { $match = $text; last }
+ if ( ($op eq '=~' && $text =~ $patt) ||
+ ($op eq '!~' && $text !~ $patt) ) { $match = $text; last }
}
next unless defined $match;
dbg("uri: text matched: '%s' %s /%s/", $match,$op,$patt);
@@ -212,8 +213,8 @@ sub check_uri_detail {
my($op,$patt) = @{$rule->{domain}};
my $match;
for my $text (keys %{ $info->{domains} }) {
- if ( ($op eq '=~' && $text =~ /$patt/) ||
- ($op eq '!~' && $text !~ /$patt/) ) { $match = $text; last }
+ if ( ($op eq '=~' && $text =~ $patt) ||
+ ($op eq '!~' && $text !~ $patt) ) { $match = $text; last }
}
next unless defined $match;
dbg("uri: domain matched: '%s' %s /%s/", $match,$op,$patt);
@@ -235,29 +236,5 @@ sub check_uri_detail {
}
# ---------------------------------------------------------------------------
-
-# turn "/foobar/i" into qr/(?i)foobar/
-sub make_qr {
- my ($self, $pattern) = @_;
-
- my $re_delim;
- if ($pattern =~ s/^m(\W)//) { # m!foo/bar!
- $re_delim = $1;
- } else { # /foo\/bar/ or !foo/bar!
- $pattern =~ s/^(\W)//; $re_delim = $1;
- }
- if (!$re_delim) {
- return;
- }
-
- $pattern =~ s/${re_delim}([imsx]*)$//;
-
- my $mods = $1;
- if ($mods) { $pattern = "(?".$mods.")".$pattern; }
-
- return qr/$pattern/;
-}
-
-# ---------------------------------------------------------------------------
1;
Modified: spamassassin/branches/3.4/lib/Mail/SpamAssassin/PluginHandler.pm
URL: http://svn.apache.org/viewvc/spamassassin/branches/3.4/lib/Mail/SpamAssassin/PluginHandler.pm?rev=1848969&r1=1848968&r2=1848969&view=diff
==============================================================================
--- spamassassin/branches/3.4/lib/Mail/SpamAssassin/PluginHandler.pm (original)
+++ spamassassin/branches/3.4/lib/Mail/SpamAssassin/PluginHandler.pm Fri Dec 14 21:05:01 2018
@@ -74,6 +74,13 @@ sub new {
sub load_plugin {
my ($self, $package, $path, $silent) = @_;
+ # Strict name checking
+ if ($package !~ /^(?:\w+::){0,10}\w+$/) {
+ warn "plugin: illegal plugin name, not loading: $package\n";
+ return;
+ }
+ $package = Mail::SpamAssassin::Util::untaint_var($package);
+
# Don't load the same plugin twice!
# Do this *before* calling ->new(), otherwise eval rules will be
# registered on a nonexistent object
@@ -86,6 +93,13 @@ sub load_plugin {
my $ret;
if ($path) {
+ if ($path !~ /^\S+\.pm/i) {
+ warn "plugin: illegal plugin filename, not loading: $path";
+ return;
+ }
+
+ $path = $self->{main}->{conf}->{parser}->fix_path_relative_to_current_file($path);
+
# bug 3717:
# At least Perl 5.8.0 seems to confuse $cwd internally at some point -- we
# need to use an absolute path here else we get a "File not found" error.