Пример #1
0
  private boolean isValidMessageToScan(HttpMessage msg) {
    if (getScannerOptions().isScanHeadersAllRequests()) {
      return true;
    }

    // First we check if it's a dynamic or static page
    // I'd to do this because scanning starts to be veeeeery slow
    // --
    // this is a trivial implementation, should be good to have
    // a page dynamic check at the parent plugin level which should
    // use or not Variants according to the behavior of the request
    // (e.g. different content or status error/redirect)
    String query = null;
    try {
      query = msg.getRequestHeader().getURI().getQuery();

    } catch (URIException e) {
      log.error(e.getMessage(), e);
    }

    // If there's almost one GET parameter go ahead
    if (query == null || query.isEmpty()) {
      // If also the Request body is null maybe it's a static page oer a null parameter page
      if (msg.getRequestBody().length() == 0) {
        return false;
      }
    }
    return true;
  }
Пример #2
0
  @Override
  public boolean onHttpRequestSend(HttpMessage message) {
    if (isActive() && SAMLUtils.hasSAMLMessage(message)) {
      try {
        SAMLMessage samlMessage = new SAMLMessage(message);

        // change the params
        for (Attribute attribute : configuration.getAutoChangeAttributes()) {
          String value = attribute.getValue().toString();
          boolean changed = samlMessage.changeAttributeValueTo(attribute.getName(), value);
          if (changed) {
            log.debug(attribute.getName() + ": value changed to " + value);
          }
        }

        // change the original message
        HttpMessage changedMessege = samlMessage.getChangedMessage();
        if (changedMessege != message) {
          // check for reference, if they are same the message is already changed,
          // else the header and body are changed
          message.setRequestBody(changedMessege.getRequestBody());
          message.setRequestHeader(changedMessege.getRequestHeader());
        }

      } catch (SAMLException ignored) {
      }
    }
    return true;
  }
  @Override
  public void scanHttpResponseReceive(HttpMessage msg, int id, Source source) {
    if (msg.getResponseHeader().getStatusCode() != HttpStatusCode.OK) {
      return;
    }

    String responseBody = msg.getRequestBody().toString();
    if (responseBody == null) {
      return;
    }

    Set<HtmlParameter> params = new TreeSet<>(msg.getFormParams());
    params.addAll(msg.getUrlParams());
    if (params.size() == 0) {
      return;
    }

    if (!isResponseHTML(msg, source) && !isResponseXML(source)) {
      return;
    }

    if (isResponseHTML(msg, source)) {
      checkMetaContentCharset(msg, id, source, params);
    } else if (isResponseXML(source)) {
      checkXmlEncodingCharset(msg, id, source, params);
    }

    checkContentTypeCharset(msg, id, params);
  }
Пример #4
0
  private void displayMessage(SearchResult sr) {
    HttpMessage msg = sr.getMessage();
    if (msg.getRequestHeader().isEmpty()) {
      requestPanel.clearView(true);
    } else {
      requestPanel.setMessage(msg);
    }

    if (msg.getResponseHeader().isEmpty()) {
      responsePanel.clearView(false);
    } else {
      responsePanel.setMessage(msg, true);
    }
    highlightFirstResult(sr);
  }
Пример #5
0
 /**
  * @param msg
  * @param originalPair
  * @param name
  * @param value
  * @param escaped
  * @return
  */
 private String setParameter(
     HttpMessage msg, NameValuePair originalPair, String name, String value, boolean escaped) {
   // Here gives null pointer exception...
   // maybe bacause the name value isn't equal to the original value one
   msg.getRequestHeader().setHeader(originalPair.getName(), value);
   return name + ":" + value;
 }
  // TODO: these methods have been extracted from CharsetMismatchScanner
  // I think we should create helper methods for them
  private boolean isResponseHTML(HttpMessage message, Source source) {
    String contentType = message.getResponseHeader().getHeader(HttpHeader.CONTENT_TYPE);
    if (contentType == null) {
      return false;
    }

    return contentType.indexOf("text/html") != -1
        || contentType.indexOf("application/xhtml+xml") != -1
        || contentType.indexOf("application/xhtml") != -1;
  }
  private void checkContentTypeCharset(HttpMessage msg, int id, Set<HtmlParameter> params) {
    String charset = msg.getResponseHeader().getCharset();
    if (charset == null || charset.equals("")) {
      return;
    }

    for (HtmlParameter param : params) {
      if (charset.equalsIgnoreCase(param.getValue())) {
        raiseAlert(msg, id, "Content-Type HTTP header", "charset", param, charset);
      }
    }
  }
