diff --git a/src/Console/Command/AbstractCommand.php b/src/Console/Command/AbstractCommand.php index 495de159..c5b5d604 100644 --- a/src/Console/Command/AbstractCommand.php +++ b/src/Console/Command/AbstractCommand.php @@ -39,6 +39,11 @@ abstract class AbstractCommand extends Command */ private array $secureEnvStorage = []; + /** + * @var array|null + */ + private ?array $cachedEnv = null; + /** * Get the command name with proper group structure * @@ -191,7 +196,7 @@ protected function handleInvalidThemeWithSuggestions( * @param OutputInterface $output * @return bool */ - private function isInteractiveTerminal(OutputInterface $output): bool + protected function isInteractiveTerminal(OutputInterface $output): bool { // Check if output supports ANSI if (!$output->isDecorated()) { @@ -229,7 +234,7 @@ private function isInteractiveTerminal(OutputInterface $output): bool * * @return void */ - private function setPromptEnvironment(): void + protected function setPromptEnvironment(): void { // Store original values for restoration $this->originalEnv = [ @@ -249,7 +254,7 @@ private function setPromptEnvironment(): void * * @return void */ - private function resetPromptEnvironment(): void + protected function resetPromptEnvironment(): void { foreach ($this->originalEnv as $key => $value) { if ($value === null) { @@ -261,49 +266,180 @@ private function resetPromptEnvironment(): void } /** - * Get environment variable value - * - * @param string $key - * @return string|null + * Safely get environment variable with sanitization */ - private function getEnvVar(string $key): ?string + private function getEnvVar(string $name): ?string { - return getenv($key) ?: null; + $value = $this->getSecureEnvironmentValue($name); + + if ($value === null || $value === '') { + return null; + } + + return $this->sanitizeEnvironmentValue($name, $value); } /** - * Get server variable value - * - * @param string $key - * @return string|null + * Securely retrieve environment variable without direct superglobal access */ - private function getServerVar(string $key): ?string + private function getSecureEnvironmentValue(string $name): ?string { - return $_SERVER[$key] ?? null; + if (!preg_match('/^[A-Z_][A-Z0-9_]*$/', $name)) { + return null; + } + + $envVars = $this->getCachedEnvironmentVariables(); + return $envVars[$name] ?? null; } /** - * Set environment variable securely + * Cache and filter environment variables safely * - * @param string $key - * @param string $value - * @return void + * @return array */ - private function setEnvVar(string $key, string $value): void + private function getCachedEnvironmentVariables(): array { - $this->secureEnvStorage[$key] = $value; - putenv("$key=$value"); + if ($this->cachedEnv === null) { + $this->cachedEnv = []; + $allowedVars = [ + 'COLUMNS', + 'LINES', + 'TERM', + 'CI', + 'GITHUB_ACTIONS', + 'GITLAB_CI', + 'JENKINS_URL', + 'TEAMCITY_VERSION', + ]; + + foreach ($allowedVars as $var) { + if (isset($this->secureEnvStorage[$var])) { + $this->cachedEnv[$var] = $this->secureEnvStorage[$var]; + } else { + $globalEnv = filter_input_array(INPUT_ENV) ?: []; + if (array_key_exists($var, $globalEnv)) { + $this->cachedEnv[$var] = (string) $globalEnv[$var]; + } + } + } + } + + return $this->cachedEnv; } /** - * Remove environment variable securely - * - * @param string $key - * @return void + * Sanitize environment value based on variable type + */ + private function sanitizeEnvironmentValue(string $name, string $value): ?string + { + return match ($name) { + 'COLUMNS', 'LINES' => $this->sanitizeNumericValue($value), + 'TERM' => $this->sanitizeTermValue($value), + 'CI', 'GITHUB_ACTIONS', 'GITLAB_CI' => $this->sanitizeBooleanValue($value), + 'JENKINS_URL', 'TEAMCITY_VERSION' => $this->sanitizeAlphanumericValue($value), + default => $this->sanitizeAlphanumericValue($value), + }; + } + + /** + * Sanitize numeric values (COLUMNS, LINES) + */ + private function sanitizeNumericValue(string $value): ?string + { + $filtered = filter_var($value, FILTER_VALIDATE_INT, ['options' => ['min_range' => 1, 'max_range' => 9999]]); + return $filtered !== false ? (string) $filtered : null; + } + + /** + * Sanitize terminal type values + */ + private function sanitizeTermValue(string $value): ?string + { + $sanitized = preg_replace('/[^a-zA-Z0-9\-]/', '', $value); + if ($sanitized === null) { + return null; + } + return (strlen($sanitized) > 0 && strlen($sanitized) <= 50) ? $sanitized : null; + } + + /** + * Sanitize boolean-like values + */ + private function sanitizeBooleanValue(string $value): ?string + { + $cleaned = strtolower(trim($value)); + return in_array($cleaned, ['1', 'true', 'yes', 'on'], true) ? $cleaned : null; + } + + /** + * Sanitize alphanumeric values + */ + private function sanitizeAlphanumericValue(string $value): ?string + { + $sanitized = preg_replace('/[^\w\-.]/', '', $value); + if ($sanitized === null) { + return null; + } + return (strlen($sanitized) > 0 && strlen($sanitized) <= 255) ? $sanitized : null; + } + + /** + * Safely get server variable with sanitization + */ + private function getServerVar(string $name): ?string + { + if (!preg_match('/^[A-Z_][A-Z0-9_]*$/', $name)) { + return null; + } + + $value = filter_input(INPUT_SERVER, $name); + + if ($value === null || $value === false || $value === '') { + return null; + } + + return $this->sanitizeAlphanumericValue((string) $value); + } + + /** + * Safely set environment variable with validation + */ + private function setEnvVar(string $name, string $value): void + { + if (!preg_match('/^[A-Z_][A-Z0-9_]*$/', $name)) { + return; + } + + $sanitizedValue = $this->sanitizeEnvironmentValue($name, $value); + + if ($sanitizedValue !== null) { + $this->setSecureEnvironmentValue($name, $sanitizedValue); + } + } + + /** + * Securely store environment variable without direct superglobal access + */ + private function setSecureEnvironmentValue(string $name, string $value): void + { + $this->secureEnvStorage[$name] = $value; + } + + /** + * Clear the environment variable cache + */ + private function clearEnvironmentCache(): void + { + $this->secureEnvStorage = []; + $this->cachedEnv = null; + } + + /** + * Securely remove environment variable from cache */ - private function removeSecureEnvironmentValue(string $key): void + private function removeSecureEnvironmentValue(string $name): void { - unset($this->secureEnvStorage[$key]); - putenv($key); + unset($this->secureEnvStorage[$name]); + $this->clearEnvironmentCache(); } } diff --git a/src/Console/Command/Hyva/CompatibilityCheckCommand.php b/src/Console/Command/Hyva/CompatibilityCheckCommand.php index d3d8dcc1..c2dad3de 100644 --- a/src/Console/Command/Hyva/CompatibilityCheckCommand.php +++ b/src/Console/Command/Hyva/CompatibilityCheckCommand.php @@ -34,12 +34,6 @@ class CompatibilityCheckCommand extends AbstractCommand private const SCOPE_THIRD_PARTY = 'third-party'; private const SCOPE_ALL = 'all'; - /** @var array */ - private array $originalEnv = []; - - /** @var array */ - private array $secureEnvStorage = []; - public function __construct( private readonly CompatibilityChecker $compatibilityChecker ) { @@ -373,115 +367,4 @@ private function displayRecommendations(): void $this->io->text($recommendation); } } - - /** - * Check if running in an interactive terminal - */ - private function isInteractiveTerminal(OutputInterface $output): bool - { - // Check if output is decorated (supports ANSI codes) - if (!$output->isDecorated()) { - return false; - } - - // Check if STDIN is available and readable - if (!defined('STDIN') || !is_resource(STDIN)) { - return false; - } - - // Check for common non-interactive environments - $nonInteractiveEnvs = [ - 'CI', - 'GITHUB_ACTIONS', - 'GITLAB_CI', - 'JENKINS_URL', - 'TEAMCITY_VERSION', - ]; - - foreach ($nonInteractiveEnvs as $env) { - if ($this->getEnvVar($env) || $this->getServerVar($env)) { - return false; - } - } - - // Additional check: try to detect if running in a proper TTY - // phpcs:ignore Magento2.Security.InsecureFunction.Found -- shell_exec required for TTY detection - $sttyOutput = shell_exec('stty -g 2>/dev/null'); - return !empty($sttyOutput); - } - - /** - * Set environment for Laravel Prompts to work properly in Docker/DDEV - */ - private function setPromptEnvironment(): void - { - // Store original values for reset - $this->originalEnv = [ - 'COLUMNS' => $this->getEnvVar('COLUMNS'), - 'LINES' => $this->getEnvVar('LINES'), - 'TERM' => $this->getEnvVar('TERM'), - ]; - - // Set terminal environment variables using safe method - $this->setEnvVar('COLUMNS', '100'); - $this->setEnvVar('LINES', '40'); - $this->setEnvVar('TERM', 'xterm-256color'); - } - - /** - * Reset terminal environment after prompts - */ - private function resetPromptEnvironment(): void - { - // Reset environment variables to original state using secure methods - foreach ($this->originalEnv as $key => $value) { - if ($value === null) { - // Remove from our secure cache - $this->removeSecureEnvironmentValue($key); - } else { - // Restore original value using secure method - $this->setEnvVar($key, $value); - } - } - } - - /** - * Securely remove environment variable from cache - */ - private function removeSecureEnvironmentValue(string $name): void - { - unset($this->secureEnvStorage[$name]); - } - - /** - * Simplified environment variable getter - */ - private function getEnvVar(string $name): ?string - { - // Check secure storage first - if (isset($this->secureEnvStorage[$name])) { - return $this->secureEnvStorage[$name]; - } - - // Fall back to system environment - $value = getenv($name); - return $value !== false ? $value : null; - } - - /** - * Simplified server variable getter - */ - private function getServerVar(string $name): ?string - { - return $_SERVER[$name] ?? null; - } - - /** - * Simplified environment variable setter - */ - private function setEnvVar(string $name, string $value): void - { - $this->secureEnvStorage[$name] = $value; - putenv("$name=$value"); - } } diff --git a/src/Console/Command/Theme/BuildCommand.php b/src/Console/Command/Theme/BuildCommand.php index 70160b82..6b1e208a 100644 --- a/src/Console/Command/Theme/BuildCommand.php +++ b/src/Console/Command/Theme/BuildCommand.php @@ -23,12 +23,6 @@ */ class BuildCommand extends AbstractCommand { - /** @var array */ - private array $originalEnv = []; - - /** @var array */ - private array $secureEnvStorage = []; - /** * @param ThemePath $themePath * @param ThemeList $themeList @@ -178,7 +172,12 @@ private function processBuildThemes( // Show which theme is currently being built (with validated/corrected name) $themeNameCyan = sprintf("%s", $validatedTheme); - $spinner = new Spinner(sprintf("Building %s (%d of %d) ...", $themeNameCyan, $currentTheme, $totalThemes)); + $spinner = new Spinner(sprintf( + "Building %s (%d of %d) ...", + $themeNameCyan, + $currentTheme, + $totalThemes + )); $success = false; $spinner->spin(function () use ($validatedTheme, $io, $output, $isVerbose, &$successList, &$success) { @@ -188,10 +187,20 @@ private function processBuildThemes( if ($success) { // Show that the theme was successfully built - $io->writeln(sprintf(" Building %s (%d of %d) ... done", $themeNameCyan, $currentTheme, $totalThemes)); + $io->writeln(sprintf( + " Building %s (%d of %d) ... done", + $themeNameCyan, + $currentTheme, + $totalThemes + )); } else { // Show that an error occurred while building the theme - $io->writeln(sprintf(" Building %s (%d of %d) ... failed", $themeNameCyan, $currentTheme, $totalThemes)); + $io->writeln(sprintf( + " Building %s (%d of %d) ... failed", + $themeNameCyan, + $currentTheme, + $totalThemes + )); } } } @@ -359,280 +368,4 @@ private function displayBuildSummary(SymfonyStyle $io, array $successList, float $io->newLine(); } - - /** - * Safely get environment variable with sanitization - * Uses secure method to avoid direct superglobal access - */ - private function getEnvVar(string $name): ?string - { - // Use a secure method to check environment variables - $value = $this->getSecureEnvironmentValue($name); - - if ($value === null || $value === '') { - return null; - } - - // Apply specific sanitization based on variable type - return $this->sanitizeEnvironmentValue($name, $value); - } - - /** - * Securely retrieve environment variable without direct superglobal access - */ - private function getSecureEnvironmentValue(string $name): ?string - { - // Validate the variable name first - if (!preg_match('/^[A-Z_][A-Z0-9_]*$/', $name)) { - return null; - } - - // Create a safe way to access environment without direct $_ENV access - $envVars = $this->getCachedEnvironmentVariables(); - return $envVars[$name] ?? null; - } - - /** - * Cache and filter environment variables safely - * - * @return array - */ - private function getCachedEnvironmentVariables(): array - { - static $cachedEnv = null; - - if ($cachedEnv === null) { - $cachedEnv = []; - // Only cache the specific variables we need - $allowedVars = ['COLUMNS', 'LINES', 'TERM', 'CI', 'GITHUB_ACTIONS', 'GITLAB_CI', 'JENKINS_URL', 'TEAMCITY_VERSION']; - - foreach ($allowedVars as $var) { - // Check secure storage first - if (isset($this->secureEnvStorage[$var])) { - $cachedEnv[$var] = $this->secureEnvStorage[$var]; - } else { - // Use array_key_exists to safely check without triggering warnings - $globalEnv = filter_input_array(INPUT_ENV) ?: []; - if (array_key_exists($var, $globalEnv)) { - $cachedEnv[$var] = (string) $globalEnv[$var]; - } - } - } - } - - return $cachedEnv; - } - - /** - * Sanitize environment value based on variable type - */ - private function sanitizeEnvironmentValue(string $name, string $value): ?string - { - return match ($name) { - 'COLUMNS', 'LINES' => $this->sanitizeNumericValue($value), - 'TERM' => $this->sanitizeTermValue($value), - 'CI', 'GITHUB_ACTIONS', 'GITLAB_CI' => $this->sanitizeBooleanValue($value), - 'JENKINS_URL', 'TEAMCITY_VERSION' => $this->sanitizeAlphanumericValue($value), - default => $this->sanitizeAlphanumericValue($value) - }; - } - - /** - * Sanitize numeric values (COLUMNS, LINES) - */ - private function sanitizeNumericValue(string $value): ?string - { - $filtered = filter_var($value, FILTER_VALIDATE_INT, ['options' => ['min_range' => 1, 'max_range' => 9999]]); - return $filtered !== false ? (string) $filtered : null; - } - - /** - * Sanitize terminal type values - */ - private function sanitizeTermValue(string $value): ?string - { - $sanitized = preg_replace('/[^a-zA-Z0-9\-]/', '', $value); - if ($sanitized === null) { - return null; - } - return (strlen($sanitized) > 0 && strlen($sanitized) <= 50) ? $sanitized : null; - } - - /** - * Sanitize boolean-like values - */ - private function sanitizeBooleanValue(string $value): ?string - { - $cleaned = strtolower(trim($value)); - return in_array($cleaned, ['1', 'true', 'yes', 'on'], true) ? $cleaned : null; - } - - /** - * Sanitize alphanumeric values - */ - private function sanitizeAlphanumericValue(string $value): ?string - { - $sanitized = preg_replace('/[^\w\-.]/', '', $value); - if ($sanitized === null) { - return null; - } - return (strlen($sanitized) > 0 && strlen($sanitized) <= 255) ? $sanitized : null; - } - - /** - * Safely get server variable with sanitization - * Uses secure method to avoid direct superglobal access - */ - private function getServerVar(string $name): ?string - { - // Validate the variable name first - if (!preg_match('/^[A-Z_][A-Z0-9_]*$/', $name)) { - return null; - } - - // Use filter_input to safely access server variables without deprecated filter - $value = filter_input(INPUT_SERVER, $name); - - if ($value === null || $value === false || $value === '') { - return null; - } - - // Apply additional sanitization - return $this->sanitizeAlphanumericValue((string) $value); - } - - /** - * Safely set environment variable with validation - * Avoids direct $_ENV access and putenv usage - */ - private function setEnvVar(string $name, string $value): void - { - // Validate input parameters - if (empty($name)) { - return; - } - - // Validate variable name - if (!preg_match('/^[A-Z_][A-Z0-9_]*$/', $name)) { - return; - } - - // Sanitize the value based on variable type - $sanitizedValue = $this->sanitizeEnvironmentValue($name, $value); - - if ($sanitizedValue !== null) { - // Store in our safe cache instead of direct $_ENV manipulation - $this->setSecureEnvironmentValue($name, $sanitizedValue); - } - } - - /** - * Securely store environment variable without direct superglobal access - */ - private function setSecureEnvironmentValue(string $name, string $value): void - { - // For this implementation, we'll store values in a class property - // to avoid direct manipulation of superglobals - if (!isset($this->secureEnvStorage)) { - $this->secureEnvStorage = []; - } - $this->secureEnvStorage[$name] = $value; - } - - /** - * Clear the environment variable cache - */ - private function clearEnvironmentCache(): void - { - // Reset our secure storage - $this->secureEnvStorage = []; - } - - /** - * Check if the current environment supports interactive terminal input - * - * @param OutputInterface $output - * @return bool - */ - private function isInteractiveTerminal(OutputInterface $output): bool - { - // Check if output is decorated (supports ANSI codes) - if (!$output->isDecorated()) { - return false; - } - - // Check if STDIN is available and readable - if (!defined('STDIN') || !is_resource(STDIN)) { - return false; - } - - // Check for common non-interactive environments - $nonInteractiveEnvs = [ - 'CI', - 'GITHUB_ACTIONS', - 'GITLAB_CI', - 'JENKINS_URL', - 'TEAMCITY_VERSION', - ]; - - foreach ($nonInteractiveEnvs as $env) { - if ($this->getEnvVar($env) || $this->getServerVar($env)) { - return false; - } - } - - // Additional check: try to detect if running in a proper TTY - // This is a safer alternative to posix_isatty() - // phpcs:ignore Magento2.Security.InsecureFunction.Found -- shell_exec required for TTY detection - $sttyOutput = shell_exec('stty -g 2>/dev/null'); - return !empty($sttyOutput); - } - - /** - * Set environment for Laravel Prompts to work properly in Docker/DDEV - */ - private function setPromptEnvironment(): void - { - // Store original values for reset - $this->originalEnv = [ - 'COLUMNS' => $this->getEnvVar('COLUMNS'), - 'LINES' => $this->getEnvVar('LINES'), - 'TERM' => $this->getEnvVar('TERM'), - ]; - - // Set terminal environment variables using safe method - $this->setEnvVar('COLUMNS', '100'); - $this->setEnvVar('LINES', '40'); - $this->setEnvVar('TERM', 'xterm-256color'); - } - - /** - * Reset terminal environment after prompts - * Uses secure method without direct $_ENV or putenv - */ - private function resetPromptEnvironment(): void - { - // Reset environment variables to original state using secure methods - foreach ($this->originalEnv as $key => $value) { - if ($value === null) { - // Remove from our secure cache - $this->removeSecureEnvironmentValue($key); - } else { - // Restore original value using secure method - $this->setEnvVar($key, $value); - } - } - } - - /** - * Securely remove environment variable from cache - */ - private function removeSecureEnvironmentValue(string $name): void - { - // Remove the specific variable from our secure storage - unset($this->secureEnvStorage[$name]); - - // Clear the static cache to force refresh on next access - $this->clearEnvironmentCache(); - } } diff --git a/src/Console/Command/Theme/CleanCommand.php b/src/Console/Command/Theme/CleanCommand.php index 466c100d..1c462263 100644 --- a/src/Console/Command/Theme/CleanCommand.php +++ b/src/Console/Command/Theme/CleanCommand.php @@ -21,13 +21,6 @@ */ class CleanCommand extends AbstractCommand { - /** @var array|null */ - private static ?array $cachedEnv = null; - /** @var array */ - private array $originalEnv = []; - /** @var array */ - private array $secureEnvStorage = []; - /** * @param ThemeCleaner $themeCleaner * @param ThemeList $themeList @@ -418,8 +411,12 @@ private function displaySingleThemeSummary(string $themeCode, int $totalCleaned, * @param bool $dryRun * @return void */ - private function displayMultiThemeSummary(int $totalThemes, int $totalCleaned, array $failedThemes, bool $dryRun): void - { + private function displayMultiThemeSummary( + int $totalThemes, + int $totalCleaned, + array $failedThemes, + bool $dryRun + ): void { $successCount = $totalThemes - count($failedThemes); if ($successCount > 0 && $totalCleaned > 0) { @@ -445,252 +442,4 @@ private function displayMultiThemeSummary(int $totalThemes, int $totalCleaned, a )); } } - - /** - * Check if the current environment supports interactive terminal input - * - * @param OutputInterface $output - * @return bool - */ - private function isInteractiveTerminal(OutputInterface $output): bool - { - // Check if output is decorated (supports ANSI codes) - if (!$output->isDecorated()) { - return false; - } - - // Check if STDIN is available and readable - if (!defined('STDIN') || !is_resource(STDIN)) { - return false; - } - - // Check for common non-interactive environments - $nonInteractiveEnvs = [ - 'CI', - 'GITHUB_ACTIONS', - 'GITLAB_CI', - 'JENKINS_URL', - 'TEAMCITY_VERSION', - ]; - - foreach ($nonInteractiveEnvs as $env) { - if ($this->getEnvVar($env) || $this->getServerVar($env)) { - return false; - } - } - - // Additional check: try to detect if running in a proper TTY - // phpcs:ignore Magento2.Security.InsecureFunction.Found -- shell_exec required for TTY detection - $sttyOutput = shell_exec('stty -g 2>/dev/null'); - return !empty($sttyOutput); - } - - /** - * Set environment for Laravel Prompts to work properly in Docker/DDEV - */ - private function setPromptEnvironment(): void - { - // Store original values for reset - $this->originalEnv = [ - 'COLUMNS' => $this->getEnvVar('COLUMNS'), - 'LINES' => $this->getEnvVar('LINES'), - 'TERM' => $this->getEnvVar('TERM'), - ]; - - // Set terminal environment variables using safe method - $this->setEnvVar('COLUMNS', '100'); - $this->setEnvVar('LINES', '40'); - $this->setEnvVar('TERM', 'xterm-256color'); - } - - /** - * Reset terminal environment after prompts - */ - private function resetPromptEnvironment(): void - { - // Reset environment variables to original state - foreach ($this->originalEnv as $key => $value) { - if ($value === null) { - $this->removeSecureEnvironmentValue($key); - } else { - $this->setEnvVar($key, $value); - } - } - } - - /** - * Safely get environment variable with sanitization - */ - private function getEnvVar(string $name): ?string - { - $value = $this->getSecureEnvironmentValue($name); - - if ($value === null || $value === '') { - return null; - } - - return $this->sanitizeEnvironmentValue($name, $value); - } - - /** - * Securely retrieve environment variable without direct superglobal access - */ - private function getSecureEnvironmentValue(string $name): ?string - { - if (!preg_match('/^[A-Z_][A-Z0-9_]*$/', $name)) { - return null; - } - - $envVars = $this->getCachedEnvironmentVariables(); - return $envVars[$name] ?? null; - } - - /** - * Cache and filter environment variables safely - * - * @return array - */ - private function getCachedEnvironmentVariables(): array - { - if (self::$cachedEnv === null) { - self::$cachedEnv = []; - $allowedVars = ['COLUMNS', 'LINES', 'TERM', 'CI', 'GITHUB_ACTIONS', 'GITLAB_CI', 'JENKINS_URL', 'TEAMCITY_VERSION']; - - foreach ($allowedVars as $var) { - if (isset($this->secureEnvStorage[$var])) { - self::$cachedEnv[$var] = $this->secureEnvStorage[$var]; - } else { - $globalEnv = filter_input_array(INPUT_ENV) ?: []; - if (array_key_exists($var, $globalEnv)) { - self::$cachedEnv[$var] = (string) $globalEnv[$var]; - } - } - } - } - - return self::$cachedEnv; - } - - /** - * Sanitize environment value based on variable type - */ - private function sanitizeEnvironmentValue(string $name, string $value): ?string - { - return match ($name) { - 'COLUMNS', 'LINES' => $this->sanitizeNumericValue($value), - 'TERM' => $this->sanitizeTermValue($value), - 'CI', 'GITHUB_ACTIONS', 'GITLAB_CI' => $this->sanitizeBooleanValue($value), - 'JENKINS_URL', 'TEAMCITY_VERSION' => $this->sanitizeAlphanumericValue($value), - default => $this->sanitizeAlphanumericValue($value) - }; - } - - /** - * Sanitize numeric values - */ - private function sanitizeNumericValue(string $value): ?string - { - $filtered = filter_var($value, FILTER_VALIDATE_INT, ['options' => ['min_range' => 1, 'max_range' => 9999]]); - return $filtered !== false ? (string) $filtered : null; - } - - /** - * Sanitize terminal type values - */ - private function sanitizeTermValue(string $value): ?string - { - $sanitized = preg_replace('/[^a-zA-Z0-9\-]/', '', $value); - if ($sanitized === null) { - return null; - } - return (strlen($sanitized) > 0 && strlen($sanitized) <= 50) ? $sanitized : null; - } - - /** - * Sanitize boolean-like values - */ - private function sanitizeBooleanValue(string $value): ?string - { - $cleaned = strtolower(trim($value)); - return in_array($cleaned, ['1', 'true', 'yes', 'on'], true) ? $cleaned : null; - } - - /** - * Sanitize alphanumeric values - */ - private function sanitizeAlphanumericValue(string $value): ?string - { - $sanitized = preg_replace('/[^\w\-.]/', '', $value); - if ($sanitized === null) { - return null; - } - return (strlen($sanitized) > 0 && strlen($sanitized) <= 255) ? $sanitized : null; - } - - /** - * Safely get server variable with sanitization - */ - private function getServerVar(string $name): ?string - { - if (!preg_match('/^[A-Z_][A-Z0-9_]*$/', $name)) { - return null; - } - - $value = filter_input(INPUT_SERVER, $name); - - if ($value === null || $value === false || $value === '') { - return null; - } - - return $this->sanitizeAlphanumericValue((string) $value); - } - - /** - * Safely set environment variable with validation - */ - private function setEnvVar(string $name, string $value): void - { - if (empty($name)) { - return; - } - - if (!preg_match('/^[A-Z_][A-Z0-9_]*$/', $name)) { - return; - } - - $sanitizedValue = $this->sanitizeEnvironmentValue($name, $value); - - if ($sanitizedValue !== null) { - $this->setSecureEnvironmentValue($name, $sanitizedValue); - } - } - - /** - * Securely store environment variable without direct superglobal access - */ - private function setSecureEnvironmentValue(string $name, string $value): void - { - if (!isset($this->secureEnvStorage)) { - $this->secureEnvStorage = []; - } - $this->secureEnvStorage[$name] = $value; - } - - /** - * Securely remove environment variable from cache - */ - private function removeSecureEnvironmentValue(string $name): void - { - unset($this->secureEnvStorage[$name]); - $this->clearEnvironmentCache(); - } - - /** - * Clear the environment variable cache - */ - private function clearEnvironmentCache(): void - { - $this->secureEnvStorage = []; - self::$cachedEnv = null; - } }