Skip to content

Commit c7977fc

Browse files
jrfnlschlessera
andauthored
New InputValidator::is_valid_rfc2616_token() method
This new method validates that an arbitrary input parameter can be considered valid for use as a "token" as per the RFC 2616 specification. Includes tests. Ref: https://datatracker.ietf.org/doc/html/rfc2616#section-2.2 Co-authored-by: jrfnl <[email protected]> Co-authored-by: Alain Schlesser <[email protected]>
1 parent 25222fc commit c7977fc

File tree

3 files changed

+207
-0
lines changed

3 files changed

+207
-0
lines changed

.phpcs.xml.dist

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -206,6 +206,13 @@
206206
</properties>
207207
</rule>
208208

209+
<!-- In contrast to WPCS (40%), we allow a slightly higher percentage. -->
210+
<rule ref="Squiz.PHP.CommentedOutCode">
211+
<properties>
212+
<property name="maxPercentage" value="50"/>
213+
</properties>
214+
</rule>
215+
209216

210217
<!--
211218
#############################################################################

src/Utility/InputValidator.php

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -109,4 +109,27 @@ public static function is_curl_handle($input) {
109109

110110
return false;
111111
}
112+
113+
/**
114+
* Verify that a received input parameter is a valid "token name" according to the
115+
* specification in RFC 2616 (HTTP/1.1).
116+
*
117+
* The short version is: 1 or more ASCII characters, CTRL chars and separators not allowed.
118+
* For the long version, see the specs in the RFC.
119+
*
120+
* @link https://datatracker.ietf.org/doc/html/rfc2616#section-2.2
121+
*
122+
* @since 2.1.0
123+
*
124+
* @param mixed $input Input parameter to verify.
125+
*
126+
* @return bool
127+
*/
128+
public static function is_valid_rfc2616_token($input) {
129+
if (!is_int($input) && !is_string($input)) {
130+
return false;
131+
}
132+
133+
return preg_match('@^[0-9A-Za-z!#$%&\'*+.^_`|~-]+$@', $input) === 1;
134+
}
112135
}
Lines changed: 177 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,177 @@
1+
<?php
2+
3+
namespace WpOrg\Requests\Tests\Utility\InputValidator;
4+
5+
use WpOrg\Requests\Tests\TestCase;
6+
use WpOrg\Requests\Tests\TypeProviderHelper;
7+
use WpOrg\Requests\Utility\InputValidator;
8+
9+
/**
10+
* @covers \WpOrg\Requests\Utility\InputValidator::is_valid_rfc2616_token
11+
*/
12+
final class IsValidRfc2616TokenTest extends TestCase {
13+
14+
/**
15+
* Test whether a received input parameter is correctly identified as a valid RFC 2616 token.
16+
*
17+
* @dataProvider dataValidIntegers
18+
* @dataProvider dataValidStrings
19+
*
20+
* @param mixed $input Input parameter to verify.
21+
*
22+
* @return void
23+
*/
24+
public function testValid($input) {
25+
$this->assertTrue(InputValidator::is_valid_rfc2616_token($input));
26+
}
27+
28+
/**
29+
* Data Provider.
30+
*
31+
* @return array
32+
*/
33+
public static function dataValidIntegers() {
34+
return TypeProviderHelper::getSelection(TypeProviderHelper::GROUP_INT);
35+
}
36+
37+
/**
38+
* Data Provider.
39+
*
40+
* Valid strings are valid tokens as per RFC 2616 section 2.2:
41+
* token = 1*<any CHAR except CTLs or separators>
42+
*
43+
* @return array
44+
*/
45+
public static function dataValidStrings() {
46+
47+
return [
48+
'string containing all valid token characters' => [
49+
'input' => implode('', self::getValidTokenCharacters()),
50+
],
51+
'string with a typical cookie name' => [
52+
'input' => 'requests-testcookie',
53+
],
54+
];
55+
}
56+
57+
/**
58+
* Test whether a received input parameter is correctly identified as NOT a valid RFC 2616 token.
59+
*
60+
* @dataProvider dataInvalidTypes
61+
* @dataProvider dataInvalidValues
62+
*
63+
* @param mixed $input Input parameter to verify.
64+
*
65+
* @return void
66+
*/
67+
public function testInvalid($input) {
68+
$this->assertFalse(InputValidator::is_valid_rfc2616_token($input));
69+
}
70+
71+
/**
72+
* Data Provider for invalid data types.
73+
*
74+
* @return array
75+
*/
76+
public static function dataInvalidTypes() {
77+
return TypeProviderHelper::getAllExcept(TypeProviderHelper::GROUP_INT, TypeProviderHelper::GROUP_STRING);
78+
}
79+
80+
/**
81+
* Data Provider for valid data types containing invalid values.
82+
*
83+
* @return array
84+
*/
85+
public static function dataInvalidValues() {
86+
$all_control = chr(127); // DEL.
87+
for ($char = 0; $char <= 31; $char++) {
88+
$all_control .= chr($char);
89+
}
90+
91+
$invalid_ascii_characters = array_diff(
92+
array_map('chr', range(0, 127)),
93+
self::getValidTokenCharacters()
94+
);
95+
96+
return [
97+
'empty string' => [
98+
'input' => '',
99+
],
100+
'string containing only control characters / all control characters' => [
101+
'input' => $all_control,
102+
],
103+
'string containing control character at start' => [
104+
'input' => chr(6) . 'some text',
105+
],
106+
'string containing control characters in text' => [
107+
'input' => "some\ntext\rwith\tcontrol\echaracters\fin\vit",
108+
],
109+
'string containing control character at end' => [
110+
'input' => 'some text' . chr(127),
111+
],
112+
'string containing only separator characters / all separator characters' => [
113+
'input' => '()<>@,;:\\"/[]?={} ',
114+
],
115+
'string containing separator character at start' => [
116+
'input' => '=value',
117+
],
118+
'string containing separator characters in text' => [
119+
'input' => 'words "with" spaces and quotes',
120+
],
121+
'string containing separator character at end' => [
122+
'input' => 'punctuated;',
123+
],
124+
'string containing separator characters - leading and trailing whitespace' => [
125+
'input' => ' words ',
126+
],
127+
'string containing non-ascii characters - Iñtërnâtiônàlizætiøn' => [
128+
'input' => 'Iñtërnâtiônàlizætiøn',
129+
],
130+
'string containing non-ascii characters - ௫' => [
131+
'input' => '', // Tamil digit five.
132+
],
133+
'string containing all invalid ASCII characters' => [
134+
'input' => implode('', $invalid_ascii_characters),
135+
],
136+
];
137+
}
138+
139+
/**
140+
* Get an array of valid RFC 2616 token characters.
141+
*
142+
* Valid token as per RFC 2616 section 2.2:
143+
* token = 1*<any CHAR except CTLs or separators>
144+
*
145+
* Disabling PHPCS checks for consistency with RFC 2616:
146+
* @phpcs:disable WordPress.Arrays.ArrayDeclarationSpacing.ArrayItemNoNewLine
147+
*
148+
* @return array<string>
149+
*/
150+
private static function getValidTokenCharacters() {
151+
// CHAR = <any US-ASCII character (octets 0 - 127)>
152+
$rfc_char = array_map('chr', range(0, 127));
153+
154+
// CTL = <any US-ASCII control character (octets 0 - 31) and DEL (127)>
155+
$rfc_ctl = array_map('chr', array_merge(range(0, 31), [127]));
156+
157+
// SP = <US-ASCII SP, space (32)>
158+
$rfc_sp = chr(32);
159+
160+
// HT = <US-ASCII HT, horizontal-tab (9)>
161+
$rfc_ht = chr(9);
162+
163+
// Separators = "(" | ")" | "<" | ">" | "@"
164+
// | "," | ";" | ":" | "\" | <">
165+
// | "/" | "[" | "]" | "?" | "="
166+
// | "{" | "}" | SP | HT
167+
$rfc_separators = [
168+
'(', ')', '<', '>', '@',
169+
',', ';', ':', '\\', '"',
170+
'/', '[', ']', '?', '=',
171+
'{', '}', $rfc_sp, $rfc_ht,
172+
];
173+
174+
// Token characters = <any CHAR except CTLs or separators>
175+
return array_diff($rfc_char, $rfc_ctl, $rfc_separators);
176+
}
177+
}

0 commit comments

Comments
 (0)