SA Bugzilla – Bug 4705
SpamAssassin uses eval() too often
Last modified: 2005-11-23 16:04:43 UTC
It seems to me that you (the authors of SpamAssassin) don't know how to use Perl's eval() correctly. The following is a list that grep(1) found, and in which all lines I considered irrelevant are removed. I'll discuss some of the lines below. Please verify for each of these lines that the eval() is not exploitable. And when you have done this it would be really nice if you could add a comment to every "eval" line that indicates that this particular instance of eval() has been checked thoroughly. This makes checking with grep(1) more effective. :) But best of all, avoid eval($string) whenever possible. ===> Please check if the following can be transformed to eval { ... }. ./t/meta.t:26:use constant HAS_DATADUMPER => eval 'use Data::Dumper; 1;'; ./masses/mass-check:99:eval "use bytes"; ./lib/Mail/SpamAssassin/DnsResolver.pm:166: eval '$family = AF_INET6; $ipv6 = 1;'; ./lib/Mail/SpamAssassin/Util/DependencyInfo.pm:189: eval ' ./lib/Mail/SpamAssassin/Util.pm:165: $AM_TAINTED = eval q(no warnings q(syntax); ${^TAINT}); ./lib/Mail/SpamAssassin/Util.pm:584: eval ' sub _getpwuid_wrapper { getpwuid($_[0]); } '; ./lib/Mail/SpamAssassin/Util.pm:587: eval ' sub _getpwuid_wrapper { _fake_getpwuid($_[0]); } '; => (Perl 5.8 allows to nest subroutines syntactically) ===> The following seem safe to me. ./lib/Mail/SpamAssassin/AutoWhitelist.pm:77: eval ' ./lib/Mail/SpamAssassin/Plugin/MIMEHeader.pm:132: eval $evalcode; ./lib/Mail/SpamAssassin/Util/DependencyInfo.pm:196: if (eval ' require '.$module.'; $modver = $'.$module.'::VERSION; 1;') ./lib/Mail/SpamAssassin/Util/DependencyInfo.pm:232: eval "use $moddef->{module} $moddef->{version};"; ./lib/Mail/SpamAssassin/Util/DependencyInfo.pm:238: eval "use $moddef->{module};"; ./lib/Mail/SpamAssassin/BayesStore/DBM.pm:1479: eval 'use ' . $dbm . '; ===> I wasn't motivated enough to check the following, not even basically: ./masses/corpora/remove-tests-from-logs:30:eval $sub; ./masses/hit-frequencies:434: eval $evalstr; ./lib/Mail/SpamAssassin/PerMsgStatus.pm:1693: eval $evalstr; ./lib/Mail/SpamAssassin/PerMsgStatus.pm:1784: eval $evalstr; ./lib/Mail/SpamAssassin/PerMsgStatus.pm:2195: eval $evalstr; ./lib/Mail/SpamAssassin/PerMsgStatus.pm:2284: eval $evalstr; ./lib/Mail/SpamAssassin/PerMsgStatus.pm:2354: eval $evalstr; ./lib/Mail/SpamAssassin/PerMsgStatus.pm:2520: eval $evalstr; ./lib/Mail/SpamAssassin/PerMsgStatus.pm:2617: eval $evalstr; => Please don't make the eval() code that complicated. ===> Maybe they are exploitable, at least they look promising: ./lib/Mail/SpamAssassin/Conf/Parser.pm:484: if (eval $eval) { ./lib/Mail/SpamAssassin/Conf/Parser.pm:848: if (eval $evalstr) { ./lib/Mail/SpamAssassin/PerMsgStatus.pm:1049: eval $fmt; ===> Obviously exploitable: ./lib/Mail/SpamAssassin/Conf/Parser.pm:913: if (eval $evalstr) { ===> Needs more error checking: ./lib/Mail/SpamAssassin/EvalTests.pm:2751: return eval "\$val $expr"; ./lib/Mail/SpamAssassin/EvalTests.pm:2815: return eval "\$val $expr"; ./lib/Mail/SpamAssassin/Dns.pm:264: $self->got_hit($rule, "SenderBase: ") if !$undef && eval $subtest; ===> Called from external sources, so I haven't checked: ./lib/Mail/SpamAssassin/PluginHandler.pm:91: $ret = eval qq{ require $package; }; ./lib/Mail/SpamAssassin/PluginHandler.pm:100: my $plugin = eval $package.q{->new ($self->{main}); }; ./lib/Mail/SpamAssassin/Logger.pm:228: eval 'use Mail::SpamAssassin::Logger::'.$class.';'; ./lib/Mail/SpamAssassin/Logger.pm:232: my $object = eval 'Mail::SpamAssassin::Logger::'.$class.'->new(%params);'; ./lib/Mail/SpamAssassin/PerMsgStatus.pm:1798: return 0 if (eval 'defined &Mail::SpamAssassin::PerMsgStatus::'.$subname); ===> Operates on data from external sources: ./lib/Mail/SpamAssassin/Bayes.pm:242: eval ' ./lib/Mail/SpamAssassin/Util.pm:832: if (eval 'require '.$mod.'; 1; ') { ===> I don't understand that at all. If you die if an error occurs, what is the purpose of eval()? ./lib/Mail/SpamAssassin.pm:328: eval '...'; ($@) and die $@; Roland
as I said in bug 4700: It's core to SpamAssassin's architecture that we evaluate chunks of perl code derived from rules read from the configuration directories; SpamAssassin's core ruleset is compiled into perl bytecode internally. If this is undesirable in your security configuration, you should not use SpamAssassin.
I hit Commit too early -- this is pissing me off. In addition to that comment, perhaps some investigation on *YOUR* part might be in order. Please check if the following can be transformed to eval { ... }. ./t/meta.t:26:use constant HAS_DATADUMPER => eval 'use Data::Dumper; 1;'; why don't YOU check? the answer is, in a word, "no" -- it cannot. The use of "eval STRING" is *core* to much software development in perl (similarly to similar constructs in Lisp, among other languages). If you wish to eradicate its use, good luck.