Skip to content

Commit 395305a

Browse files
defanatorFelipe Zimmerle
authored and
Felipe Zimmerle
committed
Tests: added proxy tests for HTTP/2
1 parent 5096a7a commit 395305a

File tree

1 file changed

+288
-0
lines changed

1 file changed

+288
-0
lines changed

tests/modsecurity-proxy-h2.t

Lines changed: 288 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,288 @@
1+
#!/usr/bin/perl
2+
3+
# (C) Andrei Belov
4+
5+
# Tests for ModSecurity over the http proxy module (HTTP/2).
6+
7+
###############################################################################
8+
9+
use warnings;
10+
use strict;
11+
12+
use Test::More;
13+
14+
BEGIN { use FindBin; chdir($FindBin::Bin); }
15+
16+
use lib 'lib';
17+
use Test::Nginx;
18+
use Test::Nginx::HTTP2;
19+
20+
###############################################################################
21+
22+
select STDERR; $| = 1;
23+
select STDOUT; $| = 1;
24+
25+
my $t = Test::Nginx->new()->has(qw/http http_v2 proxy/)->plan(23);
26+
27+
$t->write_file_expand('nginx.conf', <<'EOF');
28+
29+
%%TEST_GLOBALS%%
30+
31+
daemon off;
32+
33+
events {
34+
}
35+
36+
http {
37+
%%TEST_GLOBALS_HTTP%%
38+
39+
server {
40+
listen 127.0.0.1:8080 http2;
41+
server_name localhost;
42+
43+
location / {
44+
proxy_pass http://127.0.0.1:8081;
45+
proxy_read_timeout 1s;
46+
}
47+
48+
location /phase1 {
49+
modsecurity on;
50+
modsecurity_rules '
51+
SecRuleEngine On
52+
SecDefaultAction "phase:1,log,deny,status:403"
53+
SecRule ARGS "@streq redirect301" "id:1,phase:1,status:301,redirect:http://www.modsecurity.org"
54+
SecRule ARGS "@streq redirect302" "id:2,phase:1,status:302,redirect:http://www.modsecurity.org"
55+
SecRule ARGS "@streq block401" "id:3,phase:1,status:401,block"
56+
SecRule ARGS "@streq block403" "id:4,phase:1,status:403,block"
57+
';
58+
proxy_pass http://127.0.0.1:8081;
59+
proxy_read_timeout 1s;
60+
}
61+
location /phase2 {
62+
modsecurity on;
63+
modsecurity_rules '
64+
SecRuleEngine On
65+
SecDefaultAction "phase:2,log,deny,status:403"
66+
SecRule ARGS "@streq redirect301" "id:1,phase:2,status:301,redirect:http://www.modsecurity.org"
67+
SecRule ARGS "@streq redirect302" "id:2,phase:2,status:302,redirect:http://www.modsecurity.org"
68+
SecRule ARGS "@streq block401" "id:3,phase:2,status:401,block"
69+
SecRule ARGS "@streq block403" "id:4,phase:2,status:403,block"
70+
';
71+
proxy_pass http://127.0.0.1:8081;
72+
proxy_read_timeout 1s;
73+
}
74+
location /phase3 {
75+
modsecurity on;
76+
modsecurity_rules '
77+
SecRuleEngine On
78+
SecDefaultAction "phase:3,log,deny,status:403"
79+
SecRule ARGS "@streq redirect301" "id:1,phase:3,status:301,redirect:http://www.modsecurity.org"
80+
SecRule ARGS "@streq redirect302" "id:2,phase:3,status:302,redirect:http://www.modsecurity.org"
81+
SecRule ARGS "@streq block401" "id:3,phase:3,status:401,block"
82+
SecRule ARGS "@streq block403" "id:4,phase:3,status:403,block"
83+
';
84+
proxy_pass http://127.0.0.1:8081;
85+
proxy_read_timeout 1s;
86+
}
87+
location /phase4 {
88+
modsecurity on;
89+
modsecurity_rules '
90+
SecRuleEngine On
91+
SecResponseBodyAccess On
92+
SecDefaultAction "phase:4,log,deny,status:403"
93+
SecRule ARGS "@streq redirect301" "id:1,phase:4,status:301,redirect:http://www.modsecurity.org"
94+
SecRule ARGS "@streq redirect302" "id:2,phase:4,status:302,redirect:http://www.modsecurity.org"
95+
SecRule ARGS "@streq block401" "id:3,phase:4,status:401,block"
96+
SecRule ARGS "@streq block403" "id:4,phase:4,status:403,block"
97+
';
98+
proxy_pass http://127.0.0.1:8081;
99+
proxy_read_timeout 1s;
100+
}
101+
}
102+
}
103+
104+
EOF
105+
106+
$t->todo_alerts();
107+
$t->run_daemon(\&http_daemon);
108+
$t->run()->waitforsocket('127.0.0.1:' . port(8081));
109+
110+
###############################################################################
111+
112+
my ($phase, $s, $sid, $frames, $frame);
113+
114+
$s = Test::Nginx::HTTP2->new();
115+
$sid = $s->new_stream({ path => '/' });
116+
$frames = $s->read(all => [{ sid => $sid, fin => 1 }]);
117+
($frame) = grep { $_->{type} eq "DATA" } @$frames;
118+
like($frame->{data}, qr/SEE-THIS/, "proxy request");
119+
120+
$s = Test::Nginx::HTTP2->new();
121+
$sid = $s->new_stream({ path => '/multi' });
122+
$frames = $s->read(all => [{ sid => $sid, fin => 1 }]);
123+
($frame) = grep { $_->{type} eq "DATA" } @$frames;
124+
like($frame->{data}, qr/AND-THIS/, "proxy request with multiple packets");
125+
126+
$s = Test::Nginx::HTTP2->new();
127+
$sid = $s->new_stream({ path => '/', method => 'HEAD' });
128+
$frames = $s->read(all => [{ sid => $sid, fin => 1 }]);
129+
($frame) = grep { $_->{type} eq "DATA" } @$frames;
130+
unlike($frame->{data}, qr/SEE-THIS/, "proxy head request");
131+
132+
# Redirect (302)
133+
134+
for $phase (1 .. 3) {
135+
$s = Test::Nginx::HTTP2->new();
136+
$sid = $s->new_stream({ path => "/phase${phase}?what=redirect302" });
137+
$frames = $s->read(all => [{ sid => $sid, fin => 1 }]);
138+
($frame) = grep { $_->{type} eq "HEADERS" } @$frames;
139+
is($frame->{headers}->{':status'}, 302, "redirect 302 - phase ${phase}");
140+
}
141+
142+
SKIP: {
143+
skip 'long test', 1 unless $ENV{TEST_NGINX_UNSAFE};
144+
145+
$s = Test::Nginx::HTTP2->new();
146+
$sid = $s->new_stream({ path => '/phase4?what=redirect302' });
147+
$frames = $s->read(all => [{ sid => $sid, fin => 1 }]);
148+
($frame) = grep { $_->{type} eq "DATA" } @$frames;
149+
is($frame, undef, 'redirect 302 - phase 4');
150+
}
151+
152+
# Redirect (301)
153+
154+
for $phase (1 .. 3) {
155+
$s = Test::Nginx::HTTP2->new();
156+
$sid = $s->new_stream({ path => "/phase${phase}?what=redirect301" });
157+
$frames = $s->read(all => [{ sid => $sid, fin => 1 }]);
158+
($frame) = grep { $_->{type} eq "HEADERS" } @$frames;
159+
is($frame->{headers}->{':status'}, 301, "redirect 301 - phase ${phase}");
160+
}
161+
162+
SKIP: {
163+
skip 'long test', 1 unless $ENV{TEST_NGINX_UNSAFE};
164+
165+
$s = Test::Nginx::HTTP2->new();
166+
$sid = $s->new_stream({ path => '/phase4?what=redirect301' });
167+
$frames = $s->read(all => [{ sid => $sid, fin => 1 }]);
168+
($frame) = grep { $_->{type} eq "DATA" } @$frames;
169+
is($frame, undef, 'redirect 301 - phase 4');
170+
}
171+
172+
# Block (401)
173+
174+
for $phase (1 .. 3) {
175+
$s = Test::Nginx::HTTP2->new();
176+
$sid = $s->new_stream({ path => "/phase${phase}?what=block401" });
177+
$frames = $s->read(all => [{ sid => $sid, fin => 1 }]);
178+
($frame) = grep { $_->{type} eq "HEADERS" } @$frames;
179+
is($frame->{headers}->{':status'}, 401, "block 401 - phase ${phase}");
180+
}
181+
182+
SKIP: {
183+
skip 'long test', 1 unless $ENV{TEST_NGINX_UNSAFE};
184+
185+
$s = Test::Nginx::HTTP2->new();
186+
$sid = $s->new_stream({ path => '/phase4?what=block401' });
187+
$frames = $s->read(all => [{ sid => $sid, fin => 1 }]);
188+
($frame) = grep { $_->{type} eq "DATA" } @$frames;
189+
is($frame, undef, 'block 401 - phase 4');
190+
}
191+
192+
193+
# Block (403)
194+
195+
for $phase (1 .. 3) {
196+
$s = Test::Nginx::HTTP2->new();
197+
$sid = $s->new_stream({ path => "/phase${phase}?what=block403" });
198+
$frames = $s->read(all => [{ sid => $sid, fin => 1 }]);
199+
($frame) = grep { $_->{type} eq "HEADERS" } @$frames;
200+
is($frame->{headers}->{':status'}, 403, "block 403 - phase ${phase}");
201+
}
202+
203+
SKIP: {
204+
skip 'long test', 1 unless $ENV{TEST_NGINX_UNSAFE};
205+
206+
$s = Test::Nginx::HTTP2->new();
207+
$sid = $s->new_stream({ path => '/phase4?what=block403' });
208+
$frames = $s->read(all => [{ sid => $sid, fin => 1 }]);
209+
($frame) = grep { $_->{type} eq "DATA" } @$frames;
210+
is($frame, undef, 'block 403 - phase 4');
211+
}
212+
213+
# Nothing to detect
214+
#like(http_get('/phase1?what=nothing'), qr/phase1\?what=nothing\' not found/, 'nothing phase 1');
215+
#like(http_get('/phase2?what=nothing'), qr/phase2\?what=nothing\' not found/, 'nothing phase 2');
216+
#like(http_get('/phase3?what=nothing'), qr/phase3\?what=nothing\' not found/, 'nothing phase 3');
217+
#like(http_get('/phase4?what=nothing'), qr/phase4\?what=nothing\' not found/, 'nothing phase 4');
218+
219+
for $phase (1 .. 4) {
220+
$s = Test::Nginx::HTTP2->new();
221+
$sid = $s->new_stream({ path => "/phase${phase}?what=nothing" });
222+
$frames = $s->read(all => [{ sid => $sid, fin => 1 }]);
223+
($frame) = grep { $_->{type} eq "DATA" } @$frames;
224+
like($frame->{data}, qr/phase${phase}\?what=nothing\' not found/, "nothing phase ${phase}");
225+
}
226+
227+
###############################################################################
228+
229+
sub http_daemon {
230+
my $server = IO::Socket::INET->new(
231+
Proto => 'tcp',
232+
LocalHost => '127.0.0.1:' . port(8081),
233+
Listen => 5,
234+
Reuse => 1
235+
)
236+
or die "Can't create listening socket: $!\n";
237+
238+
local $SIG{PIPE} = 'IGNORE';
239+
240+
while (my $client = $server->accept()) {
241+
$client->autoflush(1);
242+
243+
my $headers = '';
244+
my $uri = '';
245+
246+
while (<$client>) {
247+
$headers .= $_;
248+
last if (/^\x0d?\x0a?$/);
249+
}
250+
251+
$uri = $1 if $headers =~ /^\S+\s+([^ ]+)\s+HTTP/i;
252+
253+
if ($uri eq '/') {
254+
print $client <<'EOF';
255+
HTTP/1.1 200 OK
256+
Connection: close
257+
258+
EOF
259+
print $client "TEST-OK-IF-YOU-SEE-THIS"
260+
unless $headers =~ /^HEAD/i;
261+
262+
} elsif ($uri eq '/multi') {
263+
264+
print $client <<"EOF";
265+
HTTP/1.1 200 OK
266+
Connection: close
267+
268+
TEST-OK-IF-YOU-SEE-THIS
269+
EOF
270+
271+
select undef, undef, undef, 0.1;
272+
print $client 'AND-THIS';
273+
274+
} else {
275+
276+
print $client <<"EOF";
277+
HTTP/1.1 404 Not Found
278+
Connection: close
279+
280+
Oops, '$uri' not found
281+
EOF
282+
}
283+
284+
close $client;
285+
}
286+
}
287+
288+
###############################################################################

0 commit comments

Comments
 (0)