Skip to content

Commit 04e6488

Browse files
Merge pull request #25011 from AhmedMoalla/fix-wrongly-ported-cunescape_one
Fix unescaping octal escape sequence in values of Quadlet unit files
2 parents ec6b035 + 68f29df commit 04e6488

File tree

3 files changed

+91
-12
lines changed

3 files changed

+91
-12
lines changed

pkg/systemd/parser/split.go

Lines changed: 3 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -170,14 +170,7 @@ func cUnescapeOne(p string, acceptNul bool) (int, rune, bool) {
170170

171171
ret = rune(c)
172172
count = 9
173-
case '0':
174-
case '1':
175-
case '2':
176-
case '3':
177-
case '4':
178-
case '5':
179-
case '6':
180-
case '7':
173+
case '0', '1', '2', '3', '4', '5', '6', '7':
181174
/* octal encoding */
182175

183176
if len(p) < 3 {
@@ -189,12 +182,12 @@ func cUnescapeOne(p string, acceptNul bool) (int, rune, bool) {
189182
return -1, 0, false
190183
}
191184

192-
b := unoctchar(p[0])
185+
b := unoctchar(p[1])
193186
if b < 0 {
194187
return -1, 0, false
195188
}
196189

197-
c := unoctchar(p[0])
190+
c := unoctchar(p[2])
198191
if c < 0 {
199192
return -1, 0, false
200193
}

pkg/systemd/parser/split_test.go

Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
package parser
2+
3+
import (
4+
"testing"
5+
6+
"github.com/stretchr/testify/assert"
7+
"github.com/stretchr/testify/require"
8+
)
9+
10+
func TestCUnescapeOne(t *testing.T) {
11+
t.Parallel()
12+
13+
tests := []struct {
14+
name string
15+
in string
16+
acceptNul bool
17+
18+
ret rune
19+
count int
20+
eightBit bool
21+
}{
22+
{name: "empty", in: "", ret: 0, count: -1},
23+
{name: `invalid \k`, in: "k", ret: 0, count: -1},
24+
{name: `\a`, in: "a", ret: '\a', count: 1},
25+
{name: `\b`, in: "b", ret: '\b', count: 1},
26+
{name: `\f`, in: "f", ret: '\f', count: 1},
27+
{name: `\n`, in: "n", ret: '\n', count: 1},
28+
{name: `\r`, in: "r", ret: '\r', count: 1},
29+
{name: `\t`, in: "t", ret: '\t', count: 1},
30+
{name: `\v`, in: "v", ret: '\v', count: 1},
31+
{name: `\\`, in: "\\", ret: '\\', count: 1},
32+
{name: `"`, in: "\"", ret: '"', count: 1},
33+
{name: `'`, in: "'", ret: '\'', count: 1},
34+
{name: `\s`, in: "s", ret: ' ', count: 1},
35+
{name: `too short \x1`, in: "x1", ret: 0, count: -1},
36+
{name: `invalid hex \xzz`, in: "xzz", ret: 0, count: -1},
37+
{name: `invalid hex \xaz`, in: "xaz", ret: 0, count: -1},
38+
{name: `\xAb1`, in: "xAb1", ret: '«', count: 3, eightBit: true},
39+
{name: `\x000 acceptNul=false`, in: "x000", ret: 0, count: -1},
40+
{name: `\x000 acceptNul=true`, in: "x000", ret: 0, count: 3, eightBit: true, acceptNul: true},
41+
{name: `too short \u123`, in: "u123", ret: 0, count: -1},
42+
{name: `\u2a00`, in: "u2a00", ret: '⨀', count: 5},
43+
{name: `invalid hex \u12v1A`, in: "u12v1A", ret: 0, count: -1},
44+
{name: `\u0000 acceptNul=false`, in: "u0000", ret: 0, count: -1},
45+
{name: `\u0000 acceptNul=true`, in: "u0000", ret: 0, count: 5, acceptNul: true},
46+
{name: `too short \U123`, in: "U123", ret: 0, count: -1},
47+
{name: `invalid unicode \U12345678`, in: "U12345678", ret: 0, count: -1},
48+
{name: `invalid hex \U1234V678`, in: "U1234V678", ret: 0, count: -10},
49+
{name: `\U0001F51F`, in: "U0001F51F", ret: '🔟', count: 9},
50+
{name: `\U00000000 acceptNul=false`, in: "U00000000", ret: 0, count: -1, acceptNul: false},
51+
{name: `\U00000000 acceptNul=true`, in: "U00000000", ret: 0, count: 9, acceptNul: true},
52+
{name: "376", in: "376", ret: 'þ', count: 3, eightBit: true},
53+
{name: `too short 77`, in: "77", ret: 0, count: -1},
54+
{name: `invalid octal 792`, in: "792", ret: 0, count: -1},
55+
{name: `invalid octal 758`, in: "758", ret: 0, count: -1},
56+
{name: `000 acceptNul=false`, in: "000", ret: 0, count: -1},
57+
{name: `000 acceptNul=true`, in: "000", ret: 0, count: 3, acceptNul: true, eightBit: true},
58+
{name: `too big 777 > 255 bytes`, in: "777", ret: 0, count: -1},
59+
}
60+
61+
for _, test := range tests {
62+
t.Run(test.name, func(t *testing.T) {
63+
t.Parallel()
64+
65+
count, out, eightBit := cUnescapeOne(test.in, test.acceptNul)
66+
assert.Equal(t, test.count, count)
67+
assert.Equal(t, test.ret, out)
68+
assert.Equal(t, test.eightBit, eightBit)
69+
})
70+
}
71+
}
72+
73+
func TestExtractFirstWordUnescapes(t *testing.T) {
74+
input := `\a \b \f \n \r \t \v \\ \" \' \s \x50odman is \U0001F51F/\u0031\u0030 \110ello \127orld`
75+
expected := []string{"\a", "\b", "\f", "\n", "\r", "\t", "\v", "\\", "\"", "'", " ",
76+
"Podman", "is", "🔟/10", "Hello", "World"}
77+
78+
next := input
79+
for i := range expected {
80+
word, remaining, _, err := extractFirstWord(next, " ", SplitCUnescape)
81+
require.NoError(t, err)
82+
83+
next = remaining
84+
assert.Equal(t, expected[i], word)
85+
}
86+
}

test/e2e/quadlet/escapes.container

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
1-
## assert-podman-final-args "/some/path" "an arg" "a;b\\nc\\td'e" "a;b\\nc\\td" "a\"b"
1+
## assert-podman-final-args "/some/path" "an arg" "a;b\\nc\\td'e" "a;b\\nc\\td" "a\"b" "Hello World"
22

33
[Container]
44
Image=localhost/imagename
5-
Exec=/some/path "an arg" "a;b\nc\td'e" a;b\nc\td 'a"b'
5+
Exec=/some/path "an arg" "a;b\nc\td'e" a;b\nc\td 'a"b' '\110ello \127orld'

0 commit comments

Comments
 (0)