@@ -64,7 +64,7 @@ unset( $_GET['test'] ); // Ok.
6464
6565output ( "some string {$ _POST ['some_var ' ]}" ); // Bad.
6666
67- echo ( int ) $ _GET ['test ' ]; // Ok.
67+ echo isset ( $ _GET [ ' test ' ] ) ? ( int ) $ _GET ['test ' ] : 0 ; // Ok.
6868some_func ( $ some_arg , (int ) $ _GET ['test ' ] ); // Ok.
6969
7070function zebra () {
@@ -105,7 +105,7 @@ echo sanitize_text_field( $_POST['foo545'] ); // Error for no validation, unslas
105105echo array_map ( 'sanitize_text_field ' , $ _GET ['test ' ] ); // Bad, no unslashing.
106106echo Array_Map ( 'sanitize_key ' , $ _GET ['test ' ] ); // Ok.
107107
108- foo ( AbsINT ( $ _GET ['foo ' ] ) ); // Ok.
108+ isset ( $ _GET [ ' foo ' ] ) && foo ( AbsINT ( $ _GET ['foo ' ] ) ); // Ok.
109109$ ids = array_map ( 'absint ' , $ _GET ['test ' ] ); // Ok.
110110
111111if ( is_array ( $ _GET ['test ' ] ) ) {} // Ok.
@@ -232,17 +232,17 @@ function test_allow_array_key_exists_alias() {
232232 }
233233}
234234function test_correct_multi_level_array_validation () {
235- if ( isset ( $ _POST ['toplevel ' ]['sublevel ' ] ) ) {
236- $ id = (int ) $ _POST ['toplevel ' ]; // OK, if a subkey exists, the top level key *must* also exist.
237- $ id = (int ) $ _POST ['toplevel ' ]['sublevel ' ]; // OK.
238- $ id = (int ) $ _POST ['toplevel ' ]['sublevel ' ]['subsub ' ]; // Bad x1 - validate, this sub has not been validated.
235+ if ( isset ( $ _COOKIE ['toplevel ' ]['sublevel ' ] ) ) {
236+ $ id = (int ) $ _COOKIE ['toplevel ' ]; // OK, if a subkey exists, the top level key *must* also exist.
237+ $ id = (int ) $ _COOKIE ['toplevel ' ]['sublevel ' ]; // OK.
238+ $ id = (int ) $ _COOKIE ['toplevel ' ]['sublevel ' ]['subsub ' ]; // Bad x1 - validate, this sub has not been validated.
239239 }
240240
241- if ( array_key_exists ( 'bar ' , $ _POST ['foo ' ] ) ) {
242- $ id = (int ) $ _POST ['bar ' ]; // Bad x 1 - validate.
243- $ id = (int ) $ _POST ['foo ' ]; // OK.
244- $ id = (int ) $ _POST ['foo ' ]['bar ' ]; // OK.
245- $ id = (int ) $ _POST ['foo ' ]['bar ' ]['baz ' ]; // Bad x 1 - validate.
241+ if ( array_key_exists ( 'bar ' , $ _COOKIE ['foo ' ] ) ) {
242+ $ id = (int ) $ _COOKIE ['bar ' ]; // Bad x 1 - validate.
243+ $ id = (int ) $ _COOKIE ['foo ' ]; // OK.
244+ $ id = (int ) $ _COOKIE ['foo ' ]['bar ' ]; // OK.
245+ $ id = (int ) $ _COOKIE ['foo ' ]['bar ' ]['baz ' ]; // Bad x 1 - validate.
246246 }
247247}
248248
@@ -380,11 +380,11 @@ function test_allow_array_key_exists_with_named_params_multilevel_access_wrong_p
380380}
381381
382382function test_allow_array_key_exists_with_named_params_multilevel_access_test () {
383- if ( array_key_exists ( array: $ _POST ['foo ' ], key: 'bar ' ) ) {
384- $ id = (int ) $ _POST ['bar ' ]; // Bad x 1 - validate.
385- $ id = (int ) $ _POST ['foo ' ]; // OK.
386- $ id = (int ) $ _POST ['foo ' ]['bar ' ]; // OK.
387- $ id = (int ) $ _POST ['foo ' ]['bar ' ]['baz ' ]; // Bad x 1 - validate.
383+ if ( array_key_exists ( array: $ _SERVER ['foo ' ], key: 'bar ' ) ) {
384+ $ id = (int ) $ _SERVER ['bar ' ]; // Bad x 1 - validate.
385+ $ id = (int ) $ _SERVER ['foo ' ]; // OK.
386+ $ id = (int ) $ _SERVER ['foo ' ]['bar ' ]; // OK.
387+ $ id = (int ) $ _SERVER ['foo ' ]['bar ' ]['baz ' ]; // Bad x 1 - validate.
388388 }
389389}
390390
@@ -404,3 +404,99 @@ function test_ignore_array_key_exists_as_first_class_callable() {
404404 $ callback = array_key_exists (...);
405405 $ id = (int ) $ _POST ['foo ' ]; // Bad.
406406}
407+
408+ /*
409+ * Unsetting a superglobal key is not the same as validating it.
410+ */
411+ function test_unset_is_not_validation () {
412+ unset( $ _REQUEST ['key ' ] );
413+ $ id = (int ) $ _REQUEST ['key ' ]; // Bad, missing validation.
414+ }
415+
416+ /*
417+ * Only count a variable as validated if the validation happened in the same scope.
418+ */
419+ function test_nested_closed_scopes () {
420+ $ closure = function () {
421+ return isset ( $ _POST ['bar ' ] );
422+ };
423+
424+ $ anon = new class () {
425+ public function __construct () {
426+ if ( isset ( $ _POST ['bar ' ] ) ) {
427+ // Do something.
428+ }
429+ }
430+ };
431+
432+ $ arrow = fn () => isset ( $ _POST ['bar ' ] );
433+
434+ $ id = (int ) $ _POST ['bar ' ]; // Bad x 1 - validate.
435+ }
436+
437+ function test_arrow_open_scope () {
438+ if ( ! isset ( $ _SERVER ['key ' ] ) ) {
439+ return ;
440+ }
441+
442+ $ arrow = fn () => (int ) $ _SERVER ['key ' ]; // OK, validation outside the scope of the arrow function counts.
443+
444+ $ arrow = fn () => isset ( $ _SERVER ['abc ' ] ) ? (int ) $ _SERVER ['abc ' ] : 0 ; // OK.
445+ }
446+
447+ // Ensure the sniff flags $_ENV and $_SESSION too.
448+ function test_examine_additional_superglobals_in_textstrings () {
449+ $ text = "Use {$ _SESSION ['key ' ]} for something " ; // Bad.
450+ $ text = "Use {$ _ENV ['key ' ]} for something " ; // Bad.
451+ $ text = "Use {$ GLOBALS ['key ' ]} for something " ; // OK.
452+ }
453+
454+ function test_examine_additional_superglobals_as_vars () {
455+ $ key = sanitize_text_field ( $ _SESSION ['key ' ] ); // Bad - missing validation.
456+ $ key = sanitize_text_field ( $ _ENV ['key ' ] ); // Bad - missing validation.
457+ $ key = sanitize_text_field ( $ _FILES ['key ' ] ); // Bad - missing validation.
458+
459+ if ( isset ( $ _SESSION ['key ' ], $ _ENV ['key ' ], $ _FILES ['key ' ] ) === false ) {
460+ return ;
461+ }
462+
463+ // OK.
464+ $ key = sanitize_text_field ( wp_unslash ( $ _SESSION ['key ' ] ) );
465+ $ key = sanitize_text_field ( wp_unslash ( $ _ENV ['key ' ] ) );
466+ $ key = sanitize_text_field ( wp_unslash ( $ _FILES ['key ' ] ) );
467+
468+ // OK - unslashing not needed for $_SESSION, $_ENV and $_FILES.
469+ $ key = sanitize_text_field ( $ _SESSION ['key ' ] );
470+ $ key = sanitize_text_field ( $ _ENV ['key ' ] );
471+ $ key = sanitize_text_field ( $ _FILES ['key ' ] );
472+
473+ // Bad - missing sanitization.
474+ do_something ( $ _SESSION ['key ' ] );
475+ do_something ( $ _ENV ['key ' ] );
476+ do_something ( $ _FILES ['key ' ] );
477+ }
478+
479+ function test_null_coalesce_equals_validation_extra_safeguard () {
480+ $ _POST ['key ' ] ??= 'default ' ; // OK, assignment.
481+ $ key = $ _POST ['key ' ]; // Bad, missing unslash + sanitization, validation okay.
482+ }
483+
484+ function test_in_match_condition_is_regarded_as_comparison () {
485+ if ( isset ( $ _REQUEST ['key ' ] ) ) {
486+ $ test = match ( $ _REQUEST ['key ' ] ) {
487+ 'valueA ' => 'A ' ,
488+ default => 'B ' ,
489+ };
490+ }
491+ }
492+
493+ function test_in_match_condition_is_regarded_as_comparison () {
494+ if ( isset ( $ _REQUEST ['keyA ' ], $ _REQUEST ['keyB ' ], $ _REQUEST ['keyC ' ] ) ) {
495+ $ test = match ( $ toggle ) {
496+ true => sanitize_text_field ( wp_unslash ( $ _REQUEST ['keyA ' ] ) ), // OK.
497+ false => sanitize_text_field ( $ _REQUEST ['keyB ' ] ), // Bad - missing unslash.
498+ 10 => wp_unslash ( $ _REQUEST ['keyC ' ] ), // Bad - missing sanitization.
499+ default => $ _REQUEST ['keyD ' ], // Bad - missing sanitization, unslash, validation.
500+ };
501+ }
502+ }
0 commit comments