/*
* Zed Attack Proxy (ZAP) and its related class files.
*
* ZAP is an HTTP/HTTPS proxy for assessing web application security.
*
* Copyright 2016 The ZAP core 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.brk;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import org.apache.log4j.Logger;
import org.parosproxy.paros.control.Control.Mode;
import org.zaproxy.zap.extension.httppanel.Message;
public class BreakpointMessageHandler2 {
private static final Logger LOGGER = Logger.getLogger(BreakpointMessageHandler2.class);
protected static final Object SEMAPHORE = new Object();
protected final BreakpointManagementInterface breakMgmt;
protected List<BreakpointMessageInterface> enabledBreakpoints;
private List<String> enabledKeyBreakpoints = new ArrayList<>();
public List<String> getEnabledKeyBreakpoints() {
return enabledKeyBreakpoints;
}
public void setEnabledKeyBreakpoints(List<String> enabledKeyBreakpoints) {
this.enabledKeyBreakpoints = enabledKeyBreakpoints;
}
public BreakpointMessageHandler2(BreakpointManagementInterface aBreakPanel) {
this.breakMgmt = aBreakPanel;
}
public void setEnabledBreakpoints(List<BreakpointMessageInterface> breakpoints) {
this.enabledBreakpoints = breakpoints;
}
/**
* Do not call if in {@link Mode#safe}.
*
* @param aMessage
* @param onlyIfInScope
* @return False if message should be dropped.
*/
public boolean handleMessageReceivedFromClient(Message aMessage, boolean onlyIfInScope) {
if ( ! isBreakpoint(aMessage, true, onlyIfInScope)) {
return true;
}
// Do this outside of the semaphore loop so that the 'continue' button can apply to all queued break points
// but be reset when the next break point is hit
breakMgmt.breakpointHit();
synchronized(SEMAPHORE) {
if (breakMgmt.isHoldMessage(aMessage)) {
setBreakDisplay(aMessage, true);
waitUntilContinue(aMessage, true);
}
}
breakMgmt.clearAndDisableRequest();
return ! breakMgmt.isToBeDropped();
}
/**
* Do not call if in {@link Mode#safe}.
*
* @param aMessage
* @param onlyIfInScope
* @return False if message should be dropped.
*/
public boolean handleMessageReceivedFromServer(Message aMessage, boolean onlyIfInScope) {
if (! isBreakpoint(aMessage, false, onlyIfInScope)) {
return true;
}
// Do this outside of the semaphore loop so that the 'continue' button can apply to all queued break points
// but be reset when the next break point is hit
breakMgmt.breakpointHit();
synchronized(SEMAPHORE) {
if (breakMgmt.isHoldMessage(aMessage)) {
setBreakDisplay(aMessage, false);
waitUntilContinue(aMessage, false);
}
}
breakMgmt.clearAndDisableResponse();
return ! breakMgmt.isToBeDropped();
}
private void setBreakDisplay(final Message msg, boolean isRequest) {
breakMgmt.setMessage(msg, isRequest);
breakMgmt.breakpointDisplayed();
}
private void waitUntilContinue(Message aMessage, final boolean isRequest) {
// Note that multiple requests and responses can get built up, so pressing continue only
// releases the current break, not all of them.
while (breakMgmt.isHoldMessage(aMessage)) {
try {
Thread.sleep(100);
} catch (InterruptedException e) {
LOGGER.warn(e.getMessage(), e);
}
}
breakMgmt.saveMessage(isRequest);
}
/**
* You have to handle {@link Mode#safe} outside.
*
* @param aMessage
* @param isRequest
* @param onlyIfInScope
* @return True if a breakpoint for given message exists.
*/
public boolean isBreakpoint(Message aMessage, boolean isRequest, boolean onlyIfInScope) {
if (aMessage.isForceIntercept()) {
// The browser told us to do it Your Honour
return true;
}
if (onlyIfInScope && ! aMessage.isInScope()) {
return false;
}
if (isBreakOnAllRequests(aMessage, isRequest)) {
// Break on all requests
return true;
} else if (isBreakOnAllResponses(aMessage, isRequest)) {
// Break on all responses
return true;
} else if (isBreakOnStepping(aMessage, isRequest)) {
// Stopping through all requests and responses
return true;
}
return isBreakOnEnabledBreakpoint(aMessage, isRequest, onlyIfInScope);
}
protected boolean isBreakOnAllRequests(Message aMessage, boolean isRequest) {
return isRequest && breakMgmt.isBreakRequest();
}
protected boolean isBreakOnAllResponses(Message aMessage, boolean isRequest) {
return !isRequest && breakMgmt.isBreakResponse();
}
protected boolean isBreakOnStepping(Message aMessage, boolean isRequest) {
return breakMgmt.isStepping();
}
protected boolean isBreakOnEnabledBreakpoint(Message aMessage, boolean isRequest, boolean onlyIfInScope) {
if (enabledBreakpoints.isEmpty()) {
// No break points
return false;
}
// match against the break points
synchronized (enabledBreakpoints) {
Iterator<BreakpointMessageInterface> it = enabledBreakpoints.iterator();
while(it.hasNext()) {
BreakpointMessageInterface breakpoint = it.next();
if (breakpoint.match(aMessage, isRequest, onlyIfInScope)) {
return true;
}
}
}
return false;
}
}