Skip to content

Commit 87d88c3

Browse files
Merge pull request #26432 from stefwalter/dont-html-escape-json
api: Don't HTML escape application/json responses
2 parents 43b74b1 + fed198e commit 87d88c3

File tree

2 files changed

+55
-1
lines changed

2 files changed

+55
-1
lines changed

pkg/api/handlers/utils/handler.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -120,7 +120,7 @@ func WriteJSON(w http.ResponseWriter, code int, value interface{}) {
120120
w.WriteHeader(code)
121121

122122
coder := json.NewEncoder(w)
123-
coder.SetEscapeHTML(true)
123+
coder.SetEscapeHTML(false)
124124
if err := coder.Encode(value); err != nil {
125125
logrus.Errorf("Unable to write json: %q", err)
126126
}

pkg/api/handlers/utils/handler_test.go

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,9 @@
33
package utils
44

55
import (
6+
"net/http/httptest"
7+
"reflect"
8+
"strings"
69
"testing"
710
)
811

@@ -53,3 +56,54 @@ func TestErrorEncoderFuncOmit(t *testing.T) {
5356
t.Errorf("the `errs` field shouldn't have been omitted")
5457
}
5558
}
59+
60+
func TestWriteJSONNoHTMLEscape(t *testing.T) {
61+
// Test that WriteJSON does not HTML-escape JSON content
62+
// This test verifies the fix for issue #17769
63+
64+
recorder := httptest.NewRecorder()
65+
66+
// Test data with characters that would be HTML-escaped
67+
testData := map[string]string{
68+
"message": "Hello <world> & \"friends\"",
69+
"script": "<script>alert('test')</script>",
70+
"url": "https://example.com/path?param=value&other=<test>",
71+
}
72+
73+
WriteJSON(recorder, 200, testData)
74+
75+
// Check response headers
76+
if contentType := recorder.Header().Get("Content-Type"); contentType != "application/json" {
77+
t.Errorf("Expected Content-Type 'application/json', got '%s'", contentType)
78+
}
79+
80+
// Check that response contains unescaped characters
81+
body := recorder.Body.String()
82+
83+
// These characters should NOT be HTML-escaped in JSON responses
84+
// (but quotes are still properly JSON-escaped)
85+
expectedUnescaped := []string{
86+
"<world>",
87+
"&",
88+
"\\\"friends\\\"", // JSON-escaped quotes, not HTML-escaped
89+
"<script>",
90+
"<test>",
91+
}
92+
93+
for _, expected := range expectedUnescaped {
94+
if !strings.Contains(body, expected) {
95+
t.Errorf("Expected unescaped string '%s' in response body, got: %s", expected, body)
96+
}
97+
}
98+
99+
// Verify we can parse the JSON back
100+
var parsed map[string]string
101+
if err := json.Unmarshal([]byte(body), &parsed); err != nil {
102+
t.Errorf("Failed to parse JSON response: %v", err)
103+
}
104+
105+
// Verify the data matches what we sent
106+
if !reflect.DeepEqual(parsed, testData) {
107+
t.Errorf("Parsed message doesn't match original: got %v, want %v", parsed, testData)
108+
}
109+
}

0 commit comments

Comments
 (0)