### SecAudit* directive tests # SecAuditEngine { type => "config", comment => "SecAuditEngine On", conf => qq( SecAuditEngine On SecAuditLog $ENV{AUDIT_LOG} ), match_log => { audit => [ qr/./, 1 ], }, match_response => { status => qr/^200$/, }, request => new HTTP::Request( GET => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test.txt", ), }, { type => "config", comment => "SecAuditEngine Off", conf => qq( SecAuditEngine Off SecAuditLog $ENV{AUDIT_LOG} ), match_log => { -audit => [ qr/./, 1 ], }, match_response => { status => qr/^200$/, }, request => new HTTP::Request( GET => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test.txt", ), }, { type => "config", comment => "SecAuditEngine RelevantOnly (pos)", conf => qq( SecRuleEngine On SecAuditEngine RelevantOnly SecAuditLog $ENV{AUDIT_LOG} SecDebugLog $ENV{DEBUG_LOG} SecDebugLogLevel 9 SecResponseBodyAccess On SecDefaultAction "phase:2,log,auditlog,pass" SecRule REQUEST_URI "." "phase:4,deny" ), match_log => { audit => [ qr/./, 1 ], }, match_response => { status => qr/^403$/, }, request => new HTTP::Request( GET => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test.txt", ), }, { type => "config", comment => "SecAuditEngine RelevantOnly (neg)", conf => qq( SecAuditEngine RelevantOnly SecAuditLog $ENV{AUDIT_LOG} SecResponseBodyAccess On SecDefaultAction "phase:2,log,auditlog,pass" ), match_log => { -audit => [ qr/./, 1 ], }, match_response => { status => qr/^200$/, }, request => new HTTP::Request( GET => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test.txt", ), }, # SecAuditLogType & SecAuditLogStorageDir { type => "config", comment => "SecAuditLogType Serial", conf => qq( SecAuditEngine On SecAuditLog $ENV{AUDIT_LOG} SecAuditLogType Serial ), match_log => { audit => [ qr/./, 1 ], }, match_response => { status => qr/^404$/, }, request => new HTTP::Request( GET => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/bogus", ), }, { type => "config", comment => "SecAuditLogType Concurrent", conf => qq( SecAuditEngine On SecAuditLog $ENV{AUDIT_LOG} SecAuditLogType Concurrent SecAuditLogStorageDir "$ENV{LOGS_DIR}/audit" ), test => sub { ### Perl code to parse the audit log entry and verify ### that the concurrent audit log exists and contains ### the correct data. ### ### TODO: Need some API for this :) ### # Parse log my $alogre = qr/^(?:\S+)\ (?:\S+)\ (?:\S+)\ (?:\S+)\ \[(?:[^:]+):(?:\d+:\d+:\d+)\ (?:[^\]]+)\]\ \"(?:.*)\"\ (?:\d+)\ (?:\S+)\ \"(?:.*)\"\ \"(?:.*)\"\ (\S+)\ \"(?:.*)\"\ (\S+)\ (?:\d+)\ (?:\d+)\ (?:\S+)(?:.*)$/m; my $alog = match_log("audit", $alogre, 1); chomp $alog; my @log = ($alog =~ m/$alogre/); my($id, $fn) = ($log[0], $log[1]); if (!$id or !$fn) { dbg("LOG ENTRY: $alog"); die "Failed to parse audit log: $ENV{AUDIT_LOG}\n"; } # Verify concurrent log exists my $alogdatafn = "$ENV{LOGS_DIR}/audit$fn"; if (! -e "$alogdatafn") { die "Audit log does not exist: $alogdatafn\n"; } # Verify concurrent log contents if (defined match_file($alogdatafn, qr/^--[^-]+-A--.*$id.*-Z--$/s)) { return 0; } # Error dbg("LOGDATA: \"$FILE{$alogdatafn}{buf}\""); die "Audit log data did not match.\n"; }, match_response => { status => qr/^200$/, }, request => new HTTP::Request( GET => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test.txt", ), }, # SecAuditLogRelevantStatus { type => "config", comment => "SecAuditLogRelevantStatus (pos)", conf => qq( SecAuditEngine RelevantOnly SecAuditLog $ENV{AUDIT_LOG} SecAuditLogRelevantStatus "^4" ), match_log => { audit => [ qr/./, 1 ], }, match_response => { status => qr/^404$/, }, request => new HTTP::Request( GET => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/bogus", ), }, { type => "config", comment => "SecAuditLogRelevantStatus (neg)", conf => qq( SecAuditEngine RelevantOnly SecAuditLog $ENV{AUDIT_LOG} SecAuditLogRelevantStatus "^4" ), match_log => { -audit => [ qr/./, 1 ], }, match_response => { status => qr/^200$/, }, request => new HTTP::Request( GET => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test.txt", ), }, # SecAuditLogParts { type => "config", comment => "SecAuditLogParts (minimal)", conf => qq( SecAuditEngine On SecAuditLog $ENV{AUDIT_LOG} SecRequestBodyAccess On SecResponseBodyAccess On SecAuditLogParts "AZ" ), match_log => { audit => [ qr/-A--.*-Z--/s, 1 ], -audit => [ qr/-[B-Y]--/, 1 ], }, match_response => { status => qr/^200$/, }, request => new HTTP::Request( POST => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test.txt", [ "Content-Type" => "application/x-www-form-urlencoded", ], "a=1r&=2", ), }, { type => "config", comment => "SecAuditLogParts (default)", conf => qq( SecAuditEngine On SecAuditLog $ENV{AUDIT_LOG} SecRequestBodyAccess On SecResponseBodyAccess On ), match_log => { audit => [ qr/-A--.*-B--.*-F--.*-H--.*-Z--/s, 1 ], -audit => [ qr/-[DEGIJK]--/, 1 ], }, match_response => { status => qr/^200$/, }, request => new HTTP::Request( POST => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test.txt", [ "Content-Type" => "application/x-www-form-urlencoded", ], "a=1r&=2", ), }, { type => "config", comment => "SecAuditLogParts (all)", conf => qq( SecRuleEngine On SecAuditEngine On SecAuditLog $ENV{AUDIT_LOG} SecRequestBodyAccess On SecResponseBodyAccess On SecAuditLogParts "ABCDEFGHIJKZ" SecAction "phase:4,log,auditlog,allow" ), match_log => { audit => [ qr/-A--.*-B--.*-C--.*-F--.*-E--.*-H--.*-K--.*-Z--/s, 1 ], }, match_response => { status => qr/^200$/, }, request => new HTTP::Request( POST => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test.txt", [ "Content-Type" => "application/x-www-form-urlencoded", ], "a=1r&=2", ), },