Skip to content
Closed
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
Significance: patch
Type: fixed

WAF: Skip HTTP rule evaluation when no REQUEST_METHOD is present, preventing false-positive blocks on server-side cron jobs executed via PHP wrappers.
4 changes: 3 additions & 1 deletion projects/packages/waf/src/class-waf-runner.php
Original file line number Diff line number Diff line change
Expand Up @@ -239,7 +239,9 @@ public static function run() {
define( 'JETPACK_WAF_RUN', defined( 'ABSPATH' ) ? 'plugin' : 'preload' );

// if the WAF is being run before a command line script, don't try to execute rules (there's no request).
if ( PHP_SAPI === 'cli' ) {
// Also skip when there is no REQUEST_METHOD, which covers PHP wrappers that don't report PHP_SAPI as 'cli'
// but still lack an HTTP context (e.g. server-side cron jobs run via a php-wrapper executable).
if ( PHP_SAPI === 'cli' || empty( $_SERVER['REQUEST_METHOD'] ) ) {
return;
}

Expand Down
41 changes: 41 additions & 0 deletions projects/packages/waf/tests/php/unit/WafRunnerTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@

use Automattic\Jetpack\Waf\Waf_Constants;
use Automattic\Jetpack\Waf\Waf_Runner;
use PHPUnit\Framework\Attributes\PreserveGlobalState;
use PHPUnit\Framework\Attributes\RunInSeparateProcess;

/**
Expand Down Expand Up @@ -51,4 +52,44 @@
$this->assertSame( '/pseudo/dir/jetpack-waf', JETPACK_WAF_DIR );
$this->assertSame( '/pseudo/dir/../wp-config.php', JETPACK_WAF_WPCONFIG );
}

/**
* Test that run exits early and skips rule evaluation when REQUEST_METHOD is absent.
*
* This reproduces the scenario where wp-cron.php is executed directly via a PHP
* wrapper that does not set REQUEST_METHOD (no HTTP context), which caused rule 911100
* to fire a 403 because the empty request method was not in the allowed-methods list.
*
* @runInSeparateProcess
* @preserveGlobalState disabled
*/
#[RunInSeparateProcess]
#[PreserveGlobalState(false)]

Check failure on line 67 in projects/packages/waf/tests/php/unit/WafRunnerTest.php

View workflow job for this annotation

GitHub Actions / PHP Code Sniffer (non-excluded files only)

Expected 1 spaces before closing parenthesis; 0 found (PEAR.Functions.FunctionCallSignature.SpaceBeforeCloseBracket)

Check failure on line 67 in projects/packages/waf/tests/php/unit/WafRunnerTest.php

View workflow job for this annotation

GitHub Actions / PHP Code Sniffer (non-excluded files only)

Expected 1 spaces after opening parenthesis; 0 found (PEAR.Functions.FunctionCallSignature.SpaceAfterOpenBracket)
public function testRunSkipsRulesWhenRequestMethodIsAbsent() {
define( 'ABSPATH', '/pseudo' );
define( 'WP_CONTENT_DIR', '/pseudo/dir' );

// Simulate a non-HTTP execution context: no REQUEST_METHOD present.
unset( $_SERVER['REQUEST_METHOD'] );

// Create a temporary rules file that would set a flag if evaluated.
$rules_dir = sys_get_temp_dir() . '/jetpack-waf-test-' . uniqid();
$rules_file = $rules_dir . '/rules.php';
mkdir( $rules_dir );
file_put_contents( $rules_file, '<?php define( "JETPACK_WAF_RULES_EXECUTED", true );' );

define( 'JETPACK_WAF_DIR', $rules_dir );
define( 'JETPACK_WAF_WPCONFIG', '/pseudo/wp-config.php' );
define( 'JETPACK_WAF_ENTRYPOINT', 'rules.php' );
define( 'JETPACK_WAF_MODE', 'normal' );

Waf_Runner::run();

// The rules file must NOT have been included: no HTTP context means no rules.
$this->assertFalse( defined( 'JETPACK_WAF_RULES_EXECUTED' ), 'WAF rules must not be evaluated when REQUEST_METHOD is absent.' );

// Clean up.
unlink( $rules_file );
rmdir( $rules_dir );
}
}
Loading