diff --git a/src/main/java/com/clearfolio/viewer/service/DefaultDocumentValidationService.java b/src/main/java/com/clearfolio/viewer/service/DefaultDocumentValidationService.java index 95e6722..89a401b 100644 --- a/src/main/java/com/clearfolio/viewer/service/DefaultDocumentValidationService.java +++ b/src/main/java/com/clearfolio/viewer/service/DefaultDocumentValidationService.java @@ -213,23 +213,17 @@ private String sanitizeForLog(final String value) { if (value == null) { return ""; } - // ⚡ Bolt: Single-pass string sanitization - // Avoids multiple allocations from chained replace() calls. - StringBuilder sb = null; - for (int i = 0; i < value.length(); i++) { - char c = value.charAt(i); - boolean needsReplace = c == '\u0000' || c == '\t' || c == '\r' - || c == '\n' || (c >= '\u2028' && c <= '\u202E'); - if (needsReplace) { - if (sb == null) { - sb = new StringBuilder(value.length()); - sb.append(value, 0, i); - } - sb.append('_'); - } else if (sb != null) { - sb.append(c); - } - } - return sb == null ? value : sb.toString(); + return value + .replace('\u0000', '_') + .replace('\t', '_') + .replace('\r', '_') + .replace('\n', '_') + .replace('\u2028', '_') + .replace('\u2029', '_') + .replace('\u202A', '_') + .replace('\u202B', '_') + .replace('\u202C', '_') + .replace('\u202D', '_') + .replace('\u202E', '_'); } } diff --git a/src/test/java/com/clearfolio/viewer/service/DefaultDocumentValidationServiceTest.java b/src/test/java/com/clearfolio/viewer/service/DefaultDocumentValidationServiceTest.java index 4f27bdc..66b1bdf 100644 --- a/src/test/java/com/clearfolio/viewer/service/DefaultDocumentValidationServiceTest.java +++ b/src/test/java/com/clearfolio/viewer/service/DefaultDocumentValidationServiceTest.java @@ -525,6 +525,21 @@ void sanitizeForLogReplacesTabCharacter() throws Exception { assertEquals("approver_id", sanitized); } + @Test + void sanitizeForLogReplacesLineBreaksAndUnicodeControlCharacters() throws Exception { + ConversionProperties conversionProperties = new ConversionProperties(); + DefaultDocumentValidationService validationService = new DefaultDocumentValidationService(conversionProperties); + Method method = DefaultDocumentValidationService.class.getDeclaredMethod("sanitizeForLog", String.class); + method.setAccessible(true); + + String sanitized = (String) method.invoke( + validationService, + "approver\r\n\u2028\u2029\u202A\u202B\u202C\u202D\u202E\u0000id" + ); + + assertEquals("approver" + "_".repeat(10) + "id", sanitized); + } + @Test void throwsWhenSha256DigestIsUnavailableForOverrideAuditFingerprint() { ConversionProperties conversionProperties = new ConversionProperties();