feat: Introduce shell metacharacter escaping for exec

Signed-off-by: Nathanael Bracy <nate@bracy.dev>
This commit is contained in:
Nathanael Bracy
2024-05-12 20:04:51 -04:00
parent 973a6633b3
commit 5d16bae858
5 changed files with 42 additions and 10 deletions

View File

@@ -19,16 +19,47 @@ package oci
import (
"fmt"
"os"
"regexp"
"strings"
"syscall"
)
// shellMetachars represents a set of shell metacharacters that are commonly
// used for shell scripting and may lead to security vulnerabilities if not
// properly handled.
//
// These metacharacters include: | & ; ( ) < > \t \n $ \ `
const shellMetachars = "|&;()<> \t\n$\\`"
type syscallExec struct{}
var _ Runtime = (*syscallExec)(nil)
// Escape1 escapes shell metacharacters in a single command-line argument.
func Escape1(arg string) string {
if strings.ContainsAny(arg, shellMetachars) {
// Argument contains shell metacharacters. Double quote the
// argument, and backslash-escape any characters that still have
// meaning inside of double quotes.
e := regexp.MustCompile("([$`\"\\\\])").ReplaceAllString(arg, `\$1`)
return fmt.Sprintf(`"%s"`, e)
}
return arg
}
// Escape escapes shell metacharacters in a slice of command-line arguments
// and returns a new slice containing the escaped arguments.
func Escape(args []string) []string {
escaped := make([]string, len(args))
for i := range args {
escaped[i] = Escape1(args[i])
}
return escaped
}
func (r syscallExec) Exec(args []string) error {
//nolint:gosec // TODO: Can we harden this so that there is less risk of command injection
err := syscall.Exec(args[0], args, os.Environ())
args = Escape(args)
err := syscall.Exec(args[0], args, os.Environ()) //nolint:gosec
if err != nil {
return fmt.Errorf("could not exec '%v': %v", args[0], err)
}