diff --git a/app/Livewire/Project/Shared/ExecuteContainerCommand.php b/app/Livewire/Project/Shared/ExecuteContainerCommand.php index 621ab1bac..d12d8e26a 100644 --- a/app/Livewire/Project/Shared/ExecuteContainerCommand.php +++ b/app/Livewire/Project/Shared/ExecuteContainerCommand.php @@ -168,18 +168,42 @@ class ExecuteContainerCommand extends Component return; } try { + // Validate container name format + if (! preg_match('/^[a-zA-Z0-9][a-zA-Z0-9_.-]*$/', $this->selected_container)) { + throw new \InvalidArgumentException('Invalid container name format'); + } + + // Verify container exists in our allowed list $container = collect($this->containers)->firstWhere('container.Names', $this->selected_container); if (is_null($container)) { throw new \RuntimeException('Container not found.'); } - $server = data_get($this->container, 'server'); + + // Verify server ownership and status + $server = data_get($container, 'server'); + if (! $server || ! $server instanceof Server) { + throw new \RuntimeException('Invalid server configuration.'); + } if ($server->isForceDisabled()) { throw new \RuntimeException('Server is disabled.'); } + + // Additional ownership verification based on resource type + $resourceServer = match ($this->type) { + 'application' => $this->resource->destination->server, + 'database' => $this->resource->destination->server, + 'service' => $this->resource->server, + default => throw new \RuntimeException('Invalid resource type.') + }; + + if ($server->id !== $resourceServer->id && ! $this->resource->additional_servers->contains('id', $server->id)) { + throw new \RuntimeException('Server ownership verification failed.'); + } + $this->dispatch( 'send-terminal-command', - isset($container), + true, data_get($container, 'container.Names'), data_get($container, 'server.uuid') ); diff --git a/app/Livewire/Project/Shared/Terminal.php b/app/Livewire/Project/Shared/Terminal.php index 5af8f057e..d8f101277 100644 --- a/app/Livewire/Project/Shared/Terminal.php +++ b/app/Livewire/Project/Shared/Terminal.php @@ -29,11 +29,20 @@ class Terminal extends Component $server = Server::ownedByCurrentTeam()->whereUuid($serverUuid)->firstOrFail(); if ($isContainer) { + // Validate container identifier format (alphanumeric, dashes, and underscores only) + if (! preg_match('/^[a-zA-Z0-9][a-zA-Z0-9_.-]*$/', $identifier)) { + throw new \InvalidArgumentException('Invalid container identifier format'); + } + + // Verify container exists and belongs to the user's team $status = getContainerStatus($server, $identifier); if ($status !== 'running') { return; } - $command = SshMultiplexingHelper::generateSshCommand($server, "docker exec -it {$identifier} sh -c 'PATH=\$PATH:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin && if [ -f ~/.profile ]; then . ~/.profile; fi && if [ -n \"\$SHELL\" ]; then exec \$SHELL; else sh; fi'"); + + // Escape the identifier for shell usage + $escapedIdentifier = escapeshellarg($identifier); + $command = SshMultiplexingHelper::generateSshCommand($server, "docker exec -it {$escapedIdentifier} sh -c 'PATH=\$PATH:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin && if [ -f ~/.profile ]; then . ~/.profile; fi && if [ -n \"\$SHELL\" ]; then exec \$SHELL; else sh; fi'"); } else { $command = SshMultiplexingHelper::generateSshCommand($server, 'PATH=$PATH:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin && if [ -f ~/.profile ]; then . ~/.profile; fi && if [ -n "$SHELL" ]; then exec $SHELL; else sh; fi'); }