/* /* * 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.api; import java.awt.EventQueue; import java.io.File; import java.io.IOException; import java.io.StringWriter; import java.nio.charset.StandardCharsets; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; import java.security.cert.Certificate; import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.Date; import java.util.Enumeration; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Vector; import java.util.regex.Pattern; import java.util.regex.PatternSyntaxException; import net.sf.json.JSONException; import net.sf.json.JSONObject; import org.apache.commons.httpclient.URI; import org.apache.commons.httpclient.URIException; import org.apache.log4j.Logger; import org.bouncycastle.openssl.jcajce.JcaMiscPEMGenerator; import org.bouncycastle.util.io.pem.PemWriter; import org.parosproxy.paros.Constant; import org.parosproxy.paros.control.Control; import org.parosproxy.paros.control.Control.Mode; import org.parosproxy.paros.core.proxy.ProxyParam; 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.TableAlert; import org.parosproxy.paros.db.TableHistory; import org.parosproxy.paros.extension.history.ExtensionHistory; import org.parosproxy.paros.extension.report.ReportGenerator; import org.parosproxy.paros.extension.report.ReportLastScan; import org.parosproxy.paros.model.HistoryReference; import org.parosproxy.paros.model.Model; import org.parosproxy.paros.model.Session; import org.parosproxy.paros.model.SessionListener; import org.parosproxy.paros.model.SiteMap; import org.parosproxy.paros.model.SiteNode; import org.parosproxy.paros.network.ConnectionParam; import org.parosproxy.paros.network.HttpHeader; import org.parosproxy.paros.network.HttpMalformedHeaderException; import org.parosproxy.paros.network.HttpMessage; import org.parosproxy.paros.network.HttpRequestHeader; import org.parosproxy.paros.network.HttpResponseHeader; import org.parosproxy.paros.network.HttpSender; import org.parosproxy.paros.view.View; import org.zaproxy.zap.extension.alert.ExtensionAlert; import org.zaproxy.zap.extension.dynssl.ExtensionDynSSL; import org.zaproxy.zap.model.SessionUtils; import org.zaproxy.zap.network.DomainMatcher; import org.zaproxy.zap.network.HttpRedirectionValidator; import org.zaproxy.zap.network.HttpRequestConfig; import org.zaproxy.zap.utils.HarUtils; import edu.umass.cs.benchlab.har.HarEntries; import edu.umass.cs.benchlab.har.HarLog; public class CoreAPI extends ApiImplementor implements SessionListener { private static final Logger logger = Logger.getLogger(CoreAPI.class); private enum ScanReportType { HTML, XML, MD } private static final String PREFIX = "core"; private static final String ACTION_LOAD_SESSION = "loadSession"; private static final String ACTION_NEW_SESSION = "newSession"; private static final String ACTION_SAVE_SESSION = "saveSession"; private static final String ACTION_SNAPSHOT_SESSION = "snapshotSession"; private static final String ACTION_ACCESS_URL = "accessUrl"; private static final String ACTION_SHUTDOWN = "shutdown"; private static final String ACTION_EXCLUDE_FROM_PROXY = "excludeFromProxy"; private static final String ACTION_CLEAR_EXCLUDED_FROM_PROXY = "clearExcludedFromProxy"; private static final String ACTION_SET_HOME_DIRECTORY = "setHomeDirectory"; private static final String ACTION_GENERATE_ROOT_CA = "generateRootCA"; private static final String ACTION_SEND_REQUEST = "sendRequest"; private static final String ACTION_DELETE_ALL_ALERTS = "deleteAllAlerts"; private static final String ACTION_COLLECT_GARBAGE = "runGarbageCollection"; private static final String ACTION_SET_MODE = "setMode"; private static final String ACTION_DELETE_SITE_NODE = "deleteSiteNode"; private static final String ACTION_ADD_PROXY_CHAIN_EXCLUDED_DOMAIN = "addProxyChainExcludedDomain"; private static final String ACTION_MODIFY_PROXY_CHAIN_EXCLUDED_DOMAIN = "modifyProxyChainExcludedDomain"; private static final String ACTION_REMOVE_PROXY_CHAIN_EXCLUDED_DOMAIN = "removeProxyChainExcludedDomain"; private static final String ACTION_ENABLE_ALL_PROXY_CHAIN_EXCLUDED_DOMAINS = "enableAllProxyChainExcludedDomains"; private static final String ACTION_DISABLE_ALL_PROXY_CHAIN_EXCLUDED_DOMAINS = "disableAllProxyChainExcludedDomains"; private static final String VIEW_ALERT = "alert"; private static final String VIEW_ALERTS = "alerts"; private static final String VIEW_NUMBER_OF_ALERTS= "numberOfAlerts"; private static final String VIEW_HOSTS = "hosts"; private static final String VIEW_SITES = "sites"; private static final String VIEW_URLS = "urls"; private static final String VIEW_MESSAGE = "message"; private static final String VIEW_MESSAGES = "messages"; private static final String VIEW_MODE = "mode"; private static final String VIEW_NUMBER_OF_MESSAGES = "numberOfMessages"; private static final String VIEW_VERSION = "version"; private static final String VIEW_EXCLUDED_FROM_PROXY = "excludedFromProxy"; private static final String VIEW_HOME_DIRECTORY = "homeDirectory"; private static final String VIEW_SESSION_LOCATION = "sessionLocation"; private static final String VIEW_PROXY_CHAIN_EXCLUDED_DOMAINS = "proxyChainExcludedDomains"; private static final String VIEW_OPTION_PROXY_CHAIN_SKIP_NAME = "optionProxyChainSkipName"; private static final String VIEW_OPTION_PROXY_EXCLUDED_DOMAINS = "optionProxyExcludedDomains"; private static final String VIEW_OPTION_PROXY_EXCLUDED_DOMAINS_ENABLED = "optionProxyExcludedDomainsEnabled"; private static final String OTHER_PROXY_PAC = "proxy.pac"; private static final String OTHER_SET_PROXY = "setproxy"; private static final String OTHER_ROOT_CERT = "rootcert"; private static final String OTHER_XML_REPORT = "xmlreport"; private static final String OTHER_HTML_REPORT = "htmlreport"; private static final String OTHER_MD_REPORT = "mdreport"; private static final String OTHER_MESSAGE_HAR = "messageHar"; private static final String OTHER_MESSAGES_HAR = "messagesHar"; private static final String OTHER_SEND_HAR_REQUEST = "sendHarRequest"; private static final String OTHER_SCRIPT_JS = "script.js"; private static final String PARAM_BASE_URL = "baseurl"; private static final String PARAM_COUNT = "count"; private static final String PARAM_DIR = "dir"; private static final String PARAM_SESSION = "name"; private static final String PARAM_OVERWRITE_SESSION = "overwrite"; private static final String PARAM_REGEX = "regex"; private static final String PARAM_START = "start"; private static final String PARAM_PROXY_DETAILS = "proxy"; private static final String PARAM_ID = "id"; private static final String PARAM_REQUEST = "request"; private static final String PARAM_FOLLOW_REDIRECTS = "followRedirects"; private static final String PARAM_MODE = "mode"; private static final String PARAM_URL = "url"; private static final String PARAM_METHOD = "method"; private static final String PARAM_POST_DATA = "postData"; private static final String PARAM_VALUE = "value"; private static final String PARAM_IDX = "idx"; private static final String PARAM_IS_REGEX = "isRegex"; private static final String PARAM_IS_ENABLED = "isEnabled"; /* Update the version whenever the script is changed (once per release) */ protected static final int API_SCRIPT_VERSION = 1; private static final String API_SCRIPT = "function submitScript() {\n" + " var button=document.getElementById('button');\n" + " var component=button.getAttribute('zap-component')\n" + " var type=button.getAttribute('zap-type')\n" + " var name=button.getAttribute('zap-name')\n" + " var format\n" + " if (type == 'other') {\n" + " format = 'OTHER'\n" + " } else {\n" + " format = document.getElementById('zapapiformat').value\n" + " }\n" + " \n" + " var url = '/' + format + '/' + component + '/' + type + '/' + name + '/'\n" + " var form=document.getElementById('zapform');\n" + " form.action = url;\n" + " if (form.elements[\"formMethod\"]) {\n" + " form.method = form.elements[\"formMethod\"].value;\n" + " }\n" + " form.submit();\n" + "}\n" + "document.addEventListener('DOMContentLoaded', function () {\n" + " var button=document.getElementById('button');\n" + " if (button) {\n" + " document.getElementById('button').addEventListener('click', function(e) {submitScript();}, false);\n" + " }\n" + "});\n"; /* Allow caching for up to one day */ private static final String API_SCRIPT_CACHE_CONTROL = "max-age=86400"; private SimpleDateFormat dateFormat = new SimpleDateFormat("yyyyMMdd-HHmmss"); private boolean savingSession = false; private static ExtensionHistory extHistory; public CoreAPI() { this.addApiAction( new ApiAction(ACTION_ACCESS_URL, new String[] { PARAM_URL }, new String[] { PARAM_FOLLOW_REDIRECTS })); this.addApiAction(new ApiAction(ACTION_SHUTDOWN)); this.addApiAction(new ApiAction(ACTION_NEW_SESSION, null, new String[] {PARAM_SESSION, PARAM_OVERWRITE_SESSION})); this.addApiAction(new ApiAction(ACTION_LOAD_SESSION, new String[] {PARAM_SESSION})); this.addApiAction(new ApiAction(ACTION_SAVE_SESSION, new String[] {PARAM_SESSION}, new String[] {PARAM_OVERWRITE_SESSION})); this.addApiAction(new ApiAction(ACTION_SNAPSHOT_SESSION)); this.addApiAction(new ApiAction(ACTION_CLEAR_EXCLUDED_FROM_PROXY)); this.addApiAction(new ApiAction(ACTION_EXCLUDE_FROM_PROXY, new String[] {PARAM_REGEX})); this.addApiAction(new ApiAction(ACTION_SET_HOME_DIRECTORY, new String[] {PARAM_DIR})); this.addApiAction(new ApiAction(ACTION_SET_MODE, new String[] {PARAM_MODE})); this.addApiAction(new ApiAction(ACTION_GENERATE_ROOT_CA)); this.addApiAction(new ApiAction( ACTION_SEND_REQUEST, new String[] { PARAM_REQUEST }, new String[] { PARAM_FOLLOW_REDIRECTS })); this.addApiAction(new ApiAction(ACTION_DELETE_ALL_ALERTS)); this.addApiAction(new ApiAction(ACTION_COLLECT_GARBAGE)); this.addApiAction(new ApiAction(ACTION_DELETE_SITE_NODE, new String[] {PARAM_URL}, new String[] {PARAM_METHOD, PARAM_POST_DATA})); this.addApiAction( new ApiAction( ACTION_ADD_PROXY_CHAIN_EXCLUDED_DOMAIN, new String[] { PARAM_VALUE }, new String[] { PARAM_IS_REGEX, PARAM_IS_ENABLED })); this.addApiAction( new ApiAction( ACTION_MODIFY_PROXY_CHAIN_EXCLUDED_DOMAIN, new String[] { PARAM_IDX }, new String[] { PARAM_VALUE, PARAM_IS_REGEX, PARAM_IS_ENABLED })); this.addApiAction(new ApiAction(ACTION_REMOVE_PROXY_CHAIN_EXCLUDED_DOMAIN, new String[] { PARAM_IDX })); this.addApiAction(new ApiAction(ACTION_ENABLE_ALL_PROXY_CHAIN_EXCLUDED_DOMAINS)); this.addApiAction(new ApiAction(ACTION_DISABLE_ALL_PROXY_CHAIN_EXCLUDED_DOMAINS)); this.addApiView(new ApiView(VIEW_ALERT, new String[] {PARAM_ID})); this.addApiView(new ApiView(VIEW_ALERTS, null, new String[] {PARAM_BASE_URL, PARAM_START, PARAM_COUNT})); this.addApiView(new ApiView(VIEW_NUMBER_OF_ALERTS, null, new String[] { PARAM_BASE_URL })); this.addApiView(new ApiView(VIEW_HOSTS)); this.addApiView(new ApiView(VIEW_SITES)); this.addApiView(new ApiView(VIEW_URLS)); this.addApiView(new ApiView(VIEW_MESSAGE, new String[] {PARAM_ID})); this.addApiView(new ApiView(VIEW_MESSAGES, null, new String[] {PARAM_BASE_URL, PARAM_START, PARAM_COUNT})); this.addApiView(new ApiView(VIEW_NUMBER_OF_MESSAGES, null, new String[] { PARAM_BASE_URL })); this.addApiView(new ApiView(VIEW_MODE)); this.addApiView(new ApiView(VIEW_VERSION)); this.addApiView(new ApiView(VIEW_EXCLUDED_FROM_PROXY)); this.addApiView(new ApiView(VIEW_HOME_DIRECTORY)); this.addApiView(new ApiView(VIEW_SESSION_LOCATION)); this.addApiView(new ApiView(VIEW_PROXY_CHAIN_EXCLUDED_DOMAINS)); ApiView apiView = new ApiView(VIEW_OPTION_PROXY_CHAIN_SKIP_NAME); apiView.setDeprecated(true); this.addApiView(apiView); apiView = new ApiView(VIEW_OPTION_PROXY_EXCLUDED_DOMAINS); apiView.setDeprecated(true); this.addApiView(apiView); apiView = new ApiView(VIEW_OPTION_PROXY_EXCLUDED_DOMAINS_ENABLED); apiView.setDeprecated(true); this.addApiView(apiView); this.addApiOthers(new ApiOther(OTHER_PROXY_PAC, false)); this.addApiOthers(new ApiOther(OTHER_ROOT_CERT, false)); this.addApiOthers(new ApiOther(OTHER_SET_PROXY, new String[] {PARAM_PROXY_DETAILS})); this.addApiOthers(new ApiOther(OTHER_XML_REPORT)); this.addApiOthers(new ApiOther(OTHER_HTML_REPORT)); this.addApiOthers(new ApiOther(OTHER_MD_REPORT)); this.addApiOthers(new ApiOther(OTHER_MESSAGE_HAR, new String[] {PARAM_ID})); this.addApiOthers(new ApiOther(OTHER_MESSAGES_HAR, null, new String[] {PARAM_BASE_URL, PARAM_START, PARAM_COUNT})); this.addApiOthers(new ApiOther( OTHER_SEND_HAR_REQUEST, new String[] { PARAM_REQUEST }, new String[] { PARAM_FOLLOW_REDIRECTS })); this.addApiShortcut(OTHER_PROXY_PAC); // this.addApiShortcut(OTHER_ROOT_CERT); this.addApiShortcut(OTHER_SET_PROXY); this.addApiShortcut(OTHER_SCRIPT_JS); } @Override public String getPrefix() { return PREFIX; } @Override public ApiResponse handleApiAction(String name, JSONObject params) throws ApiException { Session session = Model.getSingleton().getSession(); if (ACTION_ACCESS_URL.equals(name)) { URI uri; try { uri = new URI(params.getString(PARAM_URL), true); } catch (URIException e) { throw new ApiException(ApiException.Type.ILLEGAL_PARAMETER, PARAM_URL, e); } HttpMessage request; try { request = new HttpMessage( new HttpRequestHeader( HttpRequestHeader.GET, uri, HttpHeader.HTTP11, Model.getSingleton().getOptionsParam().getConnectionParam())); } catch (HttpMalformedHeaderException e) { throw new ApiException(ApiException.Type.ILLEGAL_PARAMETER, PARAM_URL, e); } return sendHttpMessage(request, getParam(params, PARAM_FOLLOW_REDIRECTS, false), name); } else if (ACTION_SHUTDOWN.equals(name)) { Thread thread = new Thread() { @Override public void run() { try { // Give the API a chance to return sleep(1000); } catch (InterruptedException e) { // Ignore } Control.getSingleton().shutdown(Model.getSingleton().getOptionsParam().getDatabaseParam().isCompactDatabase()); logger.info(Constant.PROGRAM_TITLE + " terminated."); System.exit(0); } }; thread.start(); } else if (ACTION_SAVE_SESSION.equalsIgnoreCase(name)) { // Ignore case for backwards compatibility Path sessionPath = SessionUtils.getSessionPath(params.getString(PARAM_SESSION)); String filename = sessionPath.toAbsolutePath().toString(); final boolean overwrite = getParam(params, PARAM_OVERWRITE_SESSION, false); boolean sameSession = false; if (!session.isNewState()) { try { sameSession = Files.isSameFile(Paths.get(session.getFileName()), sessionPath); } catch (IOException e) { throw new ApiException(ApiException.Type.INTERNAL_ERROR, e.getMessage()); } } if (Files.exists(sessionPath) && (!overwrite || sameSession)) { throw new ApiException(ApiException.Type.ALREADY_EXISTS, filename); } this.savingSession = true; try { Control.getSingleton().saveSession(filename, this); } catch (Exception e) { this.savingSession = false; throw new ApiException(ApiException.Type.INTERNAL_ERROR, e.getMessage()); } // Wait for notification that its worked ok try { while (this.savingSession) { Thread.sleep(200); } } catch (InterruptedException e) { // Probably not an error logger.debug(e.getMessage(), e); } logger.debug("Can now return after saving session"); } else if (ACTION_SNAPSHOT_SESSION.equalsIgnoreCase(name)) { // Ignore case for backwards compatibility if (session.isNewState()) { throw new ApiException(ApiException.Type.DOES_NOT_EXIST); } String fileName = session.getFileName(); if (fileName.endsWith(".session")) { fileName = fileName.substring(0, fileName.length() - 8); } fileName += "-" + dateFormat.format(new Date()) + ".session"; this.savingSession = true; try { Control.getSingleton().snapshotSession(fileName, this); } catch (Exception e) { this.savingSession = false; throw new ApiException(ApiException.Type.INTERNAL_ERROR, e.getMessage()); } // Wait for notification that its worked ok try { while (this.savingSession) { Thread.sleep(200); } } catch (InterruptedException e) { // Probably not an error logger.debug(e.getMessage(), e); } logger.debug("Can now return after saving session"); } else if (ACTION_LOAD_SESSION.equalsIgnoreCase(name)) { // Ignore case for backwards compatibility Path sessionPath = SessionUtils.getSessionPath(params.getString(PARAM_SESSION)); String filename = sessionPath.toAbsolutePath().toString(); if (!Files.exists(sessionPath)) { throw new ApiException(ApiException.Type.DOES_NOT_EXIST, filename); } try { Control.getSingleton().runCommandLineOpenSession(filename); } catch (Exception e) { throw new ApiException(ApiException.Type.INTERNAL_ERROR, e.getMessage()); } } else if (ACTION_NEW_SESSION.equalsIgnoreCase(name)) { // Ignore case for backwards compatibility String sessionName = null; try { sessionName = params.getString(PARAM_SESSION); } catch (Exception e1) { // Ignore } if (sessionName == null || sessionName.length() == 0) { // Create a new 'unnamed' session Control.getSingleton().discardSession(); try { Control.getSingleton().newSession(); } catch (Exception e) { throw new ApiException(ApiException.Type.INTERNAL_ERROR, e.getMessage()); } } else { Path sessionPath = SessionUtils.getSessionPath(sessionName); String filename = sessionPath.toAbsolutePath().toString(); final boolean overwrite = getParam(params, PARAM_OVERWRITE_SESSION, false); if (Files.exists(sessionPath) && !overwrite) { throw new ApiException(ApiException.Type.ALREADY_EXISTS, filename); } try { Control.getSingleton().runCommandLineNewSession(filename); } catch (Exception e) { throw new ApiException(ApiException.Type.INTERNAL_ERROR, e.getMessage()); } } } else if (ACTION_CLEAR_EXCLUDED_FROM_PROXY.equals(name)) { try { session.setExcludeFromProxyRegexs(new ArrayList<String>()); } catch (DatabaseException e) { throw new ApiException(ApiException.Type.INTERNAL_ERROR, e.getMessage()); } } else if (ACTION_EXCLUDE_FROM_PROXY.equals(name)) { String regex = params.getString(PARAM_REGEX); try { session.addExcludeFromProxyRegex(regex); } catch (DatabaseException e) { logger.error(e.getMessage(), e); throw new ApiException(ApiException.Type.INTERNAL_ERROR, e.getMessage()); } catch (PatternSyntaxException e) { throw new ApiException(ApiException.Type.ILLEGAL_PARAMETER, PARAM_REGEX); } } else if (ACTION_SET_HOME_DIRECTORY.equals(name)) { File f = new File(params.getString(PARAM_DIR)); if (f.exists() && f.isDirectory()) { Model.getSingleton().getOptionsParam().setUserDirectory(f); } else { throw new ApiException(ApiException.Type.ILLEGAL_PARAMETER, PARAM_DIR); } } else if (ACTION_SET_MODE.equals(name)) { try { Mode mode = Mode.valueOf(params.getString(PARAM_MODE).toLowerCase()); if (View.isInitialised()) { View.getSingleton().getMainFrame().getMainToolbarPanel().setMode(mode); } else { Control.getSingleton().setMode(mode); } } catch (Exception e) { throw new ApiException(ApiException.Type.ILLEGAL_PARAMETER, PARAM_MODE); } } else if (ACTION_GENERATE_ROOT_CA.equals(name)) { ExtensionDynSSL extDyn = (ExtensionDynSSL) Control.getSingleton().getExtensionLoader().getExtension(ExtensionDynSSL.EXTENSION_ID); if (extDyn != null) { try { extDyn.createNewRootCa(); } catch (Exception e) { throw new ApiException(ApiException.Type.INTERNAL_ERROR, e.getMessage()); } } } else if (ACTION_SEND_REQUEST.equals(name)) { HttpMessage request; try { request = createRequest(params.getString(PARAM_REQUEST)); } catch (HttpMalformedHeaderException e) { throw new ApiException(ApiException.Type.ILLEGAL_PARAMETER, PARAM_REQUEST, e); } validateForCurrentMode(request); return sendHttpMessage(request, getParam(params, PARAM_FOLLOW_REDIRECTS, false), name); } else if (ACTION_DELETE_ALL_ALERTS.equals(name)) { final ExtensionAlert extAlert = (ExtensionAlert) Control.getSingleton() .getExtensionLoader() .getExtension(ExtensionAlert.NAME); if (extAlert != null) { extAlert.deleteAllAlerts(); } else { try { Model.getSingleton().getDb().getTableAlert().deleteAllAlerts(); } catch (DatabaseException e) { logger.error(e.getMessage(), e); } SiteNode rootNode = (SiteNode) Model.getSingleton().getSession().getSiteTree().getRoot(); rootNode.deleteAllAlerts(); removeHistoryReferenceAlerts(rootNode); } } else if (ACTION_COLLECT_GARBAGE.equals(name)) { System.gc(); return ApiResponseElement.OK; } else if (ACTION_DELETE_SITE_NODE.equals(name)) { try { String url = params.getString(PARAM_URL); String method = getParam(params, PARAM_METHOD, "GET"); String postData = getParam(params, PARAM_POST_DATA, ""); URI uri = new URI(url, true); SiteMap siteMap = session.getSiteTree(); SiteNode siteNode = siteMap.findNode(uri, method, postData); if(siteNode == null) { throw new ApiException(ApiException.Type.DOES_NOT_EXIST, PARAM_URL); } if (getExtHistory() != null) { getExtHistory().purge(siteMap, siteNode); } return ApiResponseElement.OK; } catch (URIException e) { throw new ApiException(ApiException.Type.ILLEGAL_PARAMETER, PARAM_URL, e); } } else if (ACTION_ADD_PROXY_CHAIN_EXCLUDED_DOMAIN.equals(name)) { try { ConnectionParam connectionParam = Model.getSingleton().getOptionsParam().getConnectionParam(); String value = params.getString(PARAM_VALUE); DomainMatcher domain; if (getParam(params, PARAM_IS_REGEX, false)) { domain = new DomainMatcher(DomainMatcher.createPattern(value)); } else { domain = new DomainMatcher(value); } domain.setEnabled(getParam(params, PARAM_IS_ENABLED, true)); List<DomainMatcher> domains = new ArrayList<>(connectionParam.getProxyExcludedDomains()); domains.add(domain); connectionParam.setProxyExcludedDomains(domains); } catch (IllegalArgumentException e) { throw new ApiException(ApiException.Type.ILLEGAL_PARAMETER, PARAM_VALUE, e); } } else if (ACTION_MODIFY_PROXY_CHAIN_EXCLUDED_DOMAIN.equals(name)) { try { ConnectionParam connectionParam = Model.getSingleton().getOptionsParam().getConnectionParam(); int idx = params.getInt(PARAM_IDX); if (idx < 0 || idx >= connectionParam.getProxyExcludedDomains().size()) { throw new ApiException(ApiException.Type.ILLEGAL_PARAMETER, PARAM_IDX); } DomainMatcher oldDomain = connectionParam.getProxyExcludedDomains().get(idx); String value = getParam(params, PARAM_VALUE, oldDomain.getValue()); if (value.isEmpty()) { value = oldDomain.getValue(); } DomainMatcher newDomain; if (getParam(params, PARAM_IS_REGEX, oldDomain.isRegex())) { newDomain = new DomainMatcher(DomainMatcher.createPattern(value)); } else { newDomain = new DomainMatcher(value); } newDomain.setEnabled(getParam(params, PARAM_IS_ENABLED, oldDomain.isEnabled())); if (!oldDomain.equals(newDomain)) { List<DomainMatcher> domains = new ArrayList<>(connectionParam.getProxyExcludedDomains()); domains.set(idx, newDomain); connectionParam.setProxyExcludedDomains(domains); } } catch (JSONException e) { throw new ApiException(ApiException.Type.ILLEGAL_PARAMETER, PARAM_IDX, e); } catch (IllegalArgumentException e) { throw new ApiException(ApiException.Type.ILLEGAL_PARAMETER, PARAM_VALUE, e); } } else if (ACTION_REMOVE_PROXY_CHAIN_EXCLUDED_DOMAIN.equals(name)) { try { ConnectionParam connectionParam = Model.getSingleton().getOptionsParam().getConnectionParam(); int idx = params.getInt(PARAM_IDX); if (idx < 0 || idx >= connectionParam.getProxyExcludedDomains().size()) { throw new ApiException(ApiException.Type.ILLEGAL_PARAMETER, PARAM_IDX); } List<DomainMatcher> domains = new ArrayList<>( connectionParam.getProxyExcludedDomains()); domains.remove(idx); connectionParam.setProxyExcludedDomains(domains); } catch (JSONException e) { throw new ApiException(ApiException.Type.ILLEGAL_PARAMETER, PARAM_IDX, e); } } else if (ACTION_ENABLE_ALL_PROXY_CHAIN_EXCLUDED_DOMAINS.equals(name)) { setProxyChainExcludedDomainsEnabled(true); } else if (ACTION_DISABLE_ALL_PROXY_CHAIN_EXCLUDED_DOMAINS.equals(name)) { setProxyChainExcludedDomainsEnabled(false); } else { throw new ApiException(ApiException.Type.BAD_ACTION); } return ApiResponseElement.OK; } private void setProxyChainExcludedDomainsEnabled(boolean enabled) { ConnectionParam connectionParam = Model.getSingleton().getOptionsParam().getConnectionParam(); List<DomainMatcher> domains = connectionParam.getProxyExcludedDomains(); for (DomainMatcher domain : domains) { domain.setEnabled(enabled); } connectionParam.setProxyExcludedDomains(domains); } /** * Validates that the given request is valid for the current {@link Mode}. * * @param request the request that will be validated * @throws ApiException if the request is not valid for the current {@code Mode}. * @see #isValidForCurrentMode(URI) */ private static void validateForCurrentMode(HttpMessage request) throws ApiException { if (!isValidForCurrentMode(request.getRequestHeader().getURI())) { throw new ApiException(ApiException.Type.MODE_VIOLATION); } } /** * Tells whether or not the given {@code uri} is valid for the current {@link Mode}. * <p> * The {@code uri} is not valid if the mode is {@code safe} or if in {@code protect} mode is not in scope. * * @param uri the {@code URI} that will be validated * @return {@code true} if the given {@code uri} is valid, {@code false} otherwise. */ private static boolean isValidForCurrentMode(URI uri) { switch (Control.getSingleton().getMode()) { case safe: return false; case protect: return Model.getSingleton().getSession().isInScope(uri.toString()); default: return true; } } private ApiResponse sendHttpMessage(HttpMessage request, boolean followRedirects, String apiResponseName) throws ApiException { final ApiResponseList resultList = new ApiResponseList(apiResponseName); try { sendRequest(request, followRedirects, new Processor<HttpMessage>() { @Override public void process(HttpMessage msg) { int id = -1; int type = -1; HistoryReference hRef = msg.getHistoryRef(); if (hRef != null) { id = hRef.getHistoryId(); type = hRef.getHistoryType(); } resultList.addItem(ApiResponseConversionUtils.httpMessageToSet(id, type, msg)); } }); return resultList; } catch (ApiException e) { throw e; } catch (Exception e) { throw new ApiException(ApiException.Type.INTERNAL_ERROR, e.getMessage()); } } private static ExtensionHistory getExtHistory() { if (extHistory == null) { extHistory = Control.getSingleton().getExtensionLoader().getExtension(ExtensionHistory.class); } return extHistory; } private static HttpMessage createRequest(String request) throws HttpMalformedHeaderException { HttpMessage requestMsg = new HttpMessage(); String[] parts = request.split(Pattern.quote(HttpHeader.CRLF + HttpHeader.CRLF), 2); requestMsg.setRequestHeader(parts[0]); if (parts.length > 1) { requestMsg.setRequestBody(parts[1]); } else { requestMsg.setRequestBody(""); } return requestMsg; } private static void sendRequest(HttpMessage request, boolean followRedirects, Processor<HttpMessage> processor) throws IOException, ApiException { HttpSender sender = null; try { sender = createHttpSender(); if (followRedirects) { ModeRedirectionValidator redirector = new ModeRedirectionValidator(processor); sender.sendAndReceive(request, HttpRequestConfig.builder().setRedirectionValidator(redirector).build()); if (!redirector.isRequestValid()) { throw new ApiException(ApiException.Type.MODE_VIOLATION); } } else { sender.sendAndReceive(request, false); persistMessage(request); processor.process(request); } } finally { if (sender != null) { sender.shutdown(); } } } private static HttpSender createHttpSender() { return new HttpSender( Model.getSingleton().getOptionsParam().getConnectionParam(), true, HttpSender.MANUAL_REQUEST_INITIATOR); } private static void persistMessage(final HttpMessage message) { final HistoryReference historyRef; try { historyRef = new HistoryReference(Model.getSingleton().getSession(), HistoryReference.TYPE_ZAP_USER, message); } catch (Exception e) { logger.warn(e.getMessage(), e); return; } if (getExtHistory() != null) { EventQueue.invokeLater(new Runnable() { @Override public void run() { getExtHistory().addHistory(historyRef); Model.getSingleton().getSession().getSiteTree().addPath(historyRef, message); } }); } } private static void removeHistoryReferenceAlerts(SiteNode siteNode) { for (int i = 0; i < siteNode.getChildCount(); i++) { removeHistoryReferenceAlerts((SiteNode) siteNode.getChildAt(i)); } if (siteNode.getHistoryReference() != null) { siteNode.getHistoryReference().deleteAllAlerts(); } for (HistoryReference hRef : siteNode.getPastHistoryReference()) { hRef.deleteAllAlerts(); } } @Override public ApiResponse handleApiView(String name, JSONObject params) throws ApiException { ApiResponse result = null; Session session = Model.getSingleton().getSession(); if (VIEW_HOSTS.equals(name)) { result = new ApiResponseList(name); SiteNode root = (SiteNode) session.getSiteTree().getRoot(); @SuppressWarnings("unchecked") Enumeration<SiteNode> en = root.children(); while (en.hasMoreElements()) { String site = en.nextElement().getNodeName(); if (site.indexOf("//") >= 0) { site = site.substring(site.indexOf("//") + 2); } if (site.indexOf(":") >= 0) { site = site.substring(0, site.indexOf(":")); } ((ApiResponseList)result).addItem(new ApiResponseElement("host", site)); } } else if (VIEW_SITES.equals(name)) { result = new ApiResponseList(name); SiteNode root = (SiteNode) session.getSiteTree().getRoot(); @SuppressWarnings("unchecked") Enumeration<SiteNode> en = root.children(); while (en.hasMoreElements()) { ((ApiResponseList)result).addItem(new ApiResponseElement("site", en.nextElement().getNodeName())); } } else if (VIEW_URLS.equals(name)) { result = new ApiResponseList(name); SiteNode root = (SiteNode) session.getSiteTree().getRoot(); this.getURLs(root, (ApiResponseList)result); } else if (VIEW_ALERT.equals(name)){ TableAlert tableAlert = Model.getSingleton().getDb().getTableAlert(); RecordAlert recordAlert; try { recordAlert = tableAlert.read(this.getParam(params, PARAM_ID, -1)); } catch (DatabaseException e) { throw new ApiException(ApiException.Type.INTERNAL_ERROR); } if (recordAlert == null) { throw new ApiException(ApiException.Type.DOES_NOT_EXIST); } result = new ApiResponseElement(alertToSet(new Alert(recordAlert))); } else if (VIEW_ALERTS.equals(name)) { final ApiResponseList resultList = new ApiResponseList(name); processAlerts( this.getParam(params, PARAM_BASE_URL, (String) null), this.getParam(params, PARAM_START, -1), this.getParam(params, PARAM_COUNT, -1), new Processor<Alert>() { @Override public void process(Alert alert) { resultList.addItem(alertToSet(alert)); } }); result = resultList; } else if (VIEW_NUMBER_OF_ALERTS.equals(name)) { CounterProcessor<Alert> counter = new CounterProcessor<>(); processAlerts( this.getParam(params, PARAM_BASE_URL, (String) null), this.getParam(params, PARAM_START, -1), this.getParam(params, PARAM_COUNT, -1), counter); result = new ApiResponseElement(name, Integer.toString(counter.getCount())); } else if (VIEW_MESSAGE.equals(name)) { TableHistory tableHistory = Model.getSingleton().getDb().getTableHistory(); RecordHistory recordHistory; try { recordHistory = tableHistory.read(this.getParam(params, PARAM_ID, -1)); } catch (HttpMalformedHeaderException | DatabaseException e) { throw new ApiException(ApiException.Type.INTERNAL_ERROR); } if (recordHistory == null || recordHistory.getHistoryType() == HistoryReference.TYPE_TEMPORARY) { throw new ApiException(ApiException.Type.DOES_NOT_EXIST); } result = new ApiResponseElement(ApiResponseConversionUtils.httpMessageToSet(recordHistory.getHistoryId(), recordHistory.getHistoryType(), recordHistory.getHttpMessage())); } else if (VIEW_MESSAGES.equals(name)) { final ApiResponseList resultList = new ApiResponseList(name); processHttpMessages( this.getParam(params, PARAM_BASE_URL, (String) null), this.getParam(params, PARAM_START, -1), this.getParam(params, PARAM_COUNT, -1), new Processor<RecordHistory>() { @Override public void process(RecordHistory recordHistory) { resultList.addItem(ApiResponseConversionUtils.httpMessageToSet( recordHistory.getHistoryId(), recordHistory.getHistoryType(), recordHistory.getHttpMessage())); } }); result = resultList; } else if (VIEW_NUMBER_OF_MESSAGES.equals(name)) { CounterProcessor<RecordHistory> counter = new CounterProcessor<>(); processHttpMessages( this.getParam(params, PARAM_BASE_URL, (String) null), this.getParam(params, PARAM_START, -1), this.getParam(params, PARAM_COUNT, -1), counter); result = new ApiResponseElement(name, Integer.toString(counter.getCount())); } else if (VIEW_MODE.equals(name)) { result = new ApiResponseElement(name, Control.getSingleton().getMode().name()); } else if (VIEW_VERSION.equals(name)) { result = new ApiResponseElement(name, Constant.PROGRAM_VERSION); } else if (VIEW_EXCLUDED_FROM_PROXY.equals(name)) { result = new ApiResponseList(name); List<String> regexs = session.getExcludeFromProxyRegexs(); for (String regex : regexs) { ((ApiResponseList)result).addItem(new ApiResponseElement("regex", regex)); } } else if (VIEW_HOME_DIRECTORY.equals(name)) { result = new ApiResponseElement(name, Model.getSingleton().getOptionsParam().getUserDirectory().getAbsolutePath()); } else if (VIEW_SESSION_LOCATION.equals(name)) { result = new ApiResponseElement(name, session.getFileName()); } else if (VIEW_PROXY_CHAIN_EXCLUDED_DOMAINS.equals(name) || VIEW_OPTION_PROXY_EXCLUDED_DOMAINS.equals(name) || VIEW_OPTION_PROXY_CHAIN_SKIP_NAME.equals(name)) { result = proxyChainExcludedDomainsToApiResponseList( name, Model.getSingleton().getOptionsParam().getConnectionParam().getProxyExcludedDomains(), false); } else if (VIEW_OPTION_PROXY_EXCLUDED_DOMAINS_ENABLED.equals(name)) { result = proxyChainExcludedDomainsToApiResponseList( name, Model.getSingleton().getOptionsParam().getConnectionParam().getProxyExcludedDomains(), true); } else { throw new ApiException(ApiException.Type.BAD_VIEW); } return result; } private ApiResponse proxyChainExcludedDomainsToApiResponseList( String name, List<DomainMatcher> domains, boolean excludeDisabled) { ApiResponseList apiResponse = new ApiResponseList(name); for (int i = 0; i < domains.size(); i++) { DomainMatcher domain = domains.get(i); if (!domain.isEnabled() && excludeDisabled) { continue; } Map<String, Object> domainData = new HashMap<>(); domainData.put("idx", i); domainData.put("value", domain.getValue()); domainData.put("regex", domain.isRegex()); domainData.put("enabled", domain.isEnabled()); apiResponse.addItem(new ApiResponseSet<Object>("domain", domainData)); } return apiResponse; } @Override public HttpMessage handleApiOther(HttpMessage msg, String name, JSONObject params) throws ApiException { if (OTHER_PROXY_PAC.equals(name)) { final ProxyParam proxyParam = Model.getSingleton().getOptionsParam().getProxyParam(); final int port = proxyParam.getProxyPort(); try { String domain = null; if (proxyParam.isProxyIpAnyLocalAddress()) { String localDomain = msg.getRequestHeader().getHostName(); if (!API.API_DOMAIN.equals(localDomain)) { domain = localDomain; } } if (domain == null) { domain = proxyParam.getProxyIp(); } String response = this.getPacFile(domain, port); msg.setResponseHeader(API.getDefaultResponseHeader("text/html", response.length())); msg.setResponseBody(response); } catch (Exception e) { logger.error(e.getMessage(), e); } return msg; } else if (OTHER_SET_PROXY.equals(name)) { /* JSON string: * {"type":1, * "http": {"host":"proxy.corp.com","port":80}, * "ssl": {"host":"proxy.corp.com","port":80}, * "ftp":{"host":"proxy.corp.com","port":80}, * "socks":{"host":"proxy.corp.com","port":80}, * "shareSettings":true,"socksVersion":5, * "proxyExcludes":"localhost, 127.0.0.1"} */ String proxyDetails = params.getString(PARAM_PROXY_DETAILS); String response = "OK"; try { try { JSONObject json = JSONObject.fromObject(proxyDetails); if (json.getInt("type") == 1) { JSONObject httpJson = JSONObject.fromObject(json.get("http")); String proxyHost = httpJson.getString("host"); int proxyPort = httpJson.getInt("port"); if (proxyHost != null && proxyHost.length() > 0 && proxyPort > 0) { Model.getSingleton().getOptionsParam().getConnectionParam().setProxyChainName(proxyHost); Model.getSingleton().getOptionsParam().getConnectionParam().setProxyChainPort(proxyPort); } } } catch (JSONException e) { throw new ApiException(ApiException.Type.ILLEGAL_PARAMETER, PARAM_PROXY_DETAILS); } msg.setResponseHeader(API.getDefaultResponseHeader("text/html", response.length())); msg.setResponseBody(response); } catch (Exception e) { logger.error(e.getMessage(), e); } return msg; } else if (OTHER_ROOT_CERT.equals(name)) { ExtensionDynSSL extDynSSL = (ExtensionDynSSL) Control.getSingleton().getExtensionLoader().getExtension(ExtensionDynSSL.EXTENSION_ID); if (extDynSSL != null) { try { Certificate rootCA = extDynSSL.getRootCA(); if (rootCA == null) { throw new ApiException(ApiException.Type.DOES_NOT_EXIST); } final StringWriter sw = new StringWriter(); try (final PemWriter pw = new PemWriter(sw)) { pw.writeObject(new JcaMiscPEMGenerator(rootCA)); pw.flush(); } String response = sw.toString(); msg.setResponseHeader(API.getDefaultResponseHeader("application/pkix-cert;", response.length())); msg.setResponseBody(response); } catch (Exception e) { logger.error(e.getMessage(), e); throw new ApiException(ApiException.Type.INTERNAL_ERROR); } } else { throw new ApiException(ApiException.Type.DOES_NOT_EXIST); } return msg; } else if (OTHER_XML_REPORT.equals(name)) { try { writeReportLastScanTo(msg, ScanReportType.XML); return msg; } catch (Exception e) { logger.error(e.getMessage(), e); throw new ApiException(ApiException.Type.INTERNAL_ERROR); } } else if (OTHER_HTML_REPORT.equals(name)) { try { writeReportLastScanTo(msg, ScanReportType.HTML); return msg; } catch (Exception e) { logger.error(e.getMessage(), e); throw new ApiException(ApiException.Type.INTERNAL_ERROR); } } else if (OTHER_MD_REPORT.equals(name)) { try { writeReportLastScanTo(msg, ScanReportType.MD); return msg; } catch (Exception e) { logger.error(e.getMessage(), e); throw new ApiException(ApiException.Type.INTERNAL_ERROR); } } else if (OTHER_MESSAGE_HAR.equals(name)) { byte[] responseBody; try { final HarEntries entries = new HarEntries(); TableHistory tableHistory = Model.getSingleton().getDb().getTableHistory(); RecordHistory recordHistory; try { recordHistory = tableHistory.read(this.getParam(params, PARAM_ID, -1)); } catch (HttpMalformedHeaderException | DatabaseException e) { throw new ApiException(ApiException.Type.INTERNAL_ERROR); } if (recordHistory == null || recordHistory.getHistoryType() == HistoryReference.TYPE_TEMPORARY) { throw new ApiException(ApiException.Type.DOES_NOT_EXIST); } entries.addEntry(HarUtils.createHarEntry(recordHistory.getHttpMessage())); HarLog harLog = HarUtils.createZapHarLog(); harLog.setEntries(entries); responseBody = HarUtils.harLogToByteArray(harLog); } catch (Exception e) { logger.error(e.getMessage(), e); ApiException apiException = new ApiException(ApiException.Type.INTERNAL_ERROR, e.getMessage()); responseBody = apiException.toString(API.Format.JSON, incErrorDetails()).getBytes(StandardCharsets.UTF_8); } try { msg.setResponseHeader(API.getDefaultResponseHeader("application/json; charset=UTF-8", responseBody.length)); } catch (HttpMalformedHeaderException e) { logger.error("Failed to create response header: " + e.getMessage(), e); } msg.setResponseBody(responseBody); return msg; } else if (OTHER_MESSAGES_HAR.equals(name)) { byte[] responseBody; try { final HarEntries entries = new HarEntries(); processHttpMessages( this.getParam(params, PARAM_BASE_URL, (String) null), this.getParam(params, PARAM_START, -1), this.getParam(params, PARAM_COUNT, -1), new Processor<RecordHistory>() { @Override public void process(RecordHistory recordHistory) { entries.addEntry(HarUtils.createHarEntry(recordHistory.getHttpMessage())); } }); HarLog harLog = HarUtils.createZapHarLog(); harLog.setEntries(entries); responseBody = HarUtils.harLogToByteArray(harLog); } catch (Exception e) { logger.error(e.getMessage(), e); ApiException apiException = new ApiException(ApiException.Type.INTERNAL_ERROR, e.getMessage()); responseBody = apiException.toString(API.Format.JSON, incErrorDetails()).getBytes(StandardCharsets.UTF_8); } try { msg.setResponseHeader(API.getDefaultResponseHeader("application/json; charset=UTF-8", responseBody.length)); } catch (HttpMalformedHeaderException e) { logger.error("Failed to create response header: " + e.getMessage(), e); } msg.setResponseBody(responseBody); return msg; } else if (OTHER_SEND_HAR_REQUEST.equals(name)) { byte[] responseBody = {}; HttpMessage request = null; try { request = HarUtils.createHttpMessage(params.getString(PARAM_REQUEST)); } catch (IOException e) { ApiException apiException = new ApiException(ApiException.Type.ILLEGAL_PARAMETER, PARAM_REQUEST, e); responseBody = apiException.toString(API.Format.JSON, incErrorDetails()).getBytes(StandardCharsets.UTF_8); } if (request != null) { if (!isValidForCurrentMode(request.getRequestHeader().getURI())) { ApiException apiException = new ApiException(ApiException.Type.MODE_VIOLATION); responseBody = apiException.toString(API.Format.JSON, incErrorDetails()).getBytes(StandardCharsets.UTF_8); } else { boolean followRedirects = getParam(params, PARAM_FOLLOW_REDIRECTS, false); try { final HarEntries entries = new HarEntries(); sendRequest(request, followRedirects, new Processor<HttpMessage>() { @Override public void process(HttpMessage msg) { entries.addEntry(HarUtils.createHarEntry(msg)); } }); HarLog harLog = HarUtils.createZapHarLog(); harLog.setEntries(entries); responseBody = HarUtils.harLogToByteArray(harLog); } catch(ApiException e) { responseBody = e.toString(API.Format.JSON, incErrorDetails()).getBytes(StandardCharsets.UTF_8); } catch (Exception e) { logger.error(e.getMessage(), e); ApiException apiException = new ApiException(ApiException.Type.INTERNAL_ERROR, e.getMessage()); responseBody = apiException.toString(API.Format.JSON, incErrorDetails()) .getBytes(StandardCharsets.UTF_8); } } } try { msg.setResponseHeader(API.getDefaultResponseHeader("application/json; charset=UTF-8", responseBody.length)); } catch (HttpMalformedHeaderException e) { logger.error("Failed to create response header: " + e.getMessage(), e); } msg.setResponseBody(responseBody); return msg; } else if (OTHER_SCRIPT_JS.equals(name)) { try { msg.setResponseBody(API_SCRIPT); // Allow caching msg.setResponseHeader(API.getDefaultResponseHeader("text/javascript", API_SCRIPT.length(), true)); msg.getResponseHeader().addHeader(HttpResponseHeader.CACHE_CONTROL, API_SCRIPT_CACHE_CONTROL); } catch (HttpMalformedHeaderException e) { logger.error("Failed to create response header: " + e.getMessage(), e); } return msg; } else { throw new ApiException(ApiException.Type.BAD_OTHER); } } private boolean incErrorDetails() { return Model.getSingleton().getOptionsParam().getApiParam().isIncErrorDetails(); } private static void writeReportLastScanTo(HttpMessage msg, ScanReportType reportType) throws Exception { ReportLastScan rls = new ReportLastScan(); StringBuilder report = new StringBuilder(); rls.generate(report, Model.getSingleton()); String response; if (ScanReportType.XML == reportType) { // Copy as is msg.setResponseHeader(API.getDefaultResponseHeader("text/xml; charset=UTF-8")); response = report.toString(); } else if (ScanReportType.MD == reportType) { msg.setResponseHeader(API.getDefaultResponseHeader("text/markdown; charset=UTF-8")); response = ReportGenerator.stringToHtml( report.toString(), Paths.get(Constant.getZapInstall(), "xml/report.md.xsl").toString()); } else { msg.setResponseHeader(API.getDefaultResponseHeader("text/html; charset=UTF-8")); response = ReportGenerator.stringToHtml( report.toString(), Paths.get(Constant.getZapInstall(), "xml/report.html.xsl").toString()); } msg.setResponseBody(response); msg.getResponseHeader().setContentLength(msg.getResponseBody().length()); } @Override public HttpMessage handleShortcut(HttpMessage msg) throws ApiException { try { if (msg.getRequestHeader().getURI().getPath().startsWith("/" + OTHER_PROXY_PAC)) { return this.handleApiOther(msg, OTHER_PROXY_PAC, null); } else if (msg.getRequestHeader().getURI().getPath().startsWith("/" + OTHER_SET_PROXY)) { JSONObject params = new JSONObject(); params.put(PARAM_PROXY_DETAILS, msg.getRequestBody().toString()); return this.handleApiOther(msg, OTHER_SET_PROXY, params); } else if (msg.getRequestHeader().getURI().getPath().startsWith("/" + OTHER_SCRIPT_JS)) { return this.handleApiOther(msg, OTHER_SCRIPT_JS, null); } } catch (URIException e) { logger.error(e.getMessage(), e); throw new ApiException(ApiException.Type.INTERNAL_ERROR); } throw new ApiException (ApiException.Type.URL_NOT_FOUND, msg.getRequestHeader().getURI().toString()); } private String getPacFile(String host, int port) { // Could put in 'ignore urls'? StringBuilder sb = new StringBuilder(100); sb.append("function FindProxyForURL(url, host) {\n"); sb.append(" return \"PROXY ").append(host).append(':').append(port).append("\";\n"); sb.append("} // End of function\n"); return sb.toString(); } private void getURLs(SiteNode parent, ApiResponseList list) { @SuppressWarnings("unchecked") Enumeration<SiteNode> en = parent.children(); while (en.hasMoreElements()) { SiteNode child = en.nextElement(); String site = child.getNodeName(); if (site.indexOf("//") >= 0) { site = site.substring(site.indexOf("//") + 2); } try { list.addItem(new ApiResponseElement("url", child.getHistoryReference().getURI().toString())); } catch (Exception e) { logger.error(e.getMessage(), e); } getURLs(child, list); } } private ApiResponseSet<String> alertToSet(Alert alert) { Map<String, String> map = new HashMap<>(); map.put("id", String.valueOf(alert.getAlertId())); map.put("pluginId", String.valueOf(alert.getPluginId())); map.put("alert", alert.getName()); //Deprecated in 2.5.0, maintain for compatibility with custom code map.put("name", alert.getName()); map.put("description", alert.getDescription()); map.put("risk", Alert.MSG_RISK[alert.getRisk()]); map.put("confidence", Alert.MSG_CONFIDENCE[alert.getConfidence()]); map.put("url", alert.getUri()); map.put("method", alert.getMethod()); map.put("other", alert.getOtherInfo()); map.put("param", alert.getParam()); map.put("attack", alert.getAttack()); map.put("evidence", alert.getEvidence()); map.put("reference", alert.getReference()); map.put("cweid", String.valueOf(alert.getCweId())); map.put("wascid", String.valueOf(alert.getWascId())); map.put("sourceid", String.valueOf(alert.getSource().getId())); map.put("solution", alert.getSolution()); if (alert.getHistoryRef() != null) { map.put("messageId", String.valueOf(alert.getHistoryRef().getHistoryId())); } return new ApiResponseSet<String>("alert", map); } private void processAlerts(String baseUrl, int start, int count, Processor<Alert> processor) throws ApiException { List<Alert> alerts = new ArrayList<>(); try { TableAlert tableAlert = Model.getSingleton().getDb().getTableAlert(); // TODO this doesnt work, but should be used when its fixed :/ //Vector<Integer> v = tableAlert.getAlertListBySession(Model.getSingleton().getSession().getSessionId()); Vector<Integer> v = tableAlert.getAlertList(); PaginationConstraintsChecker pcc = new PaginationConstraintsChecker(start, count); for (int i = 0; i < v.size(); i++) { int alertId = v.get(i).intValue(); RecordAlert recAlert = tableAlert.read(alertId); Alert alert = new Alert(recAlert); if (alert.getConfidence() != Alert.CONFIDENCE_FALSE_POSITIVE && !alerts.contains(alert)) { if (baseUrl != null && ! alert.getUri().startsWith(baseUrl)) { // Not subordinate to the specified URL continue; } pcc.recordProcessed(); alerts.add(alert); if (!pcc.hasPageStarted()) { continue; } processor.process(alert); if (pcc.hasPageEnded()) { break; } } } } catch (DatabaseException e) { logger.error(e.getMessage(), e); throw new ApiException(ApiException.Type.INTERNAL_ERROR); } } private void processHttpMessages(String baseUrl, int start, int count, Processor<RecordHistory> processor) throws ApiException { try { TableHistory tableHistory = Model.getSingleton().getDb() .getTableHistory(); List<Integer> historyIds = tableHistory.getHistoryIdsExceptOfHistType(Model .getSingleton().getSession().getSessionId(), HistoryReference.TYPE_TEMPORARY); PaginationConstraintsChecker pcc = new PaginationConstraintsChecker(start, count); for (Integer id : historyIds) { RecordHistory recHistory = tableHistory.read(id.intValue()); HttpMessage msg = recHistory.getHttpMessage(); if (msg.getRequestHeader().isImage() || msg.getResponseHeader().isImage()) { continue; } if (baseUrl != null && ! msg.getRequestHeader().getURI().toString().startsWith(baseUrl)) { // Not subordinate to the specified URL continue; } pcc.recordProcessed(); if (!pcc.hasPageStarted()) { continue; } processor.process(recHistory); if (pcc.hasPageEnded()) { break; } } } catch (HttpMalformedHeaderException | DatabaseException e) { logger.error(e.getMessage(), e); throw new ApiException(ApiException.Type.INTERNAL_ERROR); } } private interface Processor<T> { void process(T object); } private static class CounterProcessor<T> implements Processor<T> { private int count; public CounterProcessor() { count = 0; } @Override public void process(T object) { ++count; } public int getCount() { return count; } } @Override public void sessionOpened(File file, Exception e) { // Ignore } @Override public void sessionSaved(Exception e) { logger.debug("Saved session notification"); this.savingSession = false; } @Override public void sessionSnapshot(Exception e) { logger.debug("Snaphot session notification"); this.savingSession = false; } private static class PaginationConstraintsChecker { private boolean pageStarted; private boolean pageEnded; private final int startRecord; private final boolean hasEnd; private final int finalRecord; private int recordsProcessed; public PaginationConstraintsChecker(int start, int count) { recordsProcessed = 0; if (start > 0) { pageStarted = false; startRecord = start; } else { pageStarted = true; startRecord = 0; } if (count > 0) { hasEnd = true; finalRecord = !pageStarted ? start + count - 1 : count; } else { hasEnd = false; finalRecord = 0; } pageEnded = false; } public void recordProcessed() { ++recordsProcessed; if (!pageStarted) { pageStarted = recordsProcessed >= startRecord; } if (hasEnd && !pageEnded) { pageEnded = recordsProcessed >= finalRecord; } } public boolean hasPageStarted() { return pageStarted; } public boolean hasPageEnded() { return pageEnded; } } /** * A {@link HttpRedirectionValidator} that enforces the {@link Mode} when validating the {@code URI} of redirections. * * @see #isRequestValid() */ private static class ModeRedirectionValidator implements HttpRedirectionValidator { private final Processor<HttpMessage> processor; private boolean isRequestValid; public ModeRedirectionValidator(Processor<HttpMessage> processor) { this.processor = processor; } @Override public void notifyMessageReceived(HttpMessage message) { persistMessage(message); processor.process(message); } @Override public boolean isValid(URI redirection) { isRequestValid = isValidForCurrentMode(redirection); return isRequestValid; } /** * Tells whether or not the request is valid, that is, all redirections were valid for the current {@link Mode}. * * @return {@code true} is the request is valid, {@code false} otherwise. */ public boolean isRequestValid() { return isRequestValid; } } }