Skip to content

Commit c2ad0df

Browse files
committed
First working version of ZAP multi-part support to ZAP #12
1 parent 96f3457 commit c2ad0df

File tree

3 files changed

+95
-2
lines changed

3 files changed

+95
-2
lines changed

pom.xml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,12 @@
8484
<version>1.3</version>
8585
</dependency>
8686

87+
<dependency>
88+
<groupId>commons-fileupload</groupId>
89+
<artifactId>commons-fileupload</artifactId>
90+
<version>1.3.1</version>
91+
</dependency>
92+
8793
<!-- ZAProxy -->
8894

8995
<dependency>

scriptgen-core/pom.xml

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,12 @@
3535
<artifactId>miglayout-swing</artifactId>
3636
</dependency>
3737

38+
39+
<dependency>
40+
<groupId>commons-fileupload</groupId>
41+
<artifactId>commons-fileupload</artifactId>
42+
</dependency>
43+
3844
<dependency>
3945
<groupId>com.esotericsoftware</groupId>
4046
<artifactId>minlog</artifactId>
@@ -59,6 +65,7 @@
5965
<scope>test</scope>
6066
</dependency>
6167

68+
6269
</dependencies>
6370

6471
</project>

scriptgen-zap-plugin/src/main/java/org/zaproxy/zap/extension/scriptgen/ZapHttpRequestMapper.java

Lines changed: 82 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,40 @@
11
package org.zaproxy.zap.extension.scriptgen;
22

33
import com.h3xstream.scriptgen.model.HttpRequestInfo;
4+
import com.h3xstream.scriptgen.model.MultiPartParameter;
5+
import org.apache.commons.fileupload.MultipartStream;
46
import org.apache.commons.httpclient.URI;
57
import org.parosproxy.paros.network.HtmlParameter;
68
import org.parosproxy.paros.network.HttpMessage;
79

810
import java.io.BufferedReader;
911
import java.io.ByteArrayInputStream;
12+
import java.io.ByteArrayOutputStream;
1013
import java.io.IOException;
1114
import java.io.InputStreamReader;
15+
import java.io.OutputStream;
1216
import java.net.URLDecoder;
17+
import java.util.ArrayList;
1318
import java.util.HashMap;
19+
import java.util.List;
1420
import java.util.Map;
21+
import java.util.regex.Matcher;
22+
import java.util.regex.Pattern;
1523

1624
public class ZapHttpRequestMapper {
1725

26+
27+
//Sample : multipart/form-data; boundary=---------------------------297592997116592
28+
private static final Pattern PATTERN_MULTIPART_FORM_BOUNDARY = Pattern.compile("multipart/form-data; boundary=(.*)");
29+
//Content-Disposition: form-data; name="numitems"
30+
private static final Pattern PATTERN_MULTIPART_PARAM_NAME = Pattern.compile("[^e]name=\"([^\"]+)\"");
31+
//Content-Disposition: form-data; name="uploadname2"; filename=""
32+
private static final Pattern PATTERN_MULTIPART_PARAM_FILENAME = Pattern.compile("filename=\"([^\"]+)\"");
33+
34+
private static final Pattern PATTERN_MULTIPART_PARAM_CONTENT_TYPE = Pattern.compile("[Cc]ontent-[tT]ype: ([^\n^\r]+)");
35+
36+
37+
1838
public static HttpRequestInfo buildRequestInfo(HttpMessage httpMessage) throws IOException {
1939
String method = httpMessage.getRequestHeader().getMethod();
2040
URI url = httpMessage.getRequestHeader().getURI();
@@ -44,17 +64,77 @@ public static HttpRequestInfo buildRequestInfo(HttpMessage httpMessage) throws I
4464
}
4565
}
4666

67+
String multiPartBoundary = null;
68+
4769
Map<String,String> headers = new HashMap<String, String>();
4870
String headerString = httpMessage.getRequestHeader().getHeadersAsString();
4971
BufferedReader br = new BufferedReader(new InputStreamReader(new ByteArrayInputStream(headerString.getBytes())));
5072
String headerLine = null;
5173
while((headerLine = br.readLine()) != null) {
5274
String[] headerParts = headerLine.split(":", 2);
5375
if (headerParts.length == 2) {
54-
headers.put(headerParts[0].trim(), URLDecoder.decode(headerParts[1].trim(),"UTF-8"));
76+
77+
String name = headerParts[0].trim();
78+
String value = URLDecoder.decode(headerParts[1].trim(),"UTF-8"); //Decode for cookies
79+
headers.put(name, value);
80+
81+
if (name.toLowerCase().equals("content-type") && value.startsWith("multipart/form-data")) {
82+
Matcher m = PATTERN_MULTIPART_FORM_BOUNDARY.matcher(value);
83+
if(m.find()) {
84+
multiPartBoundary = m.group(1);
85+
}
86+
}
5587
}
5688
}
5789

58-
return new HttpRequestInfo(method,urlWithoutQuery,paramsGet,paramsPost,postData,headers,null);
90+
List<MultiPartParameter> multiPartParameters = new ArrayList<MultiPartParameter>();
91+
if(multiPartBoundary != null) { //Boundary was intercept during the previous step (header parsing)
92+
parseMultiPartParameter(httpMessage.getRequestBody().getBytes(),multiPartBoundary.getBytes(), multiPartParameters, paramsPost);
93+
}
94+
95+
return new HttpRequestInfo(method,urlWithoutQuery,paramsGet,paramsPost,postData,headers,multiPartParameters);
96+
}
97+
98+
/**
99+
*
100+
* Use the low-level api of Commons-FileUpload to parse the different parameters.
101+
*
102+
* @param input
103+
* @param boundary
104+
* @param multiPartParameters
105+
* @param paramsPost
106+
*/
107+
private static void parseMultiPartParameter(byte[] input,byte[] boundary,List<MultiPartParameter> multiPartParameters,Map<String,String> paramsPost) {
108+
109+
try {
110+
MultipartStream multipartStream = new MultipartStream(new ByteArrayInputStream(input), boundary);
111+
boolean nextPart = multipartStream.skipPreamble();
112+
113+
while(nextPart) {
114+
String header = multipartStream.readHeaders();
115+
Matcher mN = PATTERN_MULTIPART_PARAM_NAME.matcher(header);
116+
Matcher mF = PATTERN_MULTIPART_PARAM_FILENAME.matcher(header);
117+
Matcher mCT = PATTERN_MULTIPART_PARAM_CONTENT_TYPE.matcher(header);
118+
119+
String name = mN.find() ? mN.group(1) : null;
120+
String filename = mF.find() ? mF.group(1) : null;
121+
String contentType = mCT.find() ? mCT.group(1) : "application/octet-stream";
122+
123+
ByteArrayOutputStream output = new ByteArrayOutputStream();
124+
multipartStream.readBodyData(output);
125+
126+
if(filename != null) { //Multi-part file
127+
multiPartParameters.add(new MultiPartParameter(name, output.toString(), contentType, filename));
128+
}
129+
else { //Multi-part regular parameter
130+
paramsPost.put(name,output.toString());
131+
}
132+
nextPart = multipartStream.readBoundary();
133+
}
134+
} catch(MultipartStream.MalformedStreamException e) {
135+
//Oops
136+
} catch(IOException e) {
137+
//Oops
138+
}
59139
}
60140
}

0 commit comments

Comments
 (0)