396886 MultiPartFilter strips bad escaping on filename="..."

This commit is contained in:
Jan Bartel 2012-12-20 13:48:45 +11:00
parent a5c1ced201
commit 7202631515
3 changed files with 130 additions and 7 deletions

View File

@ -211,7 +211,7 @@ public class MultiPartFilter implements Filter
else if(tl.startsWith("name=")) else if(tl.startsWith("name="))
name=value(t); name=value(t);
else if(tl.startsWith("filename=")) else if(tl.startsWith("filename="))
filename=value(t); filename=filenameValue(t);
} }
// Check disposition // Check disposition
@ -416,6 +416,34 @@ public class MultiPartFilter implements Filter
String value = nameEqualsValue.substring(idx+1).trim(); String value = nameEqualsValue.substring(idx+1).trim();
return QuotedStringTokenizer.unquoteOnly(value); return QuotedStringTokenizer.unquoteOnly(value);
} }
/* ------------------------------------------------------------ */
private String filenameValue(String nameEqualsValue)
{
int idx = nameEqualsValue.indexOf('=');
String value = nameEqualsValue.substring(idx+1).trim();
if (value.matches(".??[a-z,A-Z]\\:\\\\[^\\\\].*"))
{
//incorrectly escaped IE filenames that have the whole path
//we just strip any leading & trailing quotes and leave it as is
char first=value.charAt(0);
if (first=='"' || first=='\'')
value=value.substring(1);
char last=value.charAt(value.length()-1);
if (last=='"' || last=='\'')
value = value.substring(0,value.length()-1);
return value;
}
else
//unquote the string, but allow any backslashes that don't
//form a valid escape sequence to remain as many browsers
//even on *nix systems will not escape a filename containing
//backslashes
return QuotedStringTokenizer.unquoteOnly(value, true);
}
/* ------------------------------------------------------------------------------- */ /* ------------------------------------------------------------------------------- */
/** /**

View File

@ -224,7 +224,7 @@ public class MultipartFilterTest
response.parse(tester.getResponses(request.generate())); response.parse(tester.getResponses(request.generate()));
System.out.printf("Content: [%s]%n", response.getContent()); //System.out.printf("Content: [%s]%n", response.getContent());
assertThat(response.getMethod(), nullValue()); assertThat(response.getMethod(), nullValue());
assertThat(response.getStatus(), is(HttpServletResponse.SC_OK)); assertThat(response.getStatus(), is(HttpServletResponse.SC_OK));
@ -232,7 +232,80 @@ public class MultipartFilterTest
assertThat(response.getContent(), containsString("Filename [Taken on Aug 22 \\ 2012.jpg]")); assertThat(response.getContent(), containsString("Filename [Taken on Aug 22 \\ 2012.jpg]"));
assertThat(response.getContent(), containsString("How now brown cow.")); assertThat(response.getContent(), containsString("How now brown cow."));
} }
@Test
public void testBadlyEncodedMSFilename() throws Exception
{
// generated and parsed test
HttpTester request = new HttpTester();
HttpTester response = new HttpTester();
// test GET
request.setMethod("POST");
request.setVersion("HTTP/1.0");
request.setHeader("Host","tester");
request.setURI("/context/dump");
String boundary="XyXyXy";
request.setHeader("Content-Type","multipart/form-data; boundary="+boundary);
String content = "--" + boundary + "\r\n"+
"Content-Disposition: form-data; name=\"fileup\"; filename=\"c:\\this\\really\\is\\some\\path\\to\\a\\file.txt\"\r\n"+
"Content-Type: application/octet-stream\r\n\r\n"+
"How now brown cow."+
"\r\n--" + boundary + "--\r\n\r\n";
request.setContent(content);
response.parse(tester.getResponses(request.generate()));
//System.out.printf("Content: [%s]%n", response.getContent());
assertThat(response.getMethod(), nullValue());
assertThat(response.getStatus(), is(HttpServletResponse.SC_OK));
assertThat(response.getContent(), containsString("Filename [c:\\this\\really\\is\\some\\path\\to\\a\\file.txt]"));
assertThat(response.getContent(), containsString("How now brown cow."));
}
@Test
public void testCorrectlyEncodedMSFilename() throws Exception
{
// generated and parsed test
HttpTester request = new HttpTester();
HttpTester response = new HttpTester();
// test GET
request.setMethod("POST");
request.setVersion("HTTP/1.0");
request.setHeader("Host","tester");
request.setURI("/context/dump");
String boundary="XyXyXy";
request.setHeader("Content-Type","multipart/form-data; boundary="+boundary);
String content = "--" + boundary + "\r\n"+
"Content-Disposition: form-data; name=\"fileup\"; filename=\"c:\\\\this\\\\really\\\\is\\\\some\\\\path\\\\to\\\\a\\\\file.txt\"\r\n"+
"Content-Type: application/octet-stream\r\n\r\n"+
"How now brown cow."+
"\r\n--" + boundary + "--\r\n\r\n";
request.setContent(content);
response.parse(tester.getResponses(request.generate()));
//System.out.printf("Content: [%s]%n", response.getContent());
assertThat(response.getMethod(), nullValue());
assertThat(response.getStatus(), is(HttpServletResponse.SC_OK));
assertThat(response.getContent(), containsString("Filename [c:\\this\\really\\is\\some\\path\\to\\a\\file.txt]"));
assertThat(response.getContent(), containsString("How now brown cow."));
}
/* /*
* Test multipart with parts encoded in base64 (RFC1521 section 5) * Test multipart with parts encoded in base64 (RFC1521 section 5)
*/ */

