@@ -22,6 +22,8 @@ public final class B64Detector {
2222 // Allow embedded \n/\r (PEM/MIME line-wrapped Base64); = padding and trailing newline are optional.
2323 private static final Pattern BASE64_STANDARD = Pattern .compile ("^[A-Za-z0-9+/\\ n\\ r]*=*[\\ n\\ r]*$" );
2424 private static final Pattern BASE64_URL_SAFE = Pattern .compile ("^[A-Za-z0-9_\\ -\\ n\\ r]*=*[\\ n\\ r]*$" );
25+ // Line-break chars permitted as PEM/MIME wrapping above; stripped before the length-quantum check.
26+ private static final Pattern LINE_BREAKS = Pattern .compile ("[\\ n\\ r]" );
2527 // Identifier shapes — only checked below IDENTIFIER_FILTER_MAX_LEN.
2628 private static final Pattern CAMEL_CASE = Pattern .compile ("^[a-z]+([A-Z][a-z]+)+$" );
2729 private static final Pattern PASCAL_CASE = Pattern .compile ("^[A-Z][a-z]+([A-Z][a-z0-9]+)+$" );
@@ -57,7 +59,7 @@ private interface InputFilter {
5759 */
5860 private static final InputFilter [] INPUT_FILTERS = {
5961 (s , o ) -> B64FalsePositives .contains (s ),
60- (s , o ) -> o .isRequireValidLength () && s . length ( ) % 4 != 0 ,
62+ (s , o ) -> o .isRequireValidLength () && significantLength ( s ) % 4 != 0 ,
6163 (s , o ) -> isLikelyIdentifier (s , o ),
6264 (s , o ) -> !hasValidBase64Charset (s )
6365 };
@@ -116,6 +118,18 @@ private static boolean hasValidBase64Charset(String str) {
116118 return BASE64_STANDARD .matcher (str ).matches () || BASE64_URL_SAFE .matcher (str ).matches ();
117119 }
118120
121+ /**
122+ * Length excluding {@code \n}/{@code \r} — line wraps don't count toward the 4-char Base64
123+ * quantum, so {@code requireValidLength} must ignore them or it rejects line-wrapped strings.
124+ *
125+ * <p>TODO: full MIME also skips spaces/tabs; supporting it means stripping all {@code \s} here
126+ * and relaxing {@link #hasValidBase64Charset}. Only worth it with a solid false-positive
127+ * defense, else prose like {@code "Java Code"} decodes.
128+ */
129+ private static int significantLength (String str ) {
130+ return LINE_BREAKS .matcher (str ).replaceAll ("" ).length ();
131+ }
132+
119133 private static Variant [] variantsFor (String str ) {
120134 boolean hasLineBreaks = str .indexOf ('\n' ) >= 0 || str .indexOf ('\r' ) >= 0 ;
121135 return hasLineBreaks ? Variant .values () : new Variant [] { Variant .STANDARD , Variant .URL };
0 commit comments