# SATest with unused functions removed
# remove all references to spamc and spamd
# removed unused end block and untaint_cmd function
# remove read_config and a bit more
# remove call to tempdir, put it in the test
package
main;
require
v5.14.0;
# use strict;
# use warnings;
# use re 'taint';
use
Cwd;
use
Config;
use
File::Basename;
use
File::Copy;
use
File::Path;
use
File::Spec;
use
Test::Builder ();
use
Test::More ();
$SKIP_SETUID_NOBODY_TESTS $SKIP_DNSBL_TESTS
$have_inet4 $have_inet6
$workdir $siterules $localrules $userrules $userstate
$keep_workdir $mainpid)
;
my
$sa_code_dir
;
BEGIN {
@ISA
=
qw(Exporter)
;
@EXPORT
=
qw($have_inet4 $have_inet6)
;
$RUNNING_ON_WINDOWS
= ($^O =~ /^(mswin|dos|os2)/oi);
$SKIP_SETUID_NOBODY_TESTS
= 0;
$SKIP_DNSBL_TESTS
= 0;
$have_inet4
=
eval
{
my
$sock
= IO::Socket::INET->new(
LocalAddr
=>
'127.0.0.1'
,
Proto
=>
'udp'
);
$sock
->
close
or
die
"error closing inet socket: $!"
if
$sock
;
$sock
? 1 :
undef
;
};
$have_inet6
=
eval
{
my
$sock
= IO::Socket::INET6->new(
LocalAddr
=>
'::1'
,
Proto
=>
'udp'
);
$sock
->
close
or
die
"error closing inet6 socket: $!"
if
$sock
;
$sock
? 1 :
undef
;
};
# Clean PATH so taint doesn't complain
if
(!
$RUNNING_ON_WINDOWS
) {
$ENV
{
'PATH'
} =
'/bin:/usr/bin:/usr/local/bin'
;
# Remove tainted envs, at least ENV used in FreeBSD
delete
@ENV
{
qw(IFS CDPATH ENV BASH_ENV)
};
}
else
{
# Windows might need non-system directories in PATH to run a Perl installation
# The best we can do is clean out obviously bad stuff such as relative paths or \..\
my
@pathdirs
=
split
(
';'
,
$ENV
{
'PATH'
});
$ENV
{
'PATH'
} =
join
(
';'
,
# filter for only dirs that are canonical absolute paths that exist
map
{
my
$pathdir
=
$_
;
$pathdir
=~ s/\\*\z//;
my
$abspathdir
= File::Spec->canonpath(Cwd::realpath(
$pathdir
))
if
(-d
$pathdir
);
if
(
defined
$abspathdir
) {
$abspathdir
=~ /^(.*)\z/s;
$abspathdir
= $1;
# untaint it
}
((
defined
$abspathdir
) and (
lc
$pathdir
eq
lc
$abspathdir
))?(
$abspathdir
):()
}
@pathdirs
);
}
# Fix INC to point to absolute path of built SA
if
(-e
't/test_dir'
) {
$sa_code_dir
=
'blib/lib'
; }
elsif
(-e
'test_dir'
) {
$sa_code_dir
=
'../blib/lib'
; }
else
{
die
"FATAL: not in or below test directory?\n"
; }
File::Spec->rel2abs(
$sa_code_dir
) =~ /^(.*)\z/s;
$sa_code_dir
= $1;
if
(not -d
$sa_code_dir
) {
die
"FATAL: not in expected directory relative to built code tree?\n"
;
}
}
# use is run at compile time, but after the variable has been computed in the BEGIN block
sub
sa_t_init {
my
$tname
=
shift
;
$mainpid
= $$;
if
(
$config
{PERL_PATH}) {
$perl_path
=
$config
{PERL_PATH};
}
elsif
($^X =~ m|^/|) {
$perl_path
= $^X;
}
else
{
$perl_path
=
$Config
{perlpath};
$perl_path
=~ s|/[^/]*$|/$^X|;
}
$perl_cmd
=
$perl_path
;
# propagate $PERL5OPT; seems to be necessary, at least for the common idiom of
# "PERL5OPT=-MFoo::Bar ./test.t"
if
(
$ENV
{
'PERL5OPT'
}) {
my
$o
=
$ENV
{
'PERL5OPT'
};
if
(
$o
=~ /(Devel::Cover)/) {
warn
"# setting TEST_PERL_TAINT=no to avoid lack of taint-safety in $1\n"
;
$ENV
{
'TEST_PERL_TAINT'
} =
'no'
;
}
$perl_cmd
.=
" \"$o\""
;
}
$perl_cmd
.=
" -T"
if
!
defined
(
$ENV
{
'TEST_PERL_TAINT'
}) or
$ENV
{
'TEST_PERL_TAINT'
} ne
'no'
;
$perl_cmd
.=
" -w"
if
!
defined
(
$ENV
{
'TEST_PERL_WARN'
}) or
$ENV
{
'TEST_PERL_WARN'
} ne
'no'
;
# Copy directories in PERL5LIB into -I options in perl_cmd because -T suppresses use of PERL5LIB in call to ./spamassassin
# If PERL5LIB is empty copy @INC instead because on some platforms like FreeBSD MakeMaker clears PER5LIB and sets @INC
# Filter out relative paths, and canonicalize so no symlinks or /../ will be left in untainted result as a nod to security
# Since this is only used to run tests, the security considerations are not as strict as with more general situations.
my
@pathdirs
=
@INC
;
if
(
$ENV
{
'PERL5LIB'
}) {
@pathdirs
=
split
(
$Config
{path_sep},
$ENV
{
'PERL5LIB'
});
}
my
$inc_opts
=
join
(
' -I'
,
# filter for only dirs that are absolute paths that exist, then canonicalize them
map
{
my
$pathdir
=
$_
;
my
$canonpathdir
= File::Spec->canonpath(Cwd::realpath(
$pathdir
))
if
((-d
$pathdir
) and File::Spec->file_name_is_absolute(
$pathdir
));
if
(
defined
$canonpathdir
) {
$canonpathdir
=~ /^(.*)\z/s;
$canonpathdir
= $1;
# untaint it
}
((
defined
$canonpathdir
))?(
$canonpathdir
):()
}
@pathdirs
);
$perl_cmd
.=
" -I$inc_opts"
if
(
$inc_opts
);
# To work in Windows, the perl scripts have to be launched by $perl_cmd and
# the ones that are exe files have to be directly called in the command lines
$scr
=
$ENV
{
'SPAMASSASSIN_SCRIPT'
};
$scr
||=
"$perl_cmd ../spamassassin.raw"
;
$salearn
=
$ENV
{
'SALEARN_SCRIPT'
};
$salearn
||=
"$perl_cmd ../sa-learn.raw"
;
$saawl
=
$ENV
{
'SAAWL_SCRIPT'
};
$saawl
||=
"$perl_cmd ../sa-awl"
;
(-f
"t/test_dir"
) &&
chdir
(
"t"
);
# run from ..
-f
"test_dir"
or
die
"FATAL: not in test directory?\n"
;
mkdir
(
"log"
, 0755);
-d
"log"
or
die
"FATAL: failed to create log dir\n"
;
chmod
(0755,
"log"
);
# set in case log already exists with wrong permissions
if
(!
$RUNNING_ON_WINDOWS
) {
untaint_system(
"chacl -B log 2>/dev/null || setfacl -b log 2>/dev/null"
);
# remove acls that confuse test
}
# clean old workdir if sa_t_init called multiple times
if
(
defined
$workdir
) {
if
(!
$keep_workdir
) {
rmtree(
$workdir
);
}
}
##########
### Test return here but keep some code that comes after to be compiled
return
if
$tname
;
# # individual work directory to make parallel tests possible
# $workdir = tempdir("$tname.XXXXXX", DIR => "log");
# die "FATAL: failed to create workdir: $!" unless -d $workdir;
# chmod (0755, $workdir); # sometimes tempdir() ignores umask
# $keep_workdir = 0;
# # $siterules contains all stock *.pre files
# $siterules = "$workdir/siterules";
# # $localrules contains all stock *.cf files
# $localrules = "$workdir/localrules";
# # $userrules contains user rules
# $userrules = "$workdir/user.cf";
# # user_state directory
# $userstate = "$workdir/user_state";
# mkdir($siterules) or die "FATAL: failed to create $siterules\n";
# mkdir($localrules) or die "FATAL: failed to create $localrules\n";
# open(OUT, ">$userrules") or die "FATAL: failed to create $userrules\n";
# close(OUT);
# mkdir($userstate) or die "FATAL: failed to create $userstate\n";
# $scr_localrules_args = " --siteconfigpath $siterules";
# $salearn_localrules_args = " --siteconfigpath $siterules";
# $scr_cf_args = "-C $localrules";
# $scr_pref_args = "-p $userrules";
# $salearn_cf_args = "-C $localrules";
# $salearn_pref_args = "-p $userrules";
# $scr_test_args = "";
# $salearn_test_args = "";
# $set_user_prefs = 0;
# $default_cf_lines = "
# bayes_path ./$userstate/bayes
# auto_welcomelist_path ./$userstate/auto-welcomelist
# ";
# if running as root, ensure "nobody" can write to it too
# if ($> == 0) {
# $tmp_dir_mode = 0777;
# umask 022; # ensure correct permissions on files and dirs created here
# # Bug 5529 initial fix: For now don't run a test as root if it has a problem resuting from setuid nobody
# # FIXME: Eventually we can actually test setuid nobody and accessing ./log to make this test more fine grained
# # and we can create an accessible temp dir that some of the tests can use. But for now just skip those tests.
# $SKIP_SETUID_NOBODY_TESTS = 1;
# } else {
# $tmp_dir_mode = 0755;
# }
# for $tainted (<../rules/*.pm>, <../rules/*.pre>, <../rules/languages>) {
# $tainted =~ /(.*)/;
# my $file = $1;
# $base = basename $file;
# copy ($file, "$siterules/$base")
# or warn "cannot copy $file to $siterules/$base: $!";
# }
# for $tainted (<../rules/*.cf>) {
# $tainted =~ /(.*)/;
# my $file = $1;
# $base = basename $file;
# copy ($file, "$localrules/$base")
# or warn "cannot copy $file to $localrules/$base: $!";
# }
}
# Simple version of untaint_var for internal use
sub
untaint_var {
local
($1);
$_
[0] =~ /^(.*)\z/s;
return
$1;
}
# untainted system()
sub
untaint_system {
my
@args
;
push
@args
, untaint_var(
$_
)
foreach
(
@_
);
return
system
(
@args
);
}
1;