diff --git a/exec/bootstrap.php b/exec/bootstrap.php index 05633d7..2f600de 100644 --- a/exec/bootstrap.php +++ b/exec/bootstrap.php @@ -32,6 +32,8 @@ foreach ([ require_once PLUGIN_EXEC_DIR . '/handlers/_form_helpers.php'; try { + Http::bootstrapGlobals(); + $settings = Settings::load(Settings::EXTERNAL_SETTINGS); BackendRuntimeConfig::sync($settings, PLUGIN_ROOT . '/scripts/backend.sh', Settings::CONFIG_DIR . '/backend-state.json'); $user = DirectAdminUser::fromEnvironment($settings); diff --git a/exec/lib/Http.php b/exec/lib/Http.php index 27b375a..aea8b71 100644 --- a/exec/lib/Http.php +++ b/exec/lib/Http.php @@ -3,6 +3,72 @@ declare(strict_types=1); final class Http { + public static function bootstrapGlobals(): void + { + if (PHP_SAPI !== 'cli') { + return; + } + + $query = getenv('QUERY_STRING') ?: ''; + $method = strtoupper((string)(getenv('REQUEST_METHOD') ?: 'GET')); + + $_GET = []; + if ($query !== '') { + parse_str($query, $_GET); + } + + $_POST = []; + if ($method === 'POST') { + $rawPost = (string)(getenv('POST') ?: ''); + if ($rawPost === '') { + $rawPost = (string)(getenv('HTTP_POST') ?: ''); + } + + $contentType = strtolower((string)(getenv('CONTENT_TYPE') ?: '')); + $isUrlEncoded = ($contentType === '' || str_contains($contentType, 'application/x-www-form-urlencoded')); + if ($rawPost === '' && $isUrlEncoded) { + $stdin = @file_get_contents('php://stdin'); + if (is_string($stdin) && $stdin !== '') { + $rawPost = $stdin; + } + } + + if ($rawPost !== '') { + parse_str($rawPost, $_POST); + } + } + + $_REQUEST = array_merge($_GET, $_POST); + $_SERVER['REQUEST_METHOD'] = $method; + $_SERVER['QUERY_STRING'] = $query; + + foreach ([ + 'USER', + 'USERNAME', + 'HOME', + 'LANGUAGE', + 'SESSION_ID', + 'REQUEST_URI', + 'HTTP_HOST', + 'HTTPS', + 'HTTP_USER_AGENT', + 'HTTP_X_FORWARDED_PROTO', + 'HTTP_FRONT_END_HTTPS', + 'REQUEST_SCHEME', + 'SERVER_NAME', + 'SERVER_PORT', + 'REMOTE_ADDR', + 'SKIN', + ] as $key) { + if (!isset($_SERVER[$key])) { + $value = getenv($key); + if ($value !== false) { + $_SERVER[$key] = $value; + } + } + } + } + public static function method(): string { return strtoupper((string)($_SERVER['REQUEST_METHOD'] ?? 'GET')); diff --git a/plugin.conf b/plugin.conf index 9f06463..769a382 100644 --- a/plugin.conf +++ b/plugin.conf @@ -2,7 +2,7 @@ name=global-autoresponder id=global-autoresponder type=user author=HITME.PL -version=1.0.10 +version=1.0.11 active=no installed=no user_run_as=root diff --git a/tests/csrf_test.php b/tests/csrf_test.php index ea9ae4a..22dd7fc 100644 --- a/tests/csrf_test.php +++ b/tests/csrf_test.php @@ -2,6 +2,7 @@ declare(strict_types=1); require_once PLUGIN_ROOT . '/exec/lib/CsrfGuard.php'; +require_once PLUGIN_ROOT . '/exec/lib/Http.php'; require_once PLUGIN_ROOT . '/exec/lib/Settings.php'; require_once PLUGIN_ROOT . '/exec/lib/Lang.php'; require_once PLUGIN_ROOT . '/exec/lib/DirectAdminUser.php'; @@ -37,3 +38,31 @@ test('csrf form fields use plugin scoped names to avoid directadmin token collis assert_contains('name="gar_csrf_token"', $html); assert_false(str_contains($html, 'name="csrf_token"')); }); + +test('http bootstrap populates post data from directadmin cli environment', function (): void { + $oldGet = $_GET; + $oldPost = $_POST; + $oldRequest = $_REQUEST; + $oldServer = $_SERVER; + + putenv('REQUEST_METHOD=POST'); + putenv('QUERY_STRING=id=abc'); + putenv('POST=gar_csrf_ts=123&gar_csrf_sid=sid&gar_csrf_token=token&body=Hello+World'); + + try { + Http::bootstrapGlobals(); + assert_same('POST', Http::method()); + assert_same('abc', Http::getString($_GET, 'id')); + assert_same('123', Http::getString($_POST, 'gar_csrf_ts')); + assert_same('token', Http::getString($_POST, 'gar_csrf_token')); + assert_same('Hello World', Http::getString($_POST, 'body')); + } finally { + putenv('REQUEST_METHOD'); + putenv('QUERY_STRING'); + putenv('POST'); + $_GET = $oldGet; + $_POST = $oldPost; + $_REQUEST = $oldRequest; + $_SERVER = $oldServer; + } +});