/*
* Zed Attack Proxy (ZAP) and its related class files.
*
* ZAP is an HTTP/HTTPS proxy for assessing web application security.
*
* Copyright 2013 The ZAP Development Team
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.zaproxy.zap.utils;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.net.HttpCookie;
import java.util.Date;
import java.util.List;
import java.util.Locale;
import org.apache.commons.lang.StringUtils;
import org.apache.log4j.Logger;
import org.codehaus.jackson.JsonFactory;
import org.codehaus.jackson.JsonParser;
import org.parosproxy.paros.Constant;
import org.parosproxy.paros.extension.encoder.Base64;
import org.parosproxy.paros.network.HtmlParameter;
import org.parosproxy.paros.network.HttpHeader;
import org.parosproxy.paros.network.HttpHeaderField;
import org.parosproxy.paros.network.HttpMalformedHeaderException;
import org.parosproxy.paros.network.HttpMessage;
import org.parosproxy.paros.network.HttpRequestHeader;
import org.parosproxy.paros.network.HttpResponseHeader;
import org.zaproxy.zap.network.HttpRequestBody;
import edu.umass.cs.benchlab.har.HarCache;
import edu.umass.cs.benchlab.har.HarContent;
import edu.umass.cs.benchlab.har.HarCookie;
import edu.umass.cs.benchlab.har.HarCookies;
import edu.umass.cs.benchlab.har.HarCreator;
import edu.umass.cs.benchlab.har.HarEntry;
import edu.umass.cs.benchlab.har.HarEntryTimings;
import edu.umass.cs.benchlab.har.HarHeader;
import edu.umass.cs.benchlab.har.HarHeaders;
import edu.umass.cs.benchlab.har.HarLog;
import edu.umass.cs.benchlab.har.HarPostData;
import edu.umass.cs.benchlab.har.HarPostDataParam;
import edu.umass.cs.benchlab.har.HarPostDataParams;
import edu.umass.cs.benchlab.har.HarQueryParam;
import edu.umass.cs.benchlab.har.HarQueryString;
import edu.umass.cs.benchlab.har.HarRequest;
import edu.umass.cs.benchlab.har.HarResponse;
import edu.umass.cs.benchlab.har.tools.HarFileWriter;
/**
* Utility class to parse/create HTTP Archives (HAR) and do conversions between HAR Java classes and {@code HttpMessage}s
* (request and response).
*
* @see <a href="http://www.softwareishard.com/blog/har-12-spec/">HTTP Archive 1.2</a>
* @since 2.3.0
* @see HttpMessage
*/
public final class HarUtils {
private static final Logger LOGGER = Logger.getLogger(HarUtils.class);
private HarUtils() {
}
public static HarLog createZapHarLog() {
return new HarLog(new HarCreator(Constant.PROGRAM_NAME, Constant.PROGRAM_VERSION));
}
public static byte[] harLogToByteArray(HarLog harLog) throws IOException {
ByteArrayOutputStream baos = new ByteArrayOutputStream(1024);
HarFileWriter w = new HarFileWriter();
w.writeHarFile(harLog, baos);
return baos.toByteArray();
}
public static HttpMessage createHttpMessage(String jsonHarRequest) throws IOException {
return createHttpMessage(createHarRequest(jsonHarRequest));
}
public static HarRequest createHarRequest(String jsonHarRequest) throws IOException {
HarRequest harRequest;
try (JsonParser jp = new JsonFactory().createJsonParser(jsonHarRequest)) {
jp.nextToken();
jp.nextToken();
harRequest = new HarRequest(jp, null);
}
return harRequest;
}
public static HttpMessage createHttpMessage(HarRequest harRequest) throws HttpMalformedHeaderException {
StringBuilder strBuilderReqHeader = new StringBuilder();
strBuilderReqHeader.append(harRequest.getMethod())
.append(' ')
.append(harRequest.getUrl())
.append(' ')
.append(harRequest.getHttpVersion())
.append("\r\n");
for (HarHeader harHeader : harRequest.getHeaders().getHeaders()) {
strBuilderReqHeader.append(harHeader.getName()).append(": ").append(harHeader.getValue()).append("\r\n");
}
strBuilderReqHeader.append("\r\n");
StringBuilder strBuilderReqBody = new StringBuilder();
final HarPostData harPostData = harRequest.getPostData();
if (harPostData != null) {
final String text = harPostData.getText();
if (text != null && !text.isEmpty()) {
strBuilderReqBody.append(harRequest.getPostData().getText());
} else if (harPostData.getParams() != null && !harPostData.getParams().getPostDataParams().isEmpty()) {
for (HarPostDataParam param : harRequest.getPostData().getParams().getPostDataParams()) {
if (strBuilderReqBody.length() > 0) {
strBuilderReqBody.append('&');
}
strBuilderReqBody.append(param.getName()).append('=').append(param.getValue());
}
}
}
return new HttpMessage(new HttpRequestHeader(strBuilderReqHeader.toString()), new HttpRequestBody(
strBuilderReqBody.toString()));
}
public static HarEntry createHarEntry(HttpMessage httpMessage) {
HarEntryTimings timings = new HarEntryTimings(0, 0, httpMessage.getTimeElapsedMillis());
return new HarEntry(
new Date(httpMessage.getTimeSentMillis()),
httpMessage.getTimeElapsedMillis(),
createHarRequest(httpMessage),
createHarResponse(httpMessage),
new HarCache(),
timings);
}
public static HarRequest createHarRequest(HttpMessage httpMessage) {
HttpRequestHeader requestHeader = httpMessage.getRequestHeader();
HarCookies harCookies = new HarCookies();
try {
for (HttpCookie cookie : requestHeader.getHttpCookies()) {
harCookies.addCookie(new HarCookie(cookie.getName(), cookie.getValue()));
}
} catch (IllegalArgumentException e) {
LOGGER.warn("Ignoring cookies for HAR (\"request\") \"cookies\" list. Request contains invalid cookie: "
+ e.getMessage());
}
HarQueryString harQueryString = new HarQueryString();
for (HtmlParameter param : httpMessage.getUrlParams()) {
harQueryString.addQueryParam(new HarQueryParam(param.getName(), param.getValue()));
}
HarPostData harPostData = null;
HttpRequestBody requestBody = httpMessage.getRequestBody();
if (requestBody.length() >= 0) {
HarPostDataParams params = new HarPostDataParams();
String text = "";
String contentType = requestHeader.getHeader(HttpHeader.CONTENT_TYPE);
if (contentType == null) {
contentType = "";
text = requestBody.toString();
} else {
if (StringUtils.startsWithIgnoreCase(contentType.trim(), HttpHeader.FORM_URLENCODED_CONTENT_TYPE)) {
for (HtmlParameter param : httpMessage.getFormParams()) {
params.addPostDataParam(new HarPostDataParam(param.getName(), param.getValue()));
}
} else {
text = requestBody.toString();
}
}
harPostData = new HarPostData(contentType, params, text, null);
}
return new HarRequest(
requestHeader.getMethod(),
requestHeader.getURI().toString(),
requestHeader.getVersion(),
harCookies,
createHarHeaders(requestHeader),
harQueryString,
harPostData,
requestHeader.toString().length(),
httpMessage.getRequestBody().length(),
null);
}
public static HarResponse createHarResponse(HttpMessage httpMessage) {
HttpResponseHeader responseHeader = httpMessage.getResponseHeader();
HarCookies harCookies = new HarCookies();
long whenCreated = System.currentTimeMillis();
for (HttpCookie cookie : responseHeader.getHttpCookies(httpMessage.getRequestHeader().getHostName())) {
Date expires;
if (cookie.getVersion() == 0) {
expires = new Date(whenCreated + (cookie.getMaxAge() * 1000));
} else {
expires = new Date(httpMessage.getTimeSentMillis() + httpMessage.getTimeElapsedMillis()
+ (cookie.getMaxAge() * 1000));
}
harCookies.addCookie(new HarCookie(
cookie.getName(),
cookie.getValue(),
cookie.getPath(),
cookie.getDomain(),
expires,
cookie.isHttpOnly(),
cookie.getSecure(),
null));
}
String text = null;
String encoding = null;
String contentType = responseHeader.getHeader(HttpHeader.CONTENT_TYPE);
if (contentType == null) {
contentType = "";
} else if (!contentType.isEmpty()) {
String lcContentType = contentType.toLowerCase(Locale.ROOT);
final int pos = lcContentType.indexOf(';');
if (pos != -1) {
lcContentType = lcContentType.substring(0, pos).trim();
}
if (!lcContentType.startsWith("text")) {
encoding = "base64";
text = Base64.encodeBytes(httpMessage.getResponseBody().getBytes());
} else {
text = httpMessage.getResponseBody().toString();
}
}
HarContent harContent = new HarContent(httpMessage.getResponseBody().length(), 0, contentType, text, encoding, null);
String redirectUrl = responseHeader.getHeader(HttpHeader.LOCATION);
return new HarResponse(
responseHeader.getStatusCode(),
responseHeader.getReasonPhrase(),
responseHeader.getVersion(),
harCookies,
createHarHeaders(responseHeader),
harContent,
redirectUrl == null ? "" : redirectUrl,
responseHeader.toString().length(),
httpMessage.getResponseBody().length(),
null);
}
public static HarHeaders createHarHeaders(HttpHeader httpHeader) {
HarHeaders harHeaders = new HarHeaders();
List<HttpHeaderField> headers = httpHeader.getHeaders();
for (HttpHeaderField headerField : headers) {
harHeaders.addHeader(new HarHeader(headerField.getName(), headerField.getValue()));
}
return harHeaders;
}
}