diff --git a/workflow/engine/methods/setup/pluginsChange.php b/workflow/engine/methods/setup/pluginsChange.php index f51c1b16a..4dccbfc48 100644 --- a/workflow/engine/methods/setup/pluginsChange.php +++ b/workflow/engine/methods/setup/pluginsChange.php @@ -9,9 +9,31 @@ use ProcessMaker\Plugins\PluginRegistry; +/** + * Main execution block - handles plugin change requests and returns JSON response + */ +try { + $result = handlePluginChange(); + header('Content-Type: application/json'); + echo json_encode($result); +} catch (Exception $e) { + // Handle any unexpected errors with proper HTTP status + header('Content-Type: application/json'); + http_response_code(400); + echo json_encode(['success' => false, 'error' => 'Invalid request']); +} + +/** + * Handles plugin enable/disable operations with security validation + * + * @return array Response array with success status and details + * @throws InvalidArgumentException When input parameters are invalid + * @throws SecurityException When security validation fails + * @throws FileNotFoundException When plugin files are not found + */ function handlePluginChange() { try { - // Validar parĂ¡metros de entrada + // Validate required input parameters if (!isset($_GET['id']) || !isset($_GET['status'])) { throw new InvalidArgumentException('Missing required parameters'); } @@ -19,40 +41,44 @@ function handlePluginChange() { $pluginFile = $_GET['id']; $pluginStatus = $_GET['status']; - // Validar formato del archivo de plugin + // Validate plugin file format (alphanumeric, underscore, hyphen + .php extension) if (!preg_match('/^[a-zA-Z0-9_-]+\.php$/', $pluginFile)) { throw new InvalidArgumentException('Invalid plugin file format'); } - // Validar status + // Validate status parameter (only '0' or '1' allowed) if (!in_array($pluginStatus, ['0', '1'], true)) { throw new InvalidArgumentException('Invalid plugin status'); } - // Sanitizar nombre del plugin + // Sanitize plugin name by removing directory components and extension $pluginName = basename(str_replace(".php", "", $pluginFile)); + // Additional validation for plugin name format if (!preg_match('/^[a-zA-Z0-9_-]+$/', $pluginName)) { throw new InvalidArgumentException('Invalid plugin name format'); } - // Validar rutas de forma segura + // Secure path validation to prevent directory traversal attacks $pluginFilePath = realpath(PATH_PLUGINS . $pluginFile); $pluginDirPath = realpath(PATH_PLUGINS . $pluginName); $pluginsDir = realpath(PATH_PLUGINS); + // Ensure plugin file is within the allowed plugins directory if (!$pluginFilePath || strpos($pluginFilePath, $pluginsDir) !== 0) { throw new SecurityException('Plugin file outside allowed directory'); } + // Verify plugin file exists if (!is_file($pluginFilePath)) { throw new FileNotFoundException('Plugin file not found'); } + // Get plugin registry singleton instance $oPluginRegistry = PluginRegistry::loadSingleton(); if ($pluginStatus === '1') { - // Deshabilitar plugin + // Disable plugin operation $details = $oPluginRegistry->getPluginDetails($pluginFile); if ($details) { $oPluginRegistry->disablePlugin($details->getNamespace()); @@ -61,13 +87,14 @@ function handlePluginChange() { return ['success' => true, 'action' => 'disabled', 'plugin' => $details->getNamespace()]; } } else { - // Habilitar plugin + // Enable plugin operation if ($pluginDirPath && is_dir($pluginDirPath)) { + // Safely include the validated plugin file require_once($pluginFilePath); $details = $oPluginRegistry->getPluginDetails($pluginFile); if ($details) { $oPluginRegistry->enablePlugin($details->getNamespace()); - $oPluginRegistry->setupPlugins(); + $oPluginRegistry->setupPlugins(); // Initialize all enabled plugins $oPluginRegistry->savePlugin($details->getNamespace()); G::auditLog("EnablePlugin", "Plugin Name: " . $details->getNamespace()); return ['success' => true, 'action' => 'enabled', 'plugin' => $details->getNamespace()]; @@ -77,21 +104,12 @@ function handlePluginChange() { } } + // Fallback response if no operation was performed return ['success' => false, 'error' => 'Plugin operation failed']; } catch (Exception $e) { + // Log error and return failure response G::auditLog('PluginChange', 'Error: ' . $e->getMessage()); return ['success' => false, 'error' => $e->getMessage()]; } } - -// Ejecutar y devolver respuesta JSON -try { - $result = handlePluginChange(); - header('Content-Type: application/json'); - echo json_encode($result); -} catch (Exception $e) { - header('Content-Type: application/json'); - http_response_code(400); - echo json_encode(['success' => false, 'error' => 'Invalid request']); -} \ No newline at end of file