Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions MANIFEST
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ t/agent.t
t/basic.t
t/cli_smoke.t
t/config.t
t/core_bugs.t
t/dehtml.t
t/enbase64.t
t/core.t
Expand Down
2 changes: 1 addition & 1 deletion lib/Razor2/Client/Agent.pm
Original file line number Diff line number Diff line change
Expand Up @@ -851,7 +851,7 @@ sub parse_mbox {
}

foreach my $file (@ARGV) {
my $fh = new IO::File;
my $fh = IO::File->new;
my @message = ();
if ( ref $file ) {
$fh = $file;
Expand Down
6 changes: 3 additions & 3 deletions lib/Razor2/Client/Core.pm
Original file line number Diff line number Diff line change
Expand Up @@ -467,7 +467,7 @@ sub prepare_parts {

$obj->{p} = [];
foreach ( 0 .. $#bodyparts ) {
$bodyparts[$_] =~ s/\r\n/\n/gs;
${ $bodyparts[$_] } =~ s/\r\n/\n/gs;
$obj->{p}->[$_] = {
id => "$obj->{id}.$_",
body => $bodyparts[$_],
Expand Down Expand Up @@ -1033,7 +1033,7 @@ sub check_logic {
elsif ( $logic_engines eq 'all' ) {
$leng = $self->{s}->{engines};
}
elsif ( $logic_engines =~ /^(\d\,)+$/ ) {
elsif ( $logic_engines =~ /^\d+(?:,\d+)*$/ ) {
$leng = {};
foreach ( split /,/, $logic_engines ) {
unless ( $self->{s}->{engines}->{$_} ) {
Expand Down Expand Up @@ -1759,7 +1759,7 @@ sub connect {
}
}

my $select = new IO::Select($sock);
my $select = IO::Select->new($sock);
my @handles = $select->can_read(15);
if ( $handles[0] ) {
$self->log( 8, "Connection established" );
Expand Down
109 changes: 109 additions & 0 deletions t/core_bugs.t
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
#!perl

use strict;
use warnings;

use Test::More;

use Razor2::Client::Core;

# Core inherits from Logger — provide a no-op log stub
no warnings 'once', 'redefine';
*Razor2::Client::Core::log = sub { };
*Razor2::Client::Core::logobj = sub { };

# === Test 1: bodyparts CRLF normalization ===
# prepare_parts must normalize \r\n to \n in body parts (scalar refs)

subtest 'prepare_parts normalizes CRLF in body parts' => sub {
my $core = Razor2::Client::Core->new;
$core->{conf} = { debuglevel => 0, report_headers => 1 };
$core->{name_version} = 'test-1.0';

# Simulate what prepare_parts receives after prep_mail:
# prep_mail returns ($headers, @bodyparts) where each bodypart is a scalar ref
my $mail_body = "Content-Type: text/plain\r\n\r\nHello\r\nWorld\r\n";
my $mail = "From: test\@example.com\nSubject: test\n\n$mail_body";
my $obj = {
id => 1,
orig_mail => \$mail,
};

# We can't easily call prepare_parts in isolation (it calls prep_mail),
# so test the fix more directly: verify that s/\r\n/\n/gs on a deref'd
# scalar ref works correctly
my $body_with_crlf = "Hello\r\nWorld\r\n";
my $ref = \$body_with_crlf;

# This is the FIXED code path:
${$ref} =~ s/\r\n/\n/gs;
is( $$ref, "Hello\nWorld\n", 'CRLF normalized via dereferenced scalar ref' );

# Verify the bug: operating on the ref itself does NOT modify the string
my $body2 = "Hello\r\nWorld\r\n";
my $ref2 = \$body2;
$ref2 =~ s/\r\n/\n/gs; # Bug: operates on "SCALAR(0x...)" string
is( $$ref2, "Hello\r\nWorld\r\n", 'Bug confirmed: ref =~ s/// does not modify string' );
};

# === Test 2: logic_engines regex accepts comma-separated digits ===

subtest 'logic_engines regex matches valid formats' => sub {
# The fixed regex: /^\d+(?:,\d+)*$/
my $regex = qr/^\d+(?:,\d+)*$/;

# These should all match
like( '4', $regex, 'single engine' );
like( '4,8', $regex, 'two engines' );
like( '1,4,8', $regex, 'three engines' );
like( '42', $regex, 'multi-digit engine' );

# These should NOT match
unlike( '', $regex, 'empty string' );
unlike( '4,', $regex, 'trailing comma' );
unlike( ',4', $regex, 'leading comma' );
unlike( '4,,8', $regex, 'double comma' );
unlike( 'any', $regex, 'word' );
unlike( '4, 8', $regex, 'comma with space' );

# Verify the OLD regex was broken for common input
my $old_regex = qr/^(\d\,)+$/;
unlike( '4,8', $old_regex, 'old regex fails on "4,8" (no trailing comma)' );
like( '4,8,', $old_regex, 'old regex only matches with trailing comma' );
};

# === Test 3: check_logic with logic_engines as comma-separated list ===

subtest 'check_logic parses logic_engines comma list' => sub {
my $core = Razor2::Client::Core->new;
$core->{s} = {
engines => { 4 => 1, 8 => 1 },
min_cf => 50,
};
$core->{conf} = {
logic_method => 4,
logic_engines => '4,8',
};

my $obj = {
id => 1,
orig_mail => \'dummy',
spam => 0,
p => [
{
id => '1.0',
spam => 0,
sent => [ { e => 4, s => 'sig1' } ],
resp => [ { p => '1', cf => 75 } ],
},
],
};

$core->check_logic($obj);

# With the fixed regex, logic_engines '4,8' is parsed as a specific
# engine list, and engine 4 returning spam should set spam = 1
is( $obj->{spam}, 1, 'logic_engines "4,8" correctly parsed and applied' );
};

done_testing;
Loading