Skip to content

Commit a025761

Browse files
committed
Added: more tests.
Fixed: issue with the in_array match type. Changed: key parameter matching to its own property.
1 parent ede9f98 commit a025761

4 files changed

Lines changed: 231 additions & 11 deletions

File tree

src/Processor.php

Lines changed: 16 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -140,6 +140,11 @@ public function launch($mustExit = true)
140140
// Merge the rules together. First iterate through the whitelist rules.
141141
$rules = array_merge($this->whitelistRules, $this->firewallRules);
142142
foreach ($rules as $rule) {
143+
// Should never happen.
144+
if (!isset($rule->rules) || empty($rule->rules)) {
145+
continue;
146+
}
147+
143148
// Execute the firewall rule.
144149
$rule_hit = $this->executeFirewall(json_decode(json_encode($rule->rules), true));
145150

@@ -194,10 +199,15 @@ public function executeFirewall($rules)
194199
$inclusiveHits = 0;
195200

196201
// Loop through all of the conditions for this rule.
197-
foreach ($rules as $key => $rule) {
202+
foreach ($rules as $rule) {
203+
// Parameter must always be present.
204+
if (!isset($rule['parameter'])) {
205+
continue;
206+
}
207+
198208
// Extract the value of the paramater that we want.
199-
$value = $this->getParameterValue($key);
200-
if (is_null($value) && !is_numeric($key)) {
209+
$value = $this->getParameterValue($rule['parameter']);
210+
if (is_null($value)) {
201211
continue;
202212
}
203213

@@ -315,11 +325,11 @@ public function matchParameterValue($match, $value)
315325
return @!current_user_can($matchValue);
316326
}
317327

318-
if ($matchType == 'in_array' && is_array($value)) {
328+
if ($matchType == 'in_array' && !is_array($value)) {
319329
return @in_array($value, $matchValue);
320330
}
321331

322-
if ($matchType == 'not_in_array' && is_array($value)) {
332+
if ($matchType == 'not_in_array' && !is_array($value)) {
323333
return @!in_array($value, $matchValue);
324334
}
325335

@@ -345,7 +355,7 @@ public function matchParameterValue($match, $value)
345355
public function getParameterValue($parameter, $data = [])
346356
{
347357
// For when a rule contains sub-rules.
348-
if (ctype_digit($parameter)) {
358+
if (ctype_digit($parameter) || empty($parameter)) {
349359
return null;
350360
}
351361

tests/FirewallTest.php

Lines changed: 130 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,7 @@ private function alterPayload(array $payload = [])
5757

5858
$_POST = isset($payload['POST']) ? $payload['POST'] : [];
5959
$_GET = isset($payload['GET']) ? $payload['GET'] : [];
60+
$_SERVER['REQUEST_URI'] = isset($payload['SERVER'], $payload['SERVER']['REQUEST_URI']) ? $payload['SERVER']['REQUEST_URI'] : '';
6061
}
6162

6263
/**
@@ -96,5 +97,134 @@ public function testRules()
9697
);
9798
$this->assertFalse($this->processor->launch(false));
9899
$this->alterPayload();
100+
101+
// Block WordPress WP-AJAX action restaurant_system_customize_button or restaurant_system_insert_dialog, when not executed by an administrator.
102+
// Should return false because current_user_can does not exist.
103+
$this->setUpFirewallProcessor([$this->rules[3]]);
104+
$this->alterPayload(
105+
['POST' => [
106+
'action' => 'restaurant_system_customize_button'
107+
]]
108+
);
109+
$this->assertTrue($this->processor->launch(false));
110+
$this->alterPayload();
111+
112+
// Block WordPress WP-AJAX action restaurant_system_customize_button or restaurant_system_insert_dialog.
113+
$this->setUpFirewallProcessor([$this->rules[4]]);
114+
$this->alterPayload(
115+
['POST' => [
116+
'action' => 'restaurant_system_customize_button'
117+
]]
118+
);
119+
$this->assertFalse($this->processor->launch(false));
120+
$this->alterPayload();
121+
122+
// Block access to specific WP-JSON endpoint.
123+
$this->setUpFirewallProcessor([$this->rules[5]]);
124+
$this->alterPayload(
125+
['SERVER' => [
126+
'REQUEST_URI' => '/wp-json/yikes/cpt/v1/settings'
127+
]]
128+
);
129+
$this->assertFalse($this->processor->launch(false));
130+
$this->alterPayload();
131+
132+
// Block access to specific WP-JSON endpoint.
133+
$this->setUpFirewallProcessor([$this->rules[5]]);
134+
$this->alterPayload(
135+
['GET' => [
136+
'rest_route' => '/wp-json/yikes/cpt/v1/settings'
137+
]]
138+
);
139+
$this->assertFalse($this->processor->launch(false));
140+
$this->alterPayload();
141+
142+
// Block access to endpoint that should only accept an integer of less than 101.
143+
$this->setUpFirewallProcessor([$this->rules[6]]);
144+
$this->alterPayload(
145+
['GET' => [
146+
'pid' => 10000
147+
]]
148+
);
149+
$this->assertFalse($this->processor->launch(false));
150+
$this->alterPayload();
151+
152+
// Block access to endpoint that should only accept an integer of more than 99.
153+
$this->setUpFirewallProcessor([$this->rules[7]]);
154+
$this->alterPayload(
155+
['GET' => [
156+
'pid' => 99
157+
]]
158+
);
159+
$this->assertFalse($this->processor->launch(false));
160+
$this->alterPayload();
161+
162+
// Determine if a POST parameter is not a ctype_alnum.
163+
$this->setUpFirewallProcessor([$this->rules[8]]);
164+
$this->alterPayload(
165+
['POST' => [
166+
'value' => 'something)'
167+
]]
168+
);
169+
$this->assertFalse($this->processor->launch(false));
170+
$this->alterPayload();
171+
172+
// Determine if a POST parameter is not numeric.
173+
$this->setUpFirewallProcessor([$this->rules[9]]);
174+
$this->alterPayload(
175+
['POST' => [
176+
'number' => '8*8'
177+
]]
178+
);
179+
$this->assertFalse($this->processor->launch(false));
180+
$this->alterPayload();
181+
182+
// Determine if the URL matches a regex.
183+
$this->setUpFirewallProcessor([$this->rules[10]]);
184+
$this->alterPayload(
185+
['SERVER' => [
186+
'REQUEST_URI' => '/something/backdoor/something-else/'
187+
]]
188+
);
189+
$this->assertFalse($this->processor->launch(false));
190+
$this->alterPayload();
191+
192+
// Determine if value is not part of an array of values.
193+
$this->setUpFirewallProcessor([$this->rules[11]]);
194+
$this->alterPayload(
195+
['GET' => [
196+
'user' => 'simon'
197+
]]
198+
);
199+
$this->assertFalse($this->processor->launch(false));
200+
$this->alterPayload();
201+
202+
$this->setUpFirewallProcessor([$this->rules[11]]);
203+
$this->alterPayload(
204+
['GET' => [
205+
'user' => 'admin'
206+
]]
207+
);
208+
$this->assertTrue($this->processor->launch(false));
209+
$this->alterPayload();
210+
211+
// Determine if an array of values is part of a given array.
212+
$this->setUpFirewallProcessor([$this->rules[12]]);
213+
$this->alterPayload(
214+
['POST' => [
215+
'usernames' => ['simon', 'peter']
216+
]]
217+
);
218+
$this->assertTrue($this->processor->launch(false));
219+
$this->alterPayload();
220+
221+
$this->setUpFirewallProcessor([$this->rules[12]]);
222+
$this->alterPayload(
223+
['POST' => [
224+
'usernames' => ['admin']
225+
]]
226+
);
227+
$this->assertFalse($this->processor->launch(false));
228+
$this->alterPayload();
99229
}
100230
}

tests/data/Rules.json

Lines changed: 83 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,23 +2,103 @@
22
{
33
"id":1,
44
"title":"Block test parameter being present in the URL",
5-
"rules":{"get.test":{"match":{"type":"isset"}}},
5+
"rules":[{"parameter":"get.test","match":{"type":"isset"}}],
66
"cat":"TEST",
77
"type":"BLOCK",
88
"type_params":null
99
},
1010
{
1111
"id":2,
1212
"title":"Block backdoor parameter in payload set to mybackdoor and user agent containing some_backdoor_agent.",
13-
"rules":{"post.backdoor":{"match":{"type":"equals","value":"mybackdoor"},"inclusive":true},"server.HTTP_USER_AGENT":{"match":{"type":"contains","value":"some_backdoor_agent"},"inclusive":true}},
13+
"rules":[{"parameter":"post.backdoor","match":{"type":"equals","value":"mybackdoor"},"inclusive":true},{"parameter":"server.HTTP_USER_AGENT","match":{"type":"contains","value":"some_backdoor_agent"},"inclusive":true}],
1414
"cat":"TEST",
1515
"type":"BLOCK",
1616
"type_params":null
1717
},
1818
{
1919
"id":3,
2020
"title":"Block a base64 json encoded request with the user_role parameter set to administrator",
21-
"rules":{"post.payload":{"mutations":["base64_decode","json_decode"],"match":{"type":"array_key_value","key":"user_role","match":{"type":"equals","value":"administrator"}}}},
21+
"rules":[{"parameter":"post.payload","mutations":["base64_decode","json_decode"],"match":{"type":"array_key_value","key":"user_role","match":{"type":"equals","value":"administrator"}}}],
22+
"cat":"TEST",
23+
"type":"BLOCK",
24+
"type_params":null
25+
},
26+
{
27+
"id":4,
28+
"title":"Block WordPress WP-AJAX action restaurant_system_customize_button or restaurant_system_insert_dialog, when not executed by an administrator.",
29+
"rules":[{"parameter":"rules","rules":[{"parameter":"get.action","match":{"type":"in_array","value":["restaurant_system_customize_button","restaurant_system_insert_dialog"]}},{"parameter":"post.action","match":{"type":"in_array","value":["restaurant_system_customize_button","restaurant_system_insert_dialog"]}}],"inclusive":true},{"parameter":false,"match":{"type":"current_user_cannot","value":"administrator"},"inclusive":true}],
30+
"cat":"TEST",
31+
"type":"BLOCK",
32+
"type_params":null
33+
},
34+
{
35+
"id":5,
36+
"title":"Block WordPress WP-AJAX action restaurant_system_customize_button or restaurant_system_insert_dialog.",
37+
"rules":[{"parameter":"get.action","match":{"type":"in_array","value":["restaurant_system_customize_button","restaurant_system_insert_dialog"]}},{"parameter":"post.action","match":{"type":"in_array","value":["restaurant_system_customize_button","restaurant_system_insert_dialog"]}}],
38+
"cat":"TEST",
39+
"type":"BLOCK",
40+
"type_params":null
41+
},
42+
{
43+
"id":6,
44+
"title":"Block access to specific WP-JSON endpoint.",
45+
"rules":[{"parameter":"server.REQUEST_URI","match":{"type":"contains","value":"yikes\/cpt\/v1\/settings"}},{"parameter":"post.rest_route","match":{"type":"contains","value":"yikes\/cpt\/v1\/settings"}},{"parameter":"get.rest_route","match":{"type":"contains","value":"yikes\/cpt\/v1\/settings"}}],
46+
"cat":"TEST",
47+
"type":"BLOCK",
48+
"type_params":null
49+
},
50+
{
51+
"id":7,
52+
"title":"Block access to endpoint that should only accept an integer of less than 101.",
53+
"rules":[{"parameter":"get.pid","match":{"type":"ctype_digit","value":false}},{"parameter":"get.pid","match":{"type":"bigger_than","value":100}}],
54+
"cat":"TEST",
55+
"type":"BLOCK",
56+
"type_params":null
57+
},
58+
{
59+
"id":8,
60+
"title":"Block access to endpoint that should only accept an integer of more than 99.",
61+
"rules":[{"parameter":"get.pid","match":{"type":"ctype_digit","value":false}},{"parameter":"get.pid","match":{"type":"less_than","value":100}}],
62+
"cat":"TEST",
63+
"type":"BLOCK",
64+
"type_params":null
65+
},
66+
{
67+
"id":9,
68+
"title":"Determine if a POST parameter is a ctype_alnum.",
69+
"rules":[{"parameter":"post.value","match":{"type":"ctype_alnum","value":false}}],
70+
"cat":"TEST",
71+
"type":"BLOCK",
72+
"type_params":null
73+
},
74+
{
75+
"id":10,
76+
"title":"Determine if a POST parameter is a numeric.",
77+
"rules":[{"parameter":"post.number","match":{"type":"is_numeric","value":false}}],
78+
"cat":"TEST",
79+
"type":"BLOCK",
80+
"type_params":null
81+
},
82+
{
83+
"id":11,
84+
"title":"Determine if a POST parameter is a numeric.",
85+
"rules":[{"parameter":"server.REQUEST_URI","match":{"type":"regex","value":"\/(\\\/something\\\/)\/msi"}}],
86+
"cat":"TEST",
87+
"type":"BLOCK",
88+
"type_params":null
89+
},
90+
{
91+
"id":12,
92+
"title":"Determine if a value is not in an array",
93+
"rules":[{"parameter":"get.user","match":{"type":"not_in_array","value":["admin"]}}],
94+
"cat":"TEST",
95+
"type":"BLOCK",
96+
"type_params":null
97+
},
98+
{
99+
"id":13,
100+
"title":"Determine if an array contains any values from given array.",
101+
"rules":[{"parameter":"post.usernames","match":{"type":"array_in_array","value":["test","admin"]}}],
22102
"cat":"TEST",
23103
"type":"BLOCK",
24104
"type_params":null

tests/data/Whitelist.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,13 @@
22
{
33
"id":1,
44
"title":"Whitelist IP 127.0.0.1",
5-
"rules":{"server.ip":{"match":{"type":"array_in_array","value":["127.0.0.1"]}}},
5+
"rules":[{"parameter":"server.ip","match":{"type":"array_in_array","value":["127.0.0.1"]}}],
66
"type":"WHITELIST"
77
},
88
{
99
"id":2,
1010
"title":"Whitelist if POST request action parameter is set to wp_heartbeat",
11-
"rules":{"post.action":{"match":{"type":"equals","value":"wp_heartbeat"}}},
11+
"rules":[{"parameter":"post.action","match":{"type":"equals","value":"wp_heartbeat"}}],
1212
"type":"WHITELIST"
1313
}
1414
]

0 commit comments

Comments
 (0)