Skip to content
This repository was archived by the owner on Sep 11, 2025. It is now read-only.

Commit 5818398

Browse files
fix: ensure valid UTF-8 byte sequence in HTTP response (#904)
1 parent d9c393a commit 5818398

File tree

4 files changed

+68
-0
lines changed

4 files changed

+68
-0
lines changed

CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,10 @@
22

33
# Change Log
44

5+
## 2025-06-23 - Runtime 0.18.0-alpha.12
6+
7+
- fix: ensure valid UTF8 byte sequence in HTTP response [#904](https://github.com/hypermodeinc/modus/pull/904)
8+
59
## 2025-06-23 - Runtime 0.18.0-alpha.11
610

711
- fix: adjust cluster sync settings and delays [#902](https://github.com/hypermodeinc/modus/pull/902)

runtime/utils/http.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -114,8 +114,10 @@ func PostHttp[TResult any](ctx context.Context, url string, payload any, beforeS
114114
case []byte:
115115
result = any(content).(TResult)
116116
case string:
117+
content = SanitizeUTF8(content)
117118
result = any(string(content)).(TResult)
118119
default:
120+
content = SanitizeUTF8(content)
119121
if err := JsonDeserialize(content, &result); err != nil {
120122
return nil, fmt.Errorf("error deserializing response: %w", err)
121123
}

runtime/utils/strings.go

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ package utils
1111

1212
import (
1313
"unicode/utf16"
14+
"unicode/utf8"
1415
"unsafe"
1516
)
1617

@@ -44,3 +45,32 @@ func EncodeUTF16(str string) []byte {
4445
bytes := unsafe.Slice((*byte)(ptr), len(words)*2)
4546
return bytes
4647
}
48+
49+
// SanitizeUTF8 removes invalid UTF-8 sequences from a byte slice.
50+
// It skips over any byte that is a null byte (0) or a single-byte character
51+
// that is not part of a valid UTF-8 sequence.
52+
// It returns a new byte slice containing only valid UTF-8 characters.
53+
func SanitizeUTF8(s []byte) []byte {
54+
// This is adapted from bytes.ToValidUTF8
55+
b := make([]byte, 0, len(s))
56+
for i := 0; i < len(s); {
57+
c := s[i]
58+
if c == 0 {
59+
i++
60+
continue
61+
}
62+
if c < utf8.RuneSelf {
63+
i++
64+
b = append(b, c)
65+
continue
66+
}
67+
_, wid := utf8.DecodeRune(s[i:])
68+
if wid == 1 {
69+
i++
70+
continue
71+
}
72+
b = append(b, s[i:i+wid]...)
73+
i += wid
74+
}
75+
return b
76+
}

runtime/utils/strings_test.go

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,3 +43,35 @@ func Test_DecodeUTF16(t *testing.T) {
4343
t.Errorf("expected %s, got %s", testString, str)
4444
}
4545
}
46+
47+
func Test_SanitizeUTF8(t *testing.T) {
48+
// Test with a valid UTF-8 string
49+
validUTF8 := []byte("Hello, 世界")
50+
sanitized := utils.SanitizeUTF8(validUTF8)
51+
if !bytes.Equal(sanitized, validUTF8) {
52+
t.Errorf("expected %s, got %s", validUTF8, sanitized)
53+
}
54+
55+
// Test with an invalid UTF-8 sequence
56+
invalidUTF8 := []byte{0xff, 0xfe, 0xfd}
57+
sanitized = utils.SanitizeUTF8(invalidUTF8)
58+
if len(sanitized) != 0 {
59+
t.Errorf("expected empty slice for invalid UTF-8, got %s", sanitized)
60+
}
61+
62+
// Test with a mix of valid and invalid UTF-8
63+
mixedUTF8 := []byte("Hello\xffWorld")
64+
sanitized = utils.SanitizeUTF8(mixedUTF8)
65+
expected := []byte("HelloWorld")
66+
if !bytes.Equal(sanitized, expected) {
67+
t.Errorf("expected %s, got %s", expected, sanitized)
68+
}
69+
70+
// Test with some null bytes
71+
nullBytes := []byte("Hello\x00World")
72+
sanitized = utils.SanitizeUTF8(nullBytes)
73+
expected = []byte("HelloWorld")
74+
if !bytes.Equal(sanitized, expected) {
75+
t.Errorf("expected %s, got %s", expected, sanitized)
76+
}
77+
}

0 commit comments

Comments
 (0)