diff --git a/Makefile.am b/Makefile.am
index 980cd02..c346c1c 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -1,5 +1,7 @@
ACLOCAL_AMFLAGS = -I build
+postamb = "modsecurity_rules \"SecAuditEngine On\""
+
MAINTAINERCLEANFILES = \
config.log \
Makefile.in \
@@ -39,7 +41,7 @@ all:
test:
cd t/ && ./TEST -clean
cd t/ && ./TEST -configure
- cd t/ && ./TEST -httpd_conf conf/httpd.conf -httpd @APACHE@ -apxs @APXS@
+ cd t/ && ./TEST -httpd_conf conf/httpd.conf -httpd @APACHE@ -apxs @APXS@ -postamble $(postamb)
install-exec-hook: $(pkglib_LTLIBRARIES)
diff --git a/t/00-phases.t b/t/00-phases.t
new file mode 100644
index 0000000..f2468dd
--- /dev/null
+++ b/t/00-phases.t
@@ -0,0 +1,132 @@
+
+use strict;
+use Apache::Test;
+use Apache::TestRequest;
+
+require "t/find_string_in_file.pl";
+
+plan tests => 5, have_lwp;
+
+my $audit_log = Apache::Test::config()->{vars}->{t_logs} . "/audit_logs.txt";
+
+###############################################################################
+# Test phases
+# phase 1 (request headers)
+###############################################################################
+{
+ # Remember position in audit log file before our test
+ my $audit_log_start_size = -s $audit_log or die "Failed to access file: " . $audit_log;
+
+ my $url = "/00-phases/00-phases_01.html";
+ my $ct_header_name = "Content-Type";
+ my $ct_header_val = "application/x-www-form-urlencoded";
+ my $content = "arg1=val1&arg2=val2";
+ my $res1 = POST $url, $ct_header_name => $ct_header_val, content => $content;
+
+ # Expected results
+ my $status_code_expected = 200;
+ my $audit_log_expected = 'Matched "Operator \`Rx\' with parameter \`\^POST\' against variable \`REQUEST_LINE';
+ my $audit_log_missing = 'Matched [\s\S]*(ARGS|RESPONSE)';
+
+ my $audit_found_expected = find_string_in_file($audit_log, $audit_log_start_size, $audit_log_expected);
+ my $audit_found_missing = find_string_in_file($audit_log, $audit_log_start_size, $audit_log_missing);
+
+ ok (($res1->code == $status_code_expected) && ($audit_found_expected) && (not $audit_found_missing));
+}
+
+###############################################################################
+# Test phases
+# phase 2 (request body)
+###############################################################################
+{
+ # Remember position in audit log file before our test
+ my $audit_log_start_size = -s $audit_log or die "Failed to access file: " . $audit_log;
+
+ my $url = "/00-phases/00-phases_02.html";
+ my $ct_header_name = "Content-Type";
+ my $ct_header_val = "application/x-www-form-urlencoded";
+ my $content = "arg1=val1&arg2=val2";
+ my $res1 = POST $url, $ct_header_name => $ct_header_val, content => $content;
+
+ # Expected results
+ my $status_code_expected = 200;
+ my $audit_log_expected = 'Matched "Operator \`Rx\' with parameter \`\^POST\' against variable \`REQUEST_LINE[\s\S]*Matched "Operator \`Rx\' with parameter \`val1\' against variable \`ARGS';
+ my $audit_log_missing = 'Matched [\s\S]*RESPONSE';
+
+ my $audit_found_expected = find_string_in_file($audit_log, $audit_log_start_size, $audit_log_expected);
+ my $audit_found_missing = find_string_in_file($audit_log, $audit_log_start_size, $audit_log_missing);
+
+ ok (($res1->code == $status_code_expected) && ($audit_found_expected) && (not $audit_found_missing));
+}
+
+###############################################################################
+# Test phases
+# phase 3 (response headers)
+###############################################################################
+{
+ # Remember position in audit log file before our test
+ my $audit_log_start_size = -s $audit_log or die "Failed to access file: " . $audit_log;
+
+ my $url = "/00-phases/00-phases_03.html";
+ my $ct_header_name = "Content-Type";
+ my $ct_header_val = "application/x-www-form-urlencoded";
+ my $content = "arg1=val1&arg2=val2";
+ my $res1 = POST $url, $ct_header_name => $ct_header_val, content => $content;
+
+ # Expected results
+ my $status_code_expected = 200;
+ my $audit_log_expected = 'Matched "Operator \`Rx\' with parameter \`\^POST\' against variable \`REQUEST_LINE[\s\S]*Matched "Operator \`Rx\' with parameter \`val1\' against variable \`ARGS[\s\S]*Matched "Operator \`Rx\' with parameter \`.\' against variable \`RESPONSE_HEADERS';
+ my $audit_log_missing = 'Matched [\s\S]*RESPONSE_BODY';
+
+ my $audit_found_expected = find_string_in_file($audit_log, $audit_log_start_size, $audit_log_expected);
+ my $audit_found_missing = find_string_in_file($audit_log, $audit_log_start_size, $audit_log_missing);
+
+ ok (($res1->code == $status_code_expected) && ($audit_found_expected) && (not $audit_found_missing));
+}
+
+###############################################################################
+# Test phases
+# phase 4 (response body)
+###############################################################################
+{
+ # Remember position in audit log file before our test
+ my $audit_log_start_size = -s $audit_log or die "Failed to access file: " . $audit_log;
+
+ my $url = "/00-phases/00-phases_04.html";
+ my $ct_header_name = "Content-Type";
+ my $ct_header_val = "application/x-www-form-urlencoded";
+ my $content = "arg1=val1&arg2=val2";
+ my $res1 = POST $url, $ct_header_name => $ct_header_val, content => $content;
+
+ # Expected results
+ my $status_code_expected = 200;
+ my $audit_log_expected = 'Matched "Operator \`Rx\' with parameter \`\^POST\' against variable \`REQUEST_LINE[\s\S]*Matched "Operator \`Rx\' with parameter \`val1\' against variable \`ARGS[\s\S]*Matched "Operator \`Rx\' with parameter \`.\' against variable \`RESPONSE_HEADERS[\s\S]*Matched "Operator \`Rx\' with parameter \`TEST\' against variable \`RESPONSE_BODY';
+
+ my $audit_found_expected = find_string_in_file($audit_log, $audit_log_start_size, $audit_log_expected);
+
+ ok (($res1->code == $status_code_expected) && ($audit_found_expected));
+}
+
+###############################################################################
+# Test phases
+# phase 5 (logging)
+###############################################################################
+{
+ # Remember position in audit log file before our test
+ my $audit_log_start_size = -s $audit_log or die "Failed to access file: " . $audit_log;
+
+ my $url = "/00-phases/00-phases_05.html";
+ my $ct_header_name = "Content-Type";
+ my $ct_header_val = "application/x-www-form-urlencoded";
+ my $content = "arg1=val1&arg2=val2";
+ my $res1 = POST $url, $ct_header_name => $ct_header_val, content => $content;
+
+ # Expected results
+ my $status_code_expected = 200;
+ my $audit_log_expected = 'Matched "Operator \`Rx\' with parameter \`\^POST\' against variable \`REQUEST_LINE[\s\S]*Matched "Operator \`Rx\' with parameter \`val1\' against variable \`ARGS[\s\S]*Matched "Operator \`Rx\' with parameter \`.\' against variable \`RESPONSE_HEADERS[\s\S]*Matched "Operator \`Rx\' with parameter \`TEST\' against variable \`RESPONSE_BODY';
+
+ my $audit_found_expected = find_string_in_file($audit_log, $audit_log_start_size, $audit_log_expected);
+
+ ok (($res1->code == $status_code_expected) && ($audit_found_expected));
+}
+
diff --git a/t/conf/extra.conf.in b/t/conf/extra.conf.in
index 6518559..0cab08c 100644
--- a/t/conf/extra.conf.in
+++ b/t/conf/extra.conf.in
@@ -10,6 +10,10 @@ LoadModule security3_module "@ServerRoot@/.././src/.libs/mod_security3.so"
# Lets make sure that the engine is on.
modsecurity_rules 'SecRuleEngine On'
+# Audit logs -- it is actually turned on via postamble
+modsecurity_rules 'SecAuditLog @ServerRoot@/logs/audit_logs.txt'
+modsecurity_rules 'SecAuditLogParts ABIJDEFHZ'
+
# Debug logs
modsecurity_rules 'SecDebugLog @ServerRoot@/logs/debug_logs.txt'
modsecurity_rules 'SecDebugLogLevel 9'
@@ -61,3 +65,44 @@ modsecurity_rules 'SecDebugLogLevel 9'
modsecurity_rules 'SecRule ARGS "evil" "phase:5,id:114,log,status:402,block,deny"'
+
+ modsecurity_rules 'SecRequestBodyAccess On'
+ modsecurity_rules 'SecResponseBodyAccess On'
+ modsecurity_rules 'SecRule REQUEST_LINE "^POST" "phase:1,pass,log,auditlog,id:500169"'
+ modsecurity_rules 'SecRule ARGS "val1" "phase:1,pass,log,auditlog,id:500170"'
+ modsecurity_rules 'SecRule RESPONSE_HEADERS:Last-Modified "." "phase:1,pass,log,auditlog,id:500171"'
+ modsecurity_rules 'SecRule RESPONSE_BODY "TEST" "phase:1,pass,log,auditlog,id:500172"'
+
+
+ modsecurity_rules 'SecRequestBodyAccess On'
+ modsecurity_rules 'SecResponseBodyAccess On'
+ modsecurity_rules 'SecRule REQUEST_LINE "^POST" "phase:2,pass,log,auditlog,id:500173"'
+ modsecurity_rules 'SecRule ARGS "val1" "phase:2,pass,log,auditlog,id:500174"'
+ modsecurity_rules 'SecRule RESPONSE_HEADERS:Last-Modified "." "phase:2,pass,log,auditlog,id:500175"'
+ modsecurity_rules 'SecRule RESPONSE_BODY "TEST" "phase:2,pass,log,auditlog,id:500176"'
+
+
+ modsecurity_rules 'SecRequestBodyAccess On'
+ modsecurity_rules 'SecResponseBodyAccess On'
+ modsecurity_rules 'SecRule REQUEST_LINE "^POST" "phase:3,pass,log,auditlog,id:500177"'
+ modsecurity_rules 'SecRule ARGS "val1" "phase:3,pass,log,auditlog,id:500178"'
+ modsecurity_rules 'SecRule RESPONSE_HEADERS:Last-Modified "." "phase:3,pass,log,auditlog,id:500179"'
+ modsecurity_rules 'SecRule RESPONSE_BODY "TEST" "phase:3,pass,log,auditlog,id:500180"'
+
+
+ modsecurity_rules 'SecRequestBodyAccess On'
+ modsecurity_rules 'SecResponseBodyAccess On'
+ modsecurity_rules 'SecRule REQUEST_LINE "^POST" "phase:4,pass,log,auditlog,id:500181"'
+ modsecurity_rules 'SecRule ARGS "val1" "phase:4,pass,log,auditlog,id:500182"'
+ modsecurity_rules 'SecRule RESPONSE_HEADERS:Last-Modified "." "phase:4,pass,log,auditlog,id:500183"'
+ modsecurity_rules 'SecRule RESPONSE_BODY "TEST" "phase:4,pass,log,auditlog,id:500184"'
+
+
+ modsecurity_rules 'SecRequestBodyAccess On'
+ modsecurity_rules 'SecResponseBodyAccess On'
+ modsecurity_rules 'SecRule REQUEST_LINE "^POST" "phase:5,pass,log,auditlog,id:500185"'
+ modsecurity_rules 'SecRule ARGS "val1" "phase:5,pass,log,auditlog,id:500186"'
+ modsecurity_rules 'SecRule RESPONSE_HEADERS:Last-Modified "." "phase:5,pass,log,auditlog,id:500187"'
+ modsecurity_rules 'SecRule RESPONSE_BODY "TEST" "phase:5,pass,log,auditlog,id:500188"'
+
+
diff --git a/t/find_string_in_file.pl b/t/find_string_in_file.pl
new file mode 100644
index 0000000..fc386f4
--- /dev/null
+++ b/t/find_string_in_file.pl
@@ -0,0 +1,22 @@
+
+use strict;
+use FileHandle;
+
+# arg0 = filename; arg1 = start position; arg2 = string to find
+sub find_string_in_file {
+ my $file_end_size = -s $_[0] or die "(find_string_in_file.pl) Failed to access file: " . $_[0];
+ my $bytes_to_read = $file_end_size - $_[1];
+
+ my $file_content;
+ open FILE, $_[0] || die $!;
+ seek(FILE, $_[1], IO::Seekable::SEEK_SET);
+ my $bytes_read = read(FILE, $file_content, $bytes_to_read);
+
+ # if (index($file_content, $_[2]) != -1) {
+ if ($file_content =~ $_[2]) {
+ return 1;
+ }
+ return 0;
+}
+1;
+
diff --git a/t/htdocs/00-phases/00-phases_01.html b/t/htdocs/00-phases/00-phases_01.html
new file mode 100644
index 0000000..8d571db
--- /dev/null
+++ b/t/htdocs/00-phases/00-phases_01.html
@@ -0,0 +1 @@
+00-phases_01 TEST
diff --git a/t/htdocs/00-phases/00-phases_02.html b/t/htdocs/00-phases/00-phases_02.html
new file mode 100644
index 0000000..ed919cc
--- /dev/null
+++ b/t/htdocs/00-phases/00-phases_02.html
@@ -0,0 +1 @@
+00-phases_02 TEST
diff --git a/t/htdocs/00-phases/00-phases_03.html b/t/htdocs/00-phases/00-phases_03.html
new file mode 100644
index 0000000..284d5dd
--- /dev/null
+++ b/t/htdocs/00-phases/00-phases_03.html
@@ -0,0 +1 @@
+00-phases_03 TEST
diff --git a/t/htdocs/00-phases/00-phases_04.html b/t/htdocs/00-phases/00-phases_04.html
new file mode 100644
index 0000000..34225a8
--- /dev/null
+++ b/t/htdocs/00-phases/00-phases_04.html
@@ -0,0 +1 @@
+00-phases_04 TEST
diff --git a/t/htdocs/00-phases/00-phases_05.html b/t/htdocs/00-phases/00-phases_05.html
new file mode 100644
index 0000000..4ffa328
--- /dev/null
+++ b/t/htdocs/00-phases/00-phases_05.html
@@ -0,0 +1 @@
+00-phases_05 TEST