This repository has been archived on 2026-03-13. You can view files and clone it. You cannot open issues or pull requests or push a commit.
Files
greywall/internal/sandbox/utils.go
Mathieu Virbel 562f9bb65e fix: preserve terminal env vars through sudo in macOS daemon mode
sudo resets the environment, stripping TERM, COLORTERM, COLUMNS, LINES,
and other terminal-related variables that TUI apps need to render. This
caused TUI apps like opencode to show a blank screen in daemon mode.

Fix by injecting terminal and proxy env vars via `env` after `sudo` in
the daemon mode command pipeline. Also move PTY device ioctl/read/write
rules into the base sandbox profile so inherited terminals work without
requiring AllowPty.
2026-02-26 17:39:33 -06:00

130 lines
3.1 KiB
Go

package sandbox
import (
"encoding/base64"
"os"
"path/filepath"
"strings"
)
// ContainsGlobChars checks if a path pattern contains glob characters.
func ContainsGlobChars(pattern string) bool {
return strings.ContainsAny(pattern, "*?[]")
}
// RemoveTrailingGlobSuffix removes trailing /** from a path pattern.
func RemoveTrailingGlobSuffix(pattern string) string {
return strings.TrimSuffix(pattern, "/**")
}
// NormalizePath normalizes a path for sandbox configuration.
// Handles tilde expansion and relative paths.
func NormalizePath(pathPattern string) string {
home, _ := os.UserHomeDir()
cwd, _ := os.Getwd()
normalized := pathPattern
// Expand ~ and relative paths
switch {
case pathPattern == "~":
normalized = home
case strings.HasPrefix(pathPattern, "~/"):
normalized = filepath.Join(home, pathPattern[2:])
case strings.HasPrefix(pathPattern, "./"), strings.HasPrefix(pathPattern, "../"):
normalized, _ = filepath.Abs(filepath.Join(cwd, pathPattern))
case !filepath.IsAbs(pathPattern) && !ContainsGlobChars(pathPattern):
normalized, _ = filepath.Abs(filepath.Join(cwd, pathPattern))
}
// For non-glob patterns, try to resolve symlinks
if !ContainsGlobChars(normalized) {
if resolved, err := filepath.EvalSymlinks(normalized); err == nil {
return resolved
}
}
return normalized
}
// GenerateProxyEnvVars creates environment variables for proxy configuration.
// Used on macOS where transparent proxying is not available.
func GenerateProxyEnvVars(proxyURL string) []string {
envVars := []string{
"GREYWALL_SANDBOX=1",
"TMPDIR=/tmp/greywall",
}
if proxyURL == "" {
return envVars
}
// NO_PROXY for localhost and private networks
noProxy := strings.Join([]string{
"localhost",
"127.0.0.1",
"::1",
"*.local",
".local",
"169.254.0.0/16",
"10.0.0.0/8",
"172.16.0.0/12",
"192.168.0.0/16",
}, ",")
envVars = append(envVars,
"NO_PROXY="+noProxy,
"no_proxy="+noProxy,
"ALL_PROXY="+proxyURL,
"all_proxy="+proxyURL,
"HTTP_PROXY="+proxyURL,
"HTTPS_PROXY="+proxyURL,
"http_proxy="+proxyURL,
"https_proxy="+proxyURL,
)
return envVars
}
// getTerminalEnvVars returns KEY=VALUE entries for terminal-related environment
// variables that are set in the current process. These must be re-injected after
// sudo (which resets the environment) so that TUI apps can detect terminal
// capabilities, size, and color support.
func getTerminalEnvVars() []string {
termVars := []string{
"TERM",
"COLORTERM",
"COLUMNS",
"LINES",
"TERMINFO",
"TERMINFO_DIRS",
"LANG",
"LC_ALL",
"LC_CTYPE",
}
var envs []string
for _, key := range termVars {
if val := os.Getenv(key); val != "" {
envs = append(envs, key+"="+val)
}
}
return envs
}
// EncodeSandboxedCommand encodes a command for sandbox monitoring.
func EncodeSandboxedCommand(command string) string {
if len(command) > 100 {
command = command[:100]
}
return base64.StdEncoding.EncodeToString([]byte(command))
}
// DecodeSandboxedCommand decodes a base64-encoded command.
func DecodeSandboxedCommand(encoded string) (string, error) {
data, err := base64.StdEncoding.DecodeString(encoded)
if err != nil {
return "", err
}
return string(data), nil
}