Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions .jules/sentinel.md
Original file line number Diff line number Diff line change
Expand Up @@ -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-22 - Fix SSRF vulnerability in CLI executor
**Vulnerability:** G704 (SSRF) was flagged in `/app/internal/runtime/executor/cli_executor.go` because the target URL was constructed by taking a parsed URL by value, modifying the `Path` and nullifying the `RawPath` and `Fragment`, and calling `.String()`.
**Learning:** If an attacker provides an opaque URL (where `url.Opaque` is set), the `url.URL.String()` method prioritizes the `Opaque` field over any manually modified `Path` field, allowing path constraints to be bypassed. Always construct a new `url.URL{}` struct literal explicitly when reconstructing URLs to mitigate SSRF.
**Prevention:** Avoid copying a parsed `url.URL` by value and manually nullifying fields. Instead, reconstruct URLs using a strict `url.URL{...}` struct literal, assigning only the needed fields explicitly.
15 changes: 9 additions & 6 deletions internal/runtime/executor/cli_executor.go
Original file line number Diff line number Diff line change
Expand Up @@ -521,13 +521,16 @@ 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 = ""
// Explicitly reconstruct URL to prevent SSRF bypass
safeURL := url.URL{
Scheme: parsedURL.Scheme,
Host: parsedURL.Host,
User: parsedURL.User,
Path: strings.TrimSuffix(parsedURL.Path, "/") + "/run",
RawQuery: parsedURL.RawQuery,
}

httpReq, err := http.NewRequestWithContext(ctx, "POST", targetURL.String(), bytes.NewReader(jsonBody))
httpReq, err := http.NewRequestWithContext(ctx, "POST", safeURL.String(), bytes.NewReader(jsonBody))
if err != nil {
return switchailocalexecutor.Response{}, fmt.Errorf("failed to create bridge request: %w", err)
}
Expand Down
Loading