Skip to content

Commit e0184cf

Browse files
Merge pull request from GHSA-wx8q-4gm9-rj2g
* Fix JuicefsRuntime: escape customized string before constructing commands add escapeBashStr Signed-off-by: xixi <hexilee@juicedata.io> avoid bash -c in operations Signed-off-by: xixi <hexilee@juicedata.io> fix GetUsedSpace and GetFileCount Signed-off-by: xixi <hexilee@juicedata.io> move EscapeBashStr to pkg/utils/security Signed-off-by: xixi <hexilee@juicedata.io> add left Signed-off-by: xixi <hexilee@juicedata.io> resume GetFileCount Signed-off-by: xixi <hexilee@juicedata.io> Escape value.Configs.Name Signed-off-by: trafalgarzzz <trafalgarz@outlook.com> Fix unit tests Signed-off-by: trafalgarzzz <trafalgarz@outlook.com> Upgrade juicefs helm chart version to 0.2.16 Signed-off-by: trafalgarzzz <trafalgarz@outlook.com> * Fix JuicefsRuntime: escape customized string before constructing commands Signed-off-by: trafalgarzzz <trafalgarz@outlook.com> --------- Signed-off-by: trafalgarzzz <trafalgarz@outlook.com> Co-authored-by: xixi <hexilee@juicedata.io>
1 parent 8637168 commit e0184cf

File tree

7 files changed

+166
-32
lines changed

7 files changed

+166
-32
lines changed

Diff for: charts/juicefs/Chart.yaml

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
name: juicefs
22
apiVersion: v1
33
description: FileSystem aimed for data analytics and machine learning in any cloud.
4-
version: 0.2.14
4+
version: 0.2.16
55
appVersion: v1.0.0
66
home: https://juicefs.com/
77
maintainers:

Diff for: pkg/ddc/juicefs/operations/base.go

+14-9
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ import (
2727
"github.com/go-logr/logr"
2828

2929
"github.com/fluid-cloudnative/fluid/pkg/utils/kubeclient"
30+
"github.com/fluid-cloudnative/fluid/pkg/utils/security"
3031
)
3132

