From a1fd68696739703c57e6c07245259dd4dc18a4d5 Mon Sep 17 00:00:00 2001 From: George Nicolaou Date: Sat, 20 Jun 2026 23:04:03 +0300 Subject: [PATCH 1/3] Fix false positive for files with only require_once and class definitions. --- .../Plugin_Repo/Direct_File_Access_Check.php | 6 +++++- .../includes/require-once-class-only.php | 18 ++++++++++++++++++ .../Checks/Direct_File_Access_Check_Tests.php | 1 + 3 files changed, 24 insertions(+), 1 deletion(-) create mode 100644 tests/phpunit/testdata/plugins/test-plugin-direct-file-access-without-errors/includes/require-once-class-only.php diff --git a/includes/Checker/Checks/Plugin_Repo/Direct_File_Access_Check.php b/includes/Checker/Checks/Plugin_Repo/Direct_File_Access_Check.php index 62be77f8e..f5ba854c2 100644 --- a/includes/Checker/Checks/Plugin_Repo/Direct_File_Access_Check.php +++ b/includes/Checker/Checks/Plugin_Repo/Direct_File_Access_Check.php @@ -509,6 +509,10 @@ private function is_safe_expression( $expr ) { return true; } + if ( $expr instanceof Expr\Include_ ) { + return true; + } + if ( $this->is_unsafe_expression( $expr ) ) { return false; } @@ -895,7 +899,7 @@ private function has_procedural_code( $contents ) { private function has_only_safe_function_calls( $contents ) { $safe_if_count = preg_match_all( '/if\s*\([^)]*(?:class_exists|function_exists|interface_exists|trait_exists|defined)\s*\(/i', $contents ); $return_count = preg_match_all( '/return\s*;/', $contents ); - $all_function_calls = preg_match_all( '/\b(?!class_exists|function_exists|interface_exists|trait_exists|defined|return|if|else|elseif|isset|empty|unset|array|list|echo|print)\w+\s*\(/i', $contents ); + $all_function_calls = preg_match_all( '/\b(?!class_exists|function_exists|interface_exists|trait_exists|defined|return|if|else|elseif|isset|empty|unset|array|list|echo|print|require|require_once|include|include_once)\w+\s*\(/i', $contents ); return $safe_if_count > 0 && $return_count >= $safe_if_count && 0 === $all_function_calls; } diff --git a/tests/phpunit/testdata/plugins/test-plugin-direct-file-access-without-errors/includes/require-once-class-only.php b/tests/phpunit/testdata/plugins/test-plugin-direct-file-access-without-errors/includes/require-once-class-only.php new file mode 100644 index 000000000..b27b5dfaa --- /dev/null +++ b/tests/phpunit/testdata/plugins/test-plugin-direct-file-access-without-errors/includes/require-once-class-only.php @@ -0,0 +1,18 @@ +assertArrayNotHasKey( 'includes/class-only-file.php', $errors ); $this->assertArrayNotHasKey( 'includes/namespace-class-only.php', $errors ); + $this->assertArrayNotHasKey( 'includes/require-once-class-only.php', $errors ); } /** From 3813175092f61553c18220e97aed6172a3b92e52 Mon Sep 17 00:00:00 2001 From: George Nicolaou Date: Sun, 21 Jun 2026 14:03:29 +0300 Subject: [PATCH 2/3] Fix include handling for direct file access check --- .../Plugin_Repo/Direct_File_Access_Check.php | 40 ++++++++++++++++--- 1 file changed, 34 insertions(+), 6 deletions(-) diff --git a/includes/Checker/Checks/Plugin_Repo/Direct_File_Access_Check.php b/includes/Checker/Checks/Plugin_Repo/Direct_File_Access_Check.php index f5ba854c2..5885de4f5 100644 --- a/includes/Checker/Checks/Plugin_Repo/Direct_File_Access_Check.php +++ b/includes/Checker/Checks/Plugin_Repo/Direct_File_Access_Check.php @@ -400,8 +400,9 @@ private function is_ast_valid_for_direct_access( array $ast ) { Stmt\Enum_::class, ); - $has_assignments = false; - $has_returns = false; + $has_assignments = false; + $has_returns = false; + $has_structural_declaration = $this->has_structural_declaration( $ast ); foreach ( $ast as $node ) { $node_class = get_class( $node ); @@ -428,6 +429,10 @@ private function is_ast_valid_for_direct_access( array $ast ) { } if ( $node instanceof Stmt\Expression ) { + if ( $node->expr instanceof Expr\Include_ && $has_structural_declaration ) { + continue; + } + if ( $this->is_safe_expression( $node->expr ) ) { continue; } @@ -455,6 +460,33 @@ private function is_ast_valid_for_direct_access( array $ast ) { return true; } + /** + * Checks whether the AST contains structural declarations. + * + * @since n.e.x.t + * + * @param array $ast The parsed AST nodes. + * @return bool True if the AST contains class/interface/trait/enum declarations, false otherwise. + */ + private function has_structural_declaration( array $ast ) { + foreach ( $ast as $node ) { + if ( + $node instanceof Stmt\Class_ || + $node instanceof Stmt\Interface_ || + $node instanceof Stmt\Trait_ || + $node instanceof Stmt\Enum_ + ) { + return true; + } + + if ( $node instanceof Stmt\Namespace_ && ! empty( $node->stmts ) ) { + return $this->has_structural_declaration( $node->stmts ); + } + } + + return false; + } + /** * Checks if an expression is an asset file assignment (variable = array/string). * @@ -509,10 +541,6 @@ private function is_safe_expression( $expr ) { return true; } - if ( $expr instanceof Expr\Include_ ) { - return true; - } - if ( $this->is_unsafe_expression( $expr ) ) { return false; } From 63ec4d5884e446e82d4718fb1b7206b21899cd54 Mon Sep 17 00:00:00 2001 From: George Nicolaou Date: Sun, 21 Jun 2026 14:23:39 +0300 Subject: [PATCH 3/3] Fix Codecov workflow inputs --- .github/workflows/php-test.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/php-test.yml b/.github/workflows/php-test.yml index 3ff6b5bca..d017d1300 100644 --- a/.github/workflows/php-test.yml +++ b/.github/workflows/php-test.yml @@ -112,7 +112,7 @@ jobs: if: ${{ matrix.coverage }} uses: codecov/codecov-action@e79a6962e0d4c0c17b229090214935d2e33f8354 with: - file: build/logs/*.xml + files: build/logs/*.xml flags: unit token: ${{ secrets.CODECOV_TOKEN }} @@ -158,6 +158,6 @@ jobs: if: ${{ matrix.coverage }} uses: codecov/codecov-action@e79a6962e0d4c0c17b229090214935d2e33f8354 with: - file: build/logs/*.xml + files: build/logs/*.xml flags: phpcs-sniffs token: ${{ secrets.CODECOV_TOKEN }}