mirror of
https://github.com/wavetermdev/waveterm
synced 2026-04-29 18:37:16 +00:00
the PR spiraled and ended up being much larger than anticipated. it is a refactor of wshrouter to have it track "links" as opposed to just routes. this lets us simplify a lot of things when it comes to multi-level routing. * now the router can handle unauthenticated links directly, instead of a weird limbo in wshproxy * no more wshmultiproxy * no more "authtoken" weirdness * more straightforward handling in connserver (when using router option) also adds more debugging, more logging, some windows fixes, other wsl fixes
159 lines
2.7 KiB
Go
159 lines
2.7 KiB
Go
// Copyright 2025, Command Line Inc.
|
|
// SPDX-License-Identifier: Apache-2.0
|
|
|
|
package shellutil
|
|
|
|
import (
|
|
"log"
|
|
"regexp"
|
|
)
|
|
|
|
const (
|
|
MaxQuoteSize = 10000000 // 10MB
|
|
)
|
|
|
|
var (
|
|
safePattern = regexp.MustCompile(`^[a-zA-Z0-9_@:,+=/.-]+$`)
|
|
envVarNamePattern = regexp.MustCompile(`^[A-Za-z_][A-Za-z0-9_]*$`)
|
|
)
|
|
|
|
func IsValidEnvVarName(name string) bool {
|
|
return envVarNamePattern.MatchString(name)
|
|
}
|
|
|
|
func HardQuote(s string) string {
|
|
if s == "" {
|
|
return "\"\""
|
|
}
|
|
|
|
if safePattern.MatchString(s) {
|
|
return s
|
|
}
|
|
|
|
if !checkQuoteSize(s) {
|
|
return ""
|
|
}
|
|
|
|
buf := make([]byte, 0, len(s)+5)
|
|
buf = append(buf, '"')
|
|
|
|
for i := 0; i < len(s); i++ {
|
|
switch s[i] {
|
|
case '"', '\\', '$', '`':
|
|
buf = append(buf, '\\', s[i])
|
|
default:
|
|
buf = append(buf, s[i])
|
|
}
|
|
}
|
|
|
|
buf = append(buf, '"')
|
|
return string(buf)
|
|
}
|
|
|
|
// does not encode newlines or backticks
|
|
func HardQuoteFish(s string) string {
|
|
if s == "" {
|
|
return "\"\""
|
|
}
|
|
|
|
if safePattern.MatchString(s) {
|
|
return s
|
|
}
|
|
|
|
if !checkQuoteSize(s) {
|
|
return ""
|
|
}
|
|
|
|
buf := make([]byte, 0, len(s)+5)
|
|
buf = append(buf, '"')
|
|
|
|
for i := 0; i < len(s); i++ {
|
|
switch s[i] {
|
|
case '"', '\\', '$':
|
|
buf = append(buf, '\\', s[i])
|
|
default:
|
|
buf = append(buf, s[i])
|
|
}
|
|
}
|
|
|
|
buf = append(buf, '"')
|
|
return string(buf)
|
|
}
|
|
|
|
func HardQuotePowerShell(s string) string {
|
|
if s == "" {
|
|
return "\"\""
|
|
}
|
|
|
|
if !checkQuoteSize(s) {
|
|
return ""
|
|
}
|
|
|
|
buf := make([]byte, 0, len(s)+5)
|
|
buf = append(buf, '"')
|
|
|
|
for i := 0; i < len(s); i++ {
|
|
c := s[i]
|
|
// In PowerShell, backtick (`) is the escape character
|
|
switch c {
|
|
case '"', '`', '$':
|
|
buf = append(buf, '`')
|
|
case '\n':
|
|
buf = append(buf, '`', 'n') // PowerShell uses `n for newline
|
|
}
|
|
buf = append(buf, c)
|
|
}
|
|
|
|
buf = append(buf, '"')
|
|
return string(buf)
|
|
}
|
|
|
|
func SoftQuote(s string) string {
|
|
if s == "" {
|
|
return "\"\""
|
|
}
|
|
|
|
// Handle special case of ~ paths
|
|
if len(s) > 0 && s[0] == '~' {
|
|
// If it's just ~ or ~/something with no special chars, leave it as is
|
|
if len(s) == 1 || (len(s) > 1 && s[1] == '/' && safePattern.MatchString(s[2:])) {
|
|
return s
|
|
}
|
|
|
|
// Otherwise quote everything after the ~ (including the /)
|
|
if len(s) > 1 && s[1] == '/' {
|
|
return "~" + SoftQuote(s[1:])
|
|
}
|
|
}
|
|
|
|
if safePattern.MatchString(s) {
|
|
return s
|
|
}
|
|
|
|
if !checkQuoteSize(s) {
|
|
return ""
|
|
}
|
|
|
|
buf := make([]byte, 0, len(s)+5)
|
|
buf = append(buf, '"')
|
|
|
|
for i := 0; i < len(s); i++ {
|
|
c := s[i]
|
|
// In soft quote, we don't escape $ to allow expansion
|
|
if c == '"' || c == '\\' || c == '`' {
|
|
buf = append(buf, '\\')
|
|
}
|
|
buf = append(buf, c)
|
|
}
|
|
|
|
buf = append(buf, '"')
|
|
return string(buf)
|
|
}
|
|
|
|
func checkQuoteSize(s string) bool {
|
|
if len(s) > MaxQuoteSize {
|
|
log.Printf("string too long to quote: %s", s)
|
|
return false
|
|
}
|
|
return true
|
|
}
|