View File

@ -409,12 +409,21 @@ public class QuotedStringTokenizer
} }
} }
/* ------------------------------------------------------------ */
public static String unquoteOnly(String s)
{
return unquoteOnly(s, false);
}
/* ------------------------------------------------------------ */ /* ------------------------------------------------------------ */
/** Unquote a string, NOT converting unicode sequences /** Unquote a string, NOT converting unicode sequences
* @param s The string to unquote. * @param s The string to unquote.
* @param lenient if true, will leave in backslashes that aren't valid escapes
* @return quoted string * @return quoted string
*/ */
public static String unquoteOnly(String s) public static String unquoteOnly(String s, boolean lenient)
{ {
if (s==null) if (s==null)
return null; return null;
@ -435,7 +444,7 @@ public class QuotedStringTokenizer
if (escape) if (escape)
{ {
escape=false; escape=false;
if (!isValidEscaping(c)) if (lenient && !isValidEscaping(c))
{ {
b.append('\\'); b.append('\\');
} }
@ -453,13 +462,19 @@ public class QuotedStringTokenizer
return b.toString(); return b.toString();
} }
/* ------------------------------------------------------------ */
public static String unquote(String s)
{
return unquote(s,false);
}
/* ------------------------------------------------------------ */ /* ------------------------------------------------------------ */
/** Unquote a string. /** Unquote a string.
* @param s The string to unquote. * @param s The string to unquote.
* @return quoted string * @return quoted string
*/ */
public static String unquote(String s) public static String unquote(String s, boolean lenient)
{ {
if (s==null) if (s==null)
return null; return null;
@ -516,7 +531,7 @@ public class QuotedStringTokenizer
); );
break; break;
default: default:
if (!isValidEscaping(c)) if (lenient && !isValidEscaping(c))
{ {
b.append('\\'); b.append('\\');
} }
@ -536,6 +551,13 @@ public class QuotedStringTokenizer
return b.toString(); return b.toString();
} }
/* ------------------------------------------------------------ */
/** Check that char c (which is preceded by a backslash) is a valid
* escape sequence.
* @param c
* @return
*/
private static boolean isValidEscaping(char c) private static boolean isValidEscaping(char c)
{ {
return ((c == 'n') || (c == 'r') || (c == 't') || return ((c == 'n') || (c == 'r') || (c == 't') ||