diff --git a/apache2/t/regression/action/10-logging.t b/apache2/t/regression/action/10-logging.t
index c94c14ca..0c15bd42 100644
--- a/apache2/t/regression/action/10-logging.t
+++ b/apache2/t/regression/action/10-logging.t
@@ -135,7 +135,7 @@
),
match_log => {
-error => [ qr/ModSecurity: /, 1 ],
- # ENH: No message, but should have data. Is this intended?
+ # No message, but should have data. This may need changed
audit => [ qr/-H--\s+Stopwatch: /s, 1 ],
},
match_response => {
diff --git a/apache2/t/regression/config/10-request-directives.t b/apache2/t/regression/config/10-request-directives.t
index 97bd0cb8..19e7802e 100644
--- a/apache2/t/regression/config/10-request-directives.t
+++ b/apache2/t/regression/config/10-request-directives.t
@@ -190,7 +190,7 @@
SecDebugLogLevel 9
SecRequestBodyAccess On
SecRequestBodyLimit 1000
- SecRequestBodyInMemoryLimit 266
+ SecRequestBodyInMemoryLimit 276
),
match_log => {
-debug => [ qr/Input filter: Request too large to store in memory, switching to disk\./, 1 ],
@@ -198,22 +198,32 @@
match_response => {
status => qr/^200$/,
},
- request => qq(
- POST /test.txt HTTP/1.1
- Host: $ENV{SERVER_NAME}:$ENV{SERVER_PORT}
- User-Agent: $ENV{USER_AGENT}
- Content-Type: multipart/form-data; boundary=---------------------------69343412719991675451336310646
- Transfer-Encoding: chunked
+ request => normalize_raw_request_data(
+ qq(
+ POST /test.txt HTTP/1.1
+ Host: $ENV{SERVER_NAME}:$ENV{SERVER_PORT}
+ User-Agent: $ENV{USER_AGENT}
+ Content-Type: multipart/form-data; boundary=---------------------------69343412719991675451336310646
+ Transfer-Encoding: chunked
- ) . encode_chunked(q(-----------------------------69343412719991675451336310646
-Content-Disposition: form-data; name="a"
+ ),
+ )
+ .encode_chunked(
+ normalize_raw_request_data(
+ q(
+ -----------------------------69343412719991675451336310646
+ Content-Disposition: form-data; name="a"
-1
------------------------------69343412719991675451336310646
-Content-Disposition: form-data; name="b"
+ 1
+ -----------------------------69343412719991675451336310646
+ Content-Disposition: form-data; name="b"
-2
------------------------------69343412719991675451336310646--), 1024),
+ 2
+ -----------------------------69343412719991675451336310646--
+ )
+ ),
+ 1024
+ ),
},
{
type => "config",
@@ -232,22 +242,32 @@ Content-Disposition: form-data; name="b"
match_response => {
status => qr/^200$/,
},
- request => qq(
- POST /test.txt HTTP/1.1
- Host: $ENV{SERVER_NAME}:$ENV{SERVER_PORT}
- User-Agent: $ENV{USER_AGENT}
- Content-Type: multipart/form-data; boundary=---------------------------69343412719991675451336310646
- Transfer-Encoding: chunked
+ request => normalize_raw_request_data(
+ qq(
+ POST /test.txt HTTP/1.1
+ Host: $ENV{SERVER_NAME}:$ENV{SERVER_PORT}
+ User-Agent: $ENV{USER_AGENT}
+ Content-Type: multipart/form-data; boundary=---------------------------69343412719991675451336310646
+ Transfer-Encoding: chunked
- ) . encode_chunked(q(-----------------------------69343412719991675451336310646
-Content-Disposition: form-data; name="a"
+ ),
+ )
+ .encode_chunked(
+ normalize_raw_request_data(
+ q(
+ -----------------------------69343412719991675451336310646
+ Content-Disposition: form-data; name="a"
-1
------------------------------69343412719991675451336310646
-Content-Disposition: form-data; name="b"
+ 1
+ -----------------------------69343412719991675451336310646
+ Content-Disposition: form-data; name="b"
-2
------------------------------69343412719991675451336310646--), 1024),
+ 2
+ -----------------------------69343412719991675451336310646--
+ )
+ ),
+ 1024
+ ),
},
# SecCookieFormat
diff --git a/apache2/t/regression/misc/00-multipart-parser.t b/apache2/t/regression/misc/00-multipart-parser.t
new file mode 100644
index 00000000..391813f9
--- /dev/null
+++ b/apache2/t/regression/misc/00-multipart-parser.t
@@ -0,0 +1,361 @@
+### Multipart parser tests
+
+# Final CRLF or not, we should still work
+{
+ type => "misc",
+ comment => "multipart parser (final CRLF)",
+ conf => qq(
+ SecRuleEngine On
+ SecDebugLog $ENV{DEBUG_LOG}
+ SecDebugLogLevel 9
+ SecRequestBodyAccess On
+ SecRule MULTIPART_STRICT_ERROR "\@eq 1" "phase:2,deny"
+ SecRule MULTIPART_UNMATCHED_BOUNDARY "\@eq 1" "phase:2,deny"
+ SecRule REQBODY_PROCESSOR_ERROR "\@eq 1" "phase:2,deny"
+ ),
+ match_log => {
+ debug => [ qr/Adding request argument \(BODY\): name "a", value "1".*Adding request argument \(BODY\): name "b", value "2"/s, 1 ],
+ -debug => [ qr/Multipart error:/, 1 ],
+
+ },
+ match_response => {
+ status => qr/^200$/,
+ },
+ request => new HTTP::Request(
+ POST => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test.txt",
+ [
+ "Content-Type" => "multipart/form-data; boundary=---------------------------69343412719991675451336310646",
+ ],
+ normalize_raw_request_data(
+ q(
+ -----------------------------69343412719991675451336310646
+ Content-Disposition: form-data; name="a"
+
+ 1
+ -----------------------------69343412719991675451336310646
+ Content-Disposition: form-data; name="b"
+
+ 2
+ -----------------------------69343412719991675451336310646--
+ ),
+ ),
+ ),
+},
+{
+ type => "misc",
+ comment => "multipart parser (no final CRLF)",
+ conf => qq(
+ SecRuleEngine On
+ SecDebugLog $ENV{DEBUG_LOG}
+ SecDebugLogLevel 9
+ SecRequestBodyAccess On
+ SecRule MULTIPART_STRICT_ERROR "\@eq 1" "phase:2,deny"
+ SecRule MULTIPART_UNMATCHED_BOUNDARY "\@eq 1" "phase:2,deny"
+ SecRule REQBODY_PROCESSOR_ERROR "\@eq 1" "phase:2,deny"
+ ),
+ match_log => {
+ debug => [ qr/Adding request argument \(BODY\): name "a", value "1".*Adding request argument \(BODY\): name "b", value "2"/s, 1 ],
+ -debug => [ qr/Multipart error:/, 1 ],
+
+ },
+ match_response => {
+ status => qr/^200$/,
+ },
+ request => new HTTP::Request(
+ POST => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test.txt",
+ [
+ "Content-Type" => "multipart/form-data; boundary=---------------------------69343412719991675451336310646",
+ ],
+ normalize_raw_request_data(
+ q(
+ -----------------------------69343412719991675451336310646
+ Content-Disposition: form-data; name="a"
+
+ 1
+ -----------------------------69343412719991675451336310646
+ Content-Disposition: form-data; name="b"
+
+ 2
+ -----------------------------69343412719991675451336310646--),
+ ),
+ ),
+},
+
+# Should work with a boundary of "boundary"
+{
+ type => "misc",
+ comment => "multipart parser (boundary contains \"boundary\")",
+ conf => qq(
+ SecRuleEngine On
+ SecDebugLog $ENV{DEBUG_LOG}
+ SecDebugLogLevel 9
+ SecRequestBodyAccess On
+ SecRule MULTIPART_STRICT_ERROR "\@eq 1" "phase:2,deny"
+ SecRule MULTIPART_UNMATCHED_BOUNDARY "\@eq 1" "phase:2,deny"
+ SecRule REQBODY_PROCESSOR_ERROR "\@eq 1" "phase:2,deny"
+ ),
+ match_log => {
+ debug => [ qr/Adding request argument \(BODY\): name "a", value "1".*Adding request argument \(BODY\): name "b", value "2"/s, 1 ],
+ -debug => [ qr/Multipart error:/, 1 ],
+
+ },
+ match_response => {
+ status => qr/^200$/,
+ },
+ request => new HTTP::Request(
+ POST => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test.txt",
+ [
+ "Content-Type" => "multipart/form-data; boundary=------------------------------------------------boundary",
+ ],
+ normalize_raw_request_data(
+ q(
+ --------------------------------------------------boundary
+ Content-Disposition: form-data; name="a"
+
+ 1
+ --------------------------------------------------boundary
+ Content-Disposition: form-data; name="b"
+
+ 2
+ --------------------------------------------------boundary--
+ ),
+ ),
+ ),
+},
+{
+ type => "misc",
+ comment => "multipart parser (boundary contains \"BoUnDaRy\")",
+ conf => qq(
+ SecRuleEngine On
+ SecDebugLog $ENV{DEBUG_LOG}
+ SecDebugLogLevel 9
+ SecRequestBodyAccess On
+ SecRule MULTIPART_STRICT_ERROR "\@eq 1" "phase:2,deny"
+ SecRule MULTIPART_UNMATCHED_BOUNDARY "\@eq 1" "phase:2,deny"
+ SecRule REQBODY_PROCESSOR_ERROR "\@eq 1" "phase:2,deny"
+ ),
+ match_log => {
+ debug => [ qr/Adding request argument \(BODY\): name "a", value "1".*Adding request argument \(BODY\): name "b", value "2"/s, 1 ],
+ -debug => [ qr/Multipart error:/, 1 ],
+
+ },
+ match_response => {
+ status => qr/^200$/,
+ },
+ request => new HTTP::Request(
+ POST => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test.txt",
+ [
+ "Content-Type" => "multipart/form-data; boundary=------------------------------------------------BoUnDaRy",
+ ],
+ normalize_raw_request_data(
+ q(
+ --------------------------------------------------BoUnDaRy
+ Content-Disposition: form-data; name="a"
+
+ 1
+ --------------------------------------------------BoUnDaRy
+ Content-Disposition: form-data; name="b"
+
+ 2
+ --------------------------------------------------BoUnDaRy--
+ ),
+ ),
+ ),
+},
+
+# We should handle data starting with a "--"
+{
+ type => "misc",
+ comment => "multipart parser (data contains \"--\")",
+ conf => qq(
+ SecRuleEngine On
+ SecDebugLog $ENV{DEBUG_LOG}
+ SecDebugLogLevel 9
+ SecRequestBodyAccess On
+ SecRule MULTIPART_STRICT_ERROR "\@eq 1" "phase:2,deny"
+ SecRule REQBODY_PROCESSOR_ERROR "\@eq 1" "phase:2,deny"
+ ),
+ match_log => {
+ debug => [ qr/Adding request argument \(BODY\): name "a", value "--test".*Adding request argument \(BODY\): name "b", value "--"/s, 1 ],
+ -debug => [ qr/Multipart error:/, 1 ],
+
+ },
+ match_response => {
+ status => qr/^200$/,
+ },
+ request => new HTTP::Request(
+ POST => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test.txt",
+ [
+ "Content-Type" => "multipart/form-data; boundary=---------------------------69343412719991675451336310646",
+ ],
+ normalize_raw_request_data(
+ q(
+ -----------------------------69343412719991675451336310646
+ Content-Disposition: form-data; name="a"
+
+ --test
+ -----------------------------69343412719991675451336310646
+ Content-Disposition: form-data; name="b"
+
+ --
+ -----------------------------69343412719991675451336310646--),
+ ),
+ ),
+},
+
+# We should emit warnings for parsing errors
+{
+ type => "misc",
+ comment => "multipart parser error (no final boundary)",
+ conf => qq(
+ SecRuleEngine On
+ SecDebugLog $ENV{DEBUG_LOG}
+ SecDebugLogLevel 9
+ SecRequestBodyAccess On
+ SecAuditLog "$ENV{AUDIT_LOG}"
+ SecAuditEngine RelevantOnly
+ ),
+ match_log => {
+ audit => [ qr/Final boundary missing/, 1 ],
+ debug => [ qr/Final boundary missing/, 1 ],
+
+ },
+ match_response => {
+ status => qr/^200$/,
+ },
+ request => new HTTP::Request(
+ POST => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test.txt",
+ [
+ "Content-Type" => "multipart/form-data; boundary=---------------------------69343412719991675451336310646",
+ ],
+ normalize_raw_request_data(
+ q(
+ -----------------------------69343412719991675451336310646
+ Content-Disposition: form-data; name="a"
+
+ 1
+ -----------------------------69343412719991675451336310646
+ Content-Disposition: form-data; name="b"
+
+ 2
+ ),
+ ),
+ ),
+},
+{
+ type => "misc",
+ comment => "multipart parser error (no disposition)",
+ conf => qq(
+ SecRuleEngine On
+ SecDebugLog $ENV{DEBUG_LOG}
+ SecDebugLogLevel 9
+ SecRequestBodyAccess On
+ SecAuditLog "$ENV{AUDIT_LOG}"
+ SecAuditEngine RelevantOnly
+ ),
+ match_log => {
+ -debug => [ qr/Multipart error:/, 1 ],
+ audit => [ qr/Part missing Content-Disposition header/, 1 ],
+ debug => [ qr/Part missing Content-Disposition header/, 1 ],
+
+ },
+ match_response => {
+ status => qr/^200$/,
+ },
+ request => new HTTP::Request(
+ POST => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test.txt",
+ [
+ "Content-Type" => "multipart/form-data; boundary=---------------------------69343412719991675451336310646",
+ ],
+ normalize_raw_request_data(
+ q(
+ -----------------------------69343412719991675451336310646
+
+ 1
+ -----------------------------69343412719991675451336310646
+
+ 2
+ -----------------------------69343412719991675451336310646--
+ ),
+ ),
+ ),
+},
+{
+ type => "misc",
+ comment => "multipart parser error (bad disposition)",
+ conf => qq(
+ SecRuleEngine On
+ SecDebugLog $ENV{DEBUG_LOG}
+ SecDebugLogLevel 9
+ SecRequestBodyAccess On
+ SecAuditLog "$ENV{AUDIT_LOG}"
+ SecAuditEngine RelevantOnly
+ ),
+ match_log => {
+ audit => [ qr/Invalid Content-Disposition header/, 1 ],
+ debug => [ qr/Invalid Content-Disposition header/, 1 ],
+
+ },
+ match_response => {
+ status => qr/^200$/,
+ },
+ request => new HTTP::Request(
+ POST => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test.txt",
+ [
+ "Content-Type" => "multipart/form-data; boundary=---------------------------69343412719991675451336310646",
+ ],
+ normalize_raw_request_data(
+ q(
+ -----------------------------69343412719991675451336310646
+ Content-Disposition: form-data name="a"
+
+ 1
+ -----------------------------69343412719991675451336310646
+ Content-Disposition: form-data name="b"
+
+ 2
+ -----------------------------69343412719991675451336310646--
+ ),
+ ),
+ ),
+},
+{
+ type => "misc",
+ comment => "multipart parser error (no disposition name)",
+ conf => qq(
+ SecRuleEngine On
+ SecDebugLog $ENV{DEBUG_LOG}
+ SecDebugLogLevel 9
+ SecRequestBodyAccess On
+ SecAuditLog "$ENV{AUDIT_LOG}"
+ SecAuditEngine RelevantOnly
+ ),
+ match_log => {
+ -debug => [ qr/Multipart error:/, 1 ],
+ audit => [ qr/Content-Disposition header missing name field/, 1 ],
+ debug => [ qr/Content-Disposition header missing name field/, 1 ],
+
+ },
+ match_response => {
+ status => qr/^200$/,
+ },
+ request => new HTTP::Request(
+ POST => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test.txt",
+ [
+ "Content-Type" => "multipart/form-data; boundary=---------------------------69343412719991675451336310646",
+ ],
+ normalize_raw_request_data(
+ q(
+ -----------------------------69343412719991675451336310646
+ Content-Disposition: form-data;
+
+ 1
+ -----------------------------69343412719991675451336310646
+ Content-Disposition: form-data;
+
+ 2
+ -----------------------------69343412719991675451336310646--
+ ),
+ ),
+ ),
+},
diff --git a/apache2/t/regression/rule/00-basics.t b/apache2/t/regression/rule/00-basics.t
index 3c7e8c04..396d0ea1 100644
--- a/apache2/t/regression/rule/00-basics.t
+++ b/apache2/t/regression/rule/00-basics.t
@@ -2,7 +2,7 @@
# SecAction
{
- type => "config",
+ type => "rule",
comment => "SecAction (override default)",
conf => qq(
SecRuleEngine On
diff --git a/apache2/t/regression/target/00-targets.t b/apache2/t/regression/target/00-targets.t
index fcbb1fab..9a89f3b5 100644
--- a/apache2/t/regression/target/00-targets.t
+++ b/apache2/t/regression/target/00-targets.t
@@ -370,7 +370,51 @@
),
},
-# TODO: AUTH_TYPE
+# AUTH_TYPE
+#{
+# type => "target",
+# comment => "AUTH_TYPE",
+# conf => qq(
+# = 2.2>
+#
+# LoadModule authn_file_module modules/mod_authn_file.so
+#
+#
+##
+##
+## LoadModule auth_module modules/mod_auth.so
+##
+##
+#
+# AuthType Basic
+# AuthName Test
+# AuthUserFile "$ENV{CONF_DIR}/htpasswd"
+# Require user nobody
+#
+# SecRuleEngine On
+# SecRequestBodyAccess On
+# SecResponseBodyAccess On
+# SecResponseBodyMimeType null
+## SecDebugLog $ENV{DEBUG_LOG}
+## SecDebugLogLevel 9
+# SecRule REQUEST_HEADERS:Authorization "Basic (.*)" "phase:2,log,pass,capture,chain"
+# SecRule TX:1 "nobody:test" "t:none,t:base64Decode,chain"
+# SecRule AUTH_TYPE "Basic"
+# ),
+# match_log => {
+# error => [ qr/Pattern match "Basic" at AUTH_TYPE/s, 1 ],
+# },
+# match_response => {
+# status => qr/^200$/,
+# },
+# request => new HTTP::Request(
+# GET => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test.txt",
+# [
+# "Authorization" => "Basic bm9ib2R5OnRlc3Q="
+# ],
+# ),
+#},
+
# TODO: ENV
# TODO: FILES
# TODO: FILES_COMBINED_SIZE
diff --git a/apache2/t/run-regression-tests.pl.in b/apache2/t/run-regression-tests.pl.in
index 91582a52..a21e265f 100755
--- a/apache2/t/run-regression-tests.pl.in
+++ b/apache2/t/run-regression-tests.pl.in
@@ -21,7 +21,7 @@ use Data::Dumper;
use IO::Socket;
use LWP::UserAgent;
-my @TYPES = qw(config action target rule);
+my @TYPES = qw(config misc action target rule);
my $SCRIPT = basename($0);
my $SCRIPT_DIR = File::Spec->rel2abs(dirname($0));
my $REG_DIR = "$SCRIPT_DIR/regression";
@@ -324,6 +324,23 @@ sub runfile {
msg(sprintf("Passed: %2d; Failed: %2d", $pass, $testnum ? (1 - $pass) : ($n - $pass)));
}
+# Take out any indenting and translate LF -> CRLF
+sub normalize_raw_request_data {
+ my $r = $_[0];
+
+ # Allow for indenting in test file
+ $r =~ s/^[ \t]*\x0d?\x0a//s;
+ my($indention) = ($r =~ m/^([ \t]*)/s); # indention taken from first line
+ $r =~ s/^$indention//mg;
+ $r =~ s/(\x0d?\x0a)[ \t]+$/$1/s;
+
+ # Translate LF to CRLF
+ $r =~ s/^\x0a/\x0d\x0a/mg;
+ $r =~ s/([^\x0d])\x0a/$1\x0d\x0a/mg;
+
+ return $r;
+}
+
sub do_raw_request {
my $sock = new IO::Socket::INET(
Proto => "tcp",
@@ -332,15 +349,14 @@ sub do_raw_request {
) or msg("Failed to connect to localhost:$opt{p}: $@");
return unless ($sock);
- my $r = "@_";
- $r =~ s/^[^A-Z]+//s;
- $r =~ s/^[ \t]+//mg;
- $r =~ s/^\x0a/\x0d\x0a/mg;
- $r =~ s/([^\x0d])\x0a/$1\x0d\x0a/mg;
+ # Join togeather the request
+ my $r = join("", @_);
+ # Write to socket
print $sock "$r";
$sock->shutdown(1);
+ # Read from socket
my @resp = <$sock>;
$sock->close();