blob: f584346b8c5a21058291e2e61d5bd2699d8efd8c [file]
#!/usr/bin/perl -T
use lib '.'; use lib 't';
use SATest; sa_t_init("handler_archive");
use Test::More;
# ---------------------------------------------------------------------------
# End-to-end test of Mail::SpamAssassin::Handler::Archive.
#
# The handler opens zip / rar attachments and returns each inner file as a
# sub-part, which the framework re-dispatches by filename extension. So an
# inner *.js reaches the JavaScript handler and an inner *.html is rendered into
# the body. (Note: a plain *.txt member is deliberately NOT surfaced as body
# text -- only text/plain, text/html and message/* parts feed body rules, and
# .txt is not in the file-type map -- so the fixtures use *.js and *.html.)
#
# We confirm:
# * an inner *.js member reaches the JavaScript handler ('script' rule);
# * an inner *.html member is rendered into the body;
# * the same works when the zip is mislabelled application/octet-stream but
# named *.zip (Message::Node::effective_type maps it back);
# * archive_max_files caps how many members are extracted;
# * rar archives behave the same (only when 'unrar' is installed).
#
# Each fixture under data/nice/ is an ordinary MIME message (100% ASCII) whose
# attachment is a small zip/rar containing inner.js (sentinel INNERJS) and
# inner.html (sentinel INNERHTML), base64-encoded. The zip uses pure-Perl
# extraction so those cases run everywhere; the rar case needs the 'unrar'
# binary and is skipped cleanly when it is absent.
# Locate unrar the way handler_pdf.t locates pdftotext: SATest scrubs PATH to a
# hermetic default, so also probe a few common install locations.
sub find_bin {
my ($exe) = @_;
for my $dir (split(/:/, $ENV{PATH}),
qw(/usr/bin /usr/local/bin /opt/homebrew/bin /opt/local/bin)) {
my $cand = "$dir/$exe";
return $cand if -x $cand;
}
return undef;
}
my $unrar = find_bin('unrar');
# 7 zip assertions, +3 octet-stream, +2 max-files, +(4 rar when unrar present)
plan tests => 7 + 3 + 2 + ($unrar ? 4 : 0);
tstpre ("
loadhandler Mail::SpamAssassin::Handler::Archive
loadhandler Mail::SpamAssassin::Handler::HTML
loadhandler Mail::SpamAssassin::Handler::JavaScript
" . (defined $unrar ? " archive_unrar_path $unrar\n" : ""));
tstlocalrules ('
script ARC_JS /INNERJS/
score ARC_JS 1.0
describe ARC_JS an inner .js file reached the JavaScript handler
body ARC_HTML /INNERHTML/
score ARC_HTML 1.0
describe ARC_HTML an inner .html file reached the HTML renderer
body ARC_ORIG /ORIGINAL_BODY_MARKER/
score ARC_ORIG 1.0
describe ARC_ORIG original outer body preserved
body ARC_COUNT2 eval:check_archive_file_count(2)
score ARC_COUNT2 1.0
describe ARC_COUNT2 archive declares at least 2 files
body ARC_COUNT5 eval:check_archive_file_count(5)
score ARC_COUNT5 1.0
describe ARC_COUNT5 archive declares at least 5 files (should NOT fire)
');
# --- zip (real application/zip) ---------------------------------------------
%patterns = (
' 1.0 ARC_JS ', 'zip_js_member',
' 1.0 ARC_HTML ', 'zip_html_member',
' 1.0 ARC_ORIG ', 'original_body_preserved',
' 1.0 ARC_COUNT2 ', 'zip_file_count_at_least_2',
);
%anti_patterns = (
'ARC_COUNT5 ', 'zip_file_count_not_5', # zip has only 2 entries
);
ok (sarun ("-L -t < data/nice/handler_archive_zip", \&patterns_run_cb));
ok_all_patterns();
# --- same zip mislabelled application/octet-stream but named *.zip -----------
%patterns = (
' 1.0 ARC_JS ', 'octet_js_member',
' 1.0 ARC_HTML ', 'octet_html_member',
);
%anti_patterns = ();
ok (sarun ("-L -t < data/nice/handler_archive_octet", \&patterns_run_cb));
ok_all_patterns();
# --- archive_max_files cap (extract only the first member) ------------------
# inner.js is first in the zip, so with archive_max_files 1 only ARC_JS fires;
# the inner.html member must NOT be extracted, so ARC_HTML must NOT fire.
{
local %patterns = (
' 1.0 ARC_JS ', 'maxfiles_first_member',
);
local %anti_patterns = (
'ARC_HTML ', 'maxfiles_second_member_skipped',
);
ok (sarun ("-L -t --cf='archive_max_files 1' < data/nice/handler_archive_zip",
\&patterns_run_cb));
ok_all_patterns();
}
# --- rar (only when unrar is available) -------------------------------------
SKIP: {
skip("unrar not installed", 4) unless $unrar;
%patterns = (
' 1.0 ARC_JS ', 'rar_js_member',
' 1.0 ARC_HTML ', 'rar_html_member',
' 1.0 ARC_COUNT2 ', 'rar_file_count_at_least_2', # count via unrar member list
);
%anti_patterns = ();
ok (sarun ("-L -t < data/nice/handler_archive_rar", \&patterns_run_cb));
ok_all_patterns();
}