mirror of
https://github.com/portainer/portainer.git
synced 2026-08-07 10:04:49 +00:00
fix(nodeShell): detect bash shell [C9S-298] (#3328)
This commit is contained in:
committed by
GitHub
parent
df4c700667
commit
6af854dfbc
@@ -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 {
|
||||||
|
|||||||
@@ -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
|
||||||
|
}
|
||||||
@@ -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"})
|
||||||
|
}
|
||||||
@@ -111,6 +111,7 @@ module.exports = {
|
|||||||
{
|
{
|
||||||
context: ['/api'],
|
context: ['/api'],
|
||||||
target: 'http://localhost:9000',
|
target: 'http://localhost:9000',
|
||||||
|
ws: true,
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
open: true,
|
open: true,
|
||||||
|
|||||||
Reference in New Issue
Block a user