Skip to content

Commit e332af7

Browse files
Merge pull request #9 from greatislander/php-7.4-compatibility
feat: support PHP 7.4 (resolves #8)
2 parents 139db47 + bd52cec commit e332af7

6 files changed

+229
-11
lines changed

Pressbooks/Sniffs/ExtraSniffCode.php

+46
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
<?php
2+
3+
namespace Pressbooks\Sniffs;
4+
5+
use PHP_CodeSniffer\Util;
6+
7+
trait ExtraSniffCode {
8+
/**
9+
* Duplicates ignore statements from a legacy sniff.
10+
*
11+
* This allows overriding an existing sniff and retaining the existing
12+
* ignore statements.
13+
*
14+
* @param string $legacy Legacy sniff code
15+
*/
16+
protected function duplicate_ignores( $legacy ) {
17+
$expression = sprintf( '/^%s(\..+)?$/', preg_quote( $legacy ) );
18+
$base_code = Util\Common::getSniffCode( get_class( $this ) );
19+
20+
foreach ( $this->phpcsFile->tokenizer->ignoredLines as $line => $ignored ) {
21+
$additional = [];
22+
23+
if ( empty( $ignored ) ) {
24+
continue;
25+
}
26+
27+
// Find any code which matches the legacy value.
28+
foreach ( $ignored as $code => $value ) {
29+
if ( preg_match( $expression, $code, $matches ) ) {
30+
// Duplicate as the new code.
31+
$new_code = $base_code;
32+
if ( ! empty( $matches[1] ) ) {
33+
$new_code .= $matches[1];
34+
}
35+
36+
$additional[ $new_code ] = $value;
37+
}
38+
}
39+
40+
if ( ! empty( $additional ) ) {
41+
$this->phpcsFile->tokenizer->ignoredLines[ $line ] = array_merge( $ignored, $additional );
42+
}
43+
}
44+
45+
}
46+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
<?php
2+
3+
namespace Pressbooks\Sniffs\Security;
4+
5+
use Pressbooks\Sniffs\ExtraSniffCode;
6+
use PHP_CodeSniffer\Files\File as PhpcsFile;
7+
use WordPressCS\WordPress\Sniffs\Security\EscapeOutputSniff as WPCSEscapeOutputSniff;
8+
9+
/**
10+
* Verify all strings are escaped.
11+
*
12+
* This is subclassed from WPCS, as we need to disable warnings for error
13+
* logging, as we don't output those to the page.
14+
*
15+
* @see https://github.com/WordPress/WordPress-Coding-Standards/issues/1864
16+
*/
17+
class EscapeOutputSniff extends WPCSEscapeOutputSniff {
18+
use ExtraSniffCode;
19+
20+
/**
21+
* Allowed functions which are treated by WPCS as printing functions.
22+
*
23+
* @var array
24+
*/
25+
protected $hmSafePrintingFunctions = [
26+
'_deprecated_argument' => true,
27+
'_deprecated_constructor' => true,
28+
'_deprecated_file' => true,
29+
'_deprecated_function' => true,
30+
'_deprecated_hook' => true,
31+
'_doing_it_wrong' => true,
32+
'trigger_error' => true,
33+
'user_error' => true,
34+
];
35+
36+
/**
37+
* Printing functions that incorporate unsafe values.
38+
*
39+
* This is overridden from the parent class to allow unescaped
40+
* translated text.
41+
*
42+
* @var array
43+
*/
44+
protected $unsafePrintingFunctions = [];
45+
46+
/**
47+
* Constructor.
48+
*
49+
* Removes non-printing functions from the property.
50+
*/
51+
public function __construct() {
52+
// Remove error logging functions from output functions.
53+
foreach ( $this->hmSafePrintingFunctions as $function => $val ) {
54+
unset( $this->printingFunctions[ $function ] );
55+
}
56+
}
57+
58+
/**
59+
* Override init to duplicate any ignores.
60+
*
61+
* @param PhpcsFile $phpcsFile
62+
*/
63+
protected function init( PhpcsFile $phpcsFile ) {
64+
parent::init( $phpcsFile );
65+
66+
$this->duplicate_ignores( 'WordPress.Security.EscapeOutput' );
67+
}
68+
}

Pressbooks/Sniffs/CSRF/NonceVerificationSniff.php Pressbooks/Sniffs/Security/NonceVerificationSniff.php

+3-3
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
<?php
22

3-
namespace Pressbooks\Sniffs\CSRF;
3+
namespace Pressbooks\Sniffs\Security;
44

5-
class NonceVerificationSniff extends \WordPress\Sniffs\CSRF\NonceVerificationSniff {
5+
class NonceVerificationSniff extends \WordPressCS\WordPress\Sniffs\Security\NonceVerificationSniff {
66

77
/**
88
* Chill out for $_GET and $_REQUEST
@@ -15,4 +15,4 @@ class NonceVerificationSniff extends \WordPress\Sniffs\CSRF\NonceVerificationSni
1515
'$_POST' => true,
1616
'$_FILE' => true,
1717
];
18-
}
18+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,106 @@
1+
<?php
2+
3+
namespace Pressbooks\Sniffs\Security;
4+
5+
use Pressbooks\Sniffs\ExtraSniffCode;
6+
use PHP_CodeSniffer\Files\File as PhpcsFile;
7+
use WordPressCS\WordPress\Sniffs\Security\ValidatedSanitizedInputSniff as WPCSValidatedSanitizedInputSniff;
8+
9+
class ValidatedSanitizedInputSniff extends WPCSValidatedSanitizedInputSniff {
10+
use ExtraSniffCode;
11+
12+
/**
13+
* Keys to allow in the $_SERVER variable.
14+
*
15+
* Set this to override.
16+
*
17+
* @var array
18+
*/
19+
public $allowedServerKeys = [
20+
'HTTP_HOST',
21+
22+
// User-Agent is forced to a static value when passing through
23+
// CloudFront, so is safe to use.
24+
'HTTP_USER_AGENT',
25+
26+
'HTTPS',
27+
'REMOTE_ADDR',
28+
'REQUEST_METHOD',
29+
'REQUEST_TIME',
30+
'REQUEST_TIME_FLOAT',
31+
'REQUEST_URI',
32+
'QUERY_STRING',
33+
'SERVER_ADDR',
34+
];
35+
36+
/**
37+
* Override init to duplicate any ignores.
38+
*
39+
* @param PhpcsFile $phpcsFile
40+
*/
41+
protected function init( PhpcsFile $phpcsFile ) {
42+
parent::init( $phpcsFile );
43+
44+
$this->duplicate_ignores( 'WordPress.Security.ValidatedSanitizedInput' );
45+
}
46+
47+
/**
48+
* Process a token for validation and sanitisation.
49+
*
50+
* @param int $stackPtr
51+
* @return void
52+
*/
53+
public function process_token( $stackPtr ) {
54+
// Process our custom server rules first.
55+
if ( $this->tokens[ $stackPtr ]['content'] === '$_SERVER' ) {
56+
$pass = $this->check_server_variable( $stackPtr );
57+
if ( $pass ) {
58+
// Variable is fine, skip upstream checks.
59+
return;
60+
}
61+
}
62+
63+
// Not an allowed usage, so run the regular check on it.
64+
return parent::process_token( $stackPtr );
65+
}
66+
67+
/**
68+
* Check whether a $_SERVER variable is constant and allowed.
69+
*
70+
* @param int $stackPtr Current token to check.
71+
* @return bool True if this is a $_SERVER variable and is safe, false to run regular checks.
72+
*/
73+
protected function check_server_variable( $stackPtr ) {
74+
$key = $this->get_array_access_key( $stackPtr );
75+
76+
// Find the next non-whitespace token.
77+
$open_bracket = $this->phpcsFile->findNext( T_WHITESPACE, ( $stackPtr + 1 ), null, true );
78+
if ( $this->tokens[ $open_bracket ]['code'] !== T_OPEN_SQUARE_BRACKET ) {
79+
// No index access, run regular checks.
80+
return false;
81+
}
82+
83+
$index_token = $this->phpcsFile->findNext( T_WHITESPACE, ( $open_bracket + 1 ), null, true );
84+
if ( $this->tokens[ $index_token ]['code'] !== T_CONSTANT_ENCAPSED_STRING ) {
85+
// Dynamic string, run regular checks.
86+
return false;
87+
}
88+
89+
// Possible constant string, check there's no further dynamic parts.
90+
$maybe_close_bracket = $this->phpcsFile->findNext( T_WHITESPACE, ( $index_token + 1 ), null, true );
91+
if ( $this->tokens[ $maybe_close_bracket ]['code'] !== T_CLOSE_SQUARE_BRACKET ) {
92+
// Dynamic string, run regular checks.
93+
return false;
94+
}
95+
96+
// Constant string, check if it's allowed.
97+
$key = $this->strip_quotes( $this->tokens[ $index_token ]['content'] );
98+
if ( ! in_array( $key, $this->allowedServerKeys, true ) ) {
99+
// Unsafe key, requires sanitising.
100+
return false;
101+
}
102+
103+
// Safe key, allow it.
104+
return true;
105+
}
106+
}

Pressbooks/ruleset.xml

+3-4
Original file line numberDiff line numberDiff line change
@@ -13,13 +13,12 @@
1313
<!-- Use HM Coding Standards -->
1414
<rule ref="vendor/humanmade/coding-standards">
1515
<!-- Disable rules Pressbooks overrides -->
16-
<exclude name="WordPress.CSRF.NonceVerification.NoNonceVerification"/>
16+
<exclude name="HM.Security"/>
17+
<exclude name="WordPress.Security.NonceVerification"/>
1718
<!-- Disable rules Pressbooks disagrees with: -->
1819
<exclude name="WordPress.NamingConventions.ValidFunctionName.MethodNameInvalid"/>
1920
<exclude name="WordPress.NamingConventions.ValidVariableName.MemberNotSnakeCase"/>
2021
<exclude name="WordPress.NamingConventions.ValidVariableName.NotSnakeCaseMemberVar"/>
21-
<exclude name="WordPress.VIP.SessionVariableUsage"/>
22-
<exclude name="WordPress.VIP.SessionFunctionsUsage"/>
2322
<exclude name="WordPress.PHP.DiscouragedPHPFunctions.system_calls_exec"/>
2423
<exclude name="WordPress.PHP.DiscouragedPHPFunctions.runtime_configuration_ini_set"/>
2524
<exclude name="WordPress.PHP.DiscouragedPHPFunctions.runtime_configuration_putenv"/>
@@ -37,4 +36,4 @@
3736
<rule ref="HM.Functions.NamespacedFunctions">
3837
<exclude-pattern>/bin/*</exclude-pattern>
3938
</rule>
40-
</ruleset>
39+
</ruleset>

composer.json

+3-4
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,10 @@
33
"type": "project",
44
"license": "GPL-3.0-or-later",
55
"require": {
6-
"humanmade/coding-standards": "0.5.0",
7-
"fig-r/psr2r-sniffer": "0.5.0",
8-
"squizlabs/php_codesniffer": "3.3.2"
6+
"humanmade/coding-standards": "^1.0",
7+
"squizlabs/php_codesniffer": "^3.5"
98
},
109
"require-dev": {
11-
"phpunit/phpunit": "6.5.14"
10+
"phpunit/phpunit": "^7.0"
1211
}
1312
}

0 commit comments

Comments
 (0)