396886 MultiPartFilter strips bad escaping on filename="..."
This commit is contained in:
parent
a5c1ced201
commit
7202631515
|
@ -211,7 +211,7 @@ public class MultiPartFilter implements Filter
|
|||
else if(tl.startsWith("name="))
|
||||
name=value(t);
|
||||
else if(tl.startsWith("filename="))
|
||||
filename=value(t);
|
||||
filename=filenameValue(t);
|
||||
}
|
||||
|
||||
// Check disposition
|
||||
|
@ -416,6 +416,34 @@ public class MultiPartFilter implements Filter
|
|||
String value = nameEqualsValue.substring(idx+1).trim();
|
||||
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);
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------------------------- */
|
||||
/**
|
||||
|
|
|
@ -224,7 +224,7 @@ public class MultipartFilterTest
|
|||
|
||||
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.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("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)
|
||||
*/
|
||||
|
|
|
@ -409,12 +409,21 @@ public class QuotedStringTokenizer
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
public static String unquoteOnly(String s)
|
||||
{
|
||||
return unquoteOnly(s, false);
|
||||
}
|
||||
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
/** Unquote a string, NOT converting unicode sequences
|
||||
* @param s The string to unquote.
|
||||
* @param lenient if true, will leave in backslashes that aren't valid escapes
|
||||
* @return quoted string
|
||||
*/
|
||||
public static String unquoteOnly(String s)
|
||||
public static String unquoteOnly(String s, boolean lenient)
|
||||
{
|
||||
if (s==null)
|
||||
return null;
|
||||
|
@ -435,7 +444,7 @@ public class QuotedStringTokenizer
|
|||
if (escape)
|
||||
{
|
||||
escape=false;
|
||||
if (!isValidEscaping(c))
|
||||
if (lenient && !isValidEscaping(c))
|
||||
{
|
||||
b.append('\\');
|
||||
}
|
||||
|
@ -453,13 +462,19 @@ public class QuotedStringTokenizer
|
|||
|
||||
return b.toString();
|
||||
}
|
||||
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
public static String unquote(String s)
|
||||
{
|
||||
return unquote(s,false);
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
/** Unquote a string.
|
||||
* @param s The string to unquote.
|
||||
* @return quoted string
|
||||
*/
|
||||
public static String unquote(String s)
|
||||
public static String unquote(String s, boolean lenient)
|
||||
{
|
||||
if (s==null)
|
||||
return null;
|
||||
|
@ -516,7 +531,7 @@ public class QuotedStringTokenizer
|
|||
);
|
||||
break;
|
||||
default:
|
||||
if (!isValidEscaping(c))
|
||||
if (lenient && !isValidEscaping(c))
|
||||
{
|
||||
b.append('\\');
|
||||
}
|
||||
|
@ -536,6 +551,13 @@ public class QuotedStringTokenizer
|
|||
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)
|
||||
{
|
||||
return ((c == 'n') || (c == 'r') || (c == 't') ||
|
||||
|
|
Loading…
Reference in New Issue