chore: Avoid logging request body

Raw response bodies were being logged by quick tunnel provisioner. This avoids logging the raw body, which could contain sensitive data, while preserving structured Worker errors.
This commit is contained in:
Miguel da Costa Martins Marcelino
2026-09-22 19:56:12 +00:00
parent 0b37a912a2
commit 4518a7e80c
2 changed files with 33 additions and 15 deletions
+18 -15
View File
@@ -82,26 +82,14 @@ func RunQuickTunnel(sc *subcommandContext) error {
}
defer func() { _ = resp.Body.Close() }()
// This will read the entire response into memory so we can print it in case of error
respBody, err := io.ReadAll(resp.Body)
if err != nil {
return errors.Wrap(err, "failed to read quick-tunnel response")
}
if resp.StatusCode < 200 || resp.StatusCode >= 300 {
var data QuickTunnelResponse
if err := json.Unmarshal(respBody, &data); err == nil && len(data.Errors) > 0 {
return fmt.Errorf("quick tunnel provisioning failed with status %d: %s", resp.StatusCode, formatQuickTunnelErrors(data.Errors))
}
return fmt.Errorf("quick tunnel provisioning failed with status %d: %s", resp.StatusCode, string(respBody))
}
var data QuickTunnelResponse
if err := json.Unmarshal(respBody, &data); err != nil {
respString := string(respBody)
fields := map[string]interface{}{"status_code": resp.Status}
sc.log.Err(err).Fields(fields).Msgf("Error unmarshaling QuickTunnel response: %s", respString)
return errors.Wrap(err, "failed to unmarshal quick Tunnel")
data, err := decodeQuickTunnelProvisioningResponse(resp.StatusCode, respBody)
if err != nil {
return err
}
// TODO(TUN-10791): Add CLI-level coverage that provisioning errors are logged to users.
@@ -179,6 +167,21 @@ func RunQuickTunnel(sc *subcommandContext) error {
)
}
func decodeQuickTunnelProvisioningResponse(statusCode int, response []byte) (QuickTunnelResponse, error) {
var data QuickTunnelResponse
if statusCode < http.StatusOK || statusCode >= http.StatusMultipleChoices {
if err := json.Unmarshal(response, &data); err == nil && len(data.Errors) > 0 {
return QuickTunnelResponse{}, fmt.Errorf("quick tunnel provisioning failed with status %d: %s", statusCode, formatQuickTunnelErrors(data.Errors))
}
return QuickTunnelResponse{}, fmt.Errorf("quick tunnel provisioning failed with status %d", statusCode)
}
if err := json.Unmarshal(response, &data); err != nil {
return QuickTunnelResponse{}, errors.Wrap(err, "failed to unmarshal quick Tunnel")
}
return data, nil
}
type QuickTunnelResponse struct {
Success bool
Result QuickTunnel
@@ -2,6 +2,7 @@ package tunnel
import (
"encoding/json"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
@@ -27,6 +28,20 @@ func TestBuildQuickTunnelRequestBody_ProtectedMode(t *testing.T) {
assert.Equal(t, quickTunnelAuthModeOTP, result[quickTunnelAuthModeField])
}
func TestDecodeQuickTunnelProvisioningResponseDoesNotExposeBody(t *testing.T) {
t.Parallel()
const credential = "sensitive-tunnel-credential"
malformedSuccess := []byte(`{"success":true,"result":{"secret":"` + credential + `"}} trailing`)
_, err := decodeQuickTunnelProvisioningResponse(http.StatusOK, malformedSuccess)
require.Error(t, err)
assert.NotContains(t, err.Error(), credential)
_, err = decodeQuickTunnelProvisioningResponse(http.StatusBadGateway, []byte(credential))
require.Error(t, err)
assert.NotContains(t, err.Error(), credential)
}
func TestFormatQuickTunnelErrors(t *testing.T) {
t.Parallel()