All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.parosproxy.paros.model.HistoryReference Maven / Gradle / Ivy

Go to download

The Zed Attack Proxy (ZAP) is an easy to use integrated penetration testing tool for finding vulnerabilities in web applications. It is designed to be used by people with a wide range of security experience and as such is ideal for developers and functional testers who are new to penetration testing. ZAP provides automated scanners as well as a set of tools that allow you to find security vulnerabilities manually.

There is a newer version: 2.15.0
Show newest version
/*
*
* Paros and its related class files.
* 
* Paros is an HTTP/HTTPS proxy for assessing web application security.
* Copyright (C) 2003-2004 Chinotec Technologies Company
* 
* This program is free software; you can redistribute it and/or
* modify it under the terms of the Clarified Artistic License
* as published by the Free Software Foundation.
* 
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
* Clarified Artistic License for more details.
* 
* You should have received a copy of the Clarified Artistic License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
*/
// ZAP: 2011/07/23 Added TYPE_FUZZER
// ZAP: 2011/12/04 Support deleting alerts
// ZAP: 2012/03/15 Changed the method getDisplay to use the class StringBuilder 
//      instead of StringBuffer.
// ZAP: 2012/04/23 Added @Override annotation to the appropriate method.
// ZAP: 2012/05/28 Added some JavaDoc
// ZAP: 2012/06/13 Optimized alerts related code
// ZAP: 2012/08/07 Deleted some not used Spider Related constants
// ZAP: 2012/10/08 Issue 391: Performance improvements
// ZAP: 2012/02/26 Cache the response body length as part of Issue 539
// ZAP: 2013/08/07 Added TYPE_AUTHENTICATION
// ZAP: 2013/11/16 Issue 869: Differentiate proxied requests from (ZAP) user requests
// ZAP: 2013/11/16 Issue 892: Cache of response body length in HistoryReference might not be correct
// ZAP: 2014/04/10 Changed to use HttpMessageCachedData and expose the cached data
// ZAP: 2014/04/10 Issue 1042: Having significant issues opening a previous session
// ZAP: 2014/05/23 Issue 1209: Reliability becomes Confidence and add levels
// ZAP: 2014/06/10 Added TYPE_ACCESS_CONTROL
// ZAP: 2014/06/16 Issue 990: Allow to delete alerts through the API
// ZAP: 2014/08/14 Issue 1311: Differentiate temporary internal messages from temporary scanner messages
// ZAP: 2014/12/11 Update the flag webSocketUpgrade sooner to avoid re-reading the message from database
// ZAP: 2015/02/09 Issue 1525: Introduce a database interface layer to allow for alternative implementations
// ZAP: 2016/04/12 Update the SiteNode when deleting alerts
// ZAP: 2016/05/27 Moved the temporary types to this class
// ZAP: 2016/05/30 Add new type for CONNECT requests received by the proxy
// ZAP: 2016/06/15 Add TYPE_SEQUENCE_TEMPORARY
// ZAP: 2016/06/20 Add TYPE_ZEST_SCRIPT and deprecate TYPE_RESERVED_11
// ZAP: 2016/08/30 Use a Set instead of a List for the alerts
// ZAP: 2017/02/07 Add TYPE_SPIDER_AJAX_TEMPORARY.
// ZAP: 2017/03/19 Add TYPE_SPIDER_TEMPORARY.

package org.parosproxy.paros.model;

import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.Vector;

import org.apache.commons.httpclient.URI;
import org.apache.log4j.Logger;
import org.parosproxy.paros.core.scanner.Alert;
import org.parosproxy.paros.db.DatabaseException;
import org.parosproxy.paros.db.RecordAlert;
import org.parosproxy.paros.db.RecordHistory;
import org.parosproxy.paros.db.RecordTag;
import org.parosproxy.paros.db.TableAlert;
import org.parosproxy.paros.db.TableHistory;
import org.parosproxy.paros.db.TableTag;
import org.parosproxy.paros.network.HttpMalformedHeaderException;
import org.parosproxy.paros.network.HttpMessage;


/**
* 
* This class abstracts a reference to a http message stored in database.
* It reads the whole http message from database when getHttpMessage() is called.
*/
public class HistoryReference {

   /**
    * Temporary type = not retrieved from history.  To be deleted.
    */
   public static final int TYPE_TEMPORARY = 0;

    /**
     * @deprecated Use {@link #TYPE_PROXIED} instead.
     * @see #TYPE_ZAP_USER
     */
    @Deprecated
    public static final int TYPE_MANUAL = 1;

