From d96724ea2a6a38f854d59477a40b5483ece7487b Mon Sep 17 00:00:00 2001 From: Anand-240 Date: Tue, 4 Aug 2026 04:38:34 +0530 Subject: [PATCH] fix(unikernels): reject single quotes in Linux init args parseCmdLine() wrapped multi-word arguments in unescaped single quotes for urunit compatibility. An argument containing both a space and a single quote, for example sh -c "echo it's broken", produced an unbalanced quote sequence, corrupting the token-boundary parsing that urunit performs on /proc/cmdline and causing the guest init process to receive the wrong arguments. Since urunit's cmdline parser lives outside this repo and its escaping semantics are unknown, reject arguments containing a single quote with a clear error instead of guessing an escaping scheme. Fixes: #897 Signed-off-by: Anand-240 --- pkg/unikontainers/unikernels/linux.go | 8 ++- pkg/unikontainers/unikernels/linux_test.go | 80 ++++++++++++++++++++++ 2 files changed, 87 insertions(+), 1 deletion(-) create mode 100644 pkg/unikontainers/unikernels/linux_test.go diff --git a/pkg/unikontainers/unikernels/linux.go b/pkg/unikontainers/unikernels/linux.go index bca93e5a9..0a2e3be1e 100644 --- a/pkg/unikontainers/unikernels/linux.go +++ b/pkg/unikontainers/unikernels/linux.go @@ -250,10 +250,16 @@ func (l *Linux) parseCmdLine(cmdLine []string) error { return fmt.Errorf("no init was specified") } - // Wrap multi-word arguments in quotes for urunit + // Wrap multi-word arguments in quotes for urunit. Arguments that already + // contain a single quote cannot be safely wrapped this way, since naively + // concatenating quotes produces an unbalanced quote sequence that + // corrupts urunit's token-boundary parsing of the boot cmdline. normalizedArgs := make([]string, len(cmdLine)) for i, arg := range cmdLine { arg = strings.TrimSpace(arg) + if strings.Contains(arg, "'") { + return fmt.Errorf("argument %q contains an unsupported single quote character", arg) + } if strings.Contains(arg, " ") { normalizedArgs[i] = "'" + arg + "'" } else { diff --git a/pkg/unikontainers/unikernels/linux_test.go b/pkg/unikontainers/unikernels/linux_test.go new file mode 100644 index 000000000..0db2501cd --- /dev/null +++ b/pkg/unikontainers/unikernels/linux_test.go @@ -0,0 +1,80 @@ +// Copyright (c) 2023-2026, Nubificus LTD +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package unikernels + +import ( + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func TestLinuxParseCmdLine(t *testing.T) { + t.Parallel() + + testCases := []struct { + name string + cmdLine []string + expectedApp string + expectedCmd string + expectErr bool + }{ + { + name: "empty cmdline", + cmdLine: []string{}, + expectErr: true, + }, + { + name: "single word app only", + cmdLine: []string{"/urunit"}, + expectedApp: "/urunit", + expectedCmd: "", + }, + { + name: "multi-word argument gets quoted", + cmdLine: []string{"/urunit", "sh -c ls"}, + expectedApp: "/urunit", + expectedCmd: "'sh -c ls'", + }, + { + name: "argument with space and single quote is rejected", + cmdLine: []string{"/urunit", "sh -c \"echo it's broken\""}, + expectErr: true, + }, + { + name: "argument with single quote and no space is rejected", + cmdLine: []string{"/urunit", "it's"}, + expectErr: true, + }, + } + + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + t.Parallel() + + l := &Linux{} + err := l.parseCmdLine(tc.cmdLine) + + if tc.expectErr { + require.Error(t, err) + return + } + + require.NoError(t, err) + assert.Equal(t, tc.expectedApp, l.App) + assert.Equal(t, tc.expectedCmd, l.Command) + }) + } +}