Backport regression suite to 2.5.

This commit is contained in:
brectanus
2008-07-25 23:15:08 +00:00
parent 151742713b
commit ade22567bf
35 changed files with 5479 additions and 132 deletions

View File

@@ -0,0 +1,531 @@
### Tests all of the actions in each phase
# Pass
{
type => "action",
comment => "pass in phase:1",
conf => qq(
SecRuleEngine On
SecRequestBodyAccess On
SecResponseBodyAccess On
SecResponseBodyMimeType null
SecAction "phase:1,pass"
SecAction "phase:1,deny"
),
match_log => {
error => [ qr/ModSecurity: Warning. Unconditional match in SecAction/, 1 ],
},
match_response => {
status => qr/^403$/,
},
request => new HTTP::Request(
GET => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test.txt",
),
},
{
type => "action",
comment => "pass in phase:2",
conf => qq(
SecRuleEngine On
SecRequestBodyAccess On
SecResponseBodyAccess On
SecResponseBodyMimeType null
SecAction "phase:2,pass"
SecAction "phase:2,deny"
),
match_log => {
error => [ qr/ModSecurity: Warning. Unconditional match in SecAction/, 1 ],
},
match_response => {
status => qr/^403$/,
},
request => new HTTP::Request(
GET => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test.txt",
),
},
{
type => "action",
comment => "pass in phase:3",
conf => qq(
SecRuleEngine On
SecRequestBodyAccess On
SecResponseBodyAccess On
SecResponseBodyMimeType null
SecDebugLog "$ENV{DEBUG_LOG}"
SecDebugLogLevel 4
SecAction "phase:3,pass"
SecAction "phase:3,deny"
),
match_log => {
error => [ qr/ModSecurity: Warning. Unconditional match in SecAction/, 1 ],
},
match_response => {
status => qr/^403$/,
},
request => new HTTP::Request(
GET => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test.txt",
),
},
{
type => "action",
comment => "pass in phase:4",
conf => qq(
SecRuleEngine On
SecRequestBodyAccess On
SecResponseBodyAccess On
SecResponseBodyMimeType null
SecDebugLog "$ENV{DEBUG_LOG}"
SecDebugLogLevel 4
SecAction "phase:4,pass"
SecAction "phase:4,deny"
),
match_log => {
error => [ qr/ModSecurity: Warning. Unconditional match in SecAction/, 1 ],
},
match_response => {
status => qr/^403$/,
},
request => new HTTP::Request(
GET => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test.txt",
),
},
# Allow
{
type => "action",
comment => "allow in phase:1",
conf => qq(
SecRuleEngine On
SecRequestBodyAccess On
SecResponseBodyAccess On
SecResponseBodyMimeType null
SecAction "phase:1,allow"
SecAction "phase:1,deny"
),
match_log => {
error => [ qr/ModSecurity: Access allowed \(phase 1\). Unconditional match in SecAction/, 1 ],
},
match_response => {
status => qr/^200$/,
},
request => new HTTP::Request(
GET => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test.txt",
),
},
{
type => "action",
comment => "allow in phase:2",
conf => qq(
SecRuleEngine On
SecRequestBodyAccess On
SecResponseBodyAccess On
SecResponseBodyMimeType null
SecAction "phase:2,allow"
SecAction "phase:2,deny"
),
match_log => {
error => [ qr/ModSecurity: Access allowed \(phase 2\). Unconditional match in SecAction/, 1 ],
},
match_response => {
status => qr/^200$/,
},
request => new HTTP::Request(
GET => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test.txt",
),
},
{
type => "action",
comment => "allow in phase:3",
conf => qq(
SecRuleEngine On
SecRequestBodyAccess On
SecResponseBodyAccess On
SecResponseBodyMimeType null
SecDebugLog "$ENV{DEBUG_LOG}"
SecDebugLogLevel 4
SecAction "phase:3,allow"
SecAction "phase:3,deny"
),
match_log => {
error => [ qr/ModSecurity: Access allowed \(phase 3\). Unconditional match in SecAction/, 1 ],
},
match_response => {
status => qr/^200$/,
},
request => new HTTP::Request(
GET => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test.txt",
),
},
{
type => "action",
comment => "allow in phase:4",
conf => qq(
SecRuleEngine On
SecRequestBodyAccess On
SecResponseBodyAccess On
SecResponseBodyMimeType null
SecDebugLog "$ENV{DEBUG_LOG}"
SecDebugLogLevel 4
SecAction "phase:4,allow"
SecAction "phase:4,deny"
),
match_log => {
error => [ qr/ModSecurity: Access allowed \(phase 4\). Unconditional match in SecAction/, 1 ],
},
match_response => {
status => qr/^200$/,
},
request => new HTTP::Request(
GET => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test.txt",
),
},
# Deny
{
type => "action",
comment => "deny in phase:1",
conf => qq(
SecRuleEngine On
SecRequestBodyAccess On
SecResponseBodyAccess On
SecResponseBodyMimeType null
SecAction "phase:1,deny"
),
match_log => {
error => [ qr/Access denied with code 403 \(phase 1\). Unconditional match in SecAction./, 1 ],
},
match_response => {
status => qr/^403$/,
},
request => new HTTP::Request(
GET => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test.txt",
),
},
{
type => "action",
comment => "deny in phase:2",
conf => qq(
SecRuleEngine On
SecRequestBodyAccess On
SecResponseBodyAccess On
SecResponseBodyMimeType null
SecAction "phase:2,deny"
),
match_log => {
error => [ qr/Access denied with code 403 \(phase 2\). Unconditional match in SecAction./, 1 ],
},
match_response => {
status => qr/^403$/,
},
request => new HTTP::Request(
GET => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test.txt",
),
},
{
type => "action",
comment => "deny in phase:3",
conf => qq(
SecRuleEngine On
SecRequestBodyAccess On
SecResponseBodyAccess On
SecResponseBodyMimeType null
SecDebugLog "$ENV{DEBUG_LOG}"
SecDebugLogLevel 4
SecAction "phase:3,deny"
),
match_log => {
error => [ qr/Access denied with code 403 \(phase 3\). Unconditional match in SecAction./, 1 ],
},
match_response => {
status => qr/^403$/,
},
request => new HTTP::Request(
GET => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test.txt",
),
},
{
type => "action",
comment => "deny in phase:4",
conf => qq(
SecRuleEngine On
SecRequestBodyAccess On
SecResponseBodyAccess On
SecResponseBodyMimeType null
SecDebugLog "$ENV{DEBUG_LOG}"
SecDebugLogLevel 4
SecAction "phase:4,deny"
),
match_log => {
error => [ qr/Access denied with code 403 \(phase 4\). Unconditional match in SecAction./, 1 ],
},
match_response => {
status => qr/^403$/,
},
request => new HTTP::Request(
GET => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test.txt",
),
},
# Drop
{
type => "action",
comment => "drop in phase:1",
conf => qq(
SecRuleEngine On
SecRequestBodyAccess On
SecResponseBodyAccess On
SecResponseBodyMimeType null
SecAction "phase:1,drop"
),
match_log => {
error => [ qr/Access denied with connection close \(phase 1\). Unconditional match in SecAction./, 1 ],
},
match_response => {
status => qr/^500$/,
},
request => new HTTP::Request(
GET => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test.txt",
),
},
{
type => "action",
comment => "drop in phase:2",
conf => qq(
SecRuleEngine On
SecRequestBodyAccess On
SecResponseBodyAccess On
SecResponseBodyMimeType null
SecAction "phase:2,drop"
),
match_log => {
error => [ qr/Access denied with connection close \(phase 2\). Unconditional match in SecAction./, 1 ],
},
match_response => {
status => qr/^500$/,
},
request => new HTTP::Request(
GET => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test.txt",
),
},
{
type => "action",
comment => "drop in phase:3",
conf => qq(
SecRuleEngine On
SecRequestBodyAccess On
SecResponseBodyAccess On
SecResponseBodyMimeType null
SecDebugLog "$ENV{DEBUG_LOG}"
SecDebugLogLevel 4
SecAction "phase:3,drop"
),
match_log => {
error => [ qr/Access denied with connection close \(phase 3\). Unconditional match in SecAction./, 1 ],
},
match_response => {
status => qr/^500$/,
},
request => new HTTP::Request(
GET => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test.txt",
),
},
{
type => "action",
comment => "drop in phase:4",
conf => qq(
SecRuleEngine On
SecRequestBodyAccess On
SecResponseBodyAccess On
SecResponseBodyMimeType null
SecDebugLog "$ENV{DEBUG_LOG}"
SecDebugLogLevel 4
SecAction "phase:4,drop"
),
match_log => {
error => [ qr/Access denied with connection close \(phase 4\). Unconditional match in SecAction./, 1 ],
},
match_response => {
status => qr/^500$/,
},
request => new HTTP::Request(
GET => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test.txt",
),
},
# Redirect
{
type => "action",
comment => "redirect in phase:1 (get)",
conf => qq(
SecRuleEngine On
SecRequestBodyAccess On
SecResponseBodyAccess On
SecResponseBodyMimeType null
SecRule REQUEST_URI "\@streq /test2.txt" "phase:1,redirect:'http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test.txt'"
),
match_log => {
error => [ qr/ModSecurity: Access denied with redirection to .* using status 302 \(phase 1\)/, 1 ],
},
match_response => {
status => qr/^200$/,
content => qr/^TEST$/,
},
request => new HTTP::Request(
GET => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test2.txt",
),
},
{
type => "action",
comment => "redirect in phase:2 (get)",
conf => qq(
SecRuleEngine On
SecRequestBodyAccess On
SecResponseBodyAccess On
SecResponseBodyMimeType null
SecRule REQUEST_URI "\@streq /test2.txt" "phase:2,redirect:'http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test.txt'"
),
match_log => {
error => [ qr/ModSecurity: Access denied with redirection to .* using status 302 \(phase 2\)/, 1 ],
},
match_response => {
status => qr/^200$/,
content => qr/^TEST$/,
},
request => new HTTP::Request(
GET => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test2.txt",
),
},
{
type => "action",
comment => "redirect in phase:3 (get)",
conf => qq(
SecRuleEngine On
SecRequestBodyAccess On
SecResponseBodyAccess On
SecResponseBodyMimeType null
SecDebugLog "$ENV{DEBUG_LOG}"
SecDebugLogLevel 4
SecRule REQUEST_URI "\@streq /test2.txt" "phase:3,redirect:'http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test.txt'"
),
match_log => {
error => [ qr/ModSecurity: Access denied with redirection to .* using status 302 \(phase 3\)/, 1 ],
},
match_response => {
status => qr/^200$/,
content => qr/^TEST$/,
},
request => new HTTP::Request(
GET => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test2.txt",
),
},
{
type => "action",
comment => "redirect in phase:4 (get)",
conf => qq(
SecRuleEngine On
SecRequestBodyAccess On
SecResponseBodyAccess On
SecResponseBodyMimeType null
SecDebugLog "$ENV{DEBUG_LOG}"
SecDebugLogLevel 4
SecRule REQUEST_URI "\@streq /test2.txt" "phase:4,redirect:'http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test.txt'"
),
match_log => {
error => [ qr/ModSecurity: Access denied with redirection to .* using status 302 \(phase 4\)/, 1 ],
},
match_response => {
status => qr/^200$/,
content => qr/^TEST$/,
},
request => new HTTP::Request(
GET => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test2.txt",
),
},
# Proxy
{
type => "action",
comment => "proxy in phase:1 (get)",
conf => qq(
SecRuleEngine On
SecRequestBodyAccess On
SecResponseBodyAccess On
SecResponseBodyMimeType null
SecRule REQUEST_URI "\@streq /test2.txt" "phase:1,proxy:'http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test.txt'"
),
match_log => {
error => [ qr/ModSecurity: Access denied using proxy to \(phase 1\)/, 1 ],
},
match_response => {
status => qr/^200$/,
content => qr/^TEST$/,
},
request => new HTTP::Request(
GET => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test2.txt",
),
},
{
type => "action",
comment => "proxy in phase:2 (get)",
conf => qq(
SecRuleEngine On
SecRequestBodyAccess On
SecResponseBodyAccess On
SecResponseBodyMimeType null
SecRule REQUEST_URI "\@streq /test2.txt" "phase:2,proxy:'http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test.txt'"
),
match_log => {
error => [ qr/ModSecurity: Access denied using proxy to \(phase 2\)/, 1 ],
},
match_response => {
status => qr/^200$/,
content => qr/^TEST$/,
},
request => new HTTP::Request(
GET => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test2.txt",
),
},
{
type => "action",
comment => "proxy in phase:3 (get)",
conf => qq(
SecRuleEngine On
SecRequestBodyAccess On
SecResponseBodyAccess On
SecResponseBodyMimeType null
SecDebugLog "$ENV{DEBUG_LOG}"
SecDebugLogLevel 4
SecRule REQUEST_URI "\@streq /test2.txt" "phase:3,proxy:'http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test.txt'"
),
match_log => {
error => [ qr/ModSecurity: Access denied with code 500 \(phase 3\) \(Configuration Error: Proxy action requested but it does not work in output phases\)./, 1 ],
},
match_response => {
status => qr/^500$/,
},
request => new HTTP::Request(
GET => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test2.txt",
),
},
{
type => "action",
comment => "proxy in phase:4 (get)",
conf => qq(
SecRuleEngine On
SecRequestBodyAccess On
SecResponseBodyAccess On
SecResponseBodyMimeType null
SecDebugLog "$ENV{DEBUG_LOG}"
SecDebugLogLevel 4
SecRule REQUEST_URI "\@streq /test2.txt" "phase:4,proxy:'http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test.txt'"
),
match_log => {
error => [ qr/ModSecurity: Access denied with code 500 \(phase 4\) \(Configuration Error: Proxy action requested but it does not work in output phases\)./, 1 ],
},
match_response => {
status => qr/^500$/,
},
request => new HTTP::Request(
GET => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test2.txt",
),
},