3233
type JuiceFileUtils struct {
@@ -130,12 +131,11 @@ func (j JuiceFileUtils) Count(juiceSubPath string) (total int64, err error) {
130131
func (j JuiceFileUtils) GetFileCount(juiceSubPath string) (fileCount int64, err error) {
131132
var (
132133
//strs = "du -ah juiceSubPath |grep ^- |wc -l "
133-
strs = fmt.Sprintf("ls -lR %s |grep ^- |wc -l ", juiceSubPath)
134+
strs = fmt.Sprintf("ls -lR %s |grep ^- |wc -l ", security.EscapeBashStr(juiceSubPath))
134135
command = []string{"bash", "-c", strs}
135136
stdout string
136137
stderr string
137138
)
138-
139139
stdout, stderr, err = j.exec(command)
140140
if err != nil {
141141
err = fmt.Errorf("execute command %v with expectedErr: %v stdout %s and stderr %s", command, err, stdout, stderr)
@@ -266,11 +266,10 @@ func (j JuiceFileUtils) GetMetric(juicefsPath string) (metrics string, err error
266266
}
267267

268268
// GetUsedSpace Get used space in byte
269-
// use "df --block-size=1 |grep <juicefsPath>'"
269+
// equal to `df --block-size=1 | grep juicefsPath`
270270
func (j JuiceFileUtils) GetUsedSpace(juicefsPath string) (usedSpace int64, err error) {
271271
var (
272-
strs = fmt.Sprintf(`df --block-size=1 |grep %s`, juicefsPath)
273-
command = []string{"bash", "-c", strs}
272+
command = []string{"df", "--block-size=1"}
274273
stdout string
275274
stderr string
276275
)
@@ -281,9 +280,15 @@ func (j JuiceFileUtils) GetUsedSpace(juicefsPath string) (usedSpace int64, err e
281280
return
282281
}
283282

283+
var str string
284+
lines := strings.Split(stdout, "\n")
285+
for _, line := range lines {
286+
if strings.Contains(line, juicefsPath) {
287+
str = line
288+
break
289+
}
290+
}
284291
// [<Filesystem> <Size> <Used> <Avail> <Use>% <Mounted on>]
285-
str := strings.TrimSuffix(stdout, "\n")
286-
287292
data := strings.Fields(str)
288293
if len(data) != 6 {
289294
err = fmt.Errorf("failed to parse %s in GetUsedSpace method", data)
@@ -365,8 +370,8 @@ func (j JuiceFileUtils) QueryMetaDataInfoIntoFile(key KeyOfMetaDataFile, filenam
365370
j.log.Error(errors.New("the key not in metadatafile"), "key", key)
366371
}
367372
var (
368-
str = "sed -n '" + line + "' " + filename
369-
command = []string{"bash", "-c", str}
373+
str = "'" + line + "' " + filename
374+
command = []string{"sed", "-n", str}
370375
stdout string
371376
stderr string
372377
)

Diff for: pkg/ddc/juicefs/operations/base_test.go

+2-2
Original file line numberDiff line numberDiff line change
@@ -479,7 +479,7 @@ func TestJuiceFileUtils_GetUsedSpace(t *testing.T) {
479479
t.Fatal(err.Error())
480480
}
481481
a := &JuiceFileUtils{log: fake.NullLogger()}
482-
_, err = a.GetUsedSpace("/tmp")
482+
_, err = a.GetUsedSpace("/runtime-mnt/juicefs/kube-system/jfsdemo/juicefs-fuse")
483483
if err == nil {
484484
t.Error("check failure, want err, got nil")
485485
}
@@ -489,7 +489,7 @@ func TestJuiceFileUtils_GetUsedSpace(t *testing.T) {
489489
if err != nil {
490490
t.Fatal(err.Error())
491491
}
492-
usedSpace, err := a.GetUsedSpace("/tmp")
492+
usedSpace, err := a.GetUsedSpace("/runtime-mnt/juicefs/kube-system/jfsdemo/juicefs-fuse")
493493
if err != nil {
494494
t.Errorf("check failure, want nil, got err: %v", err)
495495
}

Diff for: pkg/ddc/juicefs/transform_fuse.go

+40-15
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ import (
2929
"github.com/fluid-cloudnative/fluid/pkg/common"
3030
"github.com/fluid-cloudnative/fluid/pkg/utils"
3131
"github.com/fluid-cloudnative/fluid/pkg/utils/kubeclient"
32+
"github.com/fluid-cloudnative/fluid/pkg/utils/security"
3233
)
3334

3435
func (j *JuiceFSEngine) transformFuse(runtime *datav1alpha1.JuiceFSRuntime, dataset *datav1alpha1.Dataset, value *JuiceFS) (err error) {
@@ -37,7 +38,7 @@ func (j *JuiceFSEngine) transformFuse(runtime *datav1alpha1.JuiceFSRuntime, data
3738
}
3839
mount := dataset.Spec.Mounts[0]
3940

40-
value.Configs.Name = mount.Name
41+
value.Configs.Name = security.EscapeBashStr(mount.Name)
4142

4243
// transform image
4344
image := runtime.Spec.Fuse.Image
@@ -216,7 +217,7 @@ func (j *JuiceFSEngine) genValue(mount datav1alpha1.Mount, tiredStoreLevel *data
216217
}
217218

218219
if source == "" {
219-
source = mount.Name
220+
source = security.EscapeBashStr(mount.Name)
220221
}
221222

222223
// transform source
@@ -326,8 +327,20 @@ func (j *JuiceFSEngine) genMount(value *JuiceFS, runtime *datav1alpha1.JuiceFSRu
326327
}
327328
workerOptionMap["metrics"] = fmt.Sprintf("0.0.0.0:%d", metricsPort)
328329
}
329-
mountArgs = []string{common.JuiceFSCeMountPath, value.Source, value.Fuse.MountPath, "-o", strings.Join(genOption(optionMap), ",")}
330-
mountArgsWorker = []string{common.JuiceFSCeMountPath, value.Source, value.Worker.MountPath, "-o", strings.Join(genOption(workerOptionMap), ",")}
330+
mountArgs = []string{
331+
common.JuiceFSCeMountPath,
332+
value.Source,
333+
security.EscapeBashStr(value.Fuse.MountPath),
334+
"-o",
335+
security.EscapeBashStr(strings.Join(genOption(optionMap), ",")),
336+
}
337+
mountArgsWorker = []string{
338+
common.JuiceFSCeMountPath,
339+
value.Source,
340+
security.EscapeBashStr(value.Worker.MountPath),
341+
"-o",
342+
security.EscapeBashStr(strings.Join(genOption(workerOptionMap), ",")),
343+
}
331344
} else {
332345
if readonly {
333346
optionMap["attrcacheto"] = "7200"
@@ -347,14 +360,26 @@ func (j *JuiceFSEngine) genMount(value *JuiceFS, runtime *datav1alpha1.JuiceFSRu
347360
optionMap["no-sharing"] = ""
348361
delete(workerOptionMap, "no-sharing")
349362

350-
mountArgs = []string{common.JuiceFSMountPath, value.Source, value.Fuse.MountPath, "-o", strings.Join(genOption(optionMap), ",")}
351-
mountArgsWorker = []string{common.JuiceFSMountPath, value.Source, value.Worker.MountPath, "-o", strings.Join(genOption(workerOptionMap), ",")}
363+
mountArgs = []string{
364+
common.JuiceFSMountPath,
365+
value.Source,
366+
security.EscapeBashStr(value.Fuse.MountPath),
367+
"-o",
368+
security.EscapeBashStr(strings.Join(genOption(optionMap), ",")),
369+
}
370+
mountArgsWorker = []string{
371+
common.JuiceFSMountPath,
372+
value.Source,
373+
security.EscapeBashStr(value.Worker.MountPath),
374+
"-o",
375+
security.EscapeBashStr(strings.Join(genOption(workerOptionMap), ",")),
376+
}
352377
}
353378

354379
value.Worker.Command = strings.Join(mountArgsWorker, " ")
355380
value.Fuse.Command = strings.Join(mountArgs, " ")
356-
value.Fuse.StatCmd = "stat -c %i " + value.Fuse.MountPath
357-
value.Worker.StatCmd = "stat -c %i " + value.Worker.MountPath
381+
value.Fuse.StatCmd = "stat -c %i " + security.EscapeBashStr(value.Fuse.MountPath)
382+
value.Worker.StatCmd = "stat -c %i " + security.EscapeBashStr(value.Worker.MountPath)
358383
return nil
359384
}
360385

@@ -379,7 +404,7 @@ func (j *JuiceFSEngine) genFormatCmd(value *JuiceFS, config *[]string) {
379404
for _, option := range *config {
380405
o := strings.TrimSpace(option)
381406
if o != "" {
382-
args = append(args, fmt.Sprintf("--%s", o))
407+
args = append(args, fmt.Sprintf("--%s", security.EscapeBashStr(o)))
383408
}
384409
}
385410
}
@@ -395,12 +420,12 @@ func (j *JuiceFSEngine) genFormatCmd(value *JuiceFS, config *[]string) {
395420
args = append(args, "--no-update")
396421
}
397422
if value.Configs.Storage != "" {
398-
args = append(args, fmt.Sprintf("--storage=%s", value.Configs.Storage))
423+
args = append(args, fmt.Sprintf("--storage=%s", security.EscapeBashStr(value.Configs.Storage)))
399424
}
400425
if value.Configs.Bucket != "" {
401-
args = append(args, fmt.Sprintf("--bucket=%s", value.Configs.Bucket))
426+
args = append(args, fmt.Sprintf("--bucket=%s", security.EscapeBashStr(value.Configs.Bucket)))
402427
}
403-
args = append(args, value.Source, value.Configs.Name)
428+
args = append(args, value.Source, security.EscapeBashStr(value.Configs.Name))
404429
cmd := append([]string{common.JuiceCeCliPath, "format"}, args...)
405430
value.Configs.FormatCmd = strings.Join(cmd, " ")
406431
return
@@ -418,7 +443,7 @@ func (j *JuiceFSEngine) genFormatCmd(value *JuiceFS, config *[]string) {
418443
args = append(args, "--secretkey=${SECRET_KEY}")
419444
}
420445
if value.Configs.Bucket != "" {
421-
args = append(args, fmt.Sprintf("--bucket=%s", value.Configs.Bucket))
446+
args = append(args, fmt.Sprintf("--bucket=%s", security.EscapeBashStr(value.Configs.Bucket)))
422447
}
423448
args = append(args, value.Source)
424449
cmd := append([]string{common.JuiceCliPath, "auth"}, args...)
@@ -461,7 +486,7 @@ func (j *JuiceFSEngine) genQuotaCmd(value *JuiceFS, mount datav1alpha1.Mount) er
461486
return fmt.Errorf("quota is not supported in juicefs-ce version %s", value.Fuse.ImageTag)
462487
}
463488
// juicefs quota set ${metaurl} --path ${path} --capacity ${capacity}
464-
value.Configs.QuotaCmd = fmt.Sprintf("%s quota set %s --path %s --capacity %d", common.JuiceCeCliPath, value.Source, value.Fuse.SubPath, qs)
489+
value.Configs.QuotaCmd = fmt.Sprintf("%s quota set %s --path %s --capacity %d", common.JuiceCeCliPath, value.Source, security.EscapeBashStr(value.Fuse.SubPath), qs)
465490
return nil
466491
}
467492
// ee
@@ -470,7 +495,7 @@ func (j *JuiceFSEngine) genQuotaCmd(value *JuiceFS, mount datav1alpha1.Mount) er
470495
}
471496
// juicefs quota set ${metaurl} --path ${path} --capacity ${capacity}
472497
cli := common.JuiceCliPath
473-
value.Configs.QuotaCmd = fmt.Sprintf("%s quota set %s --path %s --capacity %d", cli, value.Source, value.Fuse.SubPath, qs)
498+
value.Configs.QuotaCmd = fmt.Sprintf("%s quota set %s --path %s --capacity %d", cli, value.Source, security.EscapeBashStr(value.Fuse.SubPath), qs)
474499
return nil
475500
}
476501
}

Diff for: pkg/ddc/juicefs/ufs_test.go

+5-5
Original file line numberDiff line numberDiff line change
@@ -38,15 +38,15 @@ func mockExecCommandInContainerForTotalFileNums() (stdout string, stderr string,
3838
}
3939

4040
func mockExecCommandInContainerForUsedStorageBytes() (stdout string, stderr string, err error) {
41-
r := `JuiceFS:test 207300683100160 41460043776 207259223056384 1% /data`
41+
r := `JuiceFS:test 207300683100160 41460043776 207259223056384 1% /juicefs/juicefs/test/juicefs-fuse`
4242
return r, "", nil
4343
}
4444

4545
func TestTotalStorageBytes(t *testing.T) {
4646
statefulSet := &appsv1.StatefulSet{
4747
ObjectMeta: metav1.ObjectMeta{
4848
Name: "test-worker",
49-
Namespace: "fluid",
49+
Namespace: "juicefs",
5050
},
5151
Spec: appsv1.StatefulSetSpec{
5252
Selector: &metav1.LabelSelector{
@@ -57,7 +57,7 @@ func TestTotalStorageBytes(t *testing.T) {
5757
var pod = &corev1.Pod{
5858
ObjectMeta: metav1.ObjectMeta{
5959
Name: "test-work-0",
60-
Namespace: "fluid",
60+
Namespace: "juicefs",
6161
Labels: map[string]string{"a": "b"},
6262
},
6363
Status: corev1.PodStatus{
@@ -93,11 +93,11 @@ func TestTotalStorageBytes(t *testing.T) {
9393
name: "test",
9494
fields: fields{
9595
name: "test",
96-
namespace: "fluid",
96+
namespace: "juicefs",
9797
runtime: &datav1alpha1.JuiceFSRuntime{
9898
ObjectMeta: metav1.ObjectMeta{
9999
Name: "test",
100-
Namespace: "fluid",
100+
Namespace: "juicefs",
101101
},
102102
},
103103
},

Diff for: pkg/utils/security/escape.go

+63
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
/*
2+
Copyright 2023 The Fluid Authors.
3+
4+
Licensed under the Apache License, Version 2.0 (the "License");
5+
you may not use this file except in compliance with the License.
6+
You may obtain a copy of the License at
7+
8+
http://www.apache.org/licenses/LICENSE-2.0
9+
10+
Unless required by applicable law or agreed to in writing, software
11+
distributed under the License is distributed on an "AS IS" BASIS,
12+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
See the License for the specific language governing permissions and
14+
limitations under the License.
15+
*/
16+
17+
package security
18+
19+
import (
20+
"fmt"
21+
"strings"
22+
)
23+
24+
// According to https://www.gnu.org/software/bash/manual/html_node/ANSI_002dC-Quoting.html#ANSI_002dC-Quoting
25+
// a -> a
26+
// a b -> a b
27+
// $a -> $'$a'
28+
// $'a' -> $'$\'$a'\'
29+
func EscapeBashStr(s string) string {
30+
if !containsOne(s, []rune{'$', '`', '&', ';', '>', '|', '(', ')'}) {
31+
return s
32+
}
33+
s = strings.ReplaceAll(s, `\`, `\\`)
34+
s = strings.ReplaceAll(s, `'`, `\'`)
35+
if strings.Contains(s, `\\`) {
36+
s = strings.ReplaceAll(s, `\\\\`, `\\`)
37+
s = strings.ReplaceAll(s, `\\\'`, `\'`)
38+
s = strings.ReplaceAll(s, `\\"`, `\"`)
39+
s = strings.ReplaceAll(s, `\\a`, `\a`)
40+
s = strings.ReplaceAll(s, `\\b`, `\b`)
41+
s = strings.ReplaceAll(s, `\\e`, `\e`)
42+
s = strings.ReplaceAll(s, `\\E`, `\E`)
43+
s = strings.ReplaceAll(s, `\\n`, `\n`)
44+
s = strings.ReplaceAll(s, `\\r`, `\r`)
45+
s = strings.ReplaceAll(s, `\\t`, `\t`)
46+
s = strings.ReplaceAll(s, `\\v`, `\v`)
47+
s = strings.ReplaceAll(s, `\\?`, `\?`)
48+
}
49+
return fmt.Sprintf(`$'%s'`, s)
50+
}
51+
52+
func containsOne(target string, chars []rune) bool {
53+
charMap := make(map[rune]bool, len(chars))
54+
for _, c := range chars {
55+
charMap[c] = true
56+
}
57+
for _, s := range target {
58+
if charMap[s] {
59+
return true
60+
}
61+
}
62+
return false
63+
}

Diff for: pkg/utils/security/escape_test.go

+41
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
/*
2+
Copyright 2023 The Fluid Author.
3+
4+
Licensed under the Apache License, Version 2.0 (the "License");
5+
you may not use this file except in compliance with the License.
6+
You may obtain a copy of the License at
7+
8+
http://www.apache.org/licenses/LICENSE-2.0
9+
10+
Unless required by applicable law or agreed to in writing, software
11+
distributed under the License is distributed on an "AS IS" BASIS,
12+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
See the License for the specific language governing permissions and
14+
limitations under the License.
15+
*/
16+
17+
package security
18+
19+
import "testing"
20+
21+
func TestEscapeBashStr(t *testing.T) {
22+
cases := [][]string{
23+
{"abc", "abc"},
24+
{"test-volume", "test-volume"},
25+
{"http://minio.kube-system:9000/minio/dynamic-ce", "http://minio.kube-system:9000/minio/dynamic-ce"},
26+
{"$(cat /proc/self/status | grep CapEff > /test.txt)", "$'$(cat /proc/self/status | grep CapEff > /test.txt)'"},
27+
{"hel`cat /proc/self/status`lo", "$'hel`cat /proc/self/status`lo'"},
28+
{"'h'el`cat /proc/self/status`lo", "$'\\'h\\'el`cat /proc/self/status`lo'"},
29+
{"\\'h\\'el`cat /proc/self/status`lo", "$'\\'h\\'el`cat /proc/self/status`lo'"},
30+
{"$'h'el`cat /proc/self/status`lo", "$'$\\'h\\'el`cat /proc/self/status`lo'"},
31+
{"hel\\`cat /proc/self/status`lo", "$'hel\\\\`cat /proc/self/status`lo'"},
32+
{"hel\\\\`cat /proc/self/status`lo", "$'hel\\\\`cat /proc/self/status`lo'"},
33+
{"hel\\'`cat /proc/self/status`lo", "$'hel\\'`cat /proc/self/status`lo'"},
34+
}
35+
for _, c := range cases {
36+
escaped := EscapeBashStr(c[0])
37+
if escaped != c[1] {
38+
t.Errorf("escapeBashVar(%s) = %s, want %s", c[0], escaped, c[1])
39+
}
40+
}
41+
}

0 commit comments

Comments
 (0)