Skip to content

Allow filters to be excluded from the PHPFilterFunctions restricted list - #897

Merged
GaryJones merged 1 commit into
developfrom
GaryJones/753-configurable-restricted-filters
Jul 30, 2026
Merged

Allow filters to be excluded from the PHPFilterFunctions restricted list#897
GaryJones merged 1 commit into
developfrom
GaryJones/753-configurable-restricted-filters

Conversation

@GaryJones

Copy link
Copy Markdown
Contributor

Summary

The WordPressVIPMinimum.Security.PHPFilterFunctions sniff flags FILTER_DEFAULT and FILTER_UNSAFE_RAW because they perform no sanitisation. That was uncontroversial until PHP 8.1 deprecated FILTER_SANITIZE_STRING with no direct replacement. The pattern developers were pushed towards is to read the value with FILTER_UNSAFE_RAW (or no filter at all) and sanitise it themselves afterwards. The sniff then flags that deliberate, correct code, and there is no way to opt out short of a blanket ignore annotation, as raised in #753.

This adds an exclude_filters property so a ruleset can drop specific constants from the restricted list, for example to permit a knowing use of FILTER_UNSAFE_RAW:

<rule ref="WordPressVIPMinimum.Security.PHPFilterFunctions">
    <properties>
        <property name="exclude_filters" type="array">
            <element value="FILTER_UNSAFE_RAW"/>
        </property>
    </properties>
</rule>

The property defaults to an empty array, so nothing changes unless a ruleset opts in.

Design notes for reviewers

The issue asked for the $restricted_filters property to be made public. I have deliberately not done that. Exposing the default list hands ownership to the user: they would have to redeclare the whole list to remove one entry, and any constant VIPCS adds to the defaults later would be silently masked by their override.

Instead $restricted_filters stays private and the effective list is derived by subtracting the user's excludes, reusing the RulesetPropertyHelper::merge_custom_array() plus array_diff_key() idiom already used for the exclude property in RestrictedExtendClasses and AbstractVariableRestrictions. So VIPCS keeps ownership of the defaults and the user only expresses their delta. RulesetPropertyHelper is internal to WordPressCS, but VIPCS already relies on it in AbstractVariableRestrictionsSniff, so this introduces no new coupling. The subtraction is recalculated on each call rather than cached, matching that precedent, so an inline phpcs:set change is honoured mid-file.

One naming decision is open to bikeshedding: I went with exclude_filters (descriptive form of the existing exclude idiom). allowed_filters (positive framing) or a plain exclude (maximal consistency with the abstract sniffs) are equally reasonable — happy to rename before merge.

Fixes #753.

Test plan

Unit tests use the inline phpcs:set mechanism to prove that excluding FILTER_UNSAFE_RAW silences it while FILTER_DEFAULT remains flagged. I also verified the real ruleset-property path end to end: with the default config both filters warn, and with exclude_filters set only FILTER_DEFAULT warns.

  • composer test -- --filter PHPFilterFunctions
  • composer cs -- WordPressVIPMinimum/Sniffs/Security/PHPFilterFunctionsSniff.php

PHP 8.1 deprecated `FILTER_SANITIZE_STRING` with no direct replacement, so
the recommended pattern became reading the value with `FILTER_UNSAFE_RAW`
(or no filter) and sanitising it afterwards. The PHPFilterFunctions sniff
flags `FILTER_UNSAFE_RAW` as doing no filtering, which left developers who
had knowingly adopted that pattern with no way to silence the warning short
of a blanket ignore annotation.

Add a configurable `exclude_filters` property so a ruleset can remove
specific constants from the restricted list, for example to permit a
deliberate `FILTER_UNSAFE_RAW`. Rather than exposing the default list
directly, `$restricted_filters` stays private and the effective list is
derived by subtracting the excludes, reusing the same
`RulesetPropertyHelper::merge_custom_array()` plus `array_diff_key()` idiom
already used for the `exclude` property elsewhere in VIPCS. This keeps
ownership of the defaults with VIPCS while letting users express only their
delta. The subtraction is recalculated per call so an inline `phpcs:set`
change is honoured.

The property defaults to an empty array, so behaviour is unchanged unless a
ruleset opts in.

Fixes #753.
@GaryJones
GaryJones requested a review from a team as a code owner July 30, 2026 15:09
@GaryJones
GaryJones merged commit 2475dd7 into develop Jul 30, 2026
26 checks passed
@GaryJones
GaryJones deleted the GaryJones/753-configurable-restricted-filters branch July 30, 2026 16:13
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Make WordPressVIPMinimum.Security.PHPFilterFunctions.restricted_filters configurable

1 participant