View File

@@ -0,0 +1,8 @@
### Test meta actions
# TODO: id
# TODO: logdata
# TODO: msg
# TODO: rev
# TODO: severity
# TODO: tag

View File

@@ -0,0 +1,24 @@
### Test misc actions
# TODO: append
# TODO: block
# TODO: capture
# TODO: chain
# TODO: deprecatevar
# TODO: exec
# TODO: expirevar
# TODO: initcol
# TODO: multiMatch
# TODO: pause
# TODO: prepend
# TODO: sanitiseArg
# TODO: sanitiseMatched
# TODO: sanitiseRequestHeader
# TODO: sanitiseResponseHeader
# TODO: setuid
# TODO: setsid
# TODO: setenv
# TODO: setvar
# TODO: skip
# TODO: skipAfter
# TODO: xmlns

View File

@@ -0,0 +1,8 @@
### Transformation tests
# NOTE: individual tests done in unit tests
# TODO: t:none to override default
# TODO: t:none inline
# TODO: combined
# TODO: caching

View File

@@ -0,0 +1,56 @@
### ctl
### ruleRemoveById
{
type => "action",
comment => "ruleRemoveById existing rule across phases",
conf => qq(
SecRuleEngine On
SecAction "phase:2,id:666,deny"
SecAction "phase:1,pass,ctl:ruleRemoveById=666"
),
match_log => {
},
match_response => {
status => qr/^200$/,
},
request => new HTTP::Request(
GET => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test.txt",
),
},
{
type => "action",
comment => "ruleRemoveById future rule across phases",
conf => qq(
SecRuleEngine On
SecAction "phase:1,pass,ctl:ruleRemoveById=666"
SecAction "phase:2,id:666,deny"
),
match_log => {
},
match_response => {
status => qr/^200$/,
},
request => new HTTP::Request(
GET => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test.txt",
),
},
{
type => "action",
comment => "ruleRemoveById future rule same phase",
conf => qq(
SecRuleEngine On
SecAction "phase:1,pass,ctl:ruleRemoveById=666"
SecAction "phase:1,id:666,deny"
),
match_log => {
},
match_response => {
status => qr/^200$/,
},
request => new HTTP::Request(
GET => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test.txt",
),
},

View File