Пример #8
0
  /** @param msg */
  @Override
  public void setMessage(HttpMessage msg) {
    if (!isValidMessageToScan(msg)) {
      return;
    }

    // httpHeaders is never null, so no need to check for null
    List<HttpHeaderField> httpHeaders = msg.getRequestHeader().getHeaders();
    int headerPos = 0;
    for (HttpHeaderField header : httpHeaders) {
      if (!NON_INJECTABLE_HEADERS.contains(header.getName().toLowerCase(Locale.ROOT))) {
        params.add(
            new NameValuePair(
                NameValuePair.TYPE_HEADER, header.getName(), header.getValue(), headerPos++));
      }
    }
  }
  private void raiseAlert(
      HttpMessage msg, int id, String tag, String attr, HtmlParameter param, String charset) {
    Alert alert = new Alert(getPluginId(), Alert.RISK_MEDIUM, Alert.WARNING, getName());

    alert.setDetail(
        getDescriptionMessage(),
        msg.getRequestHeader().getURI().toString(),
        param.getName(),
        getExploitMessage(msg),
        getExtraInfoMessage(msg, tag, attr, param, charset),
        getSolutionMessage(),
        getReferenceMessage(),
        "", // No evidence
        0, // TODO CWE Id
        0, // TODO WASC Id
        msg);

    parent.raiseAlert(id, alert);
  }
