50 lines
1.4 KiB
Go
50 lines
1.4 KiB
Go
|
|
package agentci
|
||
|
|
|
||
|
|
import (
|
||
|
|
"fmt"
|
||
|
|
"os/exec"
|
||
|
|
"path/filepath"
|
||
|
|
"regexp"
|
||
|
|
"strings"
|
||
|
|
)
|
||
|
|
|
||
|
|
var safeNameRegex = regexp.MustCompile(`^[a-zA-Z0-9\-\_\.]+$`)
|
||
|
|
|
||
|
|
// SanitizePath ensures a filename or directory name is safe and prevents path traversal.
|
||
|
|
// Returns filepath.Base of the input after validation.
|
||
|
|
func SanitizePath(input string) (string, error) {
|
||
|
|
base := filepath.Base(input)
|
||
|
|
if !safeNameRegex.MatchString(base) {
|
||
|
|
return "", fmt.Errorf("invalid characters in path element: %s", input)
|
||
|
|
}
|
||
|
|
if base == "." || base == ".." || base == "/" {
|
||
|
|
return "", fmt.Errorf("invalid path element: %s", base)
|
||
|
|
}
|
||
|
|
return base, nil
|
||
|
|
}
|
||
|
|
|
||
|
|
// EscapeShellArg wraps a string in single quotes for safe remote shell insertion.
|
||
|
|
// Prefer exec.Command arguments over constructing shell strings where possible.
|
||
|
|
func EscapeShellArg(arg string) string {
|
||
|
|
return "'" + strings.ReplaceAll(arg, "'", "'\\''") + "'"
|
||
|
|
}
|
||
|
|
|
||
|
|
// SecureSSHCommand creates an SSH exec.Cmd with strict host key checking and batch mode.
|
||
|
|
func SecureSSHCommand(host string, remoteCmd string) *exec.Cmd {
|
||
|
|
return exec.Command("ssh",
|
||
|
|
"-o", "StrictHostKeyChecking=yes",
|
||
|
|
"-o", "BatchMode=yes",
|
||
|
|
"-o", "ConnectTimeout=10",
|
||
|
|
host,
|
||
|
|
remoteCmd,
|
||
|
|
)
|
||
|
|
}
|
||
|
|
|
||
|
|
// MaskToken returns a masked version of a token for safe logging.
|
||
|
|
func MaskToken(token string) string {
|
||
|
|
if len(token) < 8 {
|
||
|
|
return "*****"
|
||
|
|
}
|
||
|
|
return token[:4] + "****" + token[len(token)-4:]
|
||
|
|
}
|