137 lines
4.0 KiB
Go
137 lines
4.0 KiB
Go
package tools
|
|
|
|
import (
|
|
"fmt"
|
|
"strings"
|
|
)
|
|
|
|
// shellQuote quotes a string for safe shell use.
|
|
func shellQuote(s string) string {
|
|
if s == "" {
|
|
return "''"
|
|
}
|
|
// Simple single-quote escaping
|
|
escaped := strings.ReplaceAll(s, "'", "'\"'\"'")
|
|
return "'" + escaped + "'"
|
|
}
|
|
|
|
// containsString checks if s contains substr.
|
|
func containsString(s, substr string) bool {
|
|
return strings.Contains(s, substr)
|
|
}
|
|
|
|
// trimOutput trims whitespace from output.
|
|
func trimOutput(s string) string {
|
|
return strings.TrimSpace(s)
|
|
}
|
|
|
|
// sedEscapeLiteral escapes a literal string for use in a sed s/pattern/ context.
|
|
// Escapes: / \ & . * [ ] ^ $ and newlines.
|
|
func sedEscapeLiteral(s string) string {
|
|
replacer := strings.NewReplacer(
|
|
`\`, `\\`,
|
|
`/`, `\/`,
|
|
`&`, `\&`,
|
|
`.`, `\.`,
|
|
`*`, `\*`,
|
|
`[`, `\[`,
|
|
`]`, `\]`,
|
|
`^`, `\^`,
|
|
`$`, `\$`,
|
|
"\n", `\n`,
|
|
)
|
|
return replacer.Replace(s)
|
|
}
|
|
|
|
// sedEscapePattern escapes a regex pattern for use in sed, only escaping the delimiter.
|
|
// The pattern is passed as-is for regex matching, only / and newlines are escaped.
|
|
func sedEscapePattern(s string) string {
|
|
replacer := strings.NewReplacer(
|
|
`/`, `\/`,
|
|
"\n", `\n`,
|
|
)
|
|
return replacer.Replace(s)
|
|
}
|
|
|
|
// sedEscapeReplacement escapes a replacement string for sed s//replacement/ context.
|
|
// Only escapes: / \ & and newlines (these have special meaning in sed replacements).
|
|
func sedEscapeReplacement(s string) string {
|
|
replacer := strings.NewReplacer(
|
|
`\`, `\\`,
|
|
`/`, `\/`,
|
|
`&`, `\&`,
|
|
"\n", `\n`,
|
|
)
|
|
return replacer.Replace(s)
|
|
}
|
|
|
|
// sedEscapeInsertText escapes text for sed i\ or a\ commands.
|
|
// Newlines need to be escaped with backslash continuation for multi-line inserts.
|
|
func sedEscapeInsertText(s string) string {
|
|
return strings.ReplaceAll(s, "\n", `\n`)
|
|
}
|
|
|
|
// sedInPlace builds a portable sed in-place edit command that works on
|
|
// GNU sed (Linux), BSD sed (macOS/FreeBSD), and BusyBox sed (Alpine).
|
|
// Uses sed -i.bak + rm for universal portability.
|
|
//
|
|
// Parameters:
|
|
// - flags: extra sed flags like "-E", or "" for none
|
|
// - expr: the sed expression WITHOUT single-quote wrapping (e.g.,
|
|
// "s/foo/bar/g"). sedInPlace shell-quotes it itself (via shellQuote),
|
|
// so apostrophes in user content can't break the shell quoting.
|
|
// - path: the raw file path (will be shell-quoted internally)
|
|
func sedInPlace(flags, expr, path string) string {
|
|
quotedExpr := shellQuote(expr)
|
|
quotedPath := shellQuote(path)
|
|
quotedBak := shellQuote(path + ".bak")
|
|
if flags != "" {
|
|
flags = " " + flags
|
|
}
|
|
return fmt.Sprintf("sed -i.bak%s %s %s 2>&1 && rm -f %s",
|
|
flags, quotedExpr, quotedPath, quotedBak)
|
|
}
|
|
|
|
// sanitizeTsharkValue removes characters that could break tshark display filters.
|
|
// Allows alphanumeric, dash, dot, @, underscore, plus, colon, and space.
|
|
func sanitizeTsharkValue(s string) string {
|
|
var b strings.Builder
|
|
for _, r := range s {
|
|
if (r >= 'a' && r <= 'z') || (r >= 'A' && r <= 'Z') || (r >= '0' && r <= '9') ||
|
|
r == '-' || r == '.' || r == '@' || r == '_' || r == '+' || r == ':' || r == ' ' {
|
|
b.WriteRune(r)
|
|
}
|
|
}
|
|
return b.String()
|
|
}
|
|
|
|
// sanitizeAlphanumeric validates that a string contains only safe characters.
|
|
// Allows alphanumeric, dash, dot, and underscore. Used for network interface names,
|
|
// grep keywords, and other values embedded inside sh -c strings.
|
|
func sanitizeAlphanumeric(s string) string {
|
|
var b strings.Builder
|
|
for _, r := range s {
|
|
if (r >= 'a' && r <= 'z') || (r >= 'A' && r <= 'Z') || (r >= '0' && r <= '9') ||
|
|
r == '-' || r == '.' || r == '_' {
|
|
b.WriteRune(r)
|
|
}
|
|
}
|
|
return b.String()
|
|
}
|
|
|
|
// sanitizeShellInnerPath validates a file path used inside sh -c '...' strings.
|
|
// Rejects characters that could break out of single-quoted shell context or
|
|
// enable command injection. Returns error for unsafe paths.
|
|
func sanitizeShellInnerPath(s string) (string, error) {
|
|
if s == "" {
|
|
return "", fmt.Errorf("path cannot be empty")
|
|
}
|
|
for _, r := range s {
|
|
if r == '\'' || r == '`' || r == ';' || r == '&' || r == '|' ||
|
|
r == '$' || r == '!' || r == '\n' || r == '\r' || r < 32 {
|
|
return "", fmt.Errorf("invalid characters in path")
|
|
}
|
|
}
|
|
return s, nil
|
|
}
|