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-23 - Secure URL Reconstruction
**Vulnerability:** SSRF via taint analysis in CLI Executor where target URL was modified by copying the parsed struct and nullifying fields.
**Learning:** Copying a parsed `url.URL` struct and nullifying fields (like `RawPath`) is insufficient if an attacker provides a URL that populates the `Opaque` field. The `.String()` method prioritizes `Opaque`, allowing attackers to bypass validation and routing logic.
**Prevention:** Always reconstruct target URLs from untrusted input by explicitly creating a new `url.URL{}` struct literal, copying only the required and validated fields (e.g., `Scheme`, `Host`, `Path`).
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 = ""
// Securely construct URL to prevent SSRF bypasses via Opaque or other fields.
safeURL := 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))
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