@@ -0,0 +1,545 @@
### Tests all of the actions in each phase in detection only mode
# Pass
{
type => "action",
comment => "pass in phase:1",
conf => qq(
SecRuleEngine DetectionOnly
SecRequestBodyAccess On
SecResponseBodyAccess On
SecResponseBodyMimeType null
SecDebugLog "$ENV{DEBUG_LOG}"
SecDebugLogLevel 9
SecAction "phase:1,pass,msg:'PASSED'"
SecAction "phase:1,deny,msg:'DENIED'"
),
match_log => {
error => [ qr/ModSecurity: Warning. Unconditional match in SecAction.*PASSED/, 1 ],
},
match_response => {
status => qr/^200$/,
},
request => new HTTP::Request(
GET => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test.txt",
),
},
{
type => "action",
comment => "pass in phase:2",
conf => qq(
SecRuleEngine DetectionOnly
SecRequestBodyAccess On
SecResponseBodyAccess On
SecResponseBodyMimeType null
SecAction "phase:2,pass,msg:'PASSED'"
SecAction "phase:2,deny,msg:'DENIED'"
),
match_log => {
error => [ qr/ModSecurity: Warning. Unconditional match in SecAction.*PASSED/, 1 ],
},
match_response => {
status => qr/^200$/,
},
request => new HTTP::Request(
GET => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test.txt",
),
},
{
type => "action",
comment => "pass in phase:3",
conf => qq(
SecRuleEngine DetectionOnly
SecRequestBodyAccess On
SecResponseBodyAccess On
SecResponseBodyMimeType null
SecDebugLog "$ENV{DEBUG_LOG}"
SecDebugLogLevel 4
SecAction "phase:3,pass,msg:'PASSED'"
SecAction "phase:3,deny,msg:'DENIED'"
),
match_log => {
error => [ qr/ModSecurity: Warning. Unconditional match in SecAction.*PASSED/, 1 ],
},
match_response => {
status => qr/^200$/,
},
request => new HTTP::Request(
GET => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test.txt",
),
},
{
type => "action",
comment => "pass in phase:4",
conf => qq(
SecRuleEngine DetectionOnly
SecRequestBodyAccess On
SecResponseBodyAccess On
SecResponseBodyMimeType null
SecDebugLog "$ENV{DEBUG_LOG}"
SecDebugLogLevel 4
SecAction "phase:4,pass,msg:'PASSED'"
SecAction "phase:4,deny,msg:'DENIED'"
),
match_log => {
error => [ qr/ModSecurity: Warning. Unconditional match in SecAction.*PASSED/, 1 ],
},
match_response => {
status => qr/^200$/,
},
request => new HTTP::Request(
GET => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test.txt",
),
},
# Allow
{
type => "action",
comment => "allow in phase:1",
conf => qq(
SecRuleEngine DetectionOnly
SecRequestBodyAccess On
SecResponseBodyAccess On
SecResponseBodyMimeType null
SecAction "phase:1,allow,msg:'ALLOWED'"
SecAction "phase:1,deny,msg:'DENIED'"
),
match_log => {
error => [ qr/ModSecurity: Warning. Unconditional match in SecAction.*ALLOWED/, 1 ],
-error => [ qr/Access allowed/, 1 ],
# TODO: Allow should probably rule stop execution
# -error => [ qr/DENIED/, 1 ],
},
match_response => {
status => qr/^200$/,
},
request => new HTTP::Request(
GET => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test.txt",
),
},
{
type => "action",
comment => "allow in phase:2",
conf => qq(
SecRuleEngine DetectionOnly
SecRequestBodyAccess On
SecResponseBodyAccess On
SecResponseBodyMimeType null
SecAction "phase:2,allow,msg:'ALLOWED'"
SecAction "phase:2,deny,msg:'DENIED'"
),
match_log => {
error => [ qr/ModSecurity: Warning. Unconditional match in SecAction.*ALLOWED/, 1 ],
-error => [ qr/Access allowed/, 1 ],
# TODO: Allow should probably rule stop execution
# -error => [ qr/DENIED/, 1 ],
},
match_response => {
status => qr/^200$/,
},
request => new HTTP::Request(
GET => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test.txt",
),
},
{
type => "action",
comment => "allow in phase:3",
conf => qq(
SecRuleEngine DetectionOnly
SecRequestBodyAccess On
SecResponseBodyAccess On
SecResponseBodyMimeType null
SecAction "phase:3,allow,msg:'ALLOWED'"
SecAction "phase:3,deny,msg:'DENIED'"
),
match_log => {
error => [ qr/ModSecurity: Warning. Unconditional match in SecAction.*ALLOWED/, 1 ],
-error => [ qr/Access allowed/, 1 ],
# TODO: Allow should probably rule stop execution
# -error => [ qr/DENIED/, 1 ],
},
match_response => {
status => qr/^200$/,
},
request => new HTTP::Request(
GET => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test.txt",
),
},
{
type => "action",
comment => "allow in phase:4",
conf => qq(
SecRuleEngine DetectionOnly
SecRequestBodyAccess On
SecResponseBodyAccess On
SecResponseBodyMimeType null
SecAction "phase:4,allow,msg:'ALLOWED'"
SecAction "phase:4,deny,msg:'DENIED'"
),
match_log => {
error => [ qr/ModSecurity: Warning. Unconditional match in SecAction.*ALLOWED/, 1 ],
-error => [ qr/Access allowed/, 1 ],
# TODO: Allow should probably rule stop execution
# -error => [ qr/DENIED/, 1 ],
},
match_response => {
status => qr/^200$/,
},
request => new HTTP::Request(
GET => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test.txt",
),
},
# Deny
{
type => "action",
comment => "deny in phase:1",
conf => qq(
SecRuleEngine DetectionOnly
SecRequestBodyAccess On
SecResponseBodyAccess On
SecResponseBodyMimeType null
SecAction "phase:1,deny,msg:'DENIED'"
),
match_log => {
error => [ qr/ModSecurity: Warning. Unconditional match in SecAction.*DENIED/, 1 ],
-error => [ qr/Access denied/, 1 ],
},
match_response => {
status => qr/^200$/,
},
request => new HTTP::Request(
GET => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test.txt",
),
},
{
type => "action",
comment => "deny in phase:2",
conf => qq(
SecRuleEngine DetectionOnly
SecRequestBodyAccess On
SecResponseBodyAccess On
SecResponseBodyMimeType null
SecAction "phase:2,deny,msg:'DENIED'"
),
match_log => {
error => [ qr/ModSecurity: Warning. Unconditional match in SecAction.*DENIED/, 1 ],
-error => [ qr/Access denied/, 1 ],
},
match_response => {
status => qr/^200$/,
},
request => new HTTP::Request(
GET => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test.txt",
),
},
{
type => "action",
comment => "deny in phase:3",
conf => qq(
SecRuleEngine DetectionOnly
SecRequestBodyAccess On
SecResponseBodyAccess On
SecResponseBodyMimeType null
SecAction "phase:3,deny,msg:'DENIED'"
),
match_log => {
error => [ qr/ModSecurity: Warning. Unconditional match in SecAction.*DENIED/, 1 ],
-error => [ qr/Access denied/, 1 ],
},
match_response => {
status => qr/^200$/,
},
request => new HTTP::Request(
GET => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test.txt",
),
},
{
type => "action",
comment => "deny in phase:4",
conf => qq(
SecRuleEngine DetectionOnly
SecRequestBodyAccess On
SecResponseBodyAccess On
SecResponseBodyMimeType null
SecAction "phase:4,deny,msg:'DENIED'"
),
match_log => {
error => [ qr/ModSecurity: Warning. Unconditional match in SecAction.*DENIED/, 1 ],
-error => [ qr/Access denied/, 1 ],
},
match_response => {
status => qr/^200$/,
},
request => new HTTP::Request(
GET => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test.txt",
),
},
# Drop
{
type => "action",
comment => "drop in phase:1",
conf => qq(
SecRuleEngine DetectionOnly
SecRequestBodyAccess On
SecResponseBodyAccess On
SecResponseBodyMimeType null
SecAction "phase:1,drop,msg:'DROPPED'"
),
match_log => {
error => [ qr/ModSecurity: Warning. Unconditional match in SecAction.*DROPPED/, 1 ],
-error => [ qr/Access denied/, 1 ],
},
match_response => {
status => qr/^200$/,
},
request => new HTTP::Request(
GET => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test.txt",
),
},
{
type => "action",
comment => "drop in phase:2",
conf => qq(
SecRuleEngine DetectionOnly
SecRequestBodyAccess On
SecResponseBodyAccess On
SecResponseBodyMimeType null
SecAction "phase:2,drop,msg:'DROPPED'"
),
match_log => {
error => [ qr/ModSecurity: Warning. Unconditional match in SecAction.*DROPPED/, 1 ],
-error => [ qr/Access denied/, 1 ],
},
match_response => {
status => qr/^200$/,
},
request => new HTTP::Request(
GET => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test.txt",
),
},
{
type => "action",
comment => "drop in phase:3",
conf => qq(
SecRuleEngine DetectionOnly
SecRequestBodyAccess On
SecResponseBodyAccess On
SecResponseBodyMimeType null
SecAction "phase:3,drop,msg:'DROPPED'"
),
match_log => {
error => [ qr/ModSecurity: Warning. Unconditional match in SecAction.*DROPPED/, 1 ],
-error => [ qr/Access denied/, 1 ],
},
match_response => {
status => qr/^200$/,
},
request => new HTTP::Request(
GET => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test.txt",
),
},
{
type => "action",
comment => "drop in phase:4",
conf => qq(
SecRuleEngine DetectionOnly
SecRequestBodyAccess On
SecResponseBodyAccess On
SecResponseBodyMimeType null
SecAction "phase:4,drop,msg:'DROPPED'"
),
match_log => {
error => [ qr/ModSecurity: Warning. Unconditional match in SecAction.*DROPPED/, 1 ],
-error => [ qr/Access denied/, 1 ],
},
match_response => {
status => qr/^200$/,
},
request => new HTTP::Request(
GET => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test.txt",
),
},
# Redirect
{
type => "action",
comment => "redirect in phase:1 (get)",
conf => qq(
SecRuleEngine DetectionOnly
SecRequestBodyAccess On
SecResponseBodyAccess On
SecResponseBodyMimeType null
SecRule REQUEST_URI "\@streq /test2.txt" "phase:1,redirect:'http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test.txt',msg:'REDIRECTED'"
),
match_log => {
error => [ qr/ModSecurity: Warning. String match "\/test2.txt" at REQUEST_URI.*REDIRECTED/, 1 ],
-error => [ qr/Access denied/, 1 ],
},
match_response => {
status => qr/^200$/,
content => qr/^TEST 2$/,
},
request => new HTTP::Request(
GET => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test2.txt",
),
},
{
type => "action",
comment => "redirect in phase:2 (get)",
conf => qq(
SecRuleEngine DetectionOnly
SecRequestBodyAccess On
SecResponseBodyAccess On
SecResponseBodyMimeType null
SecRule REQUEST_URI "\@streq /test2.txt" "phase:2,redirect:'http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test.txt',msg:'REDIRECTED'"
),
match_log => {
error => [ qr/ModSecurity: Warning. String match "\/test2.txt" at REQUEST_URI.*REDIRECTED/, 1 ],
-error => [ qr/Access denied/, 1 ],
},
match_response => {
status => qr/^200$/,
content => qr/^TEST 2$/,
},
request => new HTTP::Request(
GET => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test2.txt",
),
},
{
type => "action",
comment => "redirect in phase:3 (get)",
conf => qq(
SecRuleEngine DetectionOnly
SecRequestBodyAccess On
SecResponseBodyAccess On
SecResponseBodyMimeType null
SecRule REQUEST_URI "\@streq /test2.txt" "phase:3,redirect:'http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test.txt',msg:'REDIRECTED'"
),
match_log => {
error => [ qr/ModSecurity: Warning. String match "\/test2.txt" at REQUEST_URI.*REDIRECTED/, 1 ],
-error => [ qr/Access denied/, 1 ],
},
match_response => {
status => qr/^200$/,
content => qr/^TEST 2$/,
},
request => new HTTP::Request(
GET => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test2.txt",
),
},
{
type => "action",
comment => "redirect in phase:4 (get)",
conf => qq(
SecRuleEngine DetectionOnly
SecRequestBodyAccess On
SecResponseBodyAccess On
SecResponseBodyMimeType null
SecRule REQUEST_URI "\@streq /test2.txt" "phase:4,redirect:'http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test.txt',msg:'REDIRECTED'"
),
match_log => {
error => [ qr/ModSecurity: Warning. String match "\/test2.txt" at REQUEST_URI.*REDIRECTED/, 1 ],
-error => [ qr/Access denied/, 1 ],
},
match_response => {
status => qr/^200$/,
content => qr/^TEST 2$/,
},
request => new HTTP::Request(
GET => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test2.txt",
),
},
# Proxy
{
type => "action",
comment => "proxy in phase:1 (get)",
conf => qq(
SecRuleEngine DetectionOnly
SecRequestBodyAccess On
SecResponseBodyAccess On
SecResponseBodyMimeType null
SecRule REQUEST_URI "\@streq /test2.txt" "phase:1,proxy:'http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test.txt',msg:'PROXIED'"
),
match_log => {
error => [ qr/ModSecurity: Warning. String match "\/test2.txt" at REQUEST_URI.*PROXIED/, 1 ],
-error => [ qr/Access denied/, 1 ],
},
match_response => {
status => qr/^200$/,
content => qr/^TEST 2$/,
},
request => new HTTP::Request(
GET => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test2.txt",
),
},
{
type => "action",
comment => "proxy in phase:2 (get)",
conf => qq(
SecRuleEngine DetectionOnly
SecRequestBodyAccess On
SecResponseBodyAccess On
SecResponseBodyMimeType null
SecRule REQUEST_URI "\@streq /test2.txt" "phase:2,proxy:'http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test.txt',msg:'PROXIED'"
),
match_log => {
error => [ qr/ModSecurity: Warning. String match "\/test2.txt" at REQUEST_URI.*PROXIED/, 1 ],
-error => [ qr/Access denied/, 1 ],
},
match_response => {
status => qr/^200$/,
content => qr/^TEST 2$/,
},
request => new HTTP::Request(
GET => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test2.txt",
),
},
{
type => "action",
comment => "proxy in phase:3 (get)",
conf => qq(
SecRuleEngine DetectionOnly
SecRequestBodyAccess On
SecResponseBodyAccess On
SecResponseBodyMimeType null
SecDebugLog "$ENV{DEBUG_LOG}"
SecDebugLogLevel 4
SecRule REQUEST_URI "\@streq /test2.txt" "phase:3,proxy:'http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test.txt',msg:'PROXIED'"
),
match_log => {
error => [ qr/ModSecurity: Warning. String match "\/test2.txt" at REQUEST_URI.*PROXIED/, 1 ],
-error => [ qr/Access denied/, 1 ],
},
match_response => {
status => qr/^200$/,
},
request => new HTTP::Request(
GET => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test2.txt",
),
},
{
type => "action",
comment => "proxy in phase:4 (get)",
conf => qq(
SecRuleEngine DetectionOnly
SecRequestBodyAccess On
SecResponseBodyAccess On
SecResponseBodyMimeType null
SecDebugLog "$ENV{DEBUG_LOG}"
SecDebugLogLevel 4
SecRule REQUEST_URI "\@streq /test2.txt" "phase:4,proxy:'http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test.txt',msg:'PROXIED'"
),
match_log => {
error => [ qr/ModSecurity: Warning. String match "\/test2.txt" at REQUEST_URI.*PROXIED/, 1 ],
-error => [ qr/Access denied/, 1 ],
},
match_response => {
status => qr/^200$/,
},
request => new HTTP::Request(
GET => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test2.txt",
),
},

View File

