### 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,id:500189" SecRule ARGS "val2" "phase:2,log,pass,id:500190" ), 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,id:500191" SecRule ARGS "val2" "phase:2,log,pass,id:500192" ), 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,id:500193" ), 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,id:500194" ), 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,id:500195" SecRule ARGS_NAMES "arg2" "phase:2,log,pass,id:500196" ), 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,id:500197" SecRule ARGS_NAMES "arg2" "phase:2,log,pass,id:500198" ), 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,id:500199" SecRule ARGS_GET "val2" "phase:2,log,pass,id:500200" ), 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,id:500201" SecRule ARGS_GET "val2" "phase:2,log,pass,id:500202" ), 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,id:500203" SecRule ARGS_GET_NAMES "arg2" "phase:2,log,pass,id:500204" ), 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,id:500205" SecRule ARGS_GET_NAMES "arg2" "phase:2,log,pass,id:500206" ), 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,id:500207" SecRule ARGS_POST "val2" "phase:2,log,pass,id:500208" ), 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", [ "Content-Type" => "application/x-www-form-urlencoded", ], "a=b", ), }, { 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,id:500209" SecRule ARGS_POST "val2" "phase:2,log,pass,id:500210" ), 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,id:500211" SecRule ARGS_POST_NAMES "arg2" "phase:2,log,pass,id:500212" ), 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,id:500213" SecRule ARGS_POST_NAMES "arg2" "phase:2,log,pass,id:500214" ), 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", ), }, # FULL_REQUEST { type => "target", comment => "FULL_REQUEST (get)", conf => qq( SecRuleEngine On SecRequestBodyAccess On SecResponseBodyAccess On SecDebugLog $ENV{DEBUG_LOG} SecDebugLogLevel 9 SecRule FULL_REQUEST "arg1" "phase:4,log,pass,id:500211" SecRule FULL_REQUEST "arg2" "phase:4,log,pass,id:500212" ), match_log => { error => [ qr/Pattern match "arg1" at FULL_REQUEST.*Pattern match "arg2" at FULL_REQUEST/s, 1 ], debug => [ qr/against FULL_REQUEST.*Target value: "GET \/test.txt\?arg1=val1\&arg2=val2 HTTP\/1.1\\n\\nTE: deflate,gzip;q=0.3\\nConnection: TE, close\\nHost: localhost:[0-9]+\\nUser-Agent: ModSecurity Regression Tests\/1.2.3\\n\\n\\x00"/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 => "FULL_REQUEST (post)", conf => qq( SecRuleEngine On SecRequestBodyAccess On SecResponseBodyAccess On SecDebugLog $ENV{DEBUG_LOG} SecDebugLogLevel 9 SecRule FULL_REQUEST "arg1" "phase:4,log,pass,id:500213" SecRule FULL_REQUEST "arg2" "phase:4,log,pass,id:500214" ), match_log => { error => [ qr/Pattern match "arg1" at FULL_REQUEST.*Pattern match "arg2" at FULL_REQUEST/s, 1 ], debug => [ qr/against FULL_REQUEST.*Target value: "POST \/test.txt HTTP\/1.1\\n\\nTE: deflate,gzip;q=0.3\\nConnection: TE, close\\nHost: localhost:[0-9]+\\nUser-Agent: ModSecurity Regression Tests\/1.2.3\\nContent-Type: application\/x-www-form-urlencoded\\nContent-Length: 19\\n\\narg1=val1&arg2=val2\\x00"/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", ), }, # FULL_REQUEST_LENGTH { type => "target", comment => "FULL_REQUEST_LENGTH (get)", conf => qq( SecRuleEngine On SecRequestBodyAccess On SecResponseBodyAccess On SecDebugLog $ENV{DEBUG_LOG} SecDebugLogLevel 9 SecRule FULL_REQUEST_LENGTH "\@eq 1" "phase:4,log,pass,id:500211" SecRule FULL_REQUEST_LENGTH "\@eq 115" "phase:4,log,pass,id:500212" ), match_log => { error => [ qr/Operator EQ matched 115 at FULL_REQUEST_LENGTH./s, 1 ], debug => [ qr/against FULL_REQUEST_LENGTH.*Target value: "115"/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 => "FULL_REQUEST_LENGTH (post)", conf => qq( SecRuleEngine On SecRequestBodyAccess On SecResponseBodyAccess On SecDebugLog $ENV{DEBUG_LOG} SecDebugLogLevel 9 SecRule FULL_REQUEST_LENGTH "\@eq 1" "phase:4,log,pass,id:500213" SecRule FULL_REQUEST_LENGTH "\@eq 201" "phase:4,log,pass,id:500214" ), match_log => { error => [ qr/Operator EQ matched 201 at FULL_REQUEST_LENGTH./s, 1 ], debug => [ qr/against FULL_REQUEST_LENGTH.*Target value: "201"/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( # = 2.2> # # LoadModule authn_file_module modules/mod_authn_file.so # # ## ## ## LoadModule auth_module modules/mod_auth.so ## ## # # AuthType Basic # AuthName Test # AuthUserFile "$ENV{CONF_DIR}/htpasswd" # Require user nobody # # SecRuleEngine On # SecRequestBodyAccess On # SecResponseBodyAccess On # SecResponseBodyMimeType null ## SecDebugLog $ENV{DEBUG_LOG} ## SecDebugLogLevel 9 # SecRule REQUEST_HEADERS:Authorization "Basic (.*)" "phase:2,log,pass,capture,chain" # SecRule TX:1 "nobody:test" "t:none,t:base64Decode,chain" # SecRule AUTH_TYPE "Basic" # ), # match_log => { # error => [ qr/Pattern match "Basic" at AUTH_TYPE/s, 1 ], # }, # match_response => { # status => qr/^200$/, # }, # request => new HTTP::Request( # GET => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test.txt", # [ # "Authorization" => "Basic bm9ib2R5OnRlc3Q=" # ], # ), #}, ## ENH: We cannot include this test as we cannot distribute the database. ## Instead we should create a simple test DB of our own. ## GEO #{ # type => "target", # comment => "GEO (ip)", # conf => qq( # SecRuleEngine On # SecDebugLog $ENV{DEBUG_LOG} # SecDebugLogLevel 9 # SecGeoLookupDB GeoLiteCity.dat # SecRule ARGS:ip "\@geoLookup" "phase:2,log,pass,t:none" # SecRule GEO:COUNTRY_CODE "\@streq US" "phase:2,log,pass,t:none" # SecRule GEO:COUNTRY_CODE3 "\@streq USA" "phase:2,log,pass,t:none" # SecRule GEO:COUNTRY_NAME "\@streq United States" "phase:2,log,pass,t:none" # # ENH: Not in this database? # SecRule GEO:COUNTRY_CONTINENT "\@streq NA" "phase:2,log,pass,t:none" # SecRule GEO:REGION "\@streq CA" "phase:2,log,pass,t:none" # SecRule GEO:CITY "\@streq San Diego" "phase:2,log,pass,t:none" # SecRule GEO:POSTAL_CODE "\@streq 92123" "phase:2,log,pass,t:none" # SecRule GEO:LATITUDE "\@beginsWith 32.8" "phase:2,log,pass,t:none" # SecRule GEO:LONGITUDE "\@beginsWith 117.1" "phase:2,log,pass,t:none" # SecRule GEO:DMA_CODE "\@streq 825" "phase:2,log,pass,t:none" # SecRule GEO:AREA_CODE "\@streq 858" "phase:2,log,pass,t:none" # ), # match_log => { # debug => [ qr/Geo lookup for "216.75.21.122" succeeded.*match "US" at GEO:COUNTRY_CODE.*match "USA" at GEO:COUNTRY_CODE3.*match "United States" at GEO:COUNTRY_NAME.*match "NA" at GEO:COUNTRY_CONTINENT.*match "CA" at GEO:REGION.*match "San Diego" at GEO:CITY.*match "92123" at GEO:POSTAL_CODE.*match "32.8" at GEO:LATITUDE.*match "825" at GEO:DMA_CODE.*match "858" at GEO:AREA_CODE/si, 1 ], # }, # match_response => { # status => qr/^200$/, # }, # request => new HTTP::Request( # GET => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test.txt?ip=216.75.21.122", # ), #}, #{ # type => "target", # comment => "GEO (host)", # conf => qq( # SecRuleEngine On # SecDebugLog $ENV{DEBUG_LOG} # SecDebugLogLevel 9 # SecGeoLookupDB GeoLiteCity.dat # SecRule ARGS:host "\@geoLookup" "phase:2,log,pass,t:none" # SecRule GEO:COUNTRY_CODE "\@streq US" "phase:2,log,pass,t:none" # SecRule GEO:COUNTRY_CODE3 "\@streq USA" "phase:2,log,pass,t:none" # SecRule GEO:COUNTRY_NAME "\@streq United States" "phase:2,log,pass,t:none" # # ENH: Not in this database? # SecRule GEO:COUNTRY_CONTINENT "\@streq NA" "phase:2,log,pass,t:none" # SecRule GEO:REGION "\@streq CA" "phase:2,log,pass,t:none" # SecRule GEO:CITY "\@streq San Diego" "phase:2,log,pass,t:none" # SecRule GEO:POSTAL_CODE "\@streq 92123" "phase:2,log,pass,t:none" # SecRule GEO:LATITUDE "\@beginsWith 32.8" "phase:2,log,pass,t:none" # SecRule GEO:LONGITUDE "\@beginsWith 117.1" "phase:2,log,pass,t:none" # SecRule GEO:DMA_CODE "\@streq 825" "phase:2,log,pass,t:none" # SecRule GEO:AREA_CODE "\@streq 858" "phase:2,log,pass,t:none" # ), # match_log => { # debug => [ qr/Using address "\d+\.\d+\.\d+\.\d+".*Geo lookup for "www\.modsecurity\.org" succeeded.*match "US" at GEO:COUNTRY_CODE.*match "USA" at GEO:COUNTRY_CODE3.*match "United States" at GEO:COUNTRY_NAME.*match "NA" at GEO:COUNTRY_CONTINENT.*match "CA" at GEO:REGION.*match "San Diego" at GEO:CITY.*match "92123" at GEO:POSTAL_CODE.*match "32.8" at GEO:LATITUDE.*match "825" at GEO:DMA_CODE.*match "858" at GEO:AREA_CODE/si, 1 ], # }, # match_response => { # status => qr/^200$/, # }, # request => new HTTP::Request( # GET => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test.txt?host=www.modsecurity.org", # ), #}, #{ # type => "target", # comment => "GEO (failed lookup)", # conf => qq( # SecRuleEngine On # SecDebugLog $ENV{DEBUG_LOG} # SecDebugLogLevel 9 # SecGeoLookupDB GeoLiteCity.dat # SecRule ARGS:ip "\@geoLookup" "phase:2,log,pass,t:none" # SecRule \&GEO "\@eq 0" "phase:2,log,deny,status:403,t:none" # SecRule ARGS:badip "\@geoLookup" "phase:2,log,pass,t:none" # SecRule \&GEO "!\@eq 0" "phase:2,log,deny,status:403,t:none" # ), # match_log => { # -debug => [ qr/Geo lookup for "127\.0\.0\.1" succeeded/si, 1 ], # }, # match_response => { # status => qr/^200$/, # }, # request => new HTTP::Request( # GET => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test.txt?ip=216.75.21.122&badip=127.0.0.1", # ), #}, # TODO: ENV # TODO: FILES # TODO: FILES_COMBINED_SIZE # TODO: FILES_NAMES # TODO: FILES_SIZES # TODO: FILES_TMPNAMES # 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