defmodule Argos.Validation do @moduledoc """ Módulo de validación de inputs para Argos. Proporciona funciones de validación exhaustivas para prevenir errores y mejorar la seguridad del sistema. """ @max_process_name_length 255 @process_name_pattern ~r/^[a-zA-Z0-9_.-]+$/ @doc """ Valida un nombre de proceso. ## Validaciones - Debe ser un string no vacío - Longitud máxima: 255 caracteres - Solo caracteres alfanuméricos, puntos, guiones y guiones bajos ## Ejemplos iex> Argos.Validation.validate_process_name("my_process") :ok iex> Argos.Validation.validate_process_name("") {:error, "Process name cannot be empty"} iex> Argos.Validation.validate_process_name("invalid@name") {:error, "Process name contains invalid characters"} """ @spec validate_process_name(String.t()) :: :ok | {:error, String.t()} def validate_process_name(name) when is_binary(name) do cond do String.length(name) == 0 -> {:error, "Process name cannot be empty"} String.length(name) > @max_process_name_length -> {:error, "Process name too long (max #{@max_process_name_length} characters)"} not String.match?(name, @process_name_pattern) -> {:error, "Process name contains invalid characters"} true -> :ok end end def validate_process_name(_invalid) do {:error, "Process name must be a string"} end @doc """ Valida un comando a ejecutar. ## Validaciones - No puede ser nil - Si es string, no puede estar vacío - Si es lista, debe tener al menos un elemento ## Ejemplos iex> Argos.Validation.validate_command("echo test") :ok iex> Argos.Validation.validate_command("") {:error, "Command cannot be empty"} iex> Argos.Validation.validate_command(["echo", "test"]) :ok """ @spec validate_command(String.t() | [String.t()] | term()) :: :ok | {:error, String.t()} def validate_command(command) when is_binary(command) do if String.trim(command) == "" do {:error, "Command cannot be empty"} else :ok end end def validate_command(command) when is_list(command) do if Enum.empty?(command) do {:error, "Command list cannot be empty"} else :ok end end def validate_command(nil) do {:error, "Command cannot be nil"} end def validate_command(_invalid) do {:error, "Command must be a string or a list of strings"} end @doc """ Valida una especificación de worker. ## Validaciones - `id` no puede ser nil - `tasks` debe ser una lista no vacía ## Ejemplos iex> Argos.Validation.validate_worker_spec(%{id: :w1, tasks: [fn -> :ok end]}) :ok iex> Argos.Validation.validate_worker_spec(%{id: nil, tasks: [fn -> :ok end]}) {:error, "Worker id cannot be nil"} iex> Argos.Validation.validate_worker_spec(%{id: :w1, tasks: []}) {:error, "Worker tasks cannot be empty"} """ @spec validate_worker_spec(map()) :: :ok | {:error, String.t()} def validate_worker_spec(%{id: id, tasks: tasks}) when is_list(tasks) do cond do id == nil -> {:error, "Worker id cannot be nil"} Enum.empty?(tasks) -> {:error, "Worker tasks cannot be empty"} true -> :ok end end def validate_worker_spec(_invalid) do {:error, "Worker spec must be a map with :id and :tasks keys"} end @doc """ Escapa un nombre de proceso para prevenir injection en comandos shell. ## Ejemplos iex> Argos.Validation.escape_process_name("my_process") "my_process" iex> Argos.Validation.escape_process_name("process'name") "process'\\''name" """ @spec escape_process_name(String.t()) :: String.t() def escape_process_name(name) when is_binary(name) do String.replace(name, "'", "'\\''") end @doc """ Valida un path de archivo o directorio. ## Validaciones - Debe ser un string no vacío - No puede contener caracteres peligrosos ## Ejemplos iex> Argos.Validation.validate_path("/path/to/file") :ok iex> Argos.Validation.validate_path("") {:error, "Path cannot be empty"} """ @spec validate_path(String.t()) :: :ok | {:error, String.t()} def validate_path(path) when is_binary(path) do if String.trim(path) == "" do {:error, "Path cannot be empty"} else :ok end end def validate_path(_invalid) do {:error, "Path must be a string"} end end