/* * 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.util.ArrayList; import java.util.Arrays; import java.util.Date; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Map.Entry; import java.util.regex.PatternSyntaxException; import net.sf.json.JSON; import net.sf.json.JSONException; import net.sf.json.JSONObject; import org.apache.commons.configuration.ConfigurationException; import org.apache.commons.httpclient.URI; import org.apache.commons.httpclient.URIException; import org.apache.commons.lang.StringEscapeUtils; import org.apache.commons.lang.StringUtils; import org.apache.log4j.Logger; import org.parosproxy.paros.Constant; import org.parosproxy.paros.control.Control; import org.parosproxy.paros.core.scanner.Category; import org.parosproxy.paros.core.scanner.HostProcess; import org.parosproxy.paros.core.scanner.NameValuePair; import org.parosproxy.paros.core.scanner.Plugin; import org.parosproxy.paros.core.scanner.Plugin.AlertThreshold; import org.parosproxy.paros.core.scanner.ScannerParamFilter; import org.parosproxy.paros.db.DatabaseException; import org.parosproxy.paros.model.Model; import org.parosproxy.paros.model.Session; import org.parosproxy.paros.network.HttpRequestHeader; import org.w3c.dom.Document; import org.w3c.dom.Element; import org.zaproxy.zap.extension.api.ApiAction; import org.zaproxy.zap.extension.api.ApiException; import org.zaproxy.zap.extension.api.ApiImplementor; import org.zaproxy.zap.extension.api.ApiResponse; import org.zaproxy.zap.extension.api.ApiResponseElement; import org.zaproxy.zap.extension.api.ApiResponseList; import org.zaproxy.zap.extension.api.ApiResponseSet; import org.zaproxy.zap.extension.api.ApiView; import org.zaproxy.zap.extension.api.ApiException.Type; import org.zaproxy.zap.extension.users.ExtensionUserManagement; import org.zaproxy.zap.model.Context; import org.zaproxy.zap.model.GenericScanner2; import org.zaproxy.zap.model.SessionStructure; import org.zaproxy.zap.model.StructuralNode; import org.zaproxy.zap.model.Target; import org.zaproxy.zap.users.User; import org.zaproxy.zap.utils.ApiUtils; import org.zaproxy.zap.utils.XMLStringUtil; public class ActiveScanAPI extends ApiImplementor { private static Logger log = Logger.getLogger(ActiveScanAPI.class); private static final String PREFIX = "ascan"; private static final String ACTION_SCAN = "scan"; private static final String ACTION_SCAN_AS_USER = "scanAsUser"; private static final String ACTION_PAUSE_SCAN = "pause"; private static final String ACTION_RESUME_SCAN = "resume"; private static final String ACTION_STOP_SCAN = "stop"; private static final String ACTION_PAUSE_ALL_SCANS = "pauseAllScans"; private static final String ACTION_RESUME_ALL_SCANS = "resumeAllScans"; private static final String ACTION_STOP_ALL_SCANS = "stopAllScans"; private static final String ACTION_REMOVE_SCAN = "removeScan"; private static final String ACTION_REMOVE_ALL_SCANS = "removeAllScans"; private static final String ACTION_EXCLUDE_FROM_SCAN = "excludeFromScan"; private static final String ACTION_CLEAR_EXCLUDED_FROM_SCAN = "clearExcludedFromScan"; private static final String ACTION_ENABLE_ALL_SCANNERS = "enableAllScanners"; private static final String ACTION_DISABLE_ALL_SCANNERS = "disableAllScanners"; private static final String ACTION_ENABLE_SCANNERS = "enableScanners"; private static final String ACTION_DISABLE_SCANNERS = "disableScanners"; private static final String ACTION_SET_ENABLED_POLICIES = "setEnabledPolicies"; private static final String ACTION_SET_POLICY_ATTACK_STRENGTH = "setPolicyAttackStrength"; private static final String ACTION_SET_POLICY_ALERT_THRESHOLD = "setPolicyAlertThreshold"; private static final String ACTION_SET_SCANNER_ATTACK_STRENGTH = "setScannerAttackStrength"; private static final String ACTION_SET_SCANNER_ALERT_THRESHOLD = "setScannerAlertThreshold"; private static final String ACTION_ADD_SCAN_POLICY = "addScanPolicy"; private static final String ACTION_REMOVE_SCAN_POLICY = "removeScanPolicy"; private static final String ACTION_UPDATE_SCAN_POLICY = "updateScanPolicy"; private static final String ACTION_ADD_EXCLUDED_PARAM = "addExcludedParam"; private static final String ACTION_MODIFY_EXCLUDED_PARAM = "modifyExcludedParam"; private static final String ACTION_REMOVE_EXCLUDED_PARAM = "removeExcludedParam"; private static final String VIEW_STATUS = "status"; private static final String VIEW_SCANS = "scans"; private static final String VIEW_MESSAGES_IDS = "messagesIds"; private static final String VIEW_ALERTS_IDS = "alertsIds"; private static final String VIEW_EXCLUDED_FROM_SCAN = "excludedFromScan"; private static final String VIEW_SCANNERS = "scanners"; // TODO rename? Note any changes like this to the existing API must be clearly documented to users private static final String VIEW_POLICIES = "policies"; private static final String VIEW_SCAN_POLICY_NAMES = "scanPolicyNames"; private static final String VIEW_ATTACK_MODE_QUEUE = "attackModeQueue"; private static final String VIEW_SCAN_PROGRESS = "scanProgress"; private static final String VIEW_EXCLUDED_PARAMS = "excludedParams"; private static final String VIEW_OPTION_EXCLUDED_PARAM_LIST = "optionExcludedParamList"; private static final String VIEW_EXCLUDED_PARAM_TYPES = "excludedParamTypes"; private static final String PARAM_URL = "url"; private static final String PARAM_CONTEXT_ID = "contextId"; private static final String PARAM_USER_ID = "userId"; private static final String PARAM_REGEX = "regex"; private static final String PARAM_RECURSE = "recurse"; private static final String PARAM_JUST_IN_SCOPE = "inScopeOnly"; private static final String PARAM_IDS = "ids"; private static final String PARAM_ID = "id"; private static final String PARAM_ATTACK_STRENGTH = "attackStrength"; private static final String PARAM_ALERT_THRESHOLD = "alertThreshold"; private static final String PARAM_SCAN_POLICY_NAME = "scanPolicyName"; // TODO rename to categoryId? Note any changes like this to the existing API must be clearly documented to users private static final String PARAM_CATEGORY_ID = "policyId"; private static final String PARAM_SCAN_ID = "scanId"; private static final String PARAM_METHOD = "method"; private static final String PARAM_POST_DATA = "postData"; private static final String PARAM_IDX = "idx"; private static final String PARAM_TYPE = "type"; private static final String PARAM_NAME = "name"; private ExtensionActiveScan controller = null; public ActiveScanAPI (ExtensionActiveScan controller) { this.controller = controller; this.addApiAction(new ApiAction(ACTION_SCAN, null, new String[] {PARAM_URL, PARAM_RECURSE, PARAM_JUST_IN_SCOPE, PARAM_SCAN_POLICY_NAME, PARAM_METHOD, PARAM_POST_DATA, PARAM_CONTEXT_ID})); this.addApiAction(new ApiAction( ACTION_SCAN_AS_USER, null, new String[] { PARAM_URL, PARAM_CONTEXT_ID, PARAM_USER_ID, PARAM_RECURSE, PARAM_SCAN_POLICY_NAME, PARAM_METHOD, PARAM_POST_DATA })); this.addApiAction(new ApiAction(ACTION_PAUSE_SCAN, new String[] { PARAM_SCAN_ID })); this.addApiAction(new ApiAction(ACTION_RESUME_SCAN, new String[] { PARAM_SCAN_ID })); this.addApiAction(new ApiAction(ACTION_STOP_SCAN, new String[] { PARAM_SCAN_ID })); this.addApiAction(new ApiAction(ACTION_REMOVE_SCAN, new String[] { PARAM_SCAN_ID })); this.addApiAction(new ApiAction(ACTION_PAUSE_ALL_SCANS)); this.addApiAction(new ApiAction(ACTION_RESUME_ALL_SCANS)); this.addApiAction(new ApiAction(ACTION_STOP_ALL_SCANS)); this.addApiAction(new ApiAction(ACTION_REMOVE_ALL_SCANS)); this.addApiAction(new ApiAction(ACTION_CLEAR_EXCLUDED_FROM_SCAN)); this.addApiAction(new ApiAction(ACTION_EXCLUDE_FROM_SCAN, new String[] {PARAM_REGEX})); this.addApiAction(new ApiAction(ACTION_ENABLE_ALL_SCANNERS, null, new String[] {PARAM_SCAN_POLICY_NAME})); this.addApiAction(new ApiAction(ACTION_DISABLE_ALL_SCANNERS, null, new String[] {PARAM_SCAN_POLICY_NAME})); this.addApiAction(new ApiAction(ACTION_ENABLE_SCANNERS, new String[] {PARAM_IDS}, new String[] {PARAM_SCAN_POLICY_NAME})); this.addApiAction(new ApiAction(ACTION_DISABLE_SCANNERS, new String[] {PARAM_IDS}, new String[] {PARAM_SCAN_POLICY_NAME})); this.addApiAction(new ApiAction(ACTION_SET_ENABLED_POLICIES, new String[] {PARAM_IDS}, new String[] {PARAM_SCAN_POLICY_NAME})); this.addApiAction(new ApiAction(ACTION_SET_POLICY_ATTACK_STRENGTH, new String[] { PARAM_ID, PARAM_ATTACK_STRENGTH }, new String[] {PARAM_SCAN_POLICY_NAME})); this.addApiAction(new ApiAction(ACTION_SET_POLICY_ALERT_THRESHOLD, new String[] { PARAM_ID, PARAM_ALERT_THRESHOLD }, new String[] {PARAM_SCAN_POLICY_NAME})); this.addApiAction(new ApiAction(ACTION_SET_SCANNER_ATTACK_STRENGTH, new String[] { PARAM_ID, PARAM_ATTACK_STRENGTH }, new String[] {PARAM_SCAN_POLICY_NAME})); this.addApiAction(new ApiAction(ACTION_SET_SCANNER_ALERT_THRESHOLD, new String[] { PARAM_ID, PARAM_ALERT_THRESHOLD }, new String[] {PARAM_SCAN_POLICY_NAME})); this.addApiAction(new ApiAction(ACTION_ADD_SCAN_POLICY, new String[] {PARAM_SCAN_POLICY_NAME}, new String[] {PARAM_ALERT_THRESHOLD, PARAM_ATTACK_STRENGTH})); this.addApiAction(new ApiAction(ACTION_REMOVE_SCAN_POLICY, new String[] {PARAM_SCAN_POLICY_NAME})); this.addApiAction(new ApiAction(ACTION_UPDATE_SCAN_POLICY, new String[] {PARAM_SCAN_POLICY_NAME}, new String[] {PARAM_ALERT_THRESHOLD, PARAM_ATTACK_STRENGTH})); this.addApiAction( new ApiAction(ACTION_ADD_EXCLUDED_PARAM, new String[] { PARAM_NAME }, new String[] { PARAM_TYPE, PARAM_URL })); this.addApiAction( new ApiAction( ACTION_MODIFY_EXCLUDED_PARAM, new String[] { PARAM_IDX }, new String[] { PARAM_NAME, PARAM_TYPE, PARAM_URL })); this.addApiAction(new ApiAction(ACTION_REMOVE_EXCLUDED_PARAM, new String[] { PARAM_IDX })); this.addApiView(new ApiView(VIEW_STATUS, null, new String[] { PARAM_SCAN_ID })); this.addApiView(new ApiView(VIEW_SCAN_PROGRESS, null, new String[] { PARAM_SCAN_ID })); this.addApiView(new ApiView(VIEW_MESSAGES_IDS, new String[] { PARAM_SCAN_ID })); this.addApiView(new ApiView(VIEW_ALERTS_IDS, new String[] { PARAM_SCAN_ID })); this.addApiView(new ApiView(VIEW_SCANS)); this.addApiView(new ApiView(VIEW_SCAN_POLICY_NAMES)); this.addApiView(new ApiView(VIEW_EXCLUDED_FROM_SCAN)); this.addApiView(new ApiView(VIEW_SCANNERS, null, new String[] {PARAM_SCAN_POLICY_NAME, PARAM_CATEGORY_ID})); this.addApiView(new ApiView(VIEW_POLICIES, null, new String[] {PARAM_SCAN_POLICY_NAME, PARAM_CATEGORY_ID})); this.addApiView(new ApiView(VIEW_ATTACK_MODE_QUEUE)); this.addApiView(new ApiView(VIEW_EXCLUDED_PARAMS)); ApiView view = new ApiView(VIEW_OPTION_EXCLUDED_PARAM_LIST); view.setDeprecated(true); this.addApiView(view); this.addApiView(new ApiView(VIEW_EXCLUDED_PARAM_TYPES)); } @Override public String getPrefix() { return PREFIX; } @SuppressWarnings({"fallthrough"}) @Override public ApiResponse handleApiAction(String name, JSONObject params) throws ApiException { log.debug("handleApiAction " + name + " " + params.toString()); ScanPolicy policy; int policyId; User user = null; Context context = null; try { switch(name) { case ACTION_SCAN_AS_USER: // These are not mandatory parameters on purpose, to keep the same order // of the parameters while having PARAM_URL as (now) optional. validateParamExists(params, PARAM_CONTEXT_ID); validateParamExists(params, PARAM_USER_ID); int userID = ApiUtils.getIntParam(params, PARAM_USER_ID); ExtensionUserManagement usersExtension = Control.getSingleton() .getExtensionLoader() .getExtension(ExtensionUserManagement.class); if (usersExtension == null) { throw new ApiException(Type.NO_IMPLEMENTOR, ExtensionUserManagement.NAME); } context = ApiUtils.getContextByParamId(params, PARAM_CONTEXT_ID); if (!context.isIncluded(params.getString(PARAM_URL))) { throw new ApiException(Type.URL_NOT_IN_CONTEXT, PARAM_CONTEXT_ID); } user = usersExtension.getContextUserAuthManager(context.getIndex()).getUserById(userID); if (user == null) { throw new ApiException(Type.USER_NOT_FOUND, PARAM_USER_ID); } // Same behaviour but with addition of the user to scan // $FALL-THROUGH$ case ACTION_SCAN: String url = ApiUtils.getOptionalStringParam(params, PARAM_URL); if (context == null && params.has(PARAM_CONTEXT_ID) && !params.getString(PARAM_CONTEXT_ID).isEmpty()) { context = ApiUtils.getContextByParamId(params, PARAM_CONTEXT_ID); } boolean scanJustInScope = context != null ? false : this.getParam(params, PARAM_JUST_IN_SCOPE, false); String policyName = null; policy = null; try { policyName = params.getString(PARAM_SCAN_POLICY_NAME); } catch (Exception e1) { // Ignore } try { if (policyName != null && policyName.length() > 0) { // Not specified, use the default one log.debug("handleApiAction scan policy =" + policyName); policy = controller.getPolicyManager().getPolicy(policyName); } } catch (ConfigurationException e) { throw new ApiException(ApiException.Type.DOES_NOT_EXIST, PARAM_SCAN_POLICY_NAME); } String method = this.getParam(params, PARAM_METHOD, HttpRequestHeader.GET); if (method.trim().length() == 0) { method = HttpRequestHeader.GET; } if (! Arrays.asList(HttpRequestHeader.METHODS).contains(method)) { throw new ApiException(ApiException.Type.ILLEGAL_PARAMETER, PARAM_METHOD); } int scanId = scanURL( url, user, this.getParam(params, PARAM_RECURSE, true), scanJustInScope, method, this.getParam(params, PARAM_POST_DATA, ""), policy, context); return new ApiResponseElement(name, Integer.toString(scanId)); case ACTION_PAUSE_SCAN: getActiveScan(params).pauseScan(); break; case ACTION_RESUME_SCAN: getActiveScan(params).resumeScan(); break; case ACTION_STOP_SCAN: getActiveScan(params).stopScan(); break; case ACTION_REMOVE_SCAN: GenericScanner2 activeScan = controller.removeScan(Integer.valueOf(params.getInt(PARAM_SCAN_ID))); if (activeScan == null) { throw new ApiException(ApiException.Type.DOES_NOT_EXIST, PARAM_SCAN_ID); } break; case ACTION_PAUSE_ALL_SCANS: controller.pauseAllScans(); break; case ACTION_RESUME_ALL_SCANS: controller.resumeAllScans(); break; case ACTION_STOP_ALL_SCANS: controller.stopAllScans(); break; case ACTION_REMOVE_ALL_SCANS: controller.removeAllScans(); break; case ACTION_CLEAR_EXCLUDED_FROM_SCAN: try { Session session = Model.getSingleton().getSession(); session.setExcludeFromScanRegexs(new ArrayList<String>()); } catch (DatabaseException e) { log.error(e.getMessage(), e); throw new ApiException(ApiException.Type.INTERNAL_ERROR, e.getMessage()); } break; case ACTION_EXCLUDE_FROM_SCAN: String regex = params.getString(PARAM_REGEX); try { Session session = Model.getSingleton().getSession(); session.addExcludeFromScanRegexs(regex); } catch (DatabaseException e) { log.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); } break; case ACTION_ENABLE_ALL_SCANNERS: policy = getScanPolicyFromParams(params); policy.getPluginFactory().setAllPluginEnabled(true); policy.save(); break; case ACTION_DISABLE_ALL_SCANNERS: policy = getScanPolicyFromParams(params); policy.getPluginFactory().setAllPluginEnabled(false); policy.save(); break; case ACTION_ENABLE_SCANNERS: policy = getScanPolicyFromParams(params); setScannersEnabled(policy, getParam(params, PARAM_IDS, "").split(","), true); policy.save(); break; case ACTION_DISABLE_SCANNERS: policy = getScanPolicyFromParams(params); setScannersEnabled(policy, getParam(params, PARAM_IDS, "").split(","), false); policy.save(); break; case ACTION_SET_ENABLED_POLICIES: policy = getScanPolicyFromParams(params); setEnabledPolicies(policy, getParam(params, PARAM_IDS, "").split(",")); policy.save(); break; case ACTION_SET_POLICY_ATTACK_STRENGTH: policyId = getPolicyIdFromParamId(params); policy = getScanPolicyFromParams(params); Plugin.AttackStrength attackStrength = getAttackStrengthFromParamAttack(params); for (Plugin scanner : policy.getPluginFactory().getAllPlugin()) { if (scanner.getCategory() == policyId) { scanner.setAttackStrength(attackStrength); } } policy.save(); break; case ACTION_SET_POLICY_ALERT_THRESHOLD: policyId = getPolicyIdFromParamId(params); policy = getScanPolicyFromParams(params); Plugin.AlertThreshold alertThreshold1 = getAlertThresholdFromParamAlertThreshold(params); for (Plugin scanner : policy.getPluginFactory().getAllPlugin()) { if (scanner.getCategory() == policyId) { scanner.setAlertThreshold(alertThreshold1); } } policy.save(); break; case ACTION_SET_SCANNER_ATTACK_STRENGTH: policy = getScanPolicyFromParams(params); Plugin scanner = getScannerFromParamId(policy, params); scanner.setAttackStrength(getAttackStrengthFromParamAttack(params)); policy.save(); break; case ACTION_SET_SCANNER_ALERT_THRESHOLD: policy = getScanPolicyFromParams(params); AlertThreshold alertThreshold2 = getAlertThresholdFromParamAlertThreshold(params); getScannerFromParamId(policy, params).setAlertThreshold(alertThreshold2); policy.save(); break; case ACTION_ADD_SCAN_POLICY: String newPolicyName = params.getString(PARAM_SCAN_POLICY_NAME); if (controller.getPolicyManager().getAllPolicyNames().contains(newPolicyName)) { throw new ApiException(ApiException.Type.ALREADY_EXISTS, PARAM_SCAN_POLICY_NAME); } if (! controller.getPolicyManager().isLegalPolicyName(newPolicyName)) { throw new ApiException(ApiException.Type.ILLEGAL_PARAMETER, PARAM_SCAN_POLICY_NAME); } policy = controller.getPolicyManager().getTemplatePolicy(); policy.setName(newPolicyName); setAlertThreshold(policy, params); setAttackStrength(policy, params); controller.getPolicyManager().savePolicy(policy); break; case ACTION_REMOVE_SCAN_POLICY: // Check it exists policy = getScanPolicyFromParams(params); if (controller.getPolicyManager().getAllPolicyNames().size() == 1) { // Dont remove the last one throw new ApiException(ApiException.Type.ILLEGAL_PARAMETER, "You are not allowed to remove the last scan policy"); } controller.getPolicyManager().deletePolicy(policy.getName()); break; case ACTION_UPDATE_SCAN_POLICY: policy = getScanPolicyFromParams(params); if (!isParamsChanged(policy, params)) { break; } updateAlertThreshold(policy, params); updateAttackStrength(policy, params); controller.getPolicyManager().savePolicy(policy); break; case ACTION_ADD_EXCLUDED_PARAM: int type = getParam(params, PARAM_TYPE, NameValuePair.TYPE_UNDEFINED); if (!ScannerParamFilter.getTypes().containsKey(type)) { throw new ApiException(ApiException.Type.ILLEGAL_PARAMETER, PARAM_TYPE); } url = getParam(params, PARAM_URL, "*"); if (url.isEmpty()) { url = "*"; } ScannerParamFilter excludedParam = new ScannerParamFilter(params.getString(PARAM_NAME), type, url); List<ScannerParamFilter> excludedParams = new ArrayList<>(controller.getScannerParam().getExcludedParamList()); excludedParams.add(excludedParam); controller.getScannerParam().setExcludedParamList(excludedParams); break; case ACTION_MODIFY_EXCLUDED_PARAM: try { int idx = params.getInt(PARAM_IDX); if (idx < 0 || idx >= controller.getScannerParam().getExcludedParamList().size()) { throw new ApiException(ApiException.Type.ILLEGAL_PARAMETER, PARAM_IDX); } ScannerParamFilter oldExcludedParam = controller.getScannerParam().getExcludedParamList().get(idx); String epName = getParam(params, PARAM_NAME, oldExcludedParam.getParamName()); if (epName.isEmpty()) { epName = oldExcludedParam.getParamName(); } type = getParam(params, PARAM_TYPE, oldExcludedParam.getType()); if (!ScannerParamFilter.getTypes().containsKey(type)) { throw new ApiException(ApiException.Type.ILLEGAL_PARAMETER, PARAM_TYPE); } url = getParam(params, PARAM_URL, oldExcludedParam.getWildcardedUrl()); if (url.isEmpty()) { url = "*"; } ScannerParamFilter newExcludedParam = new ScannerParamFilter(epName, type, url); if (oldExcludedParam.equals(newExcludedParam)) { break; } excludedParams = new ArrayList<>(controller.getScannerParam().getExcludedParamList()); excludedParams.set(idx, newExcludedParam); controller.getScannerParam().setExcludedParamList(excludedParams); } catch (JSONException e) { throw new ApiException(ApiException.Type.ILLEGAL_PARAMETER, PARAM_IDX, e); } break; case ACTION_REMOVE_EXCLUDED_PARAM: try { int idx = params.getInt(PARAM_IDX); if (idx < 0 || idx >= controller.getScannerParam().getExcludedParamList().size()) { throw new ApiException(ApiException.Type.ILLEGAL_PARAMETER, PARAM_IDX); } excludedParams = new ArrayList<>(controller.getScannerParam().getExcludedParamList()); excludedParams.remove(idx); controller.getScannerParam().setExcludedParamList(excludedParams); } catch (JSONException e) { throw new ApiException(ApiException.Type.ILLEGAL_PARAMETER, PARAM_IDX, e); } break; default: throw new ApiException(ApiException.Type.BAD_ACTION); } } catch (ConfigurationException e) { throw new ApiException(ApiException.Type.INTERNAL_ERROR, e.getMessage()); } return ApiResponseElement.OK; } private void setAlertThreshold(ScanPolicy policy, JSONObject params) throws ApiException { if (isParamExists(params, PARAM_ALERT_THRESHOLD)) { policy.setDefaultThreshold(getAlertThresholdFromParamAlertThreshold(params)); } } private void setAttackStrength(ScanPolicy policy, JSONObject params) throws ApiException { if (isParamExists(params, PARAM_ATTACK_STRENGTH)) { policy.setDefaultStrength(getAttackStrengthFromParamAttack(params)); } } private boolean isParamsChanged(ScanPolicy policy, JSONObject params) throws ApiException { return isAlertThresholdChanged(policy, params) || isAttackStrengthChanged(policy, params); } private boolean isAlertThresholdChanged(ScanPolicy policy, JSONObject params) throws ApiException { if (!isParamExists(params, PARAM_ALERT_THRESHOLD)) { return false; } AlertThreshold updatedAlertThreshold = getAlertThresholdFromParamAlertThreshold(params); AlertThreshold currentThreshold = policy.getDefaultThreshold(); return !currentThreshold.equals(updatedAlertThreshold); } private boolean isAttackStrengthChanged(ScanPolicy policy, JSONObject params) throws ApiException { if (!isParamExists(params, PARAM_ATTACK_STRENGTH)) { return false; } Plugin.AttackStrength updatedAttackStrength = getAttackStrengthFromParamAttack(params); Plugin.AttackStrength currentAttackStrength = policy.getDefaultStrength(); return !currentAttackStrength.equals(updatedAttackStrength); } private void updateAlertThreshold(ScanPolicy policy, JSONObject params) throws ApiException { if (isAlertThresholdChanged(policy, params)) { policy.setDefaultThreshold(getAlertThresholdFromParamAlertThreshold(params)); } } private void updateAttackStrength(ScanPolicy policy, JSONObject params) throws ApiException { if (isAttackStrengthChanged(policy, params)) { policy.setDefaultStrength(getAttackStrengthFromParamAttack(params)); } } private boolean isParamExists(JSONObject params, String key) { return params.has(key) && StringUtils.isNotBlank(params.getString(key)); } private ScanPolicy getScanPolicyFromParams(JSONObject params) throws ApiException { String policyName = null; try { policyName = params.getString(PARAM_SCAN_POLICY_NAME); } catch (Exception e1) { // Ignore } if (policyName == null || policyName.length() == 0) { // Not specified, use the default one return controller.getPolicyManager().getDefaultScanPolicy(); } try { return controller.getPolicyManager().getPolicy(policyName); } catch (ConfigurationException e) { throw new ApiException(ApiException.Type.DOES_NOT_EXIST, PARAM_SCAN_POLICY_NAME); } } /** * Returns a {@link ActiveScan} from the available active scans or the last active scan. If a scan ID ( * {@link #PARAM_SCAN_ID}) is present in the given {@code params} it will be used to the get the {@code ActiveScan} from the * available active scans, otherwise it's returned the last active scan. * * @param params the parameters of the API call * @return the {@code ActiveScan} with the given scan ID or, if not present, the last active scan * @throws ApiException if there's no scan with the given scan ID */ private ActiveScan getActiveScan(JSONObject params) throws ApiException { int id = getParam(params, PARAM_SCAN_ID, -1); GenericScanner2 activeScan = null; if (id == -1) { activeScan = controller.getLastScan(); } else { activeScan = controller.getScan(Integer.valueOf(id)); } if (activeScan == null) { throw new ApiException(ApiException.Type.DOES_NOT_EXIST, PARAM_SCAN_ID); } return (ActiveScan)activeScan; } private void setScannersEnabled(ScanPolicy policy, String [] ids, boolean enabled) throws ConfigurationException, ApiException { if (ids.length > 0) { for (String id : ids) { try { Plugin scanner = policy.getPluginFactory().getPlugin(Integer.valueOf(id.trim()).intValue()); if (scanner != null) { scanner.setEnabled(enabled); } } catch (NumberFormatException e) { log.warn("Failed to parse scanner ID: ", e); } } } } private void setEnabledPolicies(ScanPolicy policy, String[] ids) { policy.getPluginFactory().setAllPluginEnabled(false); if (ids.length > 0) { for (String id : ids) { try { int policyId = Integer.valueOf(id.trim()).intValue(); if (hasPolicyWithId(policyId)) { for (Plugin scanner : policy.getPluginFactory().getAllPlugin()) { if (scanner.getCategory() == policyId) { scanner.setEnabled(true); } } } } catch (NumberFormatException e) { log.warn("Failed to parse policy ID: ", e); } } } } private static boolean hasPolicyWithId(int policyId) { return Arrays.asList(Category.getAllNames()).contains(Category.getName(policyId)); } private int getPolicyIdFromParamId(JSONObject params) throws ApiException { final int id = getParam(params, PARAM_ID, -1); if (id == -1) { throw new ApiException(ApiException.Type.ILLEGAL_PARAMETER, PARAM_ID); } if (!hasPolicyWithId(id)) { throw new ApiException(ApiException.Type.DOES_NOT_EXIST, PARAM_ID); } return id; } private Plugin.AttackStrength getAttackStrengthFromParamAttack(JSONObject params) throws ApiException { final String paramAttackStrength = params.getString(PARAM_ATTACK_STRENGTH).trim().toUpperCase(); try { return Plugin.AttackStrength.valueOf(paramAttackStrength); } catch (IllegalArgumentException e) { throw new ApiException(ApiException.Type.DOES_NOT_EXIST, PARAM_ATTACK_STRENGTH); } } private Plugin.AlertThreshold getAlertThresholdFromParamAlertThreshold(JSONObject params) throws ApiException { final String paramAlertThreshold = params.getString(PARAM_ALERT_THRESHOLD).trim().toUpperCase(); try { return Plugin.AlertThreshold.valueOf(paramAlertThreshold); } catch (IllegalArgumentException e) { throw new ApiException(ApiException.Type.DOES_NOT_EXIST, PARAM_ALERT_THRESHOLD); } } private Plugin getScannerFromParamId(ScanPolicy policy, JSONObject params) throws ApiException { final int id = getParam(params, PARAM_ID, -1); if (id == -1) { throw new ApiException(ApiException.Type.ILLEGAL_PARAMETER, PARAM_ID); } Plugin scanner = policy.getPluginFactory().getPlugin(id); if (scanner == null) { throw new ApiException(ApiException.Type.DOES_NOT_EXIST, PARAM_ID); } return scanner; } private int scanURL(String url, User user, boolean scanChildren, boolean scanJustInScope, String method, String postData, ScanPolicy policy, Context context) throws ApiException { boolean useUrl = true; if (url == null || url.isEmpty()) { if (context == null || !context.hasNodesInContextFromSiteTree()) { throw new ApiException(Type.MISSING_PARAMETER, PARAM_URL); } useUrl = false; } else if (context != null && !context.isInContext(url)) { throw new ApiException(Type.URL_NOT_IN_CONTEXT, PARAM_URL); } StructuralNode node = null; if (useUrl) { URI startURI; try { startURI = new URI(url, true); } catch (URIException e) { throw new ApiException(ApiException.Type.ILLEGAL_PARAMETER, PARAM_URL); } String scheme = startURI.getScheme(); if (scheme == null || (!scheme.equalsIgnoreCase("http") && !scheme.equalsIgnoreCase("https"))) { throw new ApiException(ApiException.Type.ILLEGAL_PARAMETER, PARAM_URL); } try { long sessionId = Model.getSingleton().getSession().getSessionId(); node = SessionStructure.find(sessionId, startURI, method, postData); if (node == null && "GET".equalsIgnoreCase(method)) { // Check if there's a non-leaf node that matches the URI, to scan the subtree. // (GET is the default method, but non-leaf nodes do not have any method.) node = SessionStructure.find(sessionId, startURI, null, postData); } } catch (Exception e) { throw new ApiException(ApiException.Type.INTERNAL_ERROR, e); } if (node == null) { throw new ApiException(ApiException.Type.URL_NOT_FOUND); } } Target target; if (useUrl) { target = new Target(node); target.setContext(context); } else { target = new Target(context); } target.setRecurse(scanChildren); target.setInScopeOnly(scanJustInScope); switch (Control.getSingleton().getMode()) { case safe: throw new ApiException(ApiException.Type.MODE_VIOLATION); case protect: if ((useUrl && !Model.getSingleton().getSession().isInScope(url)) || (context != null && !context.isInScope())) { throw new ApiException(ApiException.Type.MODE_VIOLATION); } // No problem break; case standard: // No problem break; case attack: // No problem break; } Object[] objs = new Object[] {}; if (policy != null) { objs = new Object[] { policy }; } return controller.startScan(null, target, user, objs); } @Override public ApiResponse handleApiView(String name, JSONObject params) throws ApiException { ApiResponse result; ActiveScan activeScan = null; ScanPolicy policy; int categoryId; switch(name) { case VIEW_STATUS: activeScan = getActiveScan(params); int progress = 0; if (activeScan != null) { progress = activeScan.getProgress(); } result = new ApiResponseElement(name, String.valueOf(progress)); break; case VIEW_SCANS: ApiResponseList resultList = new ApiResponseList(name); for (GenericScanner2 scan : controller.getAllScans()) { Map<String, String> map = new HashMap<>(); map.put("id", Integer.toString(scan.getScanId())); map.put("progress", Integer.toString(scan.getProgress())); map.put("state", ((ActiveScan)scan).getState().name()); resultList.addItem(new ApiResponseSet<String>("scan", map)); } result = resultList; break; case VIEW_SCAN_PROGRESS: resultList = new ApiResponseList(name); activeScan = getActiveScan(params); if (activeScan != null) { for (HostProcess hp : activeScan.getHostProcesses()) { ApiResponseList hpList = new ApiResponseList("HostProcess"); resultList.addItem(new ApiResponseElement("id", XMLStringUtil.escapeControlChrs(hp.getHostAndPort()))); for (Plugin plugin : hp.getCompleted()) { long timeTaken = plugin.getTimeFinished().getTime() - plugin.getTimeStarted().getTime(); int reqs = hp.getPluginRequestCount(plugin.getId()); if (hp.isSkipped(plugin)) { String skippedReason = hp.getSkippedReason(plugin); if (skippedReason == null) { skippedReason = Constant.messages.getString("ascan.progress.label.skipped"); } else { skippedReason = Constant.messages.getString("ascan.progress.label.skippedWithReason", skippedReason); } hpList.addItem(createPluginProgressEntry(plugin, skippedReason, timeTaken, reqs)); } else { hpList.addItem(createPluginProgressEntry(plugin, "Complete", timeTaken, reqs)); } } for (Plugin plugin : hp.getRunning()) { int pc = hp.getTestCurrentCount(plugin) * 100 / hp.getTestTotalCount(); // Make sure not return 100 (or more) if still running... // That might happen if more nodes are being scanned that the ones enumerated at the beginning. if (pc >= 100) { pc = 99; } long timeTaken = new Date().getTime() - plugin.getTimeStarted().getTime(); int reqs = hp.getPluginRequestCount(plugin.getId()); hpList.addItem(createPluginProgressEntry(plugin, pc + "%", timeTaken, reqs)); } for (Plugin plugin : hp.getPending()) { if (hp.isSkipped(plugin)) { String skippedReason = hp.getSkippedReason(plugin); if (skippedReason == null) { skippedReason = Constant.messages.getString("ascan.progress.label.skipped"); } else { skippedReason = Constant.messages.getString("ascan.progress.label.skippedWithReason", skippedReason); } hpList.addItem(createPluginProgressEntry(plugin, skippedReason, 0, 0)); } else { hpList.addItem(createPluginProgressEntry(plugin, "Pending", 0, 0)); } } resultList.addItem(hpList); } } result = resultList; break; case VIEW_MESSAGES_IDS: resultList = new ApiResponseList(name); activeScan = getActiveScan(params); if (activeScan != null) { synchronized (activeScan.getMessagesIds()) { for (Integer id : activeScan.getMessagesIds()) { resultList.addItem(new ApiResponseElement("id", id.toString())); } } } result = resultList; break; case VIEW_ALERTS_IDS: resultList = new ApiResponseList(name); activeScan = getActiveScan(params); if (activeScan != null) { synchronized (activeScan.getAlertsIds()) { for (Integer id : activeScan.getAlertsIds()) { resultList.addItem(new ApiResponseElement("id", id.toString())); } } } result = resultList; break; case VIEW_EXCLUDED_FROM_SCAN: result = new ApiResponseList(name); Session session = Model.getSingleton().getSession(); List<String> regexs = session.getExcludeFromScanRegexs(); for (String regex : regexs) { ((ApiResponseList)result).addItem(new ApiResponseElement("regex", regex)); } break; case VIEW_SCANNERS: policy = getScanPolicyFromParams(params); List<Plugin> scanners = policy.getPluginFactory().getAllPlugin(); categoryId = getParam(params, PARAM_CATEGORY_ID, -1); if (categoryId != -1 && !hasPolicyWithId(categoryId)) { throw new ApiException(ApiException.Type.DOES_NOT_EXIST, PARAM_CATEGORY_ID); } resultList = new ApiResponseList(name); for (Plugin scanner : scanners) { if (categoryId == -1 || categoryId == scanner.getCategory()) { resultList.addItem(new ScannerApiResponse(policy, scanner)); } } result = resultList; break; case VIEW_POLICIES: policy = getScanPolicyFromParams(params); String[] policies = Category.getAllNames(); resultList = new ApiResponseList(name); for (String pluginName : policies) { categoryId = Category.getCategory(pluginName); Plugin.AttackStrength attackStrength = getPolicyAttackStrength(policy, categoryId); Plugin.AlertThreshold alertThreshold = getPolicyAlertThreshold(policy, categoryId); Map<String, String> map = new HashMap<>(); map.put("id", String.valueOf(categoryId)); map.put("name", pluginName); map.put("attackStrength", attackStrength == null ? "" : String.valueOf(attackStrength)); map.put("alertThreshold", alertThreshold == null ? "" : String.valueOf(alertThreshold)); map.put("enabled", String.valueOf(isPolicyEnabled(policy, categoryId))); resultList.addItem(new ApiResponseSet<String>("policy", map)); } result = resultList; break; case VIEW_SCAN_POLICY_NAMES: resultList = new ApiResponseList(name); for (String policyName : controller.getPolicyManager().getAllPolicyNames()) { resultList.addItem(new ApiResponseElement("policy", policyName)); } result = resultList; break; case VIEW_ATTACK_MODE_QUEUE: result = new ApiResponseElement(name, String.valueOf(controller.getAttackModeStackSize())); break; case VIEW_OPTION_EXCLUDED_PARAM_LIST: case VIEW_EXCLUDED_PARAMS: resultList = new ApiResponseList(name); List<ScannerParamFilter> excludedParams = controller.getScannerParam().getExcludedParamList(); for (int i = 0; i < excludedParams.size(); i++) { resultList.addItem(new ExcludedParamApiResponse(excludedParams.get(i), i)); } result = resultList; break; case VIEW_EXCLUDED_PARAM_TYPES: resultList = new ApiResponseList(name); for (Entry<Integer, String> type : ScannerParamFilter.getTypes().entrySet()) { Map<String, String> typeData = new HashMap<>(); typeData.put("id", Integer.toString(type.getKey())); typeData.put("name", type.getValue()); resultList.addItem(new ApiResponseSet<String>("type", typeData)); } result = resultList; break; default: throw new ApiException(ApiException.Type.BAD_VIEW); } return result; } private static ApiResponseList createPluginProgressEntry(Plugin plugin, String status, long timeTaken, int requestCount) { ApiResponseList pList = new ApiResponseList("Plugin"); pList.addItem(new ApiResponseElement("name", XMLStringUtil.escapeControlChrs(plugin.getName()))); pList.addItem(new ApiResponseElement("id", Integer.toString(plugin.getId()))); pList.addItem(new ApiResponseElement("quality", plugin.getStatus().toString())); pList.addItem(new ApiResponseElement("status", status)); pList.addItem(new ApiResponseElement("timeInMs", Long.toString(timeTaken))); pList.addItem(new ApiResponseElement("reqCount", Integer.toString(requestCount))); return pList; } private boolean isPolicyEnabled(ScanPolicy policy, int category) { for (Plugin scanner : policy.getPluginFactory().getAllPlugin()) { if (scanner.getCategory() == category && !scanner.isEnabled()) { return false; } } return true; } private Plugin.AttackStrength getPolicyAttackStrength(ScanPolicy policy, int categoryd) { Plugin.AttackStrength attackStrength = null; for (Plugin scanner : policy.getPluginFactory().getAllPlugin()) { if (scanner.getCategory() == categoryd) { if (attackStrength == null) { attackStrength = scanner.getAttackStrength(true); } else if (!attackStrength.equals(scanner.getAttackStrength(true))) { // Not all the same return null; } } } return attackStrength; } private Plugin.AlertThreshold getPolicyAlertThreshold(ScanPolicy policy, int categoryId) { Plugin.AlertThreshold alertThreshold = null; for (Plugin scanner : policy.getPluginFactory().getAllPlugin()) { if (scanner.getCategory() == categoryId) { if (alertThreshold == null) { alertThreshold = scanner.getAlertThreshold(true); } else if (!alertThreshold.equals(scanner.getAlertThreshold(true))) { // Not all the same return null; } } } return alertThreshold; } private static class ExcludedParamApiResponse extends ApiResponse { private final Map<String, String> excludedParamData; private final ApiResponseSet<String> type; private final Map<String, String> typeData; public ExcludedParamApiResponse(ScannerParamFilter param, int idx) { super("excludedParam"); excludedParamData = new HashMap<>(); excludedParamData.put("idx", Integer.toString(idx)); excludedParamData.put("parameter", param.getParamName()); excludedParamData.put("url", param.getWildcardedUrl()); typeData = new HashMap<>(); typeData.put("id", Integer.toString(param.getType())); typeData.put("name", param.getTypeString()); type = new ApiResponseSet<String>("type", typeData); } @Override public void toXML(Document doc, Element parent) { parent.setAttribute("type", "set"); for (Entry<String, String> val : excludedParamData.entrySet()) { Element el = doc.createElement(val.getKey()); el.appendChild(doc.createTextNode(XMLStringUtil.escapeControlChrs(val.getValue()))); parent.appendChild(el); } Element el = doc.createElement(type.getName()); type.toXML(doc, el); parent.appendChild(el); } @Override public JSON toJSON() { JSONObject jo = new JSONObject(); for (Entry<String, String> val : excludedParamData.entrySet()) { jo.put(val.getKey(), val.getValue()); } jo.put(type.getName(), type.toJSON()); return jo; } @Override public void toHTML(StringBuilder sb) { sb.append("<h2>" + this.getName() + "</h2>\n"); sb.append("<table border=\"1\">\n"); for (Entry<String, String> val : excludedParamData.entrySet()) { sb.append("<tr><td>\n"); sb.append(val.getKey()); sb.append("</td><td>\n"); sb.append(StringEscapeUtils.escapeHtml(val.getValue())); sb.append("</td></tr>\n"); } sb.append("<tr><td>\n"); sb.append(type.getName()); sb.append("</td><td>\n"); sb.append("<table border=\"1\">\n"); for (Entry<String, ?> val : typeData.entrySet()) { sb.append("<tr><td>\n"); sb.append(StringEscapeUtils.escapeHtml(val.getKey())); sb.append("</td><td>\n"); Object value = val.getValue(); if (value != null) { sb.append(StringEscapeUtils.escapeHtml(value.toString())); } sb.append("</td></tr>\n"); } sb.append("</table>\n"); sb.append("</td></tr>\n"); sb.append("</table>\n"); } @Override public String toString(int indent) { StringBuilder sb = new StringBuilder(); for (int i = 0; i < indent; i++) { sb.append("\t"); } sb.append("ApiResponseSet "); sb.append(this.getName()); sb.append(" : [\n"); for (Entry<String, String> val : excludedParamData.entrySet()) { for (int i = 0; i < indent + 1; i++) { sb.append("\t"); } sb.append(val.getKey()); sb.append(" = "); sb.append(val.getValue()); sb.append("\n"); } sb.append(type.toString(indent + 1)); for (int i = 0; i < indent; i++) { sb.append("\t"); } sb.append("]\n"); return sb.toString(); } } private class ScannerApiResponse extends ApiResponse { final Map<String, String> scannerData; final ApiResponseList dependencies; public ScannerApiResponse(ScanPolicy policy, Plugin scanner) { super("scanner"); scannerData = new HashMap<>(); scannerData.put("id", String.valueOf(scanner.getId())); scannerData.put("name", scanner.getName()); scannerData.put("cweId", String.valueOf(scanner.getCweId())); scannerData.put("wascId", String.valueOf(scanner.getWascId())); scannerData.put("attackStrength", String.valueOf(scanner.getAttackStrength(true))); scannerData.put("alertThreshold", String.valueOf(scanner.getAlertThreshold(true))); scannerData.put("policyId", String.valueOf(scanner.getCategory())); scannerData.put("enabled", String.valueOf(scanner.isEnabled())); scannerData.put("quality", scanner.getStatus().toString()); boolean allDepsAvailable = policy.getPluginFactory().hasAllDependenciesAvailable(scanner); scannerData.put("allDependenciesAvailable", Boolean.toString(allDepsAvailable)); dependencies = new ApiResponseList("dependencies"); for (Plugin dependency : policy.getPluginFactory().getDependencies(scanner)) { dependencies.addItem(new ApiResponseElement("dependency", Integer.toString(dependency.getId()))); } } @Override public void toXML(Document doc, Element parent) { parent.setAttribute("type", "set"); for (Entry<String, String> val : scannerData.entrySet()) { Element el = doc.createElement(val.getKey()); el.appendChild(doc.createTextNode(XMLStringUtil.escapeControlChrs(val.getValue()))); parent.appendChild(el); } Element el = doc.createElement(dependencies.getName()); dependencies.toXML(doc, el); parent.appendChild(el); } @Override public JSON toJSON() { JSONObject jo = new JSONObject(); for (Entry<String, String> val : scannerData.entrySet()) { jo.put(val.getKey(), val.getValue()); } jo.put(dependencies.getName(), ((JSONObject) dependencies.toJSON()).getJSONArray(dependencies.getName())); return jo; } @Override public void toHTML(StringBuilder sb) { sb.append("<h2>" + this.getName() + "</h2>\n"); sb.append("<table border=\"1\">\n"); for (Entry<String, String> val : scannerData.entrySet()) { sb.append("<tr><td>\n"); sb.append(val.getKey()); sb.append("</td><td>\n"); sb.append(StringEscapeUtils.escapeHtml(val.getValue())); sb.append("</td></tr>\n"); } sb.append("<tr><td>\n"); sb.append(dependencies.getName()); sb.append("</td><td>\n"); sb.append("<table border=\"1\">\n"); for (ApiResponse resp : this.dependencies.getItems()) { sb.append("<tr><td>\n"); resp.toHTML(sb); sb.append("</td></tr>\n"); } sb.append("</table>\n"); sb.append("</td></tr>\n"); sb.append("</table>\n"); } @Override public String toString(int indent) { StringBuilder sb = new StringBuilder(); for (int i = 0; i < indent; i++) { sb.append(" "); } sb.append("ScannerApiResponse "); sb.append(this.getName()); sb.append(" : [\n"); for (Entry<String, String> val : scannerData.entrySet()) { for (int i = 0; i < indent + 1; i++) { sb.append("\t"); } sb.append(val.getKey()); sb.append(" = "); sb.append(val.getValue()); sb.append("\n"); } dependencies.toString(indent + 1); for (int i = 0; i < indent; i++) { sb.append("\t"); } sb.append("]\n"); return sb.toString(); } } }