Пример #10
0
  @SuppressWarnings("unused")
  @Override
  public boolean parseResource(HttpMessage message, Source source, int depth) {

    // parse the Git index file, based on publicly available (but incomplete) documentation of the
    // file format, and some reverse-engineering.
    if (message == null || !params.isParseGit()) {
      return false;
    }
    log.debug("Parsing a Git resource...");

    // Get the response content
    byte[] data = message.getResponseBody().getBytes();
    String baseURL = message.getRequestHeader().getURI().toString();

    try {
      String fullpath = message.getRequestHeader().getURI().getPath();
      if (fullpath == null) fullpath = "";
      if (log.isDebugEnabled()) log.debug("The full path is [" + fullpath + "]");

      // make sure the file name is as expected
      Matcher gitIndexFilenameMatcher = gitIndexFilenamePattern.matcher(fullpath);
      if (gitIndexFilenameMatcher.find()) {
        // dealing with the Git Index file

        Matcher gitIndexContentMatcher = gitIndexContentPattern.matcher(new String(data));
        if (gitIndexContentMatcher.find()) {
          // it looks like a duck, and quacks like a duck.
          // although it could still be an animatronic duck

          ByteBuffer dataBuffer = ByteBuffer.wrap(data);

          byte[] dircArray = new byte[4];
          dataBuffer.get(dircArray, 0, 4);

          int indexFileVersion = dataBuffer.getInt();
          if (log.isDebugEnabled()) log.debug("The Git index file version is " + indexFileVersion);

          int indexEntryCount = dataBuffer.getInt();
          if (log.isDebugEnabled())
            log.debug(indexEntryCount + " entries were found in the Git index file ");

          if (indexFileVersion != 2 && indexFileVersion != 3 && indexFileVersion != 4) {
            throw new Exception(
                "Only Git Index File versions 2, 3, and 4 are currently supported. Git Index File Version "
                    + indexFileVersion
                    + " was found.");
          }

          // for version 4 (and upwards?), we need to know the previous entry name, so store it
          String previousIndexEntryName = "";
          for (int entryIndex = 0; entryIndex < indexEntryCount; entryIndex++) {
            int entryBytesRead = 0;
            int indexEntryCtime1 = dataBuffer.getInt();
            entryBytesRead += 4;
            if (log.isDebugEnabled())
              log.debug("Entry " + entryIndex + " has indexEntryCtime1 " + indexEntryCtime1);
            int indexEntryCtime2 = dataBuffer.getInt();
            entryBytesRead += 4;
            int indexEntryMtime1 = dataBuffer.getInt();
            entryBytesRead += 4;
            int indexEntryMtime2 = dataBuffer.getInt();
            entryBytesRead += 4;
            int indexEntryDev = dataBuffer.getInt();
            entryBytesRead += 4;
            int indexEntryInode = dataBuffer.getInt();
            entryBytesRead += 4;
            int indexEntryMode = dataBuffer.getInt();
            entryBytesRead += 4;
            int indexEntryUid = dataBuffer.getInt();
            entryBytesRead += 4;
            int indexEntryGid = dataBuffer.getInt();
            entryBytesRead += 4;
            int indexEntrySize = dataBuffer.getInt();
            entryBytesRead += 4;
            if (log.isDebugEnabled())
              log.debug("Entry " + entryIndex + " has size " + indexEntrySize);

            // size is unspecified for the entry id, but it seems to be 40 bytes SHA-1 string
            // stored as 20 bytes, network order
            byte[] indexEntryIdBuffer = new byte[20];
            dataBuffer.get(indexEntryIdBuffer, 0, 20);
            entryBytesRead += 20;
            String indexEntryId = new String(indexEntryIdBuffer);

            short indexEntryFlags = dataBuffer.getShort();
            entryBytesRead += 2;
            if (log.isDebugEnabled())
              log.debug("Entry " + entryIndex + " has flags " + indexEntryFlags);

            // mask off all but the least significant 12 bits of the index entry flags to get the
            // length of the name in bytes
            int indexEntryNameByteLength = indexEntryFlags & (int) 4095;
            if (log.isDebugEnabled())
              log.debug(
                  "Entry " + entryIndex + " has a name of length " + indexEntryNameByteLength);

            // mask off all but the second most significant 12 bit of the index entry flags to get
            // the extended flag for the entry
            // int indexEntryExtendedFlag = indexEntryFlags & (int)16384;
            int indexEntryExtendedFlag = ((indexEntryFlags & (int) (1 << 14)) >> 14);
            if (log.isDebugEnabled())
              log.debug(
                  "Entry " + entryIndex + " has an extended flag of " + indexEntryExtendedFlag);

            // check that we parsed out the index entry extended flag correctly.
            // this is more of an assertion than anything. It's already saved my bacon once.
            if (indexEntryExtendedFlag != 0 && indexEntryExtendedFlag != 1) {
              throw new Exception(
                  "Error parsing out the extended flag for index entry "
                      + entryIndex
                      + ". We got "
                      + indexEntryExtendedFlag);
            }
            if (indexFileVersion == 2 && indexEntryExtendedFlag != 0) {
              throw new Exception(
                  "Index File Version 2 is supposed to have the extended flag set to 0. For index entry "
                      + entryIndex
                      + ", it is set to "
                      + indexEntryExtendedFlag);
            }

            // specific to version 3 and above, if the extended flag is set for the entry.
            if (indexFileVersion > 2 && indexEntryExtendedFlag == 1) {
              if (log.isDebugEnabled())
                log.debug(
                    "For Index file version "
                        + indexFileVersion
                        + ", reading an extra 16 bits for Entry "
                        + entryIndex);
              short indexEntryExtendedFlags = dataBuffer.getShort();
              entryBytesRead += 2;
              if (log.isDebugEnabled())
                log.debug(
                    "Entry "
                        + entryIndex
                        + " has (optional) extended flags "
                        + indexEntryExtendedFlags);
            }

            String indexEntryName = null;
            if (indexFileVersion > 3) {
              if (log.isDebugEnabled())
                log.debug(
                    "Inflating the (deflated) entry name for index entry "
                        + entryIndex
                        + " based on the previous entry name, since Index file version "
                        + indexFileVersion
                        + " requires this");

              // get bytes until we find one with the msb NOT set. count the bytes.
              int n = 0, removeNfromPreviousName = 0;
              byte msbsetmask = (byte) (1 << 7); // 1000 0000
              byte msbunsetmask = (byte) ((~msbsetmask) & 0xFF); // 0111 1111
              while (++n > 0) {
                byte byteRead = dataBuffer.get();
                entryBytesRead++;
                if (n == 1) // zero the msb of the first byte read
                removeNfromPreviousName =
                      (removeNfromPreviousName << 8) | (0xFF & (byteRead & msbunsetmask));
                else // set the msb of subsequent bytes read
                removeNfromPreviousName =
                      (removeNfromPreviousName << 8) | (0xFF & (byteRead | msbsetmask));
                if ((byteRead & msbsetmask) == 0) break; // break if msb is NOT set in the byte
              }

              if (log.isDebugEnabled())
                log.debug(
                    "We read "
                        + n
                        + " bytes of variable length data from before the start of the entry name");
              if (n > 4)
                throw new Exception(
                    "An entry name is never expected to be > 2^^32 bytes long. Some file corruption may have occurred, or a parsing error has occurred");

              // now read the (partial) name for the current entry
              int bytesToReadCurrentNameEntry =
                  indexEntryNameByteLength
                      - (previousIndexEntryName.length() - removeNfromPreviousName);
              byte[] indexEntryNameBuffer = new byte[bytesToReadCurrentNameEntry];
              dataBuffer.get(indexEntryNameBuffer, 0, bytesToReadCurrentNameEntry);
              entryBytesRead += bytesToReadCurrentNameEntry;

              // build it up
              indexEntryName =
                  previousIndexEntryName.substring(
                          0, previousIndexEntryName.length() - removeNfromPreviousName)
                      + new String(indexEntryNameBuffer);
            } else {
              // indexFileVersion <= 3 (waaaaay simpler logic, but the index file is larger in this
              // version than for v4+)
              byte[] indexEntryNameBuffer = new byte[indexEntryNameByteLength];
              dataBuffer.get(indexEntryNameBuffer, 0, indexEntryNameByteLength);
              entryBytesRead += indexEntryNameByteLength;
              indexEntryName = new String(indexEntryNameBuffer);
            }

            if (log.isDebugEnabled())
              log.debug("Entry " + entryIndex + " has name " + indexEntryName);

            // and store off the index entry name, for the next iteration
            previousIndexEntryName = indexEntryName;
            // skip past the zero byte terminating the string (whose purpose seems completely
            // pointless to me, but hey)
            byte indexEntryNul = dataBuffer.get();
            entryBytesRead++;

            // the padding after the pathname does not exist for versions 4 or later.
            if (indexFileVersion < 4) {
              if (log.isDebugEnabled())
                log.debug(
                    "Aligning to an 8 byte boundary after Entry "
                        + entryIndex
                        + ", since Index file version "
                        + indexFileVersion
                        + " mandates 64 bit alignment for index entries");

              int entryBytesToRead = ((8 - (entryBytesRead % 8)) % 8);
              if (log.isDebugEnabled()) {
                log.debug(
                    "The number of bytes read for index entry "
                        + entryIndex
                        + " thus far is: "
                        + entryBytesRead);
                log.debug(
                    "So we must read " + entryBytesToRead + " bytes to stay on a 64 bit boundary");
              }
              // read the 0-7 (NUL) bytes to keep reading index entries on an 8 byte boundary
              byte[] indexEntryPadBuffer = new byte[entryBytesToRead];
              dataBuffer.get(indexEntryPadBuffer, 0, entryBytesToRead);
              entryBytesRead += entryBytesToRead;
            } else {
              if (log.isDebugEnabled())
                log.debug(
                    "Not aligning to an 8 byte boundary after Entry "
                        + entryIndex
                        + ", since Index file version "
                        + indexFileVersion
                        + " does not mandate 64 bit alignment for index entries");
            }

            // Git does not store entries for directories, but just files/symlinks/Git links, so no
            // need to handle directories here, unlike with SVN, for instance.
            if (indexEntryName != null && indexEntryName.length() > 0) {
              log.info(
                  "Found file/symbolic link/gitlink "
                      + indexEntryName
                      + " in the Git entries file");
              processURL(message, depth, "../" + indexEntryName, baseURL);
            }
          }
          // all good, we're outta here.
          // We consider the message fully parsed, so it doesn't get parsed by 'fallback' parsers
          return true;
        } else {
          throw new Exception(
              "The file '"
                  + fullpath
                  + "' could not be parsed as a Git Index file due to unexpected content");
        }
        // end of Git HEAD handling logic
      } else {
        throw new Exception("This path cannot be handled by the Git parser");
      }

    } catch (Exception e) {
      log.error("An error occurred trying to parse Git url '" + baseURL + "': " + e);
      // We consider the message fully parsed, so it doesn't get parsed by 'fallback' parsers
      return true;
    }
  }