@@ -0,0 +1,248 @@
### Logging tests
# log/nolog
{
type => "action",
comment => "log",
conf => qq(
SecRuleEngine On
SecAuditEngine RelevantOnly
SecAuditLog "$ENV{AUDIT_LOG}"
SecAction "phase:1,pass,log"
),
match_log => {
error => [ qr/ModSecurity: Warning. Unconditional match in SecAction\./, 1 ],
},
match_response => {
status => qr/^200$/,
},
request => new HTTP::Request(
GET => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test.txt",
),
},
{
type => "action",
comment => "nolog",
conf => qq(
SecRuleEngine On
SecAuditEngine RelevantOnly
SecAuditLog "$ENV{AUDIT_LOG}"
SecAction "phase:1,pass,nolog"
),
match_log => {
-error => [ qr/ModSecurity: /, 1 ],
-audit => [ qr/./, 1 ],
},
match_response => {
status => qr/^200$/,
},
request => new HTTP::Request(
GET => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test.txt",
),
},
# auditlog/noauditlog
{
type => "action",
comment => "auditlog",
conf => qq(
SecRuleEngine On
SecAuditEngine RelevantOnly
SecAuditLog "$ENV{AUDIT_LOG}"
SecAction "phase:1,pass,auditlog"
),
match_log => {
error => [ qr/ModSecurity: Warning. Unconditional match in SecAction\./, 1 ],
audit => [ qr/Message: Warning. Unconditional match in SecAction\./, 1 ],
},
match_response => {
status => qr/^200$/,
},
request => new HTTP::Request(
GET => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test.txt",
),
},
{
type => "action",
comment => "noauditlog",
conf => qq(
SecRuleEngine On
SecAuditEngine RelevantOnly
SecAuditLog "$ENV{AUDIT_LOG}"
SecAction "phase:1,pass,noauditlog"
),
match_log => {
error => [ qr/ModSecurity: Warning. Unconditional match in SecAction\./, 1 ],
-audit => [ qr/./, 1 ],
},
match_response => {
status => qr/^200$/,
},
request => new HTTP::Request(
GET => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test.txt",
),
},
# All log/nolog auditlog/noauditlog combos
{
type => "action",
comment => "log,auditlog",
conf => qq(
SecRuleEngine On
SecAuditEngine RelevantOnly
SecAuditLog "$ENV{AUDIT_LOG}"
SecAction "phase:1,pass,log,auditlog"
),
match_log => {
error => [ qr/ModSecurity: Warning. Unconditional match in SecAction\./, 1 ],
audit => [ qr/Message: Warning. Unconditional match in SecAction\./, 1 ],
},
match_response => {
status => qr/^200$/,
},
request => new HTTP::Request(
GET => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test.txt",
),
},
{
type => "action",
comment => "log,noauditlog",
conf => qq(
SecRuleEngine On
SecAuditEngine RelevantOnly
SecAuditLog "$ENV{AUDIT_LOG}"
SecAction "phase:1,pass,log,noauditlog"
),
match_log => {
error => [ qr/ModSecurity: Warning. Unconditional match in SecAction\./, 1 ],
-audit => [ qr/./, 1 ],
},
match_response => {
status => qr/^200$/,
},
request => new HTTP::Request(
GET => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test.txt",
),
},
{
type => "action",
comment => "nolog,auditlog",
conf => qq(
SecRuleEngine On
SecAuditEngine RelevantOnly
SecAuditLog "$ENV{AUDIT_LOG}"
SecAction "phase:1,pass,nolog,auditlog"
),
match_log => {
-error => [ qr/ModSecurity: /, 1 ],
# No message, but should have data. This may need changed
audit => [ qr/-H--\s+Stopwatch: /s, 1 ],
},
match_response => {
status => qr/^200$/,
},
request => new HTTP::Request(
GET => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test.txt",
),
},
{
type => "action",
comment => "nolog,noauditlog",
conf => qq(
SecRuleEngine On
SecAuditEngine RelevantOnly
SecAuditLog "$ENV{AUDIT_LOG}"
SecAction "phase:1,pass,nolog,noauditlog"
),
match_log => {
-error => [ qr/ModSecurity: /, 1 ],
-audit => [ qr/./, 1 ],
},
match_response => {
status => qr/^200$/,
},
request => new HTTP::Request(
GET => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test.txt",
),
},
{
type => "action",
comment => "auditlog,log",
conf => qq(
SecRuleEngine On
SecAuditEngine RelevantOnly
SecAuditLog "$ENV{AUDIT_LOG}"
SecAction "phase:1,pass,auditlog,log"
),
match_log => {
error => [ qr/ModSecurity: Warning. Unconditional match in SecAction\./, 1 ],
audit => [ qr/Message: Warning. Unconditional match in SecAction\./, 1 ],
},
match_response => {
status => qr/^200$/,
},
request => new HTTP::Request(
GET => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test.txt",
),
},
{
type => "action",
comment => "auditlog,nolog",
conf => qq(
SecRuleEngine On
SecAuditEngine RelevantOnly
SecAuditLog "$ENV{AUDIT_LOG}"
SecAction "phase:1,pass,auditlog,nolog"
),
match_log => {
-error => [ qr/ModSecurity: /, 1 ],
-audit => [ qr/./, 1 ],
},
match_response => {
status => qr/^200$/,
},
request => new HTTP::Request(
GET => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test.txt",
),
},
{
type => "action",
comment => "noauditlog,log",
conf => qq(
SecRuleEngine On
SecAuditEngine RelevantOnly
SecAuditLog "$ENV{AUDIT_LOG}"
SecAction "phase:1,pass,noauditlog,log"
),
match_log => {
error => [ qr/ModSecurity: Warning. Unconditional match in SecAction\./, 1 ],
-audit => [ qr/./, 1 ],
},
match_response => {
status => qr/^200$/,
},
request => new HTTP::Request(
GET => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test.txt",
),
},
{
type => "action",
comment => "noauditlog,nolog",
conf => qq(
SecRuleEngine On
SecAuditEngine RelevantOnly
SecAuditLog "$ENV{AUDIT_LOG}"
SecAction "phase:1,pass,noauditlog,nolog"
),
match_log => {
-error => [ qr/ModSecurity: /, 1 ],
-audit => [ qr/./, 1 ],
},
match_response => {
status => qr/^200$/,
},
request => new HTTP::Request(
GET => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test.txt",
),
},

View File

@@ -0,0 +1,23 @@
{
type => "config",
comment => "module loaded",
match_log => {
error => [ qr/ModSecurity for Apache.* configured\./, 10 ],
},
},
{
type => "config",
comment => "minimal config",
conf => sub {
# Open the minimal conf file, substituting the
# relative log paths with full paths.
open(C, "<$ENV{DIST_ROOT}/modsecurity.conf-minimal") or die "$!\n";
(my $conf = join('', <C>)) =~ s#Log logs/#Log $ENV{TEST_SERVER_ROOT}/logs/#g;
close C;
return $conf;
},
match_log => {
error => [ qr/ModSecurity for Apache.* configured\./, 10 ],
},
},

View File

@@ -0,0 +1,264 @@
### 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",
),
},

View File

@@ -0,0 +1,278 @@
### SecDebug* directive tests
{
type => "config",
comment => "SecDebugLog (pos)",
conf => qq(
SecRuleEngine On
SecDebugLog $ENV{DEBUG_LOG}
SecDebugLogLevel 9
),
match_log => {
debug => [ qr/./, 1 ],
},
match_response => {
status => qr/^200$/,
},
request => new HTTP::Request(
GET => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test.txt",
),
},
{
type => "config",
comment => "SecDebugLog (neg)",
conf => qq(
SecRuleEngine On
),
match_log => {
-debug => [ qr/./, 1 ],
},
match_response => {
status => qr/^200$/,
},
request => new HTTP::Request(
GET => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test.txt",
),
},
{
type => "config",
comment => "SecDebugLogLevel 0",
conf => qq(
SecRuleEngine On
SecDebugLog $ENV{DEBUG_LOG}
SecDebugLogLevel 0
SecRule REQUEST_URI "." "phase:1,deny"
),
match_log => {
-debug => [ qr/./, 1 ],
},
match_response => {
status => qr/^403$/,
},
request => new HTTP::Request(
GET => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test.txt",
),
},
{
type => "config",
comment => "SecDebugLogLevel 1",
conf => qq(
SecRuleEngine On
SecDebugLog $ENV{DEBUG_LOG}
SecDebugLogLevel 1
SecRuleScript "test.lua" "phase:1"
SecRule REQUEST_URI "(.)" "phase:4,deny,deprecatevar:bogus"
),
match_log => {
debug => [ qr/\]\[[1]\] /, 1 ],
-debug => [ qr/\]\[[2-9]\] /, 1 ],
},
match_response => {
status => qr/^403$/,
},
request => new HTTP::Request(
POST => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test.txt",
[
"Content-Type" => "application/x-www-form-urlencoded",
],
"a=1&b=2",
),
},
{
type => "config",
comment => "SecDebugLogLevel 2",
conf => qq(
SecRuleEngine DetectionOnly
SecDebugLog $ENV{DEBUG_LOG}
SecDebugLogLevel 2
SecRuleScript "test.lua" "phase:1"
SecRule REQUEST_URI "(.)" "phase:4,deny,deprecatevar:bogus"
),
match_log => {
debug => [ qr/\]\[2\] /, 1 ],
-debug => [ qr/\]\[[3-9]\] /, 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=1&b=2",
),
},
{
type => "config",
comment => "SecDebugLogLevel 3",
conf => qq(
SecRuleEngine On
SecDebugLog $ENV{DEBUG_LOG}
SecDebugLogLevel 3
SecRuleScript "test.lua" "phase:1"
SecRule REQUEST_URI "(.)" "phase:4,deny,deprecatevar:bogus"
),
match_log => {
debug => [ qr/\]\[3\] /, 1 ],
-debug => [ qr/\]\[[4-9]\] /, 1 ],
},
match_response => {
status => qr/^403$/,
},
request => new HTTP::Request(
POST => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test.txt",
[
"Content-Type" => "application/x-www-form-urlencoded",
],
"a=1&b=2",
),
},
{
type => "config",
comment => "SecDebugLogLevel 4",
conf => qq(
SecRuleEngine On
SecDebugLog $ENV{DEBUG_LOG}
SecDebugLogLevel 4
SecRuleScript "test.lua" "phase:1"
SecRule REQUEST_URI "(.)" "phase:4,deny,deprecatevar:bogus"
),
match_log => {
debug => [ qr/\]\[4\] /, 1 ],
-debug => [ qr/\]\[[5-9]\] /, 1 ],
},
match_response => {
status => qr/^403$/,
},
request => new HTTP::Request(
POST => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test.txt",
[
"Content-Type" => "application/x-www-form-urlencoded",
],
"a=1&b=2",
),
},
{
type => "config",
comment => "SecDebugLogLevel 5",
conf => qq(
SecRuleEngine On
SecDebugLog $ENV{DEBUG_LOG}
SecDebugLogLevel 5
SecRuleScript "test.lua" "phase:1"
SecRule REQUEST_URI "(.)" "phase:4,deny,deprecatevar:bogus"
),
match_log => {
debug => [ qr/\]\[5\] /, 1 ],
-debug => [ qr/\]\[[6-9]\] /, 1 ],
},
match_response => {
status => qr/^403$/,
},
request => new HTTP::Request(
POST => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test.txt",
[
"Content-Type" => "application/x-www-form-urlencoded",
],
"a=1&b=2",
),
},
{
type => "config",
comment => "SecDebugLogLevel 6",
conf => qq(
SecRuleEngine On
SecDebugLog $ENV{DEBUG_LOG}
SecDebugLogLevel 6
SecRuleScript "test.lua" "phase:1"
SecRule REQUEST_URI "(.)" "phase:4,deny,deprecatevar:bogus"
),
match_log => {
debug => [ qr/\]\[6\] /, 1 ],
-debug => [ qr/\]\[[7-9]\] /, 1 ],
},
match_response => {
status => qr/^403$/,
},
request => new HTTP::Request(
POST => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test.txt",
[
"Content-Type" => "application/x-www-form-urlencoded",
],
"a=1&b=2",
),
},
{
type => "config",
comment => "SecDebugLogLevel 7",
conf => qq(
SecRuleEngine On
SecDebugLog $ENV{DEBUG_LOG}
SecDebugLogLevel 7
SecRuleScript "test.lua" "phase:1"
SecRule REQUEST_URI "(.)" "phase:4,deny,deprecatevar:bogus"
),
match_log => {
debug => [ qr/\]\[7\] /, 1 ],
-debug => [ qr/\]\[[8-9]\] /, 1 ],
},
match_response => {
status => qr/^403$/,
},
request => new HTTP::Request(
POST => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test.txt",
[
"Content-Type" => "application/x-www-form-urlencoded",
],
"a=1&b=2",
),
},
{
type => "config",
comment => "SecDebugLogLevel 8",
conf => qq(
SecRuleEngine On
SecDebugLog $ENV{DEBUG_LOG}
SecDebugLogLevel 8
SecRuleScript "test.lua" "phase:1"
SecRule REQUEST_URI "(.)" "phase:4,deny,deprecatevar:bogus"
),
match_log => {
debug => [ qr/\]\[8\] /, 1 ],
-debug => [ qr/\]\[9\] /, 1 ],
},
match_response => {
status => qr/^403$/,
},
request => new HTTP::Request(
POST => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test.txt",
[
"Content-Type" => "application/x-www-form-urlencoded",
],
"a=1&b=2",
),
},
{
type => "config",
comment => "SecDebugLogLevel 9",
conf => qq(
SecRuleEngine On
SecDebugLog $ENV{DEBUG_LOG}
SecDebugLogLevel 9
SecRuleScript "test.lua" "phase:1"
SecRule REQUEST_URI "(.)" "phase:4,deny,deprecatevar:bogus"
),
match_log => {
debug => [ qr/\]\[9\] /, 1 ],
},
match_response => {
status => qr/^403$/,
},
request => new HTTP::Request(
POST => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test.txt",
[
"Content-Type" => "application/x-www-form-urlencoded",
],
"a=1&b=2",
),
},

