### 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,id:500215"
		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,id:500217"
		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,id:500219"
		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,id:500221"
		SecRule ARGS:b "@streq 2" "phase:2,deny,id:500222"
	),
	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,id:500223"
		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,id:500225"
		SecRule ARGS:b "\@streq 2" "phase:2,deny,id:500226"
	),
	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",
	),
},
{
	type => "config",
	comment => "SecRequestBodyLimit (equal - chunked)",
	conf => qq(
		SecRuleEngine On
		SecRequestBodyAccess On
		SecRequestBodyLimit 276
	),
	match_log => {
		-error => [ qr/Request body is larger than the configured limit/, 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 => "SecRequestBodyLimit (greater - chunked)",
	conf => qq(
		SecRuleEngine On
		SecRequestBodyAccess On
		SecRequestBodyLimit 256
	),
	match_log => {
		error => [ qr/Request body .*is larger than the configured limit \(256\)\./, 1 ],
	},
	match_response => {
		status => qr/^413$/,
	},
	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 => "SecRequestBodyLimit (ctl:ruleEngine=off)",
	conf => qq(
		SecRuleEngine On
		SecRequestBodyAccess On
		SecRequestBodyLimit 5

		SecAction "phase:1,pass,nolog,ctl:ruleEngine=off,id:500081"
		SecRule REQUEST_BODY "." "phase:2,deny,id:500227"
	),
	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 (ctl:requestBodyAccess=off)",
	conf => qq(
		SecRuleEngine On
		SecRequestBodyAccess On
		SecRequestBodyLimit 5

		SecAction "phase:1,pass,nolog,ctl:requestBodyAccess=off,id:500082"
		SecRule REQUEST_BODY "." "phase:2,deny,id:500228"
	),
	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 (ctl:ruleEngine=off - chunked)",
	conf => qq(
		SecRuleEngine On
		SecRequestBodyAccess On
		SecRequestBodyLimit 256

		SecAction "phase:1,pass,nolog,ctl:ruleEngine=off,id:500083"
		SecRule REQUEST_BODY "." "phase:2,deny,id:500229"
	),
	match_log => {
		-error => [ qr/Request body .*is larger than the configured limit/, 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 => "SecRequestBodyLimit (ctl:requestBodyAccess=off - chunked)",
	conf => qq(
		SecRuleEngine On
		SecRequestBodyAccess On
		SecRequestBodyLimit 256

		SecAction "phase:1,pass,nolog,ctl:requestBodyAccess=off,id:500084"
		SecRule REQUEST_BODY "." "phase:2,deny,id:500230"
	),
	match_log => {
		-error => [ qr/Request body .*is larger than the configured limit \(256\)\./, 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
	),
},

# 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
	),
},
{
	type => "config",
	comment => "SecRequestBodyLimitAction Reject (multipart/greater - chunked)",
	conf => qq(
		SecRuleEngine On
		SecDebugLog $ENV{DEBUG_LOG}
		SecDebugLogLevel 9
		SecRequestBodyAccess On
		SecRequestBodyLimitAction Reject
		SecRequestBodyLimit 20
	),
	match_log => {
		debug => [ qr/Request body is larger than the configured limit \(20\).. Deny with code \(413\)/, 1 ],
	},
	match_response => {
		status => qr/^413$/,
	},
	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 => "SecRequestBodyLimitAction Reject (plain/greater)",
	conf => qq(
		SecRuleEngine On
		SecDebugLog $ENV{DEBUG_LOG}
		SecDebugLogLevel 9
		SecRequestBodyAccess On
		SecRequestBodyLimitAction Reject
		SecRequestBodyLimit 131072
	),
	match_log => {
		-debug => [ qr/Request body is larger than the configured limit \(131072\).. Deny with code \(413\)/, 1 ],
	},
	match_response => {
		status => qr/^413$/,
	},
	request => new HTTP::Request(
		POST => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test.txt",
		[
			"Content-Type" => "application/json",
		],
		normalize_raw_request_data(
			q(
				{
					) . "'abcdefghijlmnopq'='abcdefghijlmnopqrstuvxz',\\n" x 99000 . q(
				},
			),
		),
	),
},


{
	type => "config",
	comment => "SecRequestBodyLimitAction ProcessPartial (multipart/greater - chunked)",
	conf => qq(
		SecRuleEngine On
		SecDebugLog $ENV{DEBUG_LOG}
		SecDebugLogLevel 9
		SecRequestBodyAccess On
		SecRequestBodyLimitAction ProcessPartial
		SecRequestBodyLimit 131072
	),
	match_log => {
		-debug => [ qr/Request body is larger than the configured limit/, 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) . "a" x 131072 . q(
				-----------------------------69343412719991675451336310646
				Content-Disposition: form-data; name="b"

				2) . "b" x 131072 . q(
				-----------------------------69343412719991675451336310646--
			)
		),
		131072*3
	),
},
# Known issue on nginx, disable it for now.
#{
#	type => "config",
#	comment => "SecRequestBodyLimitAction ProcessPartial (plain/greater)",
#	conf => qq(
#		SecRuleEngine On
#		SecDebugLog $ENV{DEBUG_LOG}
#		SecDebugLogLevel 9
#		SecRequestBodyAccess On
#		SecRequestBodyLimitAction ProcessPartial
#		SecRequestBodyLimit 131072
#	),
#	match_log => {
#		-debug => [ 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/json",
#		],
#		normalize_raw_request_data(
#			q(
#				{
#					) . "'abcdefghijlmnopq'='abcdefghijlmnopqrstuvxz',\\n" x 99000 . q(
#				},
#			),
#		),
#	),
#},





# 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,id:500231"
		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,id:500234"
		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,
	),
},

# SecArgumentsLimit
{
	type => "config",
	comment => "SecArgumentsLimit (pos)",
	conf => qq(
		SecRuleEngine On
		SecRequestBodyAccess On
		SecArgumentsLimit 5
		SecRule REQBODY_ERROR "!\@eq 0" "id:'500232',phase:2,log,deny,status:403,msg:'Failed to parse request body'"
	),
	match_log => {
		error => [ qr/Access denied with code 403 /, 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&c=3&d=4&e=5&f=6",
	),
},
{
	type => "config",
	comment => "SecArgumentsLimit (neg)",
	conf => qq(
		SecRuleEngine On
		SecRequestBodyAccess On
		SecArgumentsLimit 5
		SecRule REQBODY_ERROR "!\@eq 0" "id:'500233',phase:2,log,deny,status:403,msg:'Failed to parse request body'"
	),
	match_log => {
	},
	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&c=3&d=4&e=5",
	),
},

