From 424401233fd9596f2073a487c6b4329570a18a2b Mon Sep 17 00:00:00 2001 From: Sebastiaan van Stijn Date: Tue, 27 Dec 2022 16:26:08 +0100 Subject: [PATCH] cli/command/container: use strings.Cut Signed-off-by: Sebastiaan van Stijn --- cli/command/container/cp.go | 7 +++-- cli/command/container/opts.go | 51 ++++++++++++++++------------------- 2 files changed, 26 insertions(+), 32 deletions(-) diff --git a/cli/command/container/cp.go b/cli/command/container/cp.go index 525866f2a9..67e5e598ea 100644 --- a/cli/command/container/cp.go +++ b/cli/command/container/cp.go @@ -386,13 +386,12 @@ func splitCpArg(arg string) (container, path string) { return "", arg } - parts := strings.SplitN(arg, ":", 2) - - if len(parts) == 1 || strings.HasPrefix(parts[0], ".") { + container, path, ok := strings.Cut(arg, ":") + if !ok || strings.HasPrefix(container, ".") { // Either there's no `:` in the arg // OR it's an explicit local relative path like `./file:name.txt`. return "", arg } - return parts[0], parts[1] + return container, path } diff --git a/cli/command/container/opts.go b/cli/command/container/opts.go index 5849b26515..e4db20ee3e 100644 --- a/cli/command/container/opts.go +++ b/cli/command/container/opts.go @@ -354,14 +354,13 @@ func parse(flags *pflag.FlagSet, copts *containerOptions, serverOS string) (*con toBind := bind if parsed.Type == string(mounttypes.TypeBind) { - if arr := strings.SplitN(bind, ":", 2); len(arr) == 2 { - hostPart := arr[0] + if hostPart, targetPath, ok := strings.Cut(bind, ":"); ok { if strings.HasPrefix(hostPart, "."+string(filepath.Separator)) || hostPart == "." { if absHostPart, err := filepath.Abs(hostPart); err == nil { hostPart = absHostPart } } - toBind = hostPart + ":" + arr[1] + toBind = hostPart + ":" + targetPath } } @@ -377,11 +376,8 @@ func parse(flags *pflag.FlagSet, copts *containerOptions, serverOS string) (*con // Can't evaluate options passed into --tmpfs until we actually mount tmpfs := make(map[string]string) for _, t := range copts.tmpfs.GetAll() { - if arr := strings.SplitN(t, ":", 2); len(arr) > 1 { - tmpfs[arr[0]] = arr[1] - } else { - tmpfs[arr[0]] = "" - } + k, v, _ := strings.Cut(t, ":") + tmpfs[k] = v } var ( @@ -820,12 +816,11 @@ func convertToStandardNotation(ports []string) ([]string, error) { if strings.Contains(publish, "=") { params := map[string]string{"protocol": "tcp"} for _, param := range strings.Split(publish, ",") { - opt := strings.Split(param, "=") - if len(opt) < 2 { + k, v, ok := strings.Cut(param, "=") + if !ok || k == "" { return optsList, errors.Errorf("invalid publish opts format (should be name=value but got '%s')", param) } - - params[opt[0]] = opt[1] + params[k] = v } optsList = append(optsList, fmt.Sprintf("%s:%s/%s", params["published"], params["target"], params["protocol"])) } else { @@ -846,22 +841,22 @@ func parseLoggingOpts(loggingDriver string, loggingOpts []string) (map[string]st // takes a local seccomp daemon, reads the file contents for sending to the daemon func parseSecurityOpts(securityOpts []string) ([]string, error) { for key, opt := range securityOpts { - con := strings.SplitN(opt, "=", 2) - if len(con) == 1 && con[0] != "no-new-privileges" { - if strings.Contains(opt, ":") { - con = strings.SplitN(opt, ":", 2) - } else { - return securityOpts, errors.Errorf("Invalid --security-opt: %q", opt) - } + k, v, ok := strings.Cut(opt, "=") + if !ok && k != "no-new-privileges" { + k, v, ok = strings.Cut(opt, ":") } - if con[0] == "seccomp" && con[1] != "unconfined" { - f, err := os.ReadFile(con[1]) + if (!ok || v == "") && k != "no-new-privileges" { + // "no-new-privileges" is the only option that does not require a value. + return securityOpts, errors.Errorf("Invalid --security-opt: %q", opt) + } + if k == "seccomp" && v != "unconfined" { + f, err := os.ReadFile(v) if err != nil { - return securityOpts, errors.Errorf("opening seccomp profile (%s) failed: %v", con[1], err) + return securityOpts, errors.Errorf("opening seccomp profile (%s) failed: %v", v, err) } b := bytes.NewBuffer(nil) if err := json.Compact(b, f); err != nil { - return securityOpts, errors.Errorf("compacting json for seccomp profile (%s) failed: %v", con[1], err) + return securityOpts, errors.Errorf("compacting json for seccomp profile (%s) failed: %v", v, err) } securityOpts[key] = fmt.Sprintf("seccomp=%s", b.Bytes()) } @@ -893,12 +888,11 @@ func parseSystemPaths(securityOpts []string) (filtered, maskedPaths, readonlyPat func parseStorageOpts(storageOpts []string) (map[string]string, error) { m := make(map[string]string) for _, option := range storageOpts { - if strings.Contains(option, "=") { - opt := strings.SplitN(option, "=", 2) - m[opt[0]] = opt[1] - } else { + k, v, ok := strings.Cut(option, "=") + if !ok { return nil, errors.Errorf("invalid storage option") } + m[k] = v } return m, nil } @@ -919,7 +913,8 @@ func parseDevice(device, serverOS string) (container.DeviceMapping, error) { func parseLinuxDevice(device string) (container.DeviceMapping, error) { var src, dst string permissions := "rwm" - arr := strings.Split(device, ":") + // We expect 3 parts at maximum; limit to 4 parts to detect invalid options. + arr := strings.SplitN(device, ":", 4) switch len(arr) { case 3: permissions = arr[2]