View File

@@ -0,0 +1,148 @@
### Misc directive tests
### TODO:
# SecTmpDir
# SecUploadKeepFiles
# SecChrootDir
# SecGuardianLog
# SecDefaultAction
{
type => "config",
comment => "SecDefaultAction",
conf => qq(
SecRuleEngine on
SecDefaultAction "phase:1,deny,status:500"
SecRule REQUEST_URI "test.txt"
),
match_log => {
error => [ qr/ModSecurity: Access denied with code 500 \(phase 1\)/, 1 ],
},
match_response => {
status => qr/^500$/,
},
request => new HTTP::Request(
GET => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test.txt",
),
},
# SecServerSignature
{
type => "config",
comment => "SecServerSignature On",
conf => qq(
SecServerSignature "NewServerSignature"
),
match_log => {
error => [ qr/NewServerSignature/, 1 ],
},
match_response => {
status => qr/^200$/,
raw => qr/^Server: +NewServerSignature$/m,
},
request => new HTTP::Request(
GET => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test.txt",
),
},
# SecDataDir
{
type => "config",
comment => "SecDataDir",
conf => qq(
SecRuleEngine On
SecDataDir "$ENV{DATA_DIR}"
SecAction initcol:ip=%{REMOTE_ADDR},setvar:ip.dummy=1,pass
),
match_log => {
error => [ qr/ModSecurity: Warning. Unconditional match in SecAction\./, 1 ],
},
match_file => {
"$ENV{DATA_DIR}/ip.pag" => qr/\x00\x06dummy\x00\x00\x021\x00/,
},
match_response => {
status => qr/^200$/,
},
request => new HTTP::Request(
GET => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test.txt",
),
},
# SecTmpDir/SecUploadDir/SecUploadKeepFiles
{
type => "config",
comment => "SecTmpDir/SecUploadDir/SecUploadKeepFiles",
conf => qq(
SecRuleEngine On
SecRequestBodyAccess On
SecDebugLog $ENV{DEBUG_LOG}
SecDebugLogLevel 4
SecTmpDir "$ENV{TEMP_DIR}"
SecUploadKeepFiles On
SecUploadDir "$ENV{UPLOAD_DIR}"
),
test => sub {
# Get the filename and make sure the file exists
my $fn = match_log(debug => qr/Moved file from .* to ".*"\./, 5);
die "Failed to determine uploaded filename\n" unless (defined $fn);
$fn =~ s/Moved file from .* to "(.*)"\..*/$1/;
die "File does not exist: $fn\n" unless (-e $fn);
# Check the contents of the file
return 0 if (match_file($fn, qr/^TESTFILE$/m));
msg("Failed to match contents of uploaded file: $fn");
return 1;
},
match_log => {
debug => [ qr/Created temporary file: $ENV{TEMP_DIR}/, 1 ],
-debug => [ qr/Failed to /, 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=---------------------------19813181771830765643996187206",
],
q(-----------------------------19813181771830765643996187206
Content-Disposition: form-data; name="upload-file"; filename="test"
Content-Type: application/octet-stream
TESTFILE
-----------------------------19813181771830765643996187206
Content-Disposition: form-data; name="file"
Upload File
-----------------------------19813181771830765643996187206--),
),
},
# SecWebAppId
{
type => "config",
comment => "SecWebAppId",
conf => qq(
SecRuleEngine On
SecRequestBodyAccess On
SecDebugLog $ENV{DEBUG_LOG}
SecDebugLogLevel 4
SecAuditLog "$ENV{AUDIT_LOG}"
SecAuditEngine RelevantOnly
SecWebAppId "app-1"
SecAction "pass,log,auditlog,id:1"
),
match_log => {
error => [ qr/Warning\. Unconditional match in SecAction\./, 1 ],
debug => [ qr/Warning\. Unconditional match in SecAction\./, 1 ],
audit => [ qr/^WebApp-Info: "app-1"/m, 1 ],
},
match_response => {
status => qr/^200$/,
},
request => new HTTP::Request(
GET => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test.txt",
),
},

View File

@@ -0,0 +1,328 @@
### Tests for directives altering how a request is handled
# SecArgumentSeparator
{
type => "config",
comment => "SecArgumentSeparator (get-pos)",
conf => q(
SecRuleEngine On
SecArgumentSeparator ";"
SecRule ARGS:a "@streq 1" "phase:1,deny,chain"
SecRule ARGS:b "@streq 2"
),
match_log => {
error => [ qr/Access denied with code 403 \(phase 1\)\. String match "2" at ARGS:b\./, 1 ],
},
match_response => {
status => qr/^403$/,
},
request => new HTTP::Request(
GET => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test.txt?a=1;b=2",
),
},
{
type => "config",
comment => "SecArgumentSeparator (get-neg)",
conf => q(
SecRuleEngine On
SecRule ARGS:a "@streq 1" "phase:1,deny,chain"
SecRule ARGS:b "@streq 2"
),
match_log => {
-error => [ qr/Access denied/, 1 ],
},
match_response => {
status => qr/^200$/,
},
request => new HTTP::Request(
GET => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test.txt?a=1;b=2",
),
},
{
type => "config",
comment => "SecArgumentSeparator (post-pos)",
conf => q(
SecRuleEngine On
SecRequestBodyAccess On
SecArgumentSeparator ";"
SecRule ARGS:a "@streq 1" "phase:2,deny,chain"
SecRule ARGS:b "@streq 2"
),
match_log => {
error => [ qr/Access denied with code 403 \(phase 2\)\. String match "2" at ARGS:b\./, 1 ],
},
match_response => {
status => qr/^403$/,
},
request => new HTTP::Request(
POST => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test.txt",
[
"Content-Type" => "application/x-www-form-urlencoded",
],
"a=1;b=2",
),
},
{
type => "config",
comment => "SecArgumentSeparator (post-neg)",
conf => q(
SecRuleEngine On
SecRequestBodyAccess On
SecRule ARGS:a "@streq 1" "phase:2,deny"
SecRule ARGS:b "@streq 2" "phase:2,deny"
),
match_log => {
-error => [ qr/Access denied/, 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=1;b=2",
),
},
# SecRequestBodyAccess
{
type => "config",
comment => "SecRequestBodyAccess (pos)",
conf => qq(
SecRuleEngine On
SecRequestBodyAccess On
SecRule ARGS:a "\@streq 1" "phase:2,deny,chain"
SecRule ARGS:b "\@streq 2"
),
match_log => {
error => [ qr/Access denied with code 403 \(phase 2\)\. String match "2" at ARGS:b\./, 1 ],
},
match_response => {
status => qr/^403$/,
},
request => new HTTP::Request(
POST => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test.txt",
[
"Content-Type" => "application/x-www-form-urlencoded",
],
"a=1&b=2",
),
},
{
type => "config",
comment => "SecRequestBodyAccess (neg)",
conf => qq(
SecRuleEngine On
SecRequestBodyAccess Off
SecRule ARGS:a "\@streq 1" "phase:2,deny"
SecRule ARGS:b "\@streq 2" "phase:2,deny"
),
match_log => {
-error => [ qr/Access denied/, 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=1&b=2",
),
},
# SecRequestBodyLimit
{
type => "config",
comment => "SecRequestBodyLimit (equal)",
conf => qq(
SecRuleEngine On
SecRequestBodyAccess On
SecRequestBodyLimit 7
),
match_log => {
-error => [ qr/Request body is larger than the configured limit/, 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=1&b=2",
),
},
{
type => "config",
comment => "SecRequestBodyLimit (greater)",
conf => qq(
SecRuleEngine On
SecRequestBodyAccess On
SecRequestBodyLimit 5
),
match_log => {
error => [ qr/Request body is larger than the configured limit \(5\)\./, 1 ],
},
match_response => {
status => qr/^413$/,
},
request => new HTTP::Request(
POST => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test.txt",
[
"Content-Type" => "application/x-www-form-urlencoded",
],
"a=1&b=2",
),
},
# SecRequestBodyInMemoryLimit
{
type => "config",
comment => "SecRequestBodyInMemoryLimit (equal)",
conf => qq(
SecRuleEngine On
SecDebugLog $ENV{DEBUG_LOG}
SecDebugLogLevel 9
SecRequestBodyAccess On
SecRequestBodyLimit 1000
SecRequestBodyInMemoryLimit 276
),
match_log => {
-debug => [ qr/Input filter: Request too large to store in memory, switching to disk\./, 1 ],
},
match_response => {
status => qr/^200$/,
},
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(
normalize_raw_request_data(
q(
-----------------------------69343412719991675451336310646
Content-Disposition: form-data; name="a"
1
-----------------------------69343412719991675451336310646
Content-Disposition: form-data; name="b"
2
-----------------------------69343412719991675451336310646--
)
),
1024
),
},
{
type => "config",
comment => "SecRequestBodyInMemoryLimit (greater)",
conf => qq(
SecRuleEngine On
SecDebugLog $ENV{DEBUG_LOG}
SecDebugLogLevel 9
SecRequestBodyAccess On
SecRequestBodyLimit 1000
SecRequestBodyInMemoryLimit 16
),
match_log => {
debug => [ qr/Input filter: Request too large to store in memory, switching to disk\./, 1 ],
},
match_response => {
status => qr/^200$/,
},
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(
normalize_raw_request_data(
q(
-----------------------------69343412719991675451336310646
Content-Disposition: form-data; name="a"
1
-----------------------------69343412719991675451336310646
Content-Disposition: form-data; name="b"
2
-----------------------------69343412719991675451336310646--
)
),
1024
),
},
# SecCookieFormat
{
type => "config",
comment => "SecCookieFormat (pos)",
conf => qq(
SecRuleEngine On
SecDebugLog $ENV{DEBUG_LOG}
SecDebugLogLevel 5
SecCookieFormat 1
SecRule REQUEST_COOKIES_NAMES "\@streq SESSIONID" "phase:1,deny,chain"
SecRule REQUEST_COOKIES:\$SESSIONID_PATH "\@streq /" "chain"
SecRule REQUEST_COOKIES:SESSIONID "\@streq cookieval"
),
match_log => {
error => [ qr/Access denied with code 403 \(phase 1\)\. String match "cookieval" at REQUEST_COOKIES:SESSIONID\./, 1 ],
debug => [ qr(Adding request cookie: name "\$SESSIONID_PATH", value "/"), 1 ],
},
match_response => {
status => qr/^403$/,
},
request => new HTTP::Request(
GET => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test.txt",
[
"Cookie" => q($Version="1"; SESSIONID="cookieval"; $PATH="/"),
],
undef,
),
},
{
type => "config",
comment => "SecCookieFormat (neg)",
conf => qq(
SecRuleEngine On
SecDebugLog $ENV{DEBUG_LOG}
SecDebugLogLevel 5
SecCookieFormat 0
SecRule REQUEST_COOKIES_NAMES "\@streq SESSIONID" "phase:1,deny,chain"
SecRule REQUEST_COOKIES:\$SESSIONID_PATH "\@streq /" "chain"
SecRule REQUEST_COOKIES:SESSIONID "\@streq cookieval"
),
match_log => {
-error => [ qr/Access denied/, 1 ],
-debug => [ qr(Adding request cookie: name "\$SESSIONID_PATH", value "/"), 1 ],
},
match_response => {
status => qr/^200$/,
},
request => new HTTP::Request(
GET => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test.txt",
[
"Cookie" => q($Version="1"; SESSIONID="cookieval"; $PATH="/"),
],
undef,
),
},

View File

@@ -0,0 +1,174 @@
### Tests for directives altering how a response is handled
# SecResponseBodyMimeTypesClear
{
type => "config",
comment => "SecResponseBodyMimeTypesClear",
conf => qq(
SecRuleEngine On
SecResponseBodyAccess On
SecResponseBodyMimeTypesClear
SecDebugLog $ENV{DEBUG_LOG}
SecDebugLogLevel 9
SecRule RESPONSE_BODY "TEST" "phase:4,deny"
),
match_log => {
-error => [ qr/Access denied/, 1 ],
debug => [ qr/Not buffering response body for unconfigured MIME type/, 1 ],
},
match_response => {
status => qr/^200$/,
},
request => new HTTP::Request(
GET => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test.txt",
),
},
# SecResponseBodyAccess & SecResponseBodyMimeType
{
type => "config",
comment => "SecResponseBodyAccess On",
conf => qq(
SecRuleEngine On
SecDebugLog $ENV{DEBUG_LOG}
SecDebugLogLevel 9
SecResponseBodyAccess On
SecResponseBodyMimeType text/plain null
SecRule RESPONSE_BODY "TEST" "phase:4,deny"
),
match_log => {
error => [ qr/Access denied with code 403 \(phase 4\)\. Pattern match "TEST" at RESPONSE_BODY\./, 1 ],
},
match_response => {
status => qr/^403$/,
},
request => new HTTP::Request(
GET => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test.txt",
),
},
{
type => "config",
comment => "SecResponseBodyAccess Off",
conf => qq(
SecRuleEngine On
SecDebugLog $ENV{DEBUG_LOG}
SecDebugLogLevel 9
SecResponseBodyAccess Off
SecResponseBodyMimeType text/plain null
SecRule RESPONSE_BODY "TEST" "phase:4,deny"
),
match_log => {
-error => [ qr/Access denied/, 1 ],
debug => [ qr/Response body buffering is not enabled\./, 1 ],
},
match_response => {
status => qr/^200$/,
},
request => new HTTP::Request(
GET => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test.txt",
),
},
# SecResponseBodyLimit
{
type => "config",
comment => "SecResponseBodyLimit (equal)",
conf => qq(
SecRuleEngine On
SecResponseBodyAccess On
SecResponseBodyMimeType text/plain null
SecResponseBodyLimit 8192
),
match_log => {
-error => [ qr/Content-Length \(\d+\) over the limit/, 1 ],
},
match_response => {
status => qr/^200$/,
},
request => new HTTP::Request(
GET => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/8k.txt",
),
},
{
type => "config",
comment => "SecResponseBodyLimit (less)",
conf => qq(
SecRuleEngine On
SecResponseBodyAccess On
SecResponseBodyMimeType text/plain null
SecResponseBodyLimit 9000
),
match_log => {
-error => [ qr/Content-Length \(\d+\) over the limit/, 1 ],
},
match_response => {
status => qr/^200$/,
},
request => new HTTP::Request(
GET => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/8k.txt",
),
},
{
type => "config",
comment => "SecResponseBodyLimit (greater)",
conf => qq(
SecRuleEngine On
SecResponseBodyAccess On
SecResponseBodyMimeType text/plain null
SecResponseBodyLimit 8000
),
match_log => {
error => [ qr/Content-Length \(\d+\) over the limit \(8000\)\./, 1 ],
},
match_response => {
status => qr/^500$/,
},
request => new HTTP::Request(
GET => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/8k.txt",
),
},
# ResponseBodyLimitAction
{
type => "config",
comment => "SecResponseBodyLimitAction Reject",
conf => qq(
SecRuleEngine On
SecResponseBodyAccess On
SecResponseBodyMimeType text/plain null
SecResponseBodyLimit 5
SecResponseBodyLimitAction Reject
),
match_log => {
error => [ qr/Content-Length \(\d+\) over the limit \(5\)\./, 1 ],
},
match_response => {
status => qr/^500$/,
},
request => new HTTP::Request(
GET => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/8k.txt",
),
},
{
type => "config",
comment => "SecResponseBodyLimitAction ProcessPartial",
conf => qq(
SecRuleEngine On
SecResponseBodyAccess On
SecResponseBodyMimeType text/plain null
SecResponseBodyLimit 5
SecDebugLog $ENV{DEBUG_LOG}
SecDebugLogLevel 4
SecResponseBodyLimitAction ProcessPartial
),
match_log => {
-error => [ qr/Content-Length \(\d+\) over the limit/, 1 ],
debug => [ qr/Processing partial response body \(limit 5\)/, 1 ],
},
match_response => {
status => qr/^200$/,
},
request => new HTTP::Request(
GET => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/8k.txt",
),
},

View File

@@ -0,0 +1,35 @@
### SecChroot tests
# TODO: Will not work as we need root access
#{
# type => "config",
# comment => "SecChroot",
# httpd_opts => qw(
# -DCHROOT
# ),
# conf => qq(
# # These will be in the chroot
# PidFile /logs/httpd.pid
# ScoreBoardFile /logs/httpd.scoreboard
# User nobody
# Group nogroup
#
# SecAuditEngine On
# SecDebugLog $ENV{DEBUG_LOG}
# SecDebugLogLevel 9
# SecAuditLog $ENV{AUDIT_LOG}
# SecAuditLogStorageDir "/logs/audit"
# SecAuditLogType Concurrent
# SecChrootDir "$ENV{TEST_SERVER_ROOT}"
# ),
# match_log => {
# debug => [ qr/./, 1 ],
# audit => [ qr/./, 1 ],
# },
# match_response => {
# status => qr/^200$/,
# },
# request => new HTTP::Request(
# GET => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test.txt",
# ),
#},

View File

@@ -0,0 +1,364 @@
### 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\")",
note => q(
KHTML 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=--------0xKhTmLbOuNdArY",
],
normalize_raw_request_data(
q(
----------0xKhTmLbOuNdArY
Content-Disposition: form-data; name="a"
1
----------0xKhTmLbOuNdArY
Content-Disposition: form-data; name="b"
2
----------0xKhTmLbOuNdArY--
),
),
),
},
# 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--
),
),
),
},