Пример #11
0
  @SuppressWarnings({"unchecked", "rawtypes"})
  @Override
  public HttpMessage handleApiOther(HttpMessage msg, String name, JSONObject params)
      throws ApiException {
    if (OTHER_CHEETSHEET_ACTION_ORDER.equals(name) || OTHER_CHEETSHEET_KEY_ORDER.equals(name)) {

      List<KeyboardShortcut> shortcuts = this.extension.getShortcuts();

      if (OTHER_CHEETSHEET_ACTION_ORDER.equals(name)) {
        Collections.sort(
            shortcuts,
            new Comparator() {
              @Override
              public int compare(Object o1, Object o2) {
                return ((KeyboardShortcut) o1)
                    .getName()
                    .compareTo(((KeyboardShortcut) o2).getName());
              }
            });
      } else {
        Collections.sort(
            shortcuts,
            new Comparator() {
              @Override
              public int compare(Object o1, Object o2) {
                return ((KeyboardShortcut) o1)
                    .getKeyStrokeKeyCodeString()
                    .compareTo(((KeyboardShortcut) o2).getKeyStrokeKeyCodeString());
              }
            });
      }

      StringBuilder response = new StringBuilder();
      response.append(Constant.messages.getString("keyboard.api.cheatsheet.header"));
      boolean incUnset = this.getParam(params, PARAM_INC_UNSET, false);

      for (KeyboardShortcut shortcut : shortcuts) {
        if (incUnset || shortcut.getKeyStrokeKeyCodeString().length() > 0) {
          // Only show actions with actual shortcuts
          response.append(
              MessageFormat.format(
                  Constant.messages.getString("keyboard.api.cheatsheet.tablerow"),
                  shortcut.getName(),
                  shortcut.getKeyStrokeModifiersString(),
                  shortcut.getKeyStrokeKeyCodeString()));
        }
      }
      response.append(Constant.messages.getString("keyboard.api.cheatsheet.footer"));

      try {
        msg.setResponseHeader(
            "HTTP/1.1 200 OK\r\n"
                + "Pragma: no-cache\r\n"
                + "Cache-Control: no-cache\r\n"
                + "Content-Length: "
                + response.length()
                + "\r\nContent-Type: text/html;");
      } catch (HttpMalformedHeaderException e) {
        throw new ApiException(ApiException.Type.INTERNAL_ERROR, name, e);
      }
      msg.setResponseBody(response.toString());

      return msg;

    } else {
      throw new ApiException(ApiException.Type.BAD_OTHER, name);
    }
  }
Пример #12
0
  public RecordHistory getHistoryCache(HistoryReference ref, HttpMessage reqMsg)
      throws SQLException, HttpMalformedHeaderException {

    //  get the cache from provided reference.
    //  naturally, the obtained cache should be AFTER AND NEARBY to the given reference.
    //  - historyId up to historyId+200
    //  - match sessionId
    //  - history type can be MANUEL or hidden (hidden is used by images not explicitly stored in
    // history)
    //  - match URI
    PreparedStatement psReadCache = null;

    if (isExistStatusCode) {
      //          psReadCache = getConnection().prepareStatement("SELECT TOP 1 * FROM HISTORY WHERE
      // URI = ? AND METHOD = ? AND REQBODY = ? AND " + HISTORYID + " >= ? AND " + HISTORYID + " <=
      // ? AND SESSIONID = ? AND (HISTTYPE = " + HistoryReference.TYPE_MANUAL + " OR HISTTYPE = " +
      // HistoryReference.TYPE_HIDDEN + ") AND STATUSCODE != 304");
      psReadCache =
          getConnection()
              .prepareStatement(
                  "SELECT TOP 1 * FROM HISTORY WHERE URI = ? AND METHOD = ? AND REQBODY = ? AND "
                      + HISTORYID
                      + " >= ? AND "
                      + HISTORYID
                      + " <= ? AND SESSIONID = ? AND STATUSCODE != 304");

    } else {
      //          psReadCache = getConnection().prepareStatement("SELECT * FROM HISTORY WHERE URI =
      // ? AND METHOD = ? AND REQBODY = ? AND " + HISTORYID + " >= ? AND " + HISTORYID + " <= ? AND
      // SESSIONID = ? AND (HISTTYPE = " + HistoryReference.TYPE_MANUAL + " OR HISTTYPE = " +
      // HistoryReference.TYPE_HIDDEN + ")");
      psReadCache =
          getConnection()
              .prepareStatement(
                  "SELECT * FROM HISTORY WHERE URI = ? AND METHOD = ? AND REQBODY = ? AND "
                      + HISTORYID
                      + " >= ? AND "
                      + HISTORYID
                      + " <= ? AND SESSIONID = ?)");
    }
    psReadCache.setString(1, reqMsg.getRequestHeader().getURI().toString());
    psReadCache.setString(2, reqMsg.getRequestHeader().getMethod());
    psReadCache.setBytes(3, reqMsg.getRequestBody().getBytes());

    psReadCache.setInt(4, ref.getHistoryId());
    psReadCache.setInt(5, ref.getHistoryId() + 200);
    psReadCache.setLong(6, ref.getSessionId());

    ResultSet rs = psReadCache.executeQuery();
    RecordHistory rec = null;

    try {
      do {
        rec = build(rs);
        // for retrieval from cache, the message requests nature must be the same.
        // and the result should NOT be NOT_MODIFIED for rendering by browser
        if (rec != null
            && rec.getHttpMessage().equals(reqMsg)
            && rec.getHttpMessage().getResponseHeader().getStatusCode()
                != HttpStatusCode.NOT_MODIFIED) {
          return rec;
        }

      } while (rec != null);

    } finally {
      try {
        rs.close();
        psReadCache.close();
      } catch (Exception e) {
        // ZAP: Log exceptions
        log.warn(e.getMessage(), e);
      }
    }

    // if cache not exist, probably due to NOT_MODIFIED,
    // lookup from cache BEFORE the given reference

    if (isExistStatusCode) {
      //            psReadCache = getConnection().prepareStatement("SELECT TOP 1 * FROM HISTORY
      // WHERE URI = ? AND METHOD = ? AND REQBODY = ? AND SESSIONID = ? AND STATUSCODE != 304 AND
      // (HISTTYPE = " + HistoryReference.TYPE_MANUAL + " OR HISTTYPE = " +
      // HistoryReference.TYPE_HIDDEN  + ")");
      psReadCache =
          getConnection()
              .prepareStatement(
                  "SELECT TOP 1 * FROM HISTORY WHERE URI = ? AND METHOD = ? AND REQBODY = ? AND SESSIONID = ? AND STATUSCODE != 304");

    } else {
      //            psReadCache = getConnection().prepareStatement("SELECT * FROM HISTORY WHERE URI
      // = ? AND METHOD = ? AND REQBODY = ? AND SESSIONID = ? AND (HISTTYPE = " +
      // HistoryReference.TYPE_MANUAL + " OR HISTTYPE = " + HistoryReference.TYPE_HIDDEN  + ")");
      psReadCache =
          getConnection()
              .prepareStatement(
                  "SELECT * FROM HISTORY WHERE URI = ? AND METHOD = ? AND REQBODY = ? AND SESSIONID = ?");
    }
    psReadCache.setString(1, reqMsg.getRequestHeader().getURI().toString());
    psReadCache.setString(2, reqMsg.getRequestHeader().getMethod());
    psReadCache.setBytes(3, reqMsg.getRequestBody().getBytes());
    psReadCache.setLong(4, ref.getSessionId());

    rs = psReadCache.executeQuery();
    rec = null;

    try {
      do {
        rec = build(rs);
        if (rec != null
            && rec.getHttpMessage().equals(reqMsg)
            && rec.getHttpMessage().getResponseHeader().getStatusCode()
                != HttpStatusCode.NOT_MODIFIED) {
          return rec;
        }

      } while (rec != null);

    } finally {
      try {
        rs.close();
        psReadCache.close();
      } catch (Exception e) {
        // ZAP: Log exceptions
        log.warn(e.getMessage(), e);
      }
    }

    return null;
  }
Пример #13
0
  public synchronized RecordHistory write(long sessionId, int histType, HttpMessage msg)
      throws HttpMalformedHeaderException, SQLException {

    String reqHeader = "";
    byte[] reqBody = new byte[0];
    String resHeader = "";
    byte[] resBody = reqBody;
    String method = "";
    String uri = "";
    int statusCode = 0;

    if (!msg.getRequestHeader().isEmpty()) {
      reqHeader = msg.getRequestHeader().toString();
      reqBody = msg.getRequestBody().getBytes();
      method = msg.getRequestHeader().getMethod();
      uri = msg.getRequestHeader().getURI().toString();
    }

    if (!msg.getResponseHeader().isEmpty()) {
      resHeader = msg.getResponseHeader().toString();
      resBody = msg.getResponseBody().getBytes();
      statusCode = msg.getResponseHeader().getStatusCode();
    }

    // return write(sessionId, histType, msg.getTimeSentMillis(), msg.getTimeElapsedMillis(),
    // method, uri, statusCode, reqHeader, reqBody, resHeader, resBody, msg.getTag());
    return write(
        sessionId,
        histType,
        msg.getTimeSentMillis(),
        msg.getTimeElapsedMillis(),
        method,
        uri,
        statusCode,
        reqHeader,
        reqBody,
        resHeader,
        resBody,
        null);
  }