Skip to content

Commit

Permalink
New InputValidator::is_valid_rfc2616_token() method
Browse files Browse the repository at this point in the history
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]>
  • Loading branch information
3 people authored Feb 12, 2024
1 parent 25222fc commit c7977fc
Show file tree
Hide file tree
Showing 3 changed files with 207 additions and 0 deletions.
7 changes: 7 additions & 0 deletions .phpcs.xml.dist
Original file line number Diff line number Diff line change
Expand Up @@ -206,6 +206,13 @@
</properties>
</rule>

<!-- In contrast to WPCS (40%), we allow a slightly higher percentage. -->
<rule ref="Squiz.PHP.CommentedOutCode">
<properties>
<property name="maxPercentage" value="50"/>
</properties>
</rule>


<!--
#############################################################################
Expand Down
23 changes: 23 additions & 0 deletions src/Utility/InputValidator.php
Original file line number Diff line number Diff line change
Expand Up @@ -109,4 +109,27 @@ public static function is_curl_handle($input) {

return false;
}

/**
* Verify that a received input parameter is a valid "token name" according to the
* specification in RFC 2616 (HTTP/1.1).
*
* The short version is: 1 or more ASCII characters, CTRL chars and separators not allowed.
* For the long version, see the specs in the RFC.
*
* @link https://datatracker.ietf.org/doc/html/rfc2616#section-2.2
*
* @since 2.1.0
*
* @param mixed $input Input parameter to verify.
*
* @return bool
*/
public static function is_valid_rfc2616_token($input) {
if (!is_int($input) && !is_string($input)) {
return false;
}

return preg_match('@^[0-9A-Za-z!#$%&\'*+.^_`|~-]+$@', $input) === 1;
}
}
177 changes: 177 additions & 0 deletions tests/Utility/InputValidator/IsValidRfc2616TokenTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,177 @@
<?php

namespace WpOrg\Requests\Tests\Utility\InputValidator;

use WpOrg\Requests\Tests\TestCase;
use WpOrg\Requests\Tests\TypeProviderHelper;
use WpOrg\Requests\Utility\InputValidator;

/**
* @covers \WpOrg\Requests\Utility\InputValidator::is_valid_rfc2616_token
*/
final class IsValidRfc2616TokenTest extends TestCase {

/**
* Test whether a received input parameter is correctly identified as a valid RFC 2616 token.
*
* @dataProvider dataValidIntegers
* @dataProvider dataValidStrings
*
* @param mixed $input Input parameter to verify.
*
* @return void
*/
public function testValid($input) {
$this->assertTrue(InputValidator::is_valid_rfc2616_token($input));
}

/**
* Data Provider.
*
* @return array
*/
public static function dataValidIntegers() {
return TypeProviderHelper::getSelection(TypeProviderHelper::GROUP_INT);
}

/**
* Data Provider.
*
* Valid strings are valid tokens as per RFC 2616 section 2.2:
* token = 1*<any CHAR except CTLs or separators>
*
* @return array
*/
public static function dataValidStrings() {

return [
'string containing all valid token characters' => [
'input' => implode('', self::getValidTokenCharacters()),
],
'string with a typical cookie name' => [
'input' => 'requests-testcookie',
],
];
}

/**
* Test whether a received input parameter is correctly identified as NOT a valid RFC 2616 token.
*
* @dataProvider dataInvalidTypes
* @dataProvider dataInvalidValues
*
* @param mixed $input Input parameter to verify.
*
* @return void
*/
public function testInvalid($input) {
$this->assertFalse(InputValidator::is_valid_rfc2616_token($input));
}

/**
* Data Provider for invalid data types.
*
* @return array
*/
public static function dataInvalidTypes() {
return TypeProviderHelper::getAllExcept(TypeProviderHelper::GROUP_INT, TypeProviderHelper::GROUP_STRING);
}

/**
* Data Provider for valid data types containing invalid values.
*
* @return array
*/
public static function dataInvalidValues() {
$all_control = chr(127); // DEL.
for ($char = 0; $char <= 31; $char++) {
$all_control .= chr($char);
}

$invalid_ascii_characters = array_diff(
array_map('chr', range(0, 127)),
self::getValidTokenCharacters()
);

return [
'empty string' => [
'input' => '',
],
'string containing only control characters / all control characters' => [
'input' => $all_control,
],
'string containing control character at start' => [
'input' => chr(6) . 'some text',
],
'string containing control characters in text' => [
'input' => "some\ntext\rwith\tcontrol\echaracters\fin\vit",
],
'string containing control character at end' => [
'input' => 'some text' . chr(127),
],
'string containing only separator characters / all separator characters' => [
'input' => '()<>@,;:\\"/[]?={} ',
],
'string containing separator character at start' => [
'input' => '=value',
],
'string containing separator characters in text' => [
'input' => 'words "with" spaces and quotes',
],
'string containing separator character at end' => [
'input' => 'punctuated;',
],
'string containing separator characters - leading and trailing whitespace' => [
'input' => ' words ',
],
'string containing non-ascii characters - Iñtërnâtiônàlizætiøn' => [
'input' => 'Iñtërnâtiônàlizætiøn',
],
'string containing non-ascii characters - ௫' => [
'input' => '', // Tamil digit five.
],
'string containing all invalid ASCII characters' => [
'input' => implode('', $invalid_ascii_characters),
],
];
}

/**
* Get an array of valid RFC 2616 token characters.
*
* Valid token as per RFC 2616 section 2.2:
* token = 1*<any CHAR except CTLs or separators>
*
* Disabling PHPCS checks for consistency with RFC 2616:
* @phpcs:disable WordPress.Arrays.ArrayDeclarationSpacing.ArrayItemNoNewLine
*
* @return array<string>
*/
private static function getValidTokenCharacters() {
// CHAR = <any US-ASCII character (octets 0 - 127)>
$rfc_char = array_map('chr', range(0, 127));

// CTL = <any US-ASCII control character (octets 0 - 31) and DEL (127)>
$rfc_ctl = array_map('chr', array_merge(range(0, 31), [127]));

// SP = <US-ASCII SP, space (32)>
$rfc_sp = chr(32);

// HT = <US-ASCII HT, horizontal-tab (9)>
$rfc_ht = chr(9);

// Separators = "(" | ")" | "<" | ">" | "@"
// | "," | ";" | ":" | "\" | <">
// | "/" | "[" | "]" | "?" | "="
// | "{" | "}" | SP | HT
$rfc_separators = [
'(', ')', '<', '>', '@',
',', ';', ':', '\\', '"',
'/', '[', ']', '?', '=',
'{', '}', $rfc_sp, $rfc_ht,
];

// Token characters = <any CHAR except CTLs or separators>
return array_diff($rfc_char, $rfc_ctl, $rfc_separators);
}
}

0 comments on commit c7977fc

Please sign in to comment.