Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions addOns/exim/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).

## Unreleased

### Fixed
- Sites Tree export now includes full raw bodies for all HTTP methods (Issue 8941).

## [0.14.0] - 2025-03-25
### Changed
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -53,16 +53,13 @@
import org.parosproxy.paros.model.Model;
import org.parosproxy.paros.model.SiteMap;
import org.parosproxy.paros.model.SiteNode;
import org.parosproxy.paros.network.HtmlParameter.Type;
import org.parosproxy.paros.network.HttpHeader;
import org.parosproxy.paros.network.HttpMalformedHeaderException;
import org.parosproxy.paros.network.HttpMessage;
import org.parosproxy.paros.network.HttpRequestHeader;
import org.yaml.snakeyaml.LoaderOptions;
import org.yaml.snakeyaml.Yaml;
import org.zaproxy.addon.exim.ExporterResult;
import org.zaproxy.addon.exim.ExtensionExim;
import org.zaproxy.zap.model.NameValuePair;
import org.zaproxy.zap.utils.Stats;

public class SitesTreeHandler {
Expand Down Expand Up @@ -219,32 +216,19 @@ public void serialize(SiteNode value, JsonGenerator gen, SerializerProvider prov
gen.writeNumberField(EximSiteNode.STATUS_CODE_KEY, href.getStatusCode());
}

if (HttpRequestHeader.POST.equals(href.getMethod())) {
try {
HttpMessage msg = href.getHttpMessage();
String contentType =
msg.getRequestHeader().getHeader(HttpHeader.CONTENT_TYPE);
if (contentType == null
|| !contentType.startsWith(
HttpHeader.FORM_MULTIPART_CONTENT_TYPE)) {
List<NameValuePair> params =
Model.getSingleton().getSession().getParameters(msg, Type.form);
StringBuilder sb = new StringBuilder();
params.forEach(
nvp -> {
if (sb.length() > 0) {
sb.append('&');
}
sb.append(
URLEncoder.encode(
nvp.getName(), StandardCharsets.UTF_8));
sb.append("=");
});
gen.writeStringField(EximSiteNode.DATA_KEY, sb.toString());
try {
HttpMessage msg = href.getHttpMessage();
String contentType = msg.getRequestHeader().getHeader(HttpHeader.CONTENT_TYPE);
if (contentType == null
|| !contentType.startsWith(HttpHeader.FORM_MULTIPART_CONTENT_TYPE)) {
String body = msg.getRequestBody().toString();
if (!body.isEmpty()) {
String encodedBody = URLEncoder.encode(body, StandardCharsets.UTF_8);
gen.writeStringField(EximSiteNode.DATA_KEY, encodedBody);
}
} catch (IOException | DatabaseException e) {
LOGGER.error(e.getMessage(), e);
}
} catch (IOException | DatabaseException e) {
LOGGER.error(e.getMessage(), e);
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -157,7 +157,7 @@ void shouldOutputNodeWithData() throws Exception {
+ " method: POST\n"
+ " responseLength: 61\n"
+ " statusCode: 200\n"
+ " data: eee=&ggg=\n";
+ " data: eee%3Dfff%26ggg%3Dhhh\n";
HttpMessage msg =
new HttpMessage(
"POST https://www.example.com?aa=bb&cc=dd HTTP/1.1\r\n"
Expand Down Expand Up @@ -188,7 +188,7 @@ void shouldOutputNodeWithDataButNoContentType() throws Exception {
+ " method: POST\n"
+ " responseLength: 61\n"
+ " statusCode: 200\n"
+ " data: eee=&ggg=\n";
+ " data: eee%3Dfff%26ggg%3Dhhh\n";
HttpMessage msg =
new HttpMessage(
"POST https://www.example.com?aa=bb&cc=dd HTTP/1.1\r\n",
Expand Down Expand Up @@ -222,7 +222,7 @@ void shouldOutputNodes() throws Exception {
+ " method: POST\n"
+ " responseLength: 61\n"
+ " statusCode: 200\n"
+ " data: aaa=\n"
+ " data: aaa%3Dbbb\n"
+ " - node: PUT:aaa\n"
+ " url: https://www.example.com/aaa\n"
+ " method: PUT\n";
Expand Down Expand Up @@ -281,6 +281,83 @@ void shouldOutputNodeWithMultipartFormData() throws Exception {
assertThat(result.getCount(), is(3));
}

@Test
void shouldOutputNodeWithXmlBody() throws Exception {
// Given
String xmlBody =
"""
<?xml version="1.0" encoding="UTF-8"?>
<root>
<item>value</item>
</root>
""";
HttpMessage msg =
new HttpMessage(
"POST https://www.example.com/ HTTP/1.1\r\n"
+ "Content-Type: application/xml\r\n",
xmlBody.getBytes(),
"HTTP/1.1 200 OK\r\n" + "content-length: 20",
"12345678901234567890".getBytes());
siteMap.addPath(getHref(msg));
StringWriter sw = new StringWriter();
ExporterResult result = new ExporterResult();

// When
SitesTreeHandler.exportSitesTree(sw, siteMap, result);

// Then
assertThat(
sw.toString(), containsString(URLEncoder.encode(xmlBody, StandardCharsets.UTF_8)));
assertThat(result.getCount(), is(3));
}

@Test
void shouldOutputNodeWithJsonBody() throws Exception {
// Given
String jsonBody = "{\"key\":\"value\",\"array\":[1,2,3]}";
HttpMessage msg =
new HttpMessage(
"POST https://www.example.com/ HTTP/1.1\r\n"
+ "Content-Type: application/json\r\n",
jsonBody.getBytes(),
"HTTP/1.1 200 OK\r\n" + "content-length: 20",
"12345678901234567890".getBytes());
siteMap.addPath(getHref(msg));
StringWriter sw = new StringWriter();
ExporterResult result = new ExporterResult();

// When
SitesTreeHandler.exportSitesTree(sw, siteMap, result);

// Then
assertThat(
sw.toString(), containsString(URLEncoder.encode(jsonBody, StandardCharsets.UTF_8)));
assertThat(result.getCount(), is(3));
}

@Test
void shouldOutputNodeWithPutBody() throws Exception {
// Given
String body = "This is a PUT request body";
HttpMessage msg =
new HttpMessage(
"PUT https://www.example.com/ HTTP/1.1\r\n"
+ "Content-Type: text/plain\r\n",
body.getBytes(),
"HTTP/1.1 200 OK\r\n" + "content-length: 20",
"12345678901234567890".getBytes());
siteMap.addPath(getHref(msg));
StringWriter sw = new StringWriter();
ExporterResult result = new ExporterResult();

// When
SitesTreeHandler.exportSitesTree(sw, siteMap, result);

// Then
assertThat(sw.toString(), containsString(URLEncoder.encode(body, StandardCharsets.UTF_8)));
assertThat(result.getCount(), is(3));
}

@Test
void shouldOutputDdnNode() throws Exception {
// Given
Expand Down Expand Up @@ -556,7 +633,7 @@ void shouldHandleSpecialCharactersInParameterNames(
sw.toString(),
containsString(
"""
data: '%s='
data: '%s%%3Dfff'
"""
.formatted(persistedParameterName)));
}
Expand Down