diff --git a/.jules/sentinel.md b/.jules/sentinel.md index 33f2d09a..e8853df2 100644 --- a/.jules/sentinel.md +++ b/.jules/sentinel.md @@ -361,3 +361,7 @@ Remember: You're Sentinel, the guardian of switchAILocal. Security is not option **Vulnerability:** The CLI executor proxy lacked scheme validation for the user-controlled `remoteHost`, creating an SSRF vulnerability. The OAuth callback forwarder lacked prefix validation for `targetBase`, creating an open redirect vulnerability. **Learning:** Taint analysis revealed that input passed from management configurations or remote host specifications must be strictly validated before being used in HTTP requests or redirect targets to prevent protocol abuse and unauthorized redirects. **Prevention:** Always enforce strict protocol validation (e.g., scheme is 'http' or 'https') for outbound requests and validate redirect targets against allowed prefixes (e.g., 'http://localhost' or '/'). +## 2026-08-12 - Prevent SSRF with explicit URL scheme and host validation +**Vulnerability:** Taint analysis flagged an SSRF (Server-Side Request Forgery) vulnerability in the remote execution flow where the host URL was built insecurely, allowing arbitrary host modification or bypass of validation. +**Learning:** Basic string operations or prefix checks are insufficient to mitigate SSRF when dealing with unvalidated remote hosts in execution flow. Using `net/url` to explicitly parse and validate components is required. +**Prevention:** Avoid string concatenation for URLs involving external input. Always use `net/url`'s `url.Parse` to parse the URL, explicitly validate the `Host` and `Scheme` properties, and safely reconstruct the target URL using a `url.URL{...}` struct literal. diff --git a/internal/runtime/executor/cli_executor.go b/internal/runtime/executor/cli_executor.go index 419ece19..8d9ba574 100644 --- a/internal/runtime/executor/cli_executor.go +++ b/internal/runtime/executor/cli_executor.go @@ -521,11 +521,13 @@ func (e *LocalCLIExecutor) executeRemote(ctx context.Context, remoteHost, binary return switchailocalexecutor.Response{}, fmt.Errorf("invalid remote host URL, must use http or https scheme and include a host") } - targetURL := *parsedURL - targetURL.Path = strings.TrimSuffix(targetURL.Path, "/") + "/run" - targetURL.RawPath = "" - targetURL.Fragment = "" - targetURL.RawFragment = "" + targetURL := url.URL{ + Scheme: parsedURL.Scheme, + User: parsedURL.User, + Host: parsedURL.Host, + Path: strings.TrimSuffix(parsedURL.Path, "/") + "/run", + RawQuery: parsedURL.RawQuery, + } httpReq, err := http.NewRequestWithContext(ctx, "POST", targetURL.String(), bytes.NewReader(jsonBody)) if err != nil {