@@ -190,7 +190,7 @@ private static boolean isSeparator(final char ch) {
190190 * (Note the file separator returned will be correct for Windows/Unix)
191191 *
192192 * @param filename the filename to normalize, null returns null
193- * @return the normalized filename, or null if invalid
193+ * @return the normalized filename, or null if invalid. Null bytes inside string will be removed
194194 */
195195 public static String normalize (final String filename ) {
196196 return doNormalize (filename , SYSTEM_SEPARATOR , true );
@@ -236,7 +236,7 @@ public static String normalize(final String filename) {
236236 * @param filename the filename to normalize, null returns null
237237 * @param unixSeparator {@code true} if a unix separator should
238238 * be used or {@code false} if a windows separator should be used.
239- * @return the normalized filename, or null if invalid
239+ * @return the normalized filename, or null if invalid. Null bytes inside string will be removed
240240 * @since 2.0
241241 */
242242 public static String normalize (final String filename , final boolean unixSeparator ) {
@@ -284,7 +284,7 @@ public static String normalize(final String filename, final boolean unixSeparato
284284 * (Note the file separator returned will be correct for Windows/Unix)
285285 *
286286 * @param filename the filename to normalize, null returns null
287- * @return the normalized filename, or null if invalid
287+ * @return the normalized filename, or null if invalid. Null bytes inside string will be removed
288288 */
289289 public static String normalizeNoEndSeparator (final String filename ) {
290290 return doNormalize (filename , SYSTEM_SEPARATOR , false );
@@ -330,7 +330,7 @@ public static String normalizeNoEndSeparator(final String filename) {
330330 * @param filename the filename to normalize, null returns null
331331 * @param unixSeparator {@code true} if a unix separator should
332332 * be used or {@code false} if a windows separtor should be used.
333- * @return the normalized filename, or null if invalid
333+ * @return the normalized filename, or null if invalid. Null bytes inside string will be removed
334334 * @since 2.0
335335 */
336336 public static String normalizeNoEndSeparator (final String filename , final boolean unixSeparator ) {
@@ -344,23 +344,26 @@ public static String normalizeNoEndSeparator(final String filename, final boolea
344344 * @param filename the filename
345345 * @param separator The separator character to use
346346 * @param keepSeparator true to keep the final separator
347- * @return the normalized filename
347+ * @return the normalized filename. Null bytes inside string will be removed.
348348 */
349349 private static String doNormalize (final String filename , final char separator , final boolean keepSeparator ) {
350350 if (filename == null ) {
351351 return null ;
352352 }
353- int size = filename .length ();
353+
354+ String cleanFileName = filterNullBytes (filename );
355+
356+ int size = cleanFileName .length ();
354357 if (size == 0 ) {
355- return filename ;
358+ return cleanFileName ;
356359 }
357- final int prefix = getPrefixLength (filename );
360+ final int prefix = getPrefixLength (cleanFileName );
358361 if (prefix < 0 ) {
359362 return null ;
360363 }
361364
362365 final char [] array = new char [size + 2 ]; // +1 for possible extra slash, +2 for arraycopy
363- filename .getChars (0 , filename .length (), array , 0 );
366+ cleanFileName .getChars (0 , cleanFileName .length (), array , 0 );
364367
365368 // fix separators throughout
366369 final char otherSeparator = separator == SYSTEM_SEPARATOR ? OTHER_SEPARATOR : SYSTEM_SEPARATOR ;
@@ -478,7 +481,7 @@ private static String doNormalize(final String filename, final char separator, f
478481 *
479482 * @param basePath the base path to attach to, always treated as a path
480483 * @param fullFilenameToAdd the filename (or path) to attach to the base
481- * @return the concatenated path, or null if invalid
484+ * @return the concatenated path, or null if invalid. Null bytes inside string will be removed
482485 */
483486 public static String concat (final String basePath , final String fullFilenameToAdd ) {
484487 final int prefix = getPrefixLength (fullFilenameToAdd );
@@ -953,14 +956,44 @@ private static String doGetFullPath(final String filename, final boolean include
953956 * The output will be the same irrespective of the machine that the code is running on.
954957 *
955958 * @param filename the filename to query, null returns null
956- * @return the name of the file without the path, or an empty string if none exists
959+ * @return the name of the file without the path, or an empty string if none exists.
960+ * Null bytes inside string will be removed
957961 */
958962 public static String getName (final String filename ) {
959963 if (filename == null ) {
960964 return null ;
961965 }
962- final int index = indexOfLastSeparator (filename );
963- return filename .substring (index + 1 );
966+ String cleanFileName = filterNullBytes (filename );
967+ final int index = indexOfLastSeparator (cleanFileName );
968+ return cleanFileName .substring (index + 1 );
969+ }
970+
971+ /**
972+ * Check the input for null bytes, a sign of unsanitized data being passed to to file level functions.
973+ *
974+ * This may be used for poison byte attacks.
975+ * @param path the path to check
976+ */
977+ private static void failIfNullBytePresent (String path ) {
978+ int len = path .length ();
979+ for (int i = 0 ; i < len ; i ++) {
980+ if (path .charAt (i ) == 0 ) {
981+ throw new IllegalArgumentException ("Null byte present in file/path name. There are no " +
982+ "known legitimate use cases for such data, but several injection attacks may use it" );
983+ }
984+ }
985+ }
986+
987+ /**
988+ * Filters the supplied path for null byte characters. Can be used for normalizations to avoid poison byte attacks.
989+ *
990+ * This mimicks behaviour of 1.7u40+. Once minimum java requirement is above this version, this code can be removed.
991+ *
992+ * @param path the path
993+ * @return the supplied string without any embedded null characters
994+ */
995+ private static String filterNullBytes (String path ) {
996+ return path .contains ("\u0000 " ) ? path .replace ("\u0000 " , "" ) : path ;
964997 }
965998
966999 /**
@@ -978,7 +1011,8 @@ public static String getName(final String filename) {
9781011 * The output will be the same irrespective of the machine that the code is running on.
9791012 *
9801013 * @param filename the filename to query, null returns null
981- * @return the name of the file without the path, or an empty string if none exists
1014+ * @return the name of the file without the path, or an empty string if none exists. Null bytes inside string
1015+ * will be removed
9821016 */
9831017 public static String getBaseName (final String filename ) {
9841018 return removeExtension (getName (filename ));
@@ -1036,11 +1070,13 @@ public static String removeExtension(final String filename) {
10361070 if (filename == null ) {
10371071 return null ;
10381072 }
1039- final int index = indexOfExtension (filename );
1073+ String cleanFileName = filterNullBytes (filename );
1074+
1075+ final int index = indexOfExtension (cleanFileName );
10401076 if (index == NOT_FOUND ) {
1041- return filename ;
1077+ return cleanFileName ;
10421078 } else {
1043- return filename .substring (0 , index );
1079+ return cleanFileName .substring (0 , index );
10441080 }
10451081 }
10461082
@@ -1151,11 +1187,14 @@ public static boolean equals(
11511187 * @param filename the filename to query, null returns false
11521188 * @param extension the extension to check for, null or empty checks for no extension
11531189 * @return true if the filename has the specified extension
1190+ * @throws java.lang.IllegalArgumentException if the supplied filename contains null bytes
11541191 */
11551192 public static boolean isExtension (final String filename , final String extension ) {
11561193 if (filename == null ) {
11571194 return false ;
11581195 }
1196+ failIfNullBytePresent (filename );
1197+
11591198 if (extension == null || extension .isEmpty ()) {
11601199 return indexOfExtension (filename ) == NOT_FOUND ;
11611200 }
@@ -1173,11 +1212,14 @@ public static boolean isExtension(final String filename, final String extension)
11731212 * @param filename the filename to query, null returns false
11741213 * @param extensions the extensions to check for, null checks for no extension
11751214 * @return true if the filename is one of the extensions
1215+ * @throws java.lang.IllegalArgumentException if the supplied filename contains null bytes
11761216 */
11771217 public static boolean isExtension (final String filename , final String [] extensions ) {
11781218 if (filename == null ) {
11791219 return false ;
11801220 }
1221+ failIfNullBytePresent (filename );
1222+
11811223 if (extensions == null || extensions .length == 0 ) {
11821224 return indexOfExtension (filename ) == NOT_FOUND ;
11831225 }
@@ -1200,11 +1242,14 @@ public static boolean isExtension(final String filename, final String[] extensio
12001242 * @param filename the filename to query, null returns false
12011243 * @param extensions the extensions to check for, null checks for no extension
12021244 * @return true if the filename is one of the extensions
1245+ * @throws java.lang.IllegalArgumentException if the supplied filename contains null bytes
12031246 */
12041247 public static boolean isExtension (final String filename , final Collection <String > extensions ) {
12051248 if (filename == null ) {
12061249 return false ;
12071250 }
1251+ failIfNullBytePresent (filename );
1252+
12081253 if (extensions == null || extensions .isEmpty ()) {
12091254 return indexOfExtension (filename ) == NOT_FOUND ;
12101255 }
0 commit comments