From 36eafae59f2a70308fda1759f26dfa8cc081fa44 Mon Sep 17 00:00:00 2001 From: admin-mf Date: Fri, 6 Mar 2026 00:02:24 -0600 Subject: [PATCH] security(auth): sanitize OAuth error messages Replace raw HTTP response bodies in error messages with generic status-code-only messages. Full response details are logged at debug level for troubleshooting. Co-Authored-By: Claude Opus 4.6 --- pkg/auth/oauth.go | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/pkg/auth/oauth.go b/pkg/auth/oauth.go index b986c7013..fbd413f20 100644 --- a/pkg/auth/oauth.go +++ b/pkg/auth/oauth.go @@ -18,6 +18,8 @@ import ( "strconv" "strings" "time" + + "github.com/sipeed/picoclaw/pkg/logger" ) const oauthMaxResponseSize int64 = 1 << 20 // 1 MB — more than sufficient for any OAuth response @@ -226,7 +228,8 @@ func RequestDeviceCode(cfg OAuthProviderConfig) (*DeviceCodeInfo, error) { body, _ := io.ReadAll(io.LimitReader(resp.Body, oauthMaxResponseSize)) if resp.StatusCode != http.StatusOK { - return nil, fmt.Errorf("device code request failed: %s", string(body)) + logger.DebugCF("auth", "device code request failed", map[string]any{"status": resp.StatusCode, "body": string(body)}) + return nil, fmt.Errorf("device code request failed (HTTP %d)", resp.StatusCode) } deviceResp, err := parseDeviceCodeResponse(body) @@ -314,7 +317,8 @@ func LoginDeviceCode(cfg OAuthProviderConfig) (*AuthCredential, error) { body, _ := io.ReadAll(io.LimitReader(resp.Body, oauthMaxResponseSize)) if resp.StatusCode != http.StatusOK { - return nil, fmt.Errorf("device code request failed: %s", string(body)) + logger.DebugCF("auth", "device code request failed", map[string]any{"status": resp.StatusCode, "body": string(body)}) + return nil, fmt.Errorf("device code request failed (HTTP %d)", resp.StatusCode) } deviceResp, err := parseDeviceCodeResponse(body) @@ -415,7 +419,8 @@ func RefreshAccessToken(cred *AuthCredential, cfg OAuthProviderConfig) (*AuthCre body, _ := io.ReadAll(io.LimitReader(resp.Body, oauthMaxResponseSize)) if resp.StatusCode != http.StatusOK { - return nil, fmt.Errorf("token refresh failed: %s", string(body)) + logger.DebugCF("auth", "token refresh failed", map[string]any{"status": resp.StatusCode, "body": string(body)}) + return nil, fmt.Errorf("token refresh failed (HTTP %d)", resp.StatusCode) } refreshed, err := parseTokenResponse(body, cred.Provider) @@ -508,7 +513,8 @@ func ExchangeCodeForTokens(cfg OAuthProviderConfig, code, codeVerifier, redirect body, _ := io.ReadAll(io.LimitReader(resp.Body, oauthMaxResponseSize)) if resp.StatusCode != http.StatusOK { - return nil, fmt.Errorf("token exchange failed: %s", string(body)) + logger.DebugCF("auth", "token exchange failed", map[string]any{"status": resp.StatusCode, "body": string(body)}) + return nil, fmt.Errorf("token exchange failed (HTTP %d)", resp.StatusCode) } return parseTokenResponse(body, provider)