fix(shopinbit): escape non-ASCII in request bodies
What changed, and why it matters
This commit fixes a bug in the Stack Wallet app's integration with ShopInBit, a third-party shopping service. When users sent text containing non-English characters (like ± or emoji), the app encoded them incorrectly before sending over the internet. This could corrupt order details, shipping names, or item descriptions, potentially causing orders to fail or be processed with wrong information. The fix forces all special characters to be sent as safe ASCII escape sequences.
Treat as a routine correctness fix rather than a security vulnerability. Verify the fix covers all request methods (GET query parameters, DELETE bodies if any), add regression tests with non-ASCII payloads, and consider configuring the HTTP wrapper to write UTF-8 explicitly so future endpoints do not reintroduce the same encoding mismatch.
Security signals we found
Character encoding mismatch between JSON serialization and HTTP transport
Potential corruption of user-controlled request fields (names, addresses, order notes)
Third-party API integration handling PII/order data
No input validation or encoding normalization present before fix
Evidence from the diff
The Dart HTTP client wrapper used by ShopInBit writes string request bodies via HttpClientRequest.write, which defaults to latin-1 encoding. Passing a UTF-8 JSON string through latin-1 mangles multi-byte characters (e.g., U+00B1 becomes two unrelated bytes). The patch introduces _asciiSafeJson, which jsonEncodes the body and then rewrites every non-ASCII codepoint as a \uXXXX escape so the resulting string is pure ASCII and survives the latin-1 write path intact. This is a data-integrity/correctness fix in an external API client.
Changed components
lib/services/shopinbit/src/client.dartShopInBit API client POST/PATCH request body encodingStack Wallet in-app ShopInBit purchasing flowInspect captured patch +20 / −2
diff --git a/lib/services/shopinbit/src/client.dart b/lib/services/shopinbit/src/client.dart
index fe48184..a1f9b8b 100644
--- a/lib/services/shopinbit/src/client.dart
+++ b/lib/services/shopinbit/src/client.dart
@@ -544,14 +544,14 @@ class ShopInBitClient {
return _httpClient.post(
url: uri,
headers: headers,
- body: body != null ? jsonEncode(body) : null,
+ body: body != null ? _asciiSafeJson(body) : null,
proxyInfo: proxy,
);
case 'PATCH':
return _httpClient.patch(
url: uri,
headers: headers,
- body: body != null ? jsonEncode(body) : null,
+ body: body != null ? _asciiSafeJson(body) : null,
proxyInfo: proxy,
);
case 'DELETE':
@@ -561,6 +561,24 @@ class ShopInBitClient {
}
}
+ // Encode [body] as JSON with all non-ASCII characters replaced by \uXXXX
+ // escapes. The HTTP wrapper writes string bodies with the latin1 default of
+ // HttpClientRequest.write, which mangles multi-byte UTF-8 like the U+00B1/±.
+ static String _asciiSafeJson(Object body) {
+ final raw = jsonEncode(body);
+ final buf = StringBuffer();
+ for (int i = 0; i < raw.length; i++) {
+ final c = raw.codeUnitAt(i);
+ if (c < 0x80) {
+ buf.writeCharCode(c);
+ } else {
+ buf.write('\\u');
+ buf.write(c.toRadixString(16).padLeft(4, '0'));
+ }
+ }
+ return buf.toString();
+ }
+
Future<ApiResponse<T>> _request<T>(
String method,
String path, {
Why this scored 37/100
Community notes
Notes can correct, qualify, or add evidence to the AI analysis. Every note shown here has been validated by a human moderator.
The AI analysis stands alone for now. Submit a note if you can add evidence or important context.