/*
* Zed Attack Proxy (ZAP) and its related class files.
*
* ZAP is an HTTP/HTTPS proxy for assessing web application security.
*
* 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.io.IOException;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Set;
import org.apache.commons.httpclient.HttpException;
import org.apache.log4j.Logger;
import org.parosproxy.paros.Constant;
import org.parosproxy.paros.control.Control;
import org.parosproxy.paros.core.scanner.AbstractAppParamPlugin;
import org.parosproxy.paros.core.scanner.Alert;
import org.parosproxy.paros.core.scanner.Category;
import org.parosproxy.paros.network.HttpMessage;
import org.zaproxy.zap.extension.script.ExtensionScript;
import org.zaproxy.zap.extension.script.ScriptWrapper;
public class ScriptsActiveScanner extends AbstractAppParamPlugin {
private ExtensionScript extension = null;
private static Logger logger = Logger.getLogger(ScriptsActiveScanner.class);
/**
* A {@code Set} containing the scripts that do not implement {@code ActiveScript2}, to show an error if those scripts do
* not implement {@code ActiveScript} (thus not implementing any of the required interfaces).
*
* @see #scan()
* @see #scan(HttpMessage, String, String)
*/
private Set<ScriptWrapper> scriptsNoInterface = new HashSet<>();
@Override
public int getId() {
return 50000;
}
@Override
public String getName() {
return Constant.messages.getString("ascan.scripts.activescanner.title");
}
@Override
public String[] getDependency() {
return null;
}
@Override
public String getDescription() {
return "N/A";
}
@Override
public int getCategory() {
return Category.MISC;
}
@Override
public String getSolution() {
return "N/A";
}
@Override
public String getReference() {
return "N/A";
}
@Override
public void init() {
if (shouldSkipScan()) {
getParent().pluginSkipped(this, Constant.messages.getString("ascan.scripts.skip.reason"));
}
}
/**
* Tells whether or not the scanner should be skipped. The scanner should be skipped when the {@code ExtensionScript} is not
* enabled, when there are no scripts, or if there are none is enabled.
*
* @return {@code true} if the scanner should be skipped, {@code false} otherwise
*/
private boolean shouldSkipScan() {
if (this.getExtension() == null) {
return true;
}
List<ScriptWrapper> scripts = getActiveScripts();
if (scripts.isEmpty()) {
return true;
}
for (ScriptWrapper script : scripts) {
if (script.isEnabled()) {
return false;
}
}
return true;
}
/**
* Returns the scripts of active script type.
* <p>
* <strong>Note:</strong> this method should be called only when {@code getExtension()} returns non-{@code null}.
*
* @return a {@code List} containing the scripts with active script type, never {@code null}
* @see #getExtension()
* @see ExtensionActiveScan#SCRIPT_TYPE_ACTIVE
*/
private List<ScriptWrapper> getActiveScripts() {
return this.getExtension().getScripts(ExtensionActiveScan.SCRIPT_TYPE_ACTIVE);
}
private ExtensionScript getExtension() {
if (extension == null) {
extension = (ExtensionScript) Control.getSingleton().getExtensionLoader().getExtension(ExtensionScript.NAME);
}
return extension;
}
@Override
public void scan() {
List<ScriptWrapper> scripts = this.getActiveScripts();
for (Iterator<ScriptWrapper> it = scripts.iterator(); it.hasNext() && !isStop();) {
ScriptWrapper script = it.next();
try {
if (script.isEnabled()) {
ActiveScript2 s = extension.getInterface(script, ActiveScript2.class);
if (s != null) {
HttpMessage msg = this.getNewMsg();
logger.debug("Calling script " + script.getName() + " scanNode for " + msg.getRequestHeader().getURI());
s.scanNode(this, msg);
} else {
scriptsNoInterface.add(script);
}
}
} catch (Exception e) {
extension.handleScriptException(script, e);
}
}
if (!isStop()) {
super.scan();
}
scriptsNoInterface.clear();
}
@Override
public void scan(HttpMessage msg, String param, String value) {
List<ScriptWrapper> scripts = this.getActiveScripts();
for (Iterator<ScriptWrapper> it = scripts.iterator(); it.hasNext() && !isStop();) {
ScriptWrapper script = it.next();
try {
if (script.isEnabled()) {
ActiveScript s = extension.getInterface(script, ActiveScript.class);
if (s != null) {
logger.debug("Calling script " + script.getName() + " scan for " + msg.getRequestHeader().getURI() +
"param=" + param + " value=" + value);
s.scan(this, msg, param, value);
} else if (scriptsNoInterface.contains(script)) {
extension.handleFailedScriptInterface(
script,
Constant.messages.getString("ascan.scripts.interface.active.error", script.getName()));
}
}
} catch (Exception e) {
extension.handleScriptException(script, e);
}
}
}
@Override
public boolean isStop() {
return super.isStop();
}
public String setParam(HttpMessage msg, String param, String value) {
return super.setParameter(msg, param, value);
}
@Override
public void sendAndReceive(HttpMessage msg) throws HttpException, IOException {
super.sendAndReceive(msg);
}
@Override
public void sendAndReceive(HttpMessage msg, boolean isFollowRedirect) throws HttpException, IOException {
super.sendAndReceive(msg, isFollowRedirect);
}
@Override
public void sendAndReceive(HttpMessage msg, boolean isFollowRedirect, boolean handleAntiCSRF) throws HttpException, IOException {
super.sendAndReceive(msg, isFollowRedirect, handleAntiCSRF);
}
public void raiseAlert(int risk, int confidence, String name, String description, String uri,
String param, String attack, String otherInfo, String solution, String evidence,
int cweId, int wascId, HttpMessage msg) {
super.bingo(risk, confidence, name, description, uri, param, attack,
otherInfo, solution, evidence, cweId, wascId, msg);
}
@Override
public int getRisk() {
return Alert.RISK_INFO;
}
@Override
public int getCweId() {
return 0;
}
@Override
public int getWascId() {
return 0;
}
}