View File

@@ -0,0 +1,151 @@
### Test the phases
# Phase 1 (request headers)
{
type => "misc",
comment => "phase 1",
conf => qq(
SecRuleEngine On
SecRequestBodyAccess On
SecResponseBodyAccess On
SecResponseBodyMimeType text/plain null
SecRule REQUEST_LINE "^POST" "phase:1,pass,log,auditlog"
SecRule ARGS "val1" "phase:1,pass,log,auditlog"
SecRule RESPONSE_HEADERS:Last-Modified "." "phase:1,pass,log,auditlog"
SecRule RESPONSE_BODY "TEST" "phase:1,pass,log,auditlog"
),
match_log => {
error => [ qr/Pattern match "\^POST" at REQUEST_LINE/, 1 ],
-error => [ qr/Pattern match .* (ARGS|RESPONSE)/, 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",
],
"arg1=val1&arg2=val2",
),
},
# Phase 2 (request body)
{
type => "misc",
comment => "phase 2",
conf => qq(
SecRuleEngine On
SecRequestBodyAccess On
SecResponseBodyAccess On
SecResponseBodyMimeType text/plain null
SecRule REQUEST_LINE "^POST" "phase:2,pass,log,auditlog"
SecRule ARGS "val1" "phase:2,pass,log,auditlog"
SecRule RESPONSE_HEADERS:Last-Modified "." "phase:2,pass,log,auditlog"
SecRule RESPONSE_BODY "TEST" "phase:2,pass,log,auditlog"
),
match_log => {
error => [ qr/Pattern match "\^POST" at REQUEST_LINE.*Pattern match "val1" at ARGS/s, 1 ],
-error => [ qr/Pattern match .* RESPONSE/, 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",
],
"arg1=val1&arg2=val2",
),
},
# Phase 3 (response headers)
{
type => "misc",
comment => "phase 3",
conf => qq(
SecRuleEngine On
SecRequestBodyAccess On
SecResponseBodyAccess On
SecResponseBodyMimeType text/plain null
SecRule REQUEST_LINE "^POST" "phase:3,pass,log,auditlog"
SecRule ARGS "val1" "phase:3,pass,log,auditlog"
SecRule RESPONSE_HEADERS:Last-Modified "." "phase:3,pass,log,auditlog"
SecRule RESPONSE_BODY "TEST" "phase:3,pass,log,auditlog"
),
match_log => {
error => [ qr/Pattern match "\^POST" at REQUEST_LINE.*Pattern match "val1" at ARGS.*Pattern match "\." at RESPONSE_HEADERS/s, 1 ],
-error => [ qr/Pattern match .* RESPONSE_BODY/, 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",
],
"arg1=val1&arg2=val2",
),
},
# Phase 4 (response body)
{
type => "misc",
comment => "phase 4",
conf => qq(
SecRuleEngine On
SecRequestBodyAccess On
SecResponseBodyAccess On
SecResponseBodyMimeType text/plain null
SecDebugLog "$ENV{DEBUG_LOG}"
SecDebugLogLevel 9
SecRule REQUEST_LINE "^POST" "phase:4,pass,log,auditlog"
SecRule ARGS "val1" "phase:4,pass,log,auditlog"
SecRule RESPONSE_HEADERS:Last-Modified "." "phase:4,pass,log,auditlog"
SecRule RESPONSE_BODY "TEST" "phase:4,pass,log,auditlog"
),
match_log => {
error => [ qr/Pattern match "\^POST" at REQUEST_LINE.*Pattern match "val1" at ARGS.*Pattern match "\." at RESPONSE_HEADERS.*Pattern match "TEST" at RESPONSE_BODY/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",
],
"arg1=val1&arg2=val2",
),
},
# Phase 5 (logging)
{
type => "misc",
comment => "phase 5",
conf => qq(
SecRuleEngine On
SecRequestBodyAccess On
SecResponseBodyAccess On
SecResponseBodyMimeType text/plain null
SecRule REQUEST_LINE "^POST" "phase:5,pass,log,auditlog"
SecRule ARGS "val1" "phase:5,pass,log,auditlog"
SecRule RESPONSE_HEADERS:Last-Modified "." "phase:5,pass,log,auditlog"
SecRule RESPONSE_BODY "TEST" "phase:5,pass,log,auditlog"
),
match_log => {
error => [ qr/Pattern match "\^POST" at REQUEST_LINE.*Pattern match "val1" at ARGS.*Pattern match "\." at RESPONSE_HEADERS.*Pattern match "TEST" at RESPONSE_BODY/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",
],
"arg1=val1&arg2=val2",
),
},

View File

@@ -0,0 +1,24 @@
### Tests for basic rule components
# SecAction
{
type => "rule",
comment => "SecAction (override default)",
conf => qq(
SecRuleEngine On
SecDebugLog $ENV{DEBUG_LOG}
SecDebugLogLevel 4
SecAction "nolog"
),
match_log => {
-error => [ qr/ModSecurity: /, 1 ],
-audit => [ qr/./, 1 ],
debug => [ qr/Warning\. Unconditional match in SecAction\./, 1 ],
},
match_response => {
status => qr/^200$/,
},
request => new HTTP::Request(
GET => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test.txt",
),
},

View File

@@ -0,0 +1,4 @@
### Tests for rule inheritance
### TODO:
# SecRuleInheritance

View File

@@ -0,0 +1,63 @@
### Test for SecRuleScript
# Lua
{
type => "rule",
comment => "SecRuleScript (lua absolute nomatch)",
conf => qq(
SecRuleEngine On
SecDebugLog $ENV{DEBUG_LOG}
SecDebugLogLevel 1
SecRuleScript "$ENV{CONF_DIR}/test.lua" "phase:2,deny"
),
match_log => {
-error => [ qr/Lua script matched\./, 1 ],
debug => [ qr/Test message\./, 1 ],
},
match_response => {
status => qr/^200$/,
},
request => new HTTP::Request(
GET => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test.txt",
),
},
{
type => "rule",
comment => "SecRuleScript (lua relative nomatch)",
conf => qq(
SecRuleEngine On
SecDebugLog $ENV{DEBUG_LOG}
SecDebugLogLevel 1
SecRuleScript "test.lua" "phase:2,deny"
),
match_log => {
-error => [ qr/Lua script matched\./, 1 ],
debug => [ qr/Test message\./, 1 ],
},
match_response => {
status => qr/^200$/,
},
request => new HTTP::Request(
GET => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test.txt",
),
},
{
type => "rule",
comment => "SecRuleScript (lua relative match)",
conf => qq(
SecRuleEngine On
SecDebugLog $ENV{DEBUG_LOG}
SecDebugLogLevel 1
SecRuleScript "match.lua" "phase:2,deny"
),
match_log => {
error => [ qr/ModSecurity: Access denied with code 403 \(phase 2\)\. Lua script matched\./, 1 ],
debug => [ qr/Test message\./, 1 ],
},
match_response => {
status => qr/^403$/,
},
request => new HTTP::Request(
GET => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test.txt",
),
},

View File

@@ -0,0 +1,129 @@
### Tests for rule exceptions
# SecRuleRemoveByMsg
# SecRuleRemoveById
{
type => "config",
comment => "SecRuleRemoveById (single)",
conf => qq(
SecRuleEngine On
SecDebugLog $ENV{DEBUG_LOG}
SecDebugLogLevel 9
SecRule REQUEST_URI "test" "phase:1,deny,status:500,id:1"
SecRuleRemoveById 1
),
match_log => {
-error => [ qr/ModSecurity: /, 1 ],
-audit => [ qr/./, 1 ],
debug => [ qr/Starting phase REQUEST_HEADERS\..*This phase consists of 0 rule.*Starting phase RESPONSE_HEADERS\./s, 1 ],
-debug => [ qr/Access denied/, 1 ],
},
match_response => {
status => qr/^200$/,
},
request => new HTTP::Request(
GET => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test.txt",
),
},
{
type => "config",
comment => "SecRuleRemoveById (multiple)",
conf => qq(
SecRuleEngine On
SecDebugLog $ENV{DEBUG_LOG}
SecDebugLogLevel 9
SecRule REQUEST_URI "test" "phase:1,deny,status:500,id:1"
SecRule REQUEST_URI "test" "phase:1,deny,status:500,id:2"
SecRule REQUEST_URI "test" "phase:1,deny,status:500,id:3"
SecRuleRemoveById 1 2 3
),
match_log => {
-error => [ qr/ModSecurity: /, 1 ],
-audit => [ qr/./, 1 ],
debug => [ qr/Starting phase REQUEST_HEADERS\..*This phase consists of 0 rule.*Starting phase RESPONSE_HEADERS\./s, 1 ],
-debug => [ qr/Access denied/, 1 ],
},
match_response => {
status => qr/^200$/,
},
request => new HTTP::Request(
GET => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test.txt",
),
},
{
type => "config",
comment => "SecRuleRemoveById (range)",
conf => qq(
SecRuleEngine On
SecDebugLog $ENV{DEBUG_LOG}
SecDebugLogLevel 9
SecRule REQUEST_URI "test" "phase:1,deny,status:500,id:1"
SecRule REQUEST_URI "test" "phase:1,deny,status:500,id:2"
SecRule REQUEST_URI "test" "phase:1,deny,status:500,id:3"
SecRuleRemoveById 1-3
),
match_log => {
-error => [ qr/ModSecurity: /, 1 ],
-audit => [ qr/./, 1 ],
debug => [ qr/Starting phase REQUEST_HEADERS\..*This phase consists of 0 rule.*Starting phase RESPONSE_HEADERS\./s, 1 ],
-debug => [ qr/Access denied/, 1 ],
},
match_response => {
status => qr/^200$/,
},
request => new HTTP::Request(
GET => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test.txt",
),
},
{
type => "config",
comment => "SecRuleRemoveById (multiple + range)",
conf => qq(
SecRuleEngine On
SecDebugLog $ENV{DEBUG_LOG}
SecDebugLogLevel 9
SecRule REQUEST_URI "test" "phase:1,deny,status:500,id:1"
SecRule REQUEST_URI "test" "phase:1,deny,status:500,id:2"
SecRule REQUEST_URI "test" "phase:1,deny,status:500,id:3"
SecRule REQUEST_URI "test" "phase:1,deny,status:500,id:4"
SecRuleRemoveById 1 2-4
),
match_log => {
-error => [ qr/ModSecurity: /, 1 ],
-audit => [ qr/./, 1 ],
debug => [ qr/Starting phase REQUEST_HEADERS\..*This phase consists of 0 rule.*Starting phase RESPONSE_HEADERS\./s, 1 ],
-debug => [ qr/Access denied/, 1 ],
},
match_response => {
status => qr/^200$/,
},
request => new HTTP::Request(
GET => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test.txt",
),
},
# SecRuleRemoveByMsg
{
type => "config",
comment => "SecRuleRemoveByMsg",
conf => qq(
SecRuleEngine On
SecDebugLog $ENV{DEBUG_LOG}
SecDebugLogLevel 9
SecRule REQUEST_URI "test" "phase:1,deny,status:500,id:1,msg:'testing rule'"
SecRuleRemoveByMsg "testing rule"
),
match_log => {
-error => [ qr/ModSecurity: /, 1 ],
-audit => [ qr/./, 1 ],
debug => [ qr/Starting phase REQUEST_HEADERS\..*This phase consists of 0 rule.*Starting phase RESPONSE_HEADERS\./s, 1 ],
-debug => [ qr/Access denied/, 1 ],
},
match_response => {
status => qr/^200$/,
},
request => new HTTP::Request(
GET => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test.txt",
),
},

View File

@@ -0,0 +1,35 @@
### Base configuration for starting Apache httpd
<IfDefine !CHROOT>
# File locations
PidFile @MSC_REGRESSION_LOGS_DIR@/httpd.pid
ScoreBoardFile @MSC_REGRESSION_LOGS_DIR@/httpd.scoreboard
</IfDefine>
<IfModule !mod_proxy.c>
LoadModule proxy_module modules/mod_proxy.so
LoadModule proxy_http_module modules/mod_proxy_http.so
</IfModule>
<IfModule !mod_unique_id.c>
LoadModule unique_id_module modules/mod_unique_id.so
</IfModule>
<IfDefine !NOMODSEC>
# TODO: Need to have these configurable
LoadFile /usr/lib/libxml2.so
LoadFile /usr/lib/liblua5.1.so
LoadModule security2_module modules/mod_security2.so
</IfDefine>
ServerName localhost
LogLevel debug
ErrorLog @MSC_REGRESSION_LOGS_DIR@/error.log
<IfDefine !CHROOT>
DocumentRoot @MSC_REGRESSION_DOCROOT_DIR@
<Directory "@MSC_REGRESSION_DOCROOT_DIR@">
Options Indexes FollowSymLinks
AllowOverride None
</Directory>
</IfDefine>

View File

@@ -0,0 +1,14 @@
-- Test matching Lua Script to just print debug messages
function main()
m.log(1, "Test message.");
m.log(2, "Test message.");
m.log(3, "Test message.");
m.log(4, "Test message.");
m.log(5, "Test message.");
m.log(6, "Test message.");
m.log(7, "Test message.");
m.log(8, "Test message.");
m.log(9, "Test message.");
return "Lua script matched.";
end

View File

@@ -0,0 +1,14 @@
-- Test Lua Script to just print debug messages
function main()
m.log(1, "Test message.");
m.log(2, "Test message.");
m.log(3, "Test message.");
m.log(4, "Test message.");
m.log(5, "Test message.");
m.log(6, "Test message.");
m.log(7, "Test message.");
m.log(8, "Test message.");
m.log(9, "Test message.");
return nil;
end

Binary file not shown.

View File

@@ -0,0 +1 @@
INDEX

View File

@@ -0,0 +1 @@
TEST

View File

@@ -0,0 +1 @@
TEST 2

View File

