diff --git a/go.mod b/go.mod
index 2dc781b..81ef28b 100644
--- a/go.mod
+++ b/go.mod
@@ -3,6 +3,7 @@ module github.com/uselagoon/ssh-portal
 go 1.22.2
 
 require (
+	al.essio.dev/pkg/shellescape v1.5.1
 	github.com/DATA-DOG/go-sqlmock v1.5.2
 	github.com/MicahParks/keyfunc/v2 v2.1.0
 	github.com/alecthomas/assert/v2 v2.11.0
diff --git a/go.sum b/go.sum
index cf41361..cc9811e 100644
--- a/go.sum
+++ b/go.sum
@@ -1,3 +1,5 @@
+al.essio.dev/pkg/shellescape v1.5.1 h1:86HrALUujYS/h+GtqoB26SBEdkWfmMI6FubjXlsXyho=
+al.essio.dev/pkg/shellescape v1.5.1/go.mod h1:6sIqp7X2P6mThCQ7twERpZTuigpr6KbZWtls1U8I890=
 filippo.io/edwards25519 v1.1.0 h1:FNf4tywRC1HmFuKW5xopWpigGjJKiJSV0Cqo0cJWDaA=
 filippo.io/edwards25519 v1.1.0/go.mod h1:BxyFTGdWcka3PhytdK4V28tE5sGfRvvvRV7EaN4VDT4=
 github.com/DATA-DOG/go-sqlmock v1.5.2 h1:OcvFkGmslmlZibjAjaHm3L//6LiuBgolP7OputlJIzU=
@@ -67,6 +69,8 @@ github.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0=
 github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
 github.com/google/pprof v0.0.0-20240525223248-4bfdf5a9a2af h1:kmjWCqn2qkEml422C2Rrd27c3VGxi6a/6HNq8QmHRKM=
 github.com/google/pprof v0.0.0-20240525223248-4bfdf5a9a2af/go.mod h1:K1liHPHnj73Fdn/EKuT8nrFqBihUSKXoLYU0BuatOYo=
+github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 h1:El6M4kTTCOh6aBiKaUGG7oYTSPP8MxqL4YI3kZKwcP4=
+github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510/go.mod h1:pupxD2MaaD3pAXIBCelhxNneeOaAeabZDe5s4K6zSpQ=
 github.com/google/uuid v1.6.1-0.20240806143717-0e97ed3b5379 h1:9pvPp/2VCtCB2xdSUCaKe1VKCzVHMR+GGgIAVLfQxIs=
 github.com/google/uuid v1.6.1-0.20240806143717-0e97ed3b5379/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
 github.com/gorilla/securecookie v1.1.2 h1:YCIWL56dvtr73r6715mJs5ZvhtnY73hBvEF8kXD8ePA=
diff --git a/internal/sshserver/sessionhandler.go b/internal/sshserver/sessionhandler.go
index 5da1e3b..4966bc3 100644
--- a/internal/sshserver/sessionhandler.go
+++ b/internal/sshserver/sessionhandler.go
@@ -5,9 +5,9 @@ import (
 	"fmt"
 	"io"
 	"log/slog"
-	"strings"
 	"time"
 
+	"al.essio.dev/pkg/shellescape"
 	"github.com/gliderlabs/ssh"
 	"github.com/prometheus/client_golang/prometheus"
 	"github.com/prometheus/client_golang/prometheus/promauto"
@@ -86,7 +86,7 @@ func getSSHIntent(sftp bool, cmd []string) []string {
 	// if there is a command, wrap it in a shell the way openssh does
 	// https://github.com/openssh/openssh-portable/blob/
 	// 	73dcca12115aa12ed0d123b914d473c384e52651/session.c#L1705-L1713
-	return []string{"sh", "-c", strings.Join(cmd, " ")}
+	return []string{"sh", "-c", shellescape.QuoteCommand(cmd)}
 }
 
 // sessionHandler returns a ssh.Handler which connects the ssh session to the
diff --git a/internal/sshserver/sessionhandler_test.go b/internal/sshserver/sessionhandler_test.go
index 2185654..7c25f66 100644
--- a/internal/sshserver/sessionhandler_test.go
+++ b/internal/sshserver/sessionhandler_test.go
@@ -39,6 +39,42 @@ func TestExec(t *testing.T) {
 			logAccessEnabled: false,
 			pty:              false,
 		},
+		"subshell": {
+			user:             "project-test",
+			deployment:       "cli",
+			rawCommand:       []string{"/bin/sh", "-c", "( echo foo; echo bar; echo baz ) | tail -n2"},
+			command:          []string{"sh", "-c", "/bin/sh -c '( echo foo; echo bar; echo baz ) | tail -n2'"},
+			sftp:             false,
+			logAccessEnabled: false,
+			pty:              false,
+		},
+		"process substitution 1": {
+			user:             "project-test",
+			deployment:       "cli",
+			rawCommand:       []string{"/bin/sh", "-c", "sleep 3 & echo $(pgrep sleep)"},
+			command:          []string{"sh", "-c", "/bin/sh -c 'sleep 3 & echo $(pgrep sleep)'"},
+			sftp:             false,
+			logAccessEnabled: false,
+			pty:              false,
+		},
+		"process substitution 2": {
+			user:             "project-test",
+			deployment:       "cli",
+			rawCommand:       []string{"/bin/sh", "-c", "sleep 3 & echo $( pgrep sleep )"},
+			command:          []string{"sh", "-c", "/bin/sh -c 'sleep 3 & echo $( pgrep sleep )'"},
+			sftp:             false,
+			logAccessEnabled: false,
+			pty:              false,
+		},
+		"shell variables": {
+			user:             "project-test",
+			deployment:       "cli",
+			rawCommand:       []string{"/bin/sh", "-c", "echo $$ $USER"},
+			command:          []string{"sh", "-c", "/bin/sh -c 'echo $$ $USER'"},
+			sftp:             false,
+			logAccessEnabled: false,
+			pty:              false,
+		},
 	}
 	for name, tc := range testCases {
 		t.Run(name, func(tt *testing.T) {