diff --git a/app/Helpers/SSH.php b/app/Helpers/SSH.php index 934d3cf27..c9a4042aa 100755 --- a/app/Helpers/SSH.php +++ b/app/Helpers/SSH.php @@ -170,8 +170,11 @@ public function exec(string|View $command, string $log = '', ?int $siteId = null try { if ($this->asUser !== null && $this->asUser !== '' && $this->asUser !== '0') { - $command = base64_encode((string) $command); - $command = "sudo su - {$this->asUser} -c 'bash -c \"echo {$command} | base64 -d | bash\"'"; + $command = <<asUser} bash <<'EOF' + {$command} + EOF + BASH; } $this->connection->setTimeout(0); diff --git a/tests/Unit/Models/ServerModelTest.php b/tests/Unit/Models/ServerModelTest.php index e4a441972..33eca1fda 100644 --- a/tests/Unit/Models/ServerModelTest.php +++ b/tests/Unit/Models/ServerModelTest.php @@ -4,7 +4,10 @@ use App\Enums\ServerStatus; use App\Facades\SSH; +use App\Helpers\SSH as SSHHelper; use Illuminate\Foundation\Testing\RefreshDatabase; +use phpseclib3\Net\SSH2; +use ReflectionProperty; use Tests\TestCase; class ServerModelTest extends TestCase @@ -47,4 +50,61 @@ public function test_connection_failed(): void 'status' => ServerStatus::DISCONNECTED, ]); } + + public function test_exec_wraps_command_when_using_custom_user(): void + { + $ssh = (new SSHHelper)->init($this->server, 'deploy'); + + $connection = $this->getMockBuilder(SSH2::class) + ->disableOriginalConstructor() + ->onlyMethods(['setTimeout', 'exec', 'getExitStatus', 'disconnect']) + ->getMock(); + + $executedCommand = null; + + $connection->expects($this->once()) + ->method('setTimeout') + ->with(0); + + $connection->expects($this->once()) + ->method('exec') + ->with( + $this->isType('string'), + $this->isType('callable') + ) + ->willReturnCallback(function ($command, $callback) use (&$executedCommand) { + $executedCommand = $command; + $callback(''); + + return ''; + }); + + $connection->expects($this->once()) + ->method('getExitStatus') + ->willReturn(0); + + $connection->method('disconnect'); + + $reflection = new ReflectionProperty(SSHHelper::class, 'connection'); + $reflection->setAccessible(true); + $reflection->setValue($ssh, $connection); + + $command = <<<'BASH' +pwd +ls -la +BASH; + + $output = $ssh->exec($command); + $ssh->disconnect(); + + $expected = <<<'BASH' +sudo -u deploy bash <<'EOF' +pwd +ls -la +EOF +BASH; + + $this->assertSame('', $output); + $this->assertSame($expected, $executedCommand); + } }