feat: add reusable proxy configuration
This commit is contained in:
@@ -3,8 +3,10 @@ package domain
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"net"
|
||||
"net/url"
|
||||
"path/filepath"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
@@ -70,11 +72,38 @@ func (s LaunchSpec) Normalize() (LaunchSpec, error) {
|
||||
normalized.UserDataDir = filepath.Clean(userDataDir)
|
||||
normalized.ProfileDirectory = strings.TrimSpace(s.ProfileDirectory)
|
||||
normalized.TargetURL = target
|
||||
normalized.ProxyServer = strings.TrimSpace(s.ProxyServer)
|
||||
proxy, err := NormalizeProxyServer(s.ProxyServer)
|
||||
if err != nil {
|
||||
return LaunchSpec{}, err
|
||||
}
|
||||
normalized.ProxyServer = proxy
|
||||
normalized.ExtraArgs = append([]string(nil), s.ExtraArgs...)
|
||||
return normalized, nil
|
||||
}
|
||||
|
||||
// NormalizeProxyServer accepts only non-authenticated Chromium proxy endpoints.
|
||||
// Keeping this validation in domain makes configuration and process launch share
|
||||
// the same security boundary: no proxy userinfo can reach disk or command args.
|
||||
func NormalizeProxyServer(value string) (string, error) {
|
||||
proxy := strings.TrimSpace(value)
|
||||
if proxy == "" {
|
||||
return "", nil
|
||||
}
|
||||
parsed, err := url.Parse(proxy)
|
||||
if err != nil || parsed.User != nil || parsed.Hostname() == "" || parsed.Port() == "" || parsed.Path != "" || parsed.RawQuery != "" || parsed.Fragment != "" {
|
||||
return "", fmt.Errorf("%w: proxy must be scheme://host:port without credentials", ErrInvalidLaunchSpec)
|
||||
}
|
||||
scheme := strings.ToLower(parsed.Scheme)
|
||||
if scheme != "http" && scheme != "https" && scheme != "socks4" && scheme != "socks5" {
|
||||
return "", fmt.Errorf("%w: unsupported proxy scheme", ErrInvalidLaunchSpec)
|
||||
}
|
||||
port, err := strconv.Atoi(parsed.Port())
|
||||
if err != nil || port < 1 || port > 65535 {
|
||||
return "", fmt.Errorf("%w: proxy port out of range", ErrInvalidLaunchSpec)
|
||||
}
|
||||
return scheme + "://" + net.JoinHostPort(strings.ToLower(parsed.Hostname()), strconv.Itoa(port)), nil
|
||||
}
|
||||
|
||||
func ValidRemoteDebugPort(port int) bool {
|
||||
return port >= MinRemoteDebugPort && port <= MaxRemoteDebugPort
|
||||
}
|
||||
|
||||
@@ -63,3 +63,20 @@ func TestLaunchSpecNormalizeRejectsInvalidRemoteDebugPort(t *testing.T) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestNormalizeProxyServerCanonicalizesAndRejectsCredentials(t *testing.T) {
|
||||
got, err := NormalizeProxyServer(" HTTPS://Proxy.Local:8443 ")
|
||||
if err != nil || got != "https://proxy.local:8443" {
|
||||
t.Fatalf("NormalizeProxyServer() = %q, %v", got, err)
|
||||
}
|
||||
for _, value := range []string{
|
||||
"http://user:secret@127.0.0.1:8080",
|
||||
"http://127.0.0.1:8080/path",
|
||||
"ftp://127.0.0.1:21",
|
||||
"socks5://127.0.0.1:70000",
|
||||
} {
|
||||
if _, err := NormalizeProxyServer(value); !errors.Is(err, ErrInvalidLaunchSpec) {
|
||||
t.Errorf("NormalizeProxyServer(%q) error = %v", value, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user