@@ -0,0 +1,487 @@
### Test basic targets
# ARGS
{
type => "target",
comment => "ARGS (get)",
conf => qq(
SecRuleEngine On
SecRequestBodyAccess On
SecResponseBodyAccess On
SecResponseBodyMimeType null
SecDebugLog $ENV{DEBUG_LOG}
SecDebugLogLevel 9
SecRule ARGS "val1" "phase:2,log,pass"
SecRule ARGS "val2" "phase:2,log,pass"
),
match_log => {
error => [ qr/Pattern match "val1" at ARGS.*Pattern match "val2" at ARGS/s, 1 ],
debug => [ qr/Adding request argument \(QUERY_STRING\): name "arg1", value "val1".*Adding request argument \(QUERY_STRING\): name "arg2", value "val2"/s, 1 ],
},
match_response => {
status => qr/^200$/,
},
request => new HTTP::Request(
GET => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test.txt?arg1=val1&arg2=val2",
),
},
{
type => "target",
comment => "ARGS (post)",
conf => qq(
SecRuleEngine On
SecRequestBodyAccess On
SecResponseBodyAccess On
SecResponseBodyMimeType null
SecDebugLog $ENV{DEBUG_LOG}
SecDebugLogLevel 9
SecRule ARGS "val1" "phase:2,log,pass"
SecRule ARGS "val2" "phase:2,log,pass"
),
match_log => {
error => [ qr/Pattern match "val1" at ARGS.*Pattern match "val2" at ARGS/s, 1 ],
debug => [ qr/Adding request argument \(BODY\): name "arg1", value "val1".*Adding request argument \(BODY\): name "arg2", value "val2"/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",
],
"arg1=val1&arg2=val2",
),
},
# ARGS_COMBINED_SIZE
{
type => "target",
comment => "ARGS_COMBINED_SIZE (get)",
conf => qq(
SecRuleEngine On
SecRequestBodyAccess On
SecResponseBodyAccess On
SecResponseBodyMimeType null
SecRule ARGS_COMBINED_SIZE "\@eq 16" "phase:2,log,pass"
),
match_log => {
error => [ qr/Operator EQ matched 16 at ARGS_COMBINED_SIZE\./s, 1 ],
},
match_response => {
status => qr/^200$/,
},
request => new HTTP::Request(
GET => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test.txt?arg1=val1&arg2=val2",
),
},
{
type => "target",
comment => "ARGS_COMBINED_SIZE (post)",
conf => qq(
SecRuleEngine On
SecRequestBodyAccess On
SecResponseBodyAccess On
SecResponseBodyMimeType null
SecRule ARGS_COMBINED_SIZE "\@eq 16" "phase:2,log,pass"
),
match_log => {
error => [ qr/Operator EQ matched 16 at ARGS_COMBINED_SIZE\./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",
],
"arg1=val1&arg2=val2",
),
},
# ARGS_NAMES
{
type => "target",
comment => "ARGS_NAMES (get)",
conf => qq(
SecRuleEngine On
SecRequestBodyAccess On
SecResponseBodyAccess On
SecResponseBodyMimeType null
SecDebugLog $ENV{DEBUG_LOG}
SecDebugLogLevel 9
SecRule ARGS_NAMES "arg1" "phase:2,log,pass"
SecRule ARGS_NAMES "arg2" "phase:2,log,pass"
),
match_log => {
error => [ qr/Pattern match "arg1" at ARGS.*Pattern match "arg2" at ARGS/s, 1 ],
debug => [ qr/Adding request argument \(QUERY_STRING\): name "arg1", value "val1".*Adding request argument \(QUERY_STRING\): name "arg2", value "val2"/s, 1 ],
},
match_response => {
status => qr/^200$/,
},
request => new HTTP::Request(
GET => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test.txt?arg1=val1&arg2=val2",
),
},
{
type => "target",
comment => "ARGS_NAMES (post)",
conf => qq(
SecRuleEngine On
SecRequestBodyAccess On
SecResponseBodyAccess On
SecResponseBodyMimeType null
SecDebugLog $ENV{DEBUG_LOG}
SecDebugLogLevel 9
SecRule ARGS_NAMES "arg1" "phase:2,log,pass"
SecRule ARGS_NAMES "arg2" "phase:2,log,pass"
),
match_log => {
error => [ qr/Pattern match "arg1" at ARGS_NAMES.*Pattern match "arg2" at ARGS_NAMES/s, 1 ],
debug => [ qr/Adding request argument \(BODY\): name "arg1", value "val1".*Adding request argument \(BODY\): name "arg2", value "val2"/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",
],
"arg1=val1&arg2=val2",
),
},
# ARGS_GET
{
type => "target",
comment => "ARGS_GET (get)",
conf => qq(
SecRuleEngine On
SecRequestBodyAccess On
SecResponseBodyAccess On
SecResponseBodyMimeType null
SecDebugLog $ENV{DEBUG_LOG}
SecDebugLogLevel 9
SecRule ARGS_GET "val1" "phase:2,log,pass"
SecRule ARGS_GET "val2" "phase:2,log,pass"
),
match_log => {
error => [ qr/Pattern match "val1" at ARGS_GET.*Pattern match "val2" at ARGS_GET/s, 1 ],
debug => [ qr/Adding request argument \(QUERY_STRING\): name "arg1", value "val1".*Adding request argument \(QUERY_STRING\): name "arg2", value "val2"/s, 1 ],
},
match_response => {
status => qr/^200$/,
},
request => new HTTP::Request(
GET => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test.txt?arg1=val1&arg2=val2",
),
},
{
type => "target",
comment => "ARGS_GET (post)",
conf => qq(
SecRuleEngine On
SecRequestBodyAccess On
SecResponseBodyAccess On
SecResponseBodyMimeType null
SecDebugLog $ENV{DEBUG_LOG}
SecDebugLogLevel 9
SecRule ARGS_GET "val1" "phase:2,log,pass"
SecRule ARGS_GET "val2" "phase:2,log,pass"
),
match_log => {
-error => [ qr/Pattern match/, 1 ],
debug => [ qr/Adding request argument \(BODY\): name "arg1", value "val1".*Adding request argument \(BODY\): name "arg2", value "val2"/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",
],
"arg1=val1&arg2=val2",
),
},
# ARGS_GET_NAMES
{
type => "target",
comment => "ARGS_GET_NAMES (get)",
conf => qq(
SecRuleEngine On
SecRequestBodyAccess On
SecResponseBodyAccess On
SecResponseBodyMimeType null
SecDebugLog $ENV{DEBUG_LOG}
SecDebugLogLevel 9
SecRule ARGS_GET_NAMES "arg1" "phase:2,log,pass"
SecRule ARGS_GET_NAMES "arg2" "phase:2,log,pass"
),
match_log => {
error => [ qr/Pattern match "arg1" at ARGS_GET.*Pattern match "arg2" at ARGS_GET/s, 1 ],
debug => [ qr/Adding request argument \(QUERY_STRING\): name "arg1", value "val1".*Adding request argument \(QUERY_STRING\): name "arg2", value "val2"/s, 1 ],
},
match_response => {
status => qr/^200$/,
},
request => new HTTP::Request(
GET => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test.txt?arg1=val1&arg2=val2",
),
},
{
type => "target",
comment => "ARGS_GET_NAMES (post)",
conf => qq(
SecRuleEngine On
SecRequestBodyAccess On
SecResponseBodyAccess On
SecResponseBodyMimeType null
SecDebugLog $ENV{DEBUG_LOG}
SecDebugLogLevel 9
SecRule ARGS_GET_NAMES "arg1" "phase:2,log,pass"
SecRule ARGS_GET_NAMES "arg2" "phase:2,log,pass"
),
match_log => {
-error => [ qr/Pattern match/, 1 ],
debug => [ qr/Adding request argument \(BODY\): name "arg1", value "val1".*Adding request argument \(BODY\): name "arg2", value "val2"/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",
],
"arg1=val1&arg2=val2",
),
},
# ARGS_POST
{
type => "target",
comment => "ARGS_POST (get)",
conf => qq(
SecRuleEngine On
SecRequestBodyAccess On
SecResponseBodyAccess On
SecResponseBodyMimeType null
SecDebugLog $ENV{DEBUG_LOG}
SecDebugLogLevel 9
SecRule ARGS_POST "val1" "phase:2,log,pass"
SecRule ARGS_POST "val2" "phase:2,log,pass"
),
match_log => {
-error => [ qr/Pattern match/, 1 ],
debug => [ qr/Adding request argument \(QUERY_STRING\): name "arg1", value "val1".*Adding request argument \(QUERY_STRING\): name "arg2", value "val2"/s, 1 ],
},
match_response => {
status => qr/^200$/,
},
request => new HTTP::Request(
POST => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test.txt?arg1=val1&arg2=val2",
),
},
{
type => "target",
comment => "ARGS_POST (post)",
conf => qq(
SecRuleEngine On
SecRequestBodyAccess On
SecResponseBodyAccess On
SecResponseBodyMimeType null
SecDebugLog $ENV{DEBUG_LOG}
SecDebugLogLevel 9
SecRule ARGS_POST "val1" "phase:2,log,pass"
SecRule ARGS_POST "val2" "phase:2,log,pass"
),
match_log => {
error => [ qr/Pattern match "val1" at ARGS_POST.*Pattern match "val2" at ARGS_POST/s, 1 ],
debug => [ qr/Adding request argument \(BODY\): name "arg1", value "val1".*Adding request argument \(BODY\): name "arg2", value "val2"/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",
],
"arg1=val1&arg2=val2",
),
},
# ARGS_POST_NAMES
{
type => "target",
comment => "ARGS_POST_NAMES (get)",
conf => qq(
SecRuleEngine On
SecRequestBodyAccess On
SecResponseBodyAccess On
SecResponseBodyMimeType null
SecDebugLog $ENV{DEBUG_LOG}
SecDebugLogLevel 9
SecRule ARGS_POST_NAMES "arg1" "phase:2,log,pass"
SecRule ARGS_POST_NAMES "arg2" "phase:2,log,pass"
),
match_log => {
-error => [ qr/Pattern match/, 1 ],
debug => [ qr/Adding request argument \(QUERY_STRING\): name "arg1", value "val1".*Adding request argument \(QUERY_STRING\): name "arg2", value "val2"/s, 1 ],
},
match_response => {
status => qr/^200$/,
},
request => new HTTP::Request(
GET => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test.txt?arg1=val1&arg2=val2",
),
},
{
type => "target",
comment => "ARGS_POST_NAMES (post)",
conf => qq(
SecRuleEngine On
SecRequestBodyAccess On
SecResponseBodyAccess On
SecResponseBodyMimeType null
SecDebugLog $ENV{DEBUG_LOG}
SecDebugLogLevel 9
SecRule ARGS_POST_NAMES "arg1" "phase:2,log,pass"
SecRule ARGS_POST_NAMES "arg2" "phase:2,log,pass"
),
match_log => {
error => [ qr/Pattern match "arg1" at ARGS_POST.*Pattern match "arg2" at ARGS_POST/s, 1 ],
debug => [ qr/Adding request argument \(BODY\): name "arg1", value "val1".*Adding request argument \(BODY\): name "arg2", value "val2"/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",
],
"arg1=val1&arg2=val2",
),
},
# AUTH_TYPE
#{
# type => "target",
# comment => "AUTH_TYPE",
# conf => qq(
# <IfVersion >= 2.2>
# <IfModule !mod_authn_file.c>
# LoadModule authn_file_module modules/mod_authn_file.so
# </IfModule>
# </IfVersion>
## <IfVersion ~ ^2.0.>
## <IfModule !mod_auth.c>
## LoadModule auth_module modules/mod_auth.so
## </IfModule>
## </IfVersion>
# <Location />
# AuthType Basic
# AuthName Test
# AuthUserFile "$ENV{CONF_DIR}/htpasswd"
# Require user nobody
# </Location>
# 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
# TODO: FILES_NAMES
# TODO: FILES_SIZES
# TODO: FILES_TMPNAMES
# TODO: GEO
# TODO: HIGHEST_SEVERITY
# TODO: MATCHED_VAR
# TODO: MATCHED_VAR_NAME
# TODO: MODSEC_BUILD
# TODO: MULTIPART_CRLF_LF_LINES
# TODO: MULTIPART_STRICT_ERROR
# TODO: MULTIPART_UNMATCHED_BOUNDARY
# TODO: PATH_INFO
# TODO: QUERY_STRING
# TODO: REMOTE_ADDR
# TODO: REMOTE_HOST
# TODO: REMOTE_PORT
# TODO: REMOTE_USER
# TODO: REQBODY_PROCESSOR
# TODO: REQBODY_PROCESSOR_ERROR
# TODO: REQBODY_PROCESSOR_ERROR_MSG
# TODO: REQUEST_BASENAME
# TODO: REQUEST_BODY
# TODO: REQUEST_COOKIES
# TODO: REQUEST_COOKIES_NAMES
# TODO: REQUEST_FILENAME
# TODO: REQUEST_HEADERS
# TODO: REQUEST_HEADERS_NAMES
# TODO: REQUEST_LINE
# TODO: REQUEST_METHOD
# TODO: REQUEST_PROTOCOL
# TODO: REQUEST_URI
# TODO: REQUEST_URI_RAW
# TODO: RESPONSE_BODY
# TODO: RESPONSE_CONTENT_LENGTH
# TODO: RESPONSE_CONTENT_TYPE
# TODO: RESPONSE_HEADERS
# TODO: RESPONSE_HEADERS_NAMES
# TODO: RESPONSE_PROTOCOL
# TODO: RESPONSE_STATUS
# TODO: RULE
# TODO: SCRIPT_BASENAME
# TODO: SCRIPT_FILENAME
# TODO: SCRIPT_GID
# TODO: SCRIPT_GROUPNAME
# TODO: SCRIPT_MODE
# TODO: SCRIPT_UID
# TODO: SCRIPT_USERNAME
# TODO: SERVER_ADDR
# TODO: SERVER_NAME
# TODO: SERVER_PORT
# TODO: SESSION
# TODO: SESSIONID
# TODO: TIME
# TODO: TIME_DAY
# TODO: TIME_EPOCH
# TODO: TIME_HOUR
# TODO: TIME_MIN
# TODO: TIME_MON
# TODO: TIME_SEC
# TODO: TIME_WDAY
# TODO: TIME_YEAR
# TODO: TX
# TODO: USERID
# TODO: WEBAPPID
# TODO: WEBSERVER_ERROR_LOG
# TODO: XML