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

org.zaproxy.zap.extension.ascan.ActiveScan 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.16.0
Show newest version
/*
 * 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.extension.ascan;

import java.awt.EventQueue;
import java.util.ArrayList;
import java.util.Collections;
import java.util.ConcurrentModificationException;
import java.util.Date;
import java.util.List;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.TimeUnit;

import javax.swing.DefaultListModel;

import org.apache.log4j.Logger;
import org.parosproxy.paros.core.scanner.Alert;
import org.parosproxy.paros.core.scanner.HostProcess;
import org.parosproxy.paros.core.scanner.ScannerListener;
import org.parosproxy.paros.core.scanner.ScannerParam;
import org.parosproxy.paros.db.DatabaseException;
import org.parosproxy.paros.model.HistoryReference;
import org.parosproxy.paros.model.Model;
import org.parosproxy.paros.model.SiteNode;
import org.parosproxy.paros.network.ConnectionParam;
import org.parosproxy.paros.network.HttpMalformedHeaderException;
import org.parosproxy.paros.network.HttpMessage;
import org.parosproxy.paros.view.View;
import org.zaproxy.zap.extension.ruleconfig.RuleConfigParam;
import org.zaproxy.zap.model.GenericScanner2;
import org.zaproxy.zap.model.Target;

public class ActiveScan extends org.parosproxy.paros.core.scanner.Scanner implements GenericScanner2, ScannerListener {
	
	/**
	 * The maximum number of statistic history records cached
	 */
	private static final int MAX_STATS_HISTORY_SIZE = 240;
	
	public static enum State {
		NOT_STARTED,
		RUNNING,
		PAUSED,
		FINISHED
	};

	private String displayName = null;
	private int progress = 0;
	private ActiveScanTableModel messagesTableModel = new ActiveScanTableModel();
	private SiteNode startNode = null;
	private ResponseCountSnapshot rcTotals = new ResponseCountSnapshot();
	private ResponseCountSnapshot rcLastSnapshot = new ResponseCountSnapshot();
	private List rcHistory = new ArrayList();

	private Date timeStarted = null;
	private Date timeFinished = null;
	private int maxResultsToList = 0;

	private final List hRefs = Collections.synchronizedList(new ArrayList());
	private final List alerts = Collections.synchronizedList(new ArrayList());

	private ScheduledExecutorService scheduler;
    private ScheduledFuture schedHandle;

	private static final Logger log = Logger.getLogger(ActiveScan.class);

	@Deprecated
	public ActiveScan(String displayName, ScannerParam scannerParam, 
			ConnectionParam param, ScanPolicy scanPolicy) {
		this(displayName, scannerParam, param, scanPolicy, null);
	}

	public ActiveScan(String displayName, ScannerParam scannerParam, 
			ConnectionParam param, ScanPolicy scanPolicy, RuleConfigParam ruleConfigParam) {
		super(scannerParam, param, scanPolicy, ruleConfigParam);
		this.displayName = displayName;
		this.maxResultsToList = scannerParam.getMaxResultsToList();
		// Easiest way to get the messages and alerts ;) 
		this.addScannerListener(this);
	}

	@Override
	public int getMaximum() {
		return 100;
	}

	@Override
	public int getProgress() {
		return progress;
	}

	@Override
	public boolean isRunning() {
		return ! this.isStop();
	}

	@Override
	public boolean isStopped() {
		return super.isStop();
	}

	@Override
	public void pauseScan() {
		if (this.isRunning()) {
			super.pause();
		}
	}

    public int getTotalRequests() {
		int total = 0;
		for (HostProcess process : this.getHostProcesses()) {
			total += process.getRequestCount();
		}
		return total;
	}
	
	public ResponseCountSnapshot getRequestHistory() {
		if (this.rcHistory.size() > 0) {
			try {
				return this.rcHistory.remove(0);
			} catch (Exception e) {
				// Ignore - another thread must have just removed the last snapshot
			}
		}
		return null;
	}
    
	@Override
	public void start(Target target) {
		reset();
		this.timeStarted = new Date();
		this.progress = 0;
		final int period = 2;
		
		super.start(target);
		
		if (View.isInitialised()) {
			scheduler = Executors.newScheduledThreadPool(1);
			// For now this is only supported in the desktop UI
			final Runnable requestCounter = new Runnable() {
	            public void run() {
	            	if (isStop()) {
	            		schedHandle.cancel(true);
	            		return;
	            	}
	            	ResponseCountSnapshot currentSnapshot = rcTotals.clone();
	            	rcHistory.add(currentSnapshot.getDifference(rcLastSnapshot));
	            	if (rcHistory.size() > MAX_STATS_HISTORY_SIZE) {
	            		// Trim it to prevent it from getting too big
	            		rcHistory.remove(0);
	            	}
	            	rcLastSnapshot = currentSnapshot;
	            }
	        };
	        schedHandle = scheduler.scheduleWithFixedDelay(requestCounter, period, period, TimeUnit.SECONDS);
		}
	}

	@Override
	public void stopScan() {
		super.stop();
		if (schedHandle != null) {
			schedHandle.cancel(true);
		}
	}

	@Override
	public void resumeScan() {
		if (this.isPaused()) {
			super.resume();
		}
	}

	@Override
	public void alertFound(Alert alert) {
		int alertId = alert.getAlertId();
		if (alertId != -1) {
			alerts.add(Integer.valueOf(alert.getAlertId()));
		}
	}

	@Override
	public void hostComplete(int id, String hostAndPort) {
	}

	@Override
	public void hostNewScan(int id, String hostAndPort, HostProcess hostThread) {
	}

	@Override
	public void hostProgress(int id, String hostAndPort, String msg, int percentage) {
		// Calculate the percentage based on the average of all of the host processes
		// This is an approximation as different host process make significantly different times 
		int tot = 0;
		for (HostProcess process : this.getHostProcesses()) {
			tot += process.getPercentageComplete();
		}
		this.progress = tot / this.getHostProcesses().size();
	}
	
	/**
	 * @deprecated (2.5.0) No longer used/needed, the request count is automatically updated/maintained by
	 *             {@link HostProcess}.
	 */
	@Deprecated
	public void updatePluginRequestCounts() {
		// No longer used.
	}

	@Override
	public void scannerComplete(int id) {
		this.timeFinished = new Date();
		if (scheduler != null) {
			scheduler.shutdown();
		}
	}

	//@Override
	public DefaultListModel getList() {
		return null;
	}

	public ActiveScanTableModel getMessagesTableModel() {
	    return messagesTableModel;
	}
	
	@Override
	public void notifyNewMessage(final HttpMessage msg) {
		HistoryReference hRef = msg.getHistoryRef();
		if (hRef == null) {
			try {
				hRef = new HistoryReference(
						Model.getSingleton().getSession(),
						HistoryReference.TYPE_SCANNER_TEMPORARY,
						msg);
				msg.setHistoryRef(null);
				hRefs.add(Integer.valueOf(hRef.getHistoryId()));
			} catch (HttpMalformedHeaderException | DatabaseException e) {
				log.error(e.getMessage(), e);
			}
		} else {
			hRefs.add(Integer.valueOf(hRef.getHistoryId()));
		}
		
		this.rcTotals.incResponseCodeCount(msg.getResponseHeader().getStatusCode());

		if (hRef != null && View.isInitialised()) {
			// Very large lists significantly impact the UI responsiveness
			// limiting them makes large scans _much_ quicker
			if (this.rcTotals.getTotal() > this.maxResultsToList) {
				removeFirstHistoryReferenceInEdt();
			}
			addHistoryReferenceInEdt(hRef);
		}
	}

	private void addHistoryReferenceInEdt(final HistoryReference hRef) {
		EventQueue.invokeLater(new Runnable() {

			@Override
			public void run() {
				messagesTableModel.addHistoryReference(hRef);
			}
		});
	}

	private void removeFirstHistoryReferenceInEdt() {
		EventQueue.invokeLater(new Runnable() {

			@Override
			public void run() {
				messagesTableModel.removeHistoryReference(getMessagesTableModel().getEntry(0).getHistoryReference());
			}
		});
	}
	
	@Override
	public SiteNode getStartNode() {
		return this.startNode;
	}

	@Override
	public void setStartNode(SiteNode startNode) {
		this.startNode = startNode;
		super.setStartNode(startNode);
	}

	public void reset() {
	    if (!View.isInitialised() || EventQueue.isDispatchThread()) {
	        this.messagesTableModel.clear();
        } else {
            EventQueue.invokeLater(new Runnable() {
                @Override
                public void run() {
                    reset();
                }
            });
        }
	}

	public Date getTimeStarted() {
		return timeStarted;
	}

	public Date getTimeFinished() {
		return timeFinished;
	}

	/**
	 * Returns the IDs of all messages sent/created during the scan. The message must be recreated with a HistoryReference.
	 * 

* Note: Iterations must be {@code synchronized} on returned object. Failing to do so might result in * {@code ConcurrentModificationException}. *

* * @return the IDs of all the messages sent/created during the scan * @see HistoryReference * @see ConcurrentModificationException */ public List getMessagesIds() { return hRefs; } /** * Returns the IDs of all alerts raised during the scan. *

* Note: Iterations must be {@code synchronized} on returned object. Failing to do so might result in * {@code ConcurrentModificationException}. *

* * @return the IDs of all the alerts raised during the scan * @see ConcurrentModificationException */ public List getAlertsIds() { return alerts; } public State getState() { if (this.timeStarted == null) { return State.NOT_STARTED; } else if (this.isStop()) { return State.FINISHED; } else if (this.isPaused()) { return State.PAUSED; } else { return State.RUNNING; } } @Override public void setDisplayName(String name) { this.displayName = name; } @Override public String getDisplayName() { return this.displayName; } @Override public void setScanId(int id) { this.setId(id); } @Override public int getScanId() { return this.getId(); } }




© 2015 - 2025 Weber Informatics LLC | Privacy Policy