    /**
     * A HTTP message that was proxied through ZAP.
     */
    public static final int TYPE_PROXIED = 1;

    /**
     * A HTTP message sent by the user from within ZAP, for example, using "Manual Request Editor" or "Resend" dialogues.
     */
    public static final int TYPE_ZAP_USER = 15;

   public static final int TYPE_SPIDER = 2;
   public static final int TYPE_SCANNER = 3;
   public static final int TYPE_HIDDEN = 6;
   // ZAP: Added TYPE_BRUTE_FORCE
   public static final int TYPE_BRUTE_FORCE = 7;
   public static final int TYPE_FUZZER = 8;
    /**
     * A (temporary) HTTP message of the spider.
     * 

* The type is used to off-load the messages (of resources found but not yet fetched) from the memory. * * @since 2.0.0 * @see #TYPE_SPIDER * @see #TYPE_SPIDER_TEMPORARY * @see #DEFAULT_TEMPORARY_HISTORY_TYPES */ public static final int TYPE_SPIDER_TASK = 9; /** * A HTTP message sent by the AJAX Spider. * * @since 2.0.0 * @see #TYPE_SPIDER_AJAX_TEMPORARY */ public static final int TYPE_SPIDER_AJAX = 10; /** * A (temporary) HTTP message that (attempts to) authenticates a {@link org.zaproxy.zap.users.User User}. * * @since 2.4.0 * @see #DEFAULT_TEMPORARY_HISTORY_TYPES */ public static final int TYPE_AUTHENTICATION = 11; // ZAP: Added TYPE_ACCESS_CONTROL for use in access control testing methods public static final int TYPE_ACCESS_CONTROL = 13; /** * A HTTP message sent by a Zest script. *

* Not all HTTP messages sent by Zest scripts will have this type, some might use the type(s) of the underlying component * (for example, Zest Active Rules will use the types of the active scanner, {@link #TYPE_SCANNER_TEMPORARY} or * {@link #TYPE_SCANNER}). * * @since 2.6.0 */ public static final int TYPE_ZEST_SCRIPT = 12; /** * @deprecated (2.6.0) Use {@link #TYPE_ZEST_SCRIPT} instead. * @since 2.1.0 */ @Deprecated public static final int TYPE_RESERVED_11 = TYPE_ZEST_SCRIPT; /** * A (temporary) HTTP message sent by the (active) scanner. * * @since 2.4.0 * @see #DEFAULT_TEMPORARY_HISTORY_TYPES */ public static final int TYPE_SCANNER_TEMPORARY = 14; /** * The {@code Set} of temporary history types. * * @see #addTemporaryType(int) * @see #getTemporaryTypes() * @see #DEFAULT_TEMPORARY_HISTORY_TYPES */ private static final Set TEMPORARY_HISTORY_TYPES = new HashSet<>(); /** * The {@code Set} with default temporary history types: *

    *
  • {@link #TYPE_TEMPORARY};
  • *
  • {@link #TYPE_SCANNER_TEMPORARY};
  • *
  • {@link #TYPE_AUTHENTICATION};
  • *
  • {@link #TYPE_SPIDER_TASK};
  • *
  • {@link #TYPE_SEQUENCE_TEMPORARY};
  • *
  • {@link #TYPE_SPIDER_AJAX_TEMPORARY};
  • *
*

* Persisted messages with temporary types are deleted when the session is closed. *

* Note: This set does not allow modifications, any attempt to modify it will result in an * {@code UnsupportedOperationException}. * * @since 2.5.0 * @see #getTemporaryTypes() */ public static final Set DEFAULT_TEMPORARY_HISTORY_TYPES; /** * A HTTP CONNECT request received (and processed) by the local proxy. * * @since 2.5.0 */ public static final int TYPE_PROXY_CONNECT = 16; /** * A (temporary) HTTP message created/used when active scanning sequences. * * @since 2.6.0 * @see #DEFAULT_TEMPORARY_HISTORY_TYPES */ public static final int TYPE_SEQUENCE_TEMPORARY = 17; /** * A (temporary) HTTP message of the AJAX spider. *

* Normally a message that was not processed (i.e. not on spider scope). * * @since 2.6.0 * @see #TYPE_SPIDER_AJAX * @see #DEFAULT_TEMPORARY_HISTORY_TYPES */ public static final int TYPE_SPIDER_AJAX_TEMPORARY = 18; /** * A (temporary) HTTP message of the spider. *

* Normally a message that was not processed (i.e. not successfully sent to the server). * * @since 2.6.0 * @see #TYPE_SPIDER * @see #TYPE_SPIDER_TASK * @see #DEFAULT_TEMPORARY_HISTORY_TYPES */ public static final int TYPE_SPIDER_TEMPORARY = 19; private static java.text.DecimalFormat decimalFormat = new java.text.DecimalFormat("##0.###"); private static TableHistory staticTableHistory = null; // ZAP: Support for multiple tags private static TableTag staticTableTag = null; // ZAP: Support for loading alerts from db private static TableAlert staticTableAlert = null; static { Set defaultHistoryTypes = new HashSet<>(); defaultHistoryTypes.add(Integer.valueOf(HistoryReference.TYPE_TEMPORARY)); defaultHistoryTypes.add(Integer.valueOf(HistoryReference.TYPE_SCANNER_TEMPORARY)); defaultHistoryTypes.add(Integer.valueOf(HistoryReference.TYPE_AUTHENTICATION)); defaultHistoryTypes.add(Integer.valueOf(HistoryReference.TYPE_SPIDER_TASK)); defaultHistoryTypes.add(Integer.valueOf(HistoryReference.TYPE_SEQUENCE_TEMPORARY)); defaultHistoryTypes.add(Integer.valueOf(HistoryReference.TYPE_SPIDER_AJAX_TEMPORARY)); defaultHistoryTypes.add(Integer.valueOf(HistoryReference.TYPE_SPIDER_TEMPORARY)); DEFAULT_TEMPORARY_HISTORY_TYPES = Collections.unmodifiableSet(defaultHistoryTypes); TEMPORARY_HISTORY_TYPES.addAll(DEFAULT_TEMPORARY_HISTORY_TYPES); } private int historyId = 0; private int historyType = TYPE_PROXIED; private SiteNode siteNode = null; private String display = null; private long sessionId = 0; //ZAP: Support for specific icons private ArrayList icons = null; private ArrayList clearIfManual = null; // ZAP: Support for linking Alerts to Hrefs private Set alerts; private List tags = new ArrayList<>(); private boolean webSocketUpgrade; private static Logger log = Logger.getLogger(HistoryReference.class); private HttpMessageCachedData httpMessageCachedData; /** * @return Returns the sessionId. */ public long getSessionId() { return sessionId; } public HistoryReference(int historyId) throws HttpMalformedHeaderException, DatabaseException { RecordHistory history = null; this.icons = new ArrayList<>(); this.clearIfManual = new ArrayList<>(); history = staticTableHistory.read(historyId); if (history == null) { throw new HttpMalformedHeaderException(); } HttpMessage msg = history.getHttpMessage(); // ZAP: Support for multiple tags List rtags = staticTableTag.getTagsForHistoryID(historyId); for (RecordTag rtag : rtags) { this.tags.add(rtag.getTag()); } build(history.getSessionId(), history.getHistoryId(), history.getHistoryType(), msg); } public HistoryReference(Session session, int historyType, HttpMessage msg) throws HttpMalformedHeaderException, DatabaseException { RecordHistory history = null; this.icons = new ArrayList<>(); this.clearIfManual = new ArrayList<>(); history = staticTableHistory.write(session.getSessionId(), historyType, msg); build(session.getSessionId(), history.getHistoryId(), history.getHistoryType(), msg); // ZAP: Init HttpMessage HistoryReference field msg.setHistoryRef(this); List rtags = staticTableTag.getTagsForHistoryID(historyId); for (RecordTag rtag : rtags) { this.tags.add(rtag.getTag()); } // ZAP: Support for loading the alerts from the db List alerts = staticTableAlert.getAlertsBySourceHistoryId(historyId); for (RecordAlert alert: alerts) { this.addAlert(new Alert(alert, this)); } } /** * * @return whether the icon has to be cleaned when being manually visited or not. */ public ArrayList getClearIfManual() { return this.clearIfManual; } /** * * @return The icon's string path (i.e. /resource/icon/16/xx.png) */ public ArrayList getCustomIcons(){ return this.icons; } /** * * @param i the icon's URL (i.e. /resource/icon/16/xx.png) * @param c if the icon has to be cleaned when the node is manually visited */ public void setCustomIcon(String i, boolean c){ this.icons.add(i); this.clearIfManual.add(c); } private void build(long sessionId, int historyId, int historyType, HttpMessage msg) { this.sessionId = sessionId; this.historyId = historyId; this.historyType = historyType; this.webSocketUpgrade = msg.isWebSocketUpgrade(); if (historyType == TYPE_PROXIED || historyType == TYPE_ZAP_USER) { this.display = getDisplay(msg); } // ZAP: Init HttpMessage HistoryReference field msg.setHistoryRef(this); // Cache info commonly used so that we dont need to keep reading the HttpMessage from the db. httpMessageCachedData = new HttpMessageCachedData(msg); } public static void setTableHistory(TableHistory tableHistory) { staticTableHistory = tableHistory; } public static void setTableTag(TableTag tableTag) { staticTableTag = tableTag; } public static void setTableAlert(TableAlert tableAlert) { staticTableAlert = tableAlert; } /** * @return Returns the historyId. */ public int getHistoryId() { return historyId; } /** * Gets the corresponding http message from the database. Try to minimise calls to this method as much as possible. * But also dont cache the HttpMessage either as this can significantly increase ZAP's memory usage. * * @return the http message * @throws HttpMalformedHeaderException the http malformed header exception * @throws SQLException the sQL exception */ public HttpMessage getHttpMessage() throws HttpMalformedHeaderException, DatabaseException { // fetch complete message RecordHistory history = staticTableHistory.read(historyId); if (history == null) { throw new HttpMalformedHeaderException("No history reference for id " + historyId + " type=" + historyType); } // ZAP: Init HttpMessage HistoryReference field history.getHttpMessage().setHistoryRef(this); return history.getHttpMessage(); } public URI getURI() { return httpMessageCachedData.getUri(); } @Override public String toString() { if (display != null) { return display; } HttpMessage msg = null; try { msg = getHttpMessage(); display = getDisplay(msg); } catch (HttpMalformedHeaderException e1) { display = ""; } catch (DatabaseException e) { display = ""; } return display; } /** * @return Returns the historyType. */ public int getHistoryType() { return historyType; } /** * Delete this HistoryReference from database * This should typically only be called via the ExtensionHistory.delete(href) method * */ public void delete() { if (historyId > 0) { try { // ZAP: Support for multiple tags staticTableTag.deleteTagsForHistoryID(historyId); staticTableHistory.delete(historyId); } catch (DatabaseException e) { log.error(e.getMessage(), e); } } } /** * @return Returns the siteNode. */ public SiteNode getSiteNode() { return siteNode; } /** * @param siteNode The siteNode to set. */ public void setSiteNode(SiteNode siteNode) { this.siteNode = siteNode; } private String getDisplay(HttpMessage msg) { StringBuilder sb = new StringBuilder(Integer.toString(historyId)); sb.append(' '); sb.append(msg.getRequestHeader().getPrimeHeader()); if (!msg.getResponseHeader().isEmpty()) { sb.append(" \t=> ").append(msg.getResponseHeader().getPrimeHeader()); sb.append("\t [").append(decimalFormat.format(msg.getTimeElapsedMillis()/1000.0)).append(" s]"); } return sb.toString(); } // ZAP: Support for multiple tags public void addTag(String tag) { try { staticTableTag.insert(historyId, tag); this.tags.add(tag); } catch (DatabaseException e) { log.error(e.getMessage(), e); } } public void deleteTag(String tag) { try { staticTableTag.delete(historyId, tag); this.tags.remove(tag); } catch (DatabaseException e) { log.error(e.getMessage(), e); } } public List getTags() { return this.tags; } // ZAP: Added setNote method to HistoryReference public void setNote(String note) { try { staticTableHistory.updateNote(historyId, note); httpMessageCachedData.setNote(note != null && note.length() > 0); } catch (DatabaseException e) { log.error(e.getMessage(), e); } } public void loadAlerts() { // ZAP: Support for loading the alerts from the db List alerts; try { alerts = staticTableAlert.getAlertsBySourceHistoryId(historyId); for (RecordAlert alert: alerts) { this.addAlert(new Alert(alert, this)); } } catch (DatabaseException e) { log.error(e.getMessage(), e); } } public synchronized boolean addAlert(Alert alert) { //If this is the first alert if (alerts == null) { alerts = new HashSet<>(); } boolean added = false; if (alerts.add(alert)) { alert.setHistoryRef(this); added = true; } // Try to add to the SiteHNode anyway - that will also check if its already added if (this.siteNode != null) { siteNode.addAlert(alert); } return added; } public synchronized void updateAlert(Alert alert) { //If there are no alerts yet if (alerts == null) { return; } for (Alert a : alerts) { if (a.getAlertId() == alert.getAlertId()) { // Have to use the alertId instead of 'equals' as any of the // other params might have changed this.alerts.remove(a); this.alerts.add(alert); if (this.siteNode != null) { siteNode.updateAlert(alert); } return; } } } public synchronized void deleteAlert(Alert alert) { if (alerts != null) { alerts.remove(alert); if (siteNode != null) { siteNode.deleteAlert(alert); } } } public synchronized void deleteAllAlerts() { if (alerts != null) { alerts.clear(); } } /** * Tells whether or not this history reference has the given alert. * * @param alert the alert to check * @return {@code true} if it has the given alert, {@code false} otherwise. * @since 2.6.0 * @see #hasAlerts() * @see #addAlert(Alert) */ public synchronized boolean hasAlert(Alert alert) { if (alerts == null) { return false; } return alerts.contains(alert); } /** * Tells whether or not this history reference has alerts. * * @return {@code true} if it has alerts, {@code false} otherwise. * @since 2.6.0 * @see #hasAlert(Alert) * @see #addAlert(Alert) */ public synchronized boolean hasAlerts() { if (alerts == null) { return false; } return !alerts.isEmpty(); } public int getHighestAlert() { int i = -1; //If there are no alerts if(alerts==null) return i; for (Alert a : alerts) { if (a.getConfidence() != Alert.CONFIDENCE_FALSE_POSITIVE && a.getRisk() > i) { i = a.getRisk(); } } return i; } /** * Gets the alerts. *

* If alerts where never added, an unmodifiable empty list is returned, otherwise it's returned a copy of the internal * collection. * * @return the alerts * @see #addAlert(Alert) * @see #hasAlerts() * @see #hasAlert(Alert) */ public synchronized List getAlerts() { if (alerts == null) { return Collections.emptyList(); } return new ArrayList<>(this.alerts); } public String getMethod() { return httpMessageCachedData.getMethod(); } public int getStatusCode() { return httpMessageCachedData.getStatusCode(); } public String getReason() { return httpMessageCachedData.getReason(); } public int getRtt() { return httpMessageCachedData.getRtt(); } public void setTags(Vector tags) { this.tags = tags; } public boolean hasNote() { return httpMessageCachedData.hasNote(); } public long getTimeSentMillis() { return httpMessageCachedData.getTimeSentMillis(); } public long getTimeReceivedMillis() { return httpMessageCachedData.getTimeReceivedMillis(); } public boolean isWebSocketUpgrade() { return webSocketUpgrade; } public int getRequestHeaderLength() { return httpMessageCachedData.getRequestHeaderLength(); } public int getRequestBodyLength() { return httpMessageCachedData.getRequestBodyLength(); } public int getResponseHeaderLength() { return httpMessageCachedData.getResponseHeaderLength(); } public int getResponseBodyLength() { return httpMessageCachedData.getResponseBodyLength(); } public String getRequestBody() { String requestBody = httpMessageCachedData.getRequestBody(); if (requestBody == null) { try { requestBody = getHttpMessage().getRequestBody().toString(); httpMessageCachedData.setRequestBody(requestBody); } catch (HttpMalformedHeaderException | DatabaseException e) { log.error("Failed to reload request body from database with history ID: " + historyId, e); requestBody = ""; } } return requestBody; } /** * Adds the given {@code type} to the set of temporary types. *

* Persisted messages with temporary types are deleted when the session is closed. * * @since 2.5.0 * @param type the history type that will be added * @see #removeTemporaryType(int) * @see #getTemporaryTypes() */ public static void addTemporaryType(int type) { synchronized (TEMPORARY_HISTORY_TYPES) { TEMPORARY_HISTORY_TYPES.add(Integer.valueOf(type)); } } /** * Removes the given {@code type} from the set of temporary types. *

* Attempting to remove a default temporary type has no effect. * * @since 2.5.0 * @param type the history type that will be removed * @see #DEFAULT_TEMPORARY_HISTORY_TYPES * @see #addTemporaryType(int) * @see #getTemporaryTypes() */ public static void removeTemporaryType(int type) { Integer typeInteger = Integer.valueOf(type); if (DEFAULT_TEMPORARY_HISTORY_TYPES.contains(typeInteger)) { return; } synchronized (TEMPORARY_HISTORY_TYPES) { TEMPORARY_HISTORY_TYPES.remove(typeInteger); } } /** * Gets the temporary history types. *

* Persisted messages with temporary types are deleted when the session is closed. * * @return a {@code Set} with the temporary history types * @see #addTemporaryType(int) * @see #removeTemporaryType(int) */ public static Set getTemporaryTypes() { synchronized (TEMPORARY_HISTORY_TYPES) { return new HashSet<>(TEMPORARY_HISTORY_TYPES); } } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy