fix(nodeShell): detect bash shell [C9S-298] (#3328)

This commit is contained in:
bernard-portainer
2026-08-06 13:46:58 +12:00
committed by GitHub
parent df4c700667
commit 6af854dfbc
4 changed files with 68 additions and 2 deletions
+1 -2
View File
@@ -4,7 +4,6 @@ import (
"errors" "errors"
"io" "io"
"net/http" "net/http"
"strings"
portainer "github.com/portainer/portainer/api" portainer "github.com/portainer/portainer/api"
"github.com/portainer/portainer/api/http/proxy/factory/kubernetes" "github.com/portainer/portainer/api/http/proxy/factory/kubernetes"
@@ -123,7 +122,7 @@ func (handler *Handler) hijackPodExecStartOperation(
endpoint *portainer.Endpoint, endpoint *portainer.Endpoint,
namespace, podName, containerName, command string, namespace, podName, containerName, command string,
) *httperror.HandlerError { ) *httperror.HandlerError {
commandArray := strings.Split(command, " ") commandArray := ws.SplitExecCommand(command)
websocketConn, err := handler.connectionUpgrader.Upgrade(w, r, nil) websocketConn, err := handler.connectionUpgrader.Upgrade(w, r, nil)
if err != nil { if err != nil {
+41
View File
@@ -0,0 +1,41 @@
package ws
import "strings"
// SplitExecCommand splits a command string into exec argv, treating a
// single-quoted segment as one argument even if it contains spaces (e.g.
// `sh -c 'echo a b'` stays 3 tokens instead of being shredded on every
// space). Single quotes are not escapable, matching POSIX shell semantics for
// single-quoted strings; there is no support for double quotes since callers
// only ever need one quoted tail argument (a `sh -c` script).
func SplitExecCommand(command string) []string {
var (
args []string
current strings.Builder
inQuote bool
started bool
)
for _, r := range command {
switch {
case r == '\'':
inQuote = !inQuote
started = true
case r == ' ' && !inQuote:
if started {
args = append(args, current.String())
current.Reset()
started = false
}
default:
current.WriteRune(r)
started = true
}
}
if started {
args = append(args, current.String())
}
return args
}
+25
View File
@@ -0,0 +1,25 @@
package ws
import (
"testing"
"github.com/stretchr/testify/require"
)
func TestSplitExecCommand(t *testing.T) {
t.Parallel()
f := func(input string, expected []string) {
t.Helper()
require.Equal(t, expected, SplitExecCommand(input))
}
f("", nil)
f("bash", []string{"bash"})
f("env TERM=xterm-256color /bin/bash", []string{"env", "TERM=xterm-256color", "/bin/bash"})
f("sh -c 'echo a b'", []string{"sh", "-c", "echo a b"})
f("sh -c 'command -v bash >/dev/null 2>&1 && exec bash || exec sh'",
[]string{"sh", "-c", "command -v bash >/dev/null 2>&1 && exec bash || exec sh"})
f("a b", []string{"a", "b"})
f("'unterminated", []string{"unterminated"})
}
+1
View File
@@ -111,6 +111,7 @@ module.exports = {
{ {
context: ['/api'], context: ['/api'],
target: 'http://localhost:9000', target: 'http://localhost:9000',
ws: true,
}, },
], ],
open: true, open: true,