@@ -182,4 +182,57 @@ TEST(AIJsonSerializerTests, valuesSanitized)
182182 EXPECT_EQ (expectLarge, result[" data" ][" baseData" ][" properties" ][expectSmall150].get <std::string>());
183183}
184184
185+ TEST (AIJsonSerializerTests, replacesInvalidUtf8BytesInStrings)
186+ {
187+ std::unique_ptr<AIJsonSerializer> aiSerializer = std::make_unique<AIJsonSerializer>();
188+
189+ ::CsProtocol::App app;
190+ app.ver = " appVer" ;
191+ app.locale = " appLocale" ;
192+
193+ ::CsProtocol::Device device;
194+ device.localId = " deviceId" ;
195+ device.deviceClass = " deviceClass" ;
196+
197+ ::CsProtocol::Protocol proto;
198+ proto.devMake = " protoDevMake" ;
199+ proto.devModel = " protoDevModel" ;
200+
201+ ::CsProtocol::Os os;
202+ os.name = " osName" ;
203+ os.ver = " osVer" ;
204+
205+ ::CsProtocol::User user;
206+ user.localId = " userId" ;
207+
208+ // Inject an invalid UTF-8 byte (0xA9) into a string property.
209+ // With json::error_handler_t::replace, serialization should not throw and
210+ // the invalid byte should be replaced by the UTF-8 encoding of U+FFFD.
211+ std::string invalidUtf8 = " ab" ;
212+ invalidUtf8.push_back (static_cast <char >(0xA9 ));
213+ invalidUtf8 += " cd" ;
214+
215+ ::CsProtocol::Data data;
216+ ::CsProtocol::Value prop;
217+ prop.stringValue = invalidUtf8;
218+ data.properties [" bad_utf8" ] = prop;
219+
220+ std::unique_ptr<::CsProtocol::Record> record = createTestRecord (
221+ " eventUtf8" , 1 , app, device, proto, os, user, data
222+ );
223+ IncomingEventContext context (PAL::generateUuidString (), TEST_TOKEN , EventLatency_Unspecified, EventPersistence_Normal, record.get ());
224+
225+ EXPECT_NO_THROW (aiSerializer->serialize (&context));
226+ ASSERT_FALSE (context.record .blob .empty ());
227+
228+ ::nlohmann::json parsed;
229+ EXPECT_NO_THROW (parsed = nlohmann::json::parse (context.record .blob .begin (), context.record .blob .end ()));
230+
231+ const std::string actual = parsed[" data" ][" baseData" ][" properties" ][" bad_utf8" ].get <std::string>();
232+ const std::string expected = std::string (" ab" ) + " \xEF\xBF\xBD " + " cd" ;
233+
234+ EXPECT_EQ (expected, actual);
235+ EXPECT_EQ (std::string::npos, actual.find (static_cast <char >(0xA9 )));
236+ }
237+
185238#endif // HAVE_MAT_AI
0 commit comments