1 /*
2  * Zed Attack Proxy (ZAP) and its related class files.
3  *
4  * ZAP is an HTTP/HTTPS proxy for assessing web application security.
5  *
6  * Copyright 2011 The ZAP Development Team
7  *
8  * Licensed under the Apache License, Version 2.0 (the "License");
9  * you may not use this file except in compliance with the License.
10  * You may obtain a copy of the License at
11  *
12  *     http://www.apache.org/licenses/LICENSE-2.0
13  *
14  * Unless required by applicable law or agreed to in writing, software
15  * distributed under the License is distributed on an "AS IS" BASIS,
16  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17  * See the License for the specific language governing permissions and
18  * limitations under the License.
19  */
20 package org.zaproxy.zap.extension.ascan;
21 
22 import java.io.File;
23 import java.util.ArrayList;
24 import java.util.Arrays;
25 import java.util.Date;
26 import java.util.HashMap;
27 import java.util.List;
28 import java.util.Map;
29 import java.util.Map.Entry;
30 import java.util.regex.PatternSyntaxException;
31 import net.sf.json.JSON;
32 import net.sf.json.JSONException;
33 import net.sf.json.JSONObject;
34 import org.apache.commons.configuration.ConfigurationException;
35 import org.apache.commons.httpclient.URI;
36 import org.apache.commons.httpclient.URIException;
37 import org.apache.commons.lang.StringEscapeUtils;
38 import org.apache.commons.lang.StringUtils;
39 import org.apache.logging.log4j.LogManager;
40 import org.apache.logging.log4j.Logger;
41 import org.parosproxy.paros.Constant;
42 import org.parosproxy.paros.control.Control;
43 import org.parosproxy.paros.core.scanner.Category;
44 import org.parosproxy.paros.core.scanner.HostProcess;
45 import org.parosproxy.paros.core.scanner.NameValuePair;
46 import org.parosproxy.paros.core.scanner.Plugin;
47 import org.parosproxy.paros.core.scanner.Plugin.AlertThreshold;
48 import org.parosproxy.paros.core.scanner.ScannerParamFilter;
49 import org.parosproxy.paros.db.DatabaseException;
50 import org.parosproxy.paros.model.Model;
51 import org.parosproxy.paros.model.Session;
52 import org.parosproxy.paros.network.HttpRequestHeader;
53 import org.w3c.dom.Document;
54 import org.w3c.dom.Element;
55 import org.zaproxy.zap.extension.api.ApiAction;
56 import org.zaproxy.zap.extension.api.ApiException;
57 import org.zaproxy.zap.extension.api.ApiException.Type;
58 import org.zaproxy.zap.extension.api.ApiImplementor;
59 import org.zaproxy.zap.extension.api.ApiResponse;
60 import org.zaproxy.zap.extension.api.ApiResponseElement;
61 import org.zaproxy.zap.extension.api.ApiResponseList;
62 import org.zaproxy.zap.extension.api.ApiResponseSet;
63 import org.zaproxy.zap.extension.api.ApiView;
64 import org.zaproxy.zap.extension.users.ExtensionUserManagement;
65 import org.zaproxy.zap.model.Context;
66 import org.zaproxy.zap.model.SessionStructure;
67 import org.zaproxy.zap.model.StructuralNode;
68 import org.zaproxy.zap.model.Target;
69 import org.zaproxy.zap.users.User;
70 import org.zaproxy.zap.utils.ApiUtils;
71 import org.zaproxy.zap.utils.XMLStringUtil;
72 import org.zaproxy.zap.utils.ZapXmlConfiguration;
73 
74 public class ActiveScanAPI extends ApiImplementor {
75 
76     private static Logger log = LogManager.getLogger(ActiveScanAPI.class);
77 
78     private static final String PREFIX = "ascan";
79     private static final String ACTION_SCAN = "scan";
80     private static final String ACTION_SCAN_AS_USER = "scanAsUser";
81     private static final String ACTION_PAUSE_SCAN = "pause";
82     private static final String ACTION_RESUME_SCAN = "resume";
83     private static final String ACTION_STOP_SCAN = "stop";
84     private static final String ACTION_PAUSE_ALL_SCANS = "pauseAllScans";
85     private static final String ACTION_RESUME_ALL_SCANS = "resumeAllScans";
86     private static final String ACTION_STOP_ALL_SCANS = "stopAllScans";
87     private static final String ACTION_REMOVE_SCAN = "removeScan";
88     private static final String ACTION_REMOVE_ALL_SCANS = "removeAllScans";
89 
90     private static final String ACTION_EXCLUDE_FROM_SCAN = "excludeFromScan";
91     private static final String ACTION_CLEAR_EXCLUDED_FROM_SCAN = "clearExcludedFromScan";
92     private static final String ACTION_ENABLE_ALL_SCANNERS = "enableAllScanners";
93     private static final String ACTION_DISABLE_ALL_SCANNERS = "disableAllScanners";
94     private static final String ACTION_ENABLE_SCANNERS = "enableScanners";
95     private static final String ACTION_DISABLE_SCANNERS = "disableScanners";
96     private static final String ACTION_SET_ENABLED_POLICIES = "setEnabledPolicies";
97     private static final String ACTION_SET_POLICY_ATTACK_STRENGTH = "setPolicyAttackStrength";
98     private static final String ACTION_SET_POLICY_ALERT_THRESHOLD = "setPolicyAlertThreshold";
99     private static final String ACTION_SET_SCANNER_ATTACK_STRENGTH = "setScannerAttackStrength";
100     private static final String ACTION_SET_SCANNER_ALERT_THRESHOLD = "setScannerAlertThreshold";
101     private static final String ACTION_ADD_SCAN_POLICY = "addScanPolicy";
102     private static final String ACTION_REMOVE_SCAN_POLICY = "removeScanPolicy";
103     private static final String ACTION_UPDATE_SCAN_POLICY = "updateScanPolicy";
104     private static final String ACTION_IMPORT_SCAN_POLICY = "importScanPolicy";
105 
106     private static final String ACTION_ADD_EXCLUDED_PARAM = "addExcludedParam";
107     private static final String ACTION_MODIFY_EXCLUDED_PARAM = "modifyExcludedParam";
108     private static final String ACTION_REMOVE_EXCLUDED_PARAM = "removeExcludedParam";
109 
110     private static final String ACTION_SKIP_SCANNER = "skipScanner";
111 
112     private static final String VIEW_STATUS = "status";
113     private static final String VIEW_SCANS = "scans";
114     private static final String VIEW_MESSAGES_IDS = "messagesIds";
115     private static final String VIEW_ALERTS_IDS = "alertsIds";
116     private static final String VIEW_EXCLUDED_FROM_SCAN = "excludedFromScan";
117     private static final String VIEW_SCANNERS = "scanners";
118     // TODO rename? Note any changes like this to the existing API must be clearly documented to
119     // users
120     private static final String VIEW_POLICIES = "policies";
121     private static final String VIEW_SCAN_POLICY_NAMES = "scanPolicyNames";
122     private static final String VIEW_ATTACK_MODE_QUEUE = "attackModeQueue";
123     private static final String VIEW_SCAN_PROGRESS = "scanProgress";
124     private static final String VIEW_EXCLUDED_PARAMS = "excludedParams";
125     private static final String VIEW_OPTION_EXCLUDED_PARAM_LIST = "optionExcludedParamList";
126     private static final String VIEW_EXCLUDED_PARAM_TYPES = "excludedParamTypes";
127 
128     private static final String PARAM_URL = "url";
129     private static final String PARAM_CONTEXT_ID = "contextId";
130     private static final String PARAM_USER_ID = "userId";
131     private static final String PARAM_REGEX = "regex";
132     private static final String PARAM_RECURSE = "recurse";
133     private static final String PARAM_JUST_IN_SCOPE = "inScopeOnly";
134     private static final String PARAM_IDS = "ids";
135     private static final String PARAM_ID = "id";
136     private static final String PARAM_ATTACK_STRENGTH = "attackStrength";
137     private static final String PARAM_ALERT_THRESHOLD = "alertThreshold";
138     private static final String PARAM_SCAN_POLICY_NAME = "scanPolicyName";
139     private static final String PARAM_PATH = "path";
140     // TODO rename to categoryId? Note any changes like this to the existing API must be clearly
141     // documented to users
142     private static final String PARAM_CATEGORY_ID = "policyId";
143     private static final String PARAM_SCAN_ID = "scanId";
144     private static final String PARAM_SCANNER_ID = "scannerId";
145     private static final String PARAM_METHOD = "method";
146     private static final String PARAM_POST_DATA = "postData";
147     private static final String PARAM_IDX = "idx";
148     private static final String PARAM_TYPE = "type";
149     private static final String PARAM_NAME = "name";
150 
151     private ExtensionActiveScan controller = null;
152 
ActiveScanAPI(ExtensionActiveScan controller)153     public ActiveScanAPI(ExtensionActiveScan controller) {
154         this.controller = controller;
155         this.addApiAction(
156                 new ApiAction(
157                         ACTION_SCAN,
158                         null,
159                         new String[] {
160                             PARAM_URL,
161                             PARAM_RECURSE,
162                             PARAM_JUST_IN_SCOPE,
163                             PARAM_SCAN_POLICY_NAME,
164                             PARAM_METHOD,
165                             PARAM_POST_DATA,
166                             PARAM_CONTEXT_ID
167                         }));
168         this.addApiAction(
169                 new ApiAction(
170                         ACTION_SCAN_AS_USER,
171                         null,
172                         new String[] {
173                             PARAM_URL,
174                             PARAM_CONTEXT_ID,
175                             PARAM_USER_ID,
176                             PARAM_RECURSE,
177                             PARAM_SCAN_POLICY_NAME,
178                             PARAM_METHOD,
179                             PARAM_POST_DATA
180                         }));
181         this.addApiAction(new ApiAction(ACTION_PAUSE_SCAN, new String[] {PARAM_SCAN_ID}));
182         this.addApiAction(new ApiAction(ACTION_RESUME_SCAN, new String[] {PARAM_SCAN_ID}));
183         this.addApiAction(new ApiAction(ACTION_STOP_SCAN, new String[] {PARAM_SCAN_ID}));
184         this.addApiAction(new ApiAction(ACTION_REMOVE_SCAN, new String[] {PARAM_SCAN_ID}));
185         this.addApiAction(new ApiAction(ACTION_PAUSE_ALL_SCANS));
186         this.addApiAction(new ApiAction(ACTION_RESUME_ALL_SCANS));
187         this.addApiAction(new ApiAction(ACTION_STOP_ALL_SCANS));
188         this.addApiAction(new ApiAction(ACTION_REMOVE_ALL_SCANS));
189         this.addApiAction(new ApiAction(ACTION_CLEAR_EXCLUDED_FROM_SCAN));
190         this.addApiAction(new ApiAction(ACTION_EXCLUDE_FROM_SCAN, new String[] {PARAM_REGEX}));
191         this.addApiAction(
192                 new ApiAction(
193                         ACTION_ENABLE_ALL_SCANNERS, null, new String[] {PARAM_SCAN_POLICY_NAME}));
194         this.addApiAction(
195                 new ApiAction(
196                         ACTION_DISABLE_ALL_SCANNERS, null, new String[] {PARAM_SCAN_POLICY_NAME}));
197         this.addApiAction(
198                 new ApiAction(
199                         ACTION_ENABLE_SCANNERS,
200                         new String[] {PARAM_IDS},
201                         new String[] {PARAM_SCAN_POLICY_NAME}));
202         this.addApiAction(
203                 new ApiAction(
204                         ACTION_DISABLE_SCANNERS,
205                         new String[] {PARAM_IDS},
206                         new String[] {PARAM_SCAN_POLICY_NAME}));
207         this.addApiAction(
208                 new ApiAction(
209                         ACTION_SET_ENABLED_POLICIES,
210                         new String[] {PARAM_IDS},
211                         new String[] {PARAM_SCAN_POLICY_NAME}));
212         this.addApiAction(
213                 new ApiAction(
214                         ACTION_SET_POLICY_ATTACK_STRENGTH,
215                         new String[] {PARAM_ID, PARAM_ATTACK_STRENGTH},
216                         new String[] {PARAM_SCAN_POLICY_NAME}));
217         this.addApiAction(
218                 new ApiAction(
219                         ACTION_SET_POLICY_ALERT_THRESHOLD,
220                         new String[] {PARAM_ID, PARAM_ALERT_THRESHOLD},
221                         new String[] {PARAM_SCAN_POLICY_NAME}));
222         this.addApiAction(
223                 new ApiAction(
224                         ACTION_SET_SCANNER_ATTACK_STRENGTH,
225                         new String[] {PARAM_ID, PARAM_ATTACK_STRENGTH},
226                         new String[] {PARAM_SCAN_POLICY_NAME}));
227         this.addApiAction(
228                 new ApiAction(
229                         ACTION_SET_SCANNER_ALERT_THRESHOLD,
230                         new String[] {PARAM_ID, PARAM_ALERT_THRESHOLD},
231                         new String[] {PARAM_SCAN_POLICY_NAME}));
232         this.addApiAction(
233                 new ApiAction(
234                         ACTION_ADD_SCAN_POLICY,
235                         new String[] {PARAM_SCAN_POLICY_NAME},
236                         new String[] {PARAM_ALERT_THRESHOLD, PARAM_ATTACK_STRENGTH}));
237         this.addApiAction(
238                 new ApiAction(ACTION_REMOVE_SCAN_POLICY, new String[] {PARAM_SCAN_POLICY_NAME}));
239         this.addApiAction(
240                 new ApiAction(
241                         ACTION_UPDATE_SCAN_POLICY,
242                         new String[] {PARAM_SCAN_POLICY_NAME},
243                         new String[] {PARAM_ALERT_THRESHOLD, PARAM_ATTACK_STRENGTH}));
244         this.addApiAction(new ApiAction(ACTION_IMPORT_SCAN_POLICY, new String[] {PARAM_PATH}));
245 
246         this.addApiAction(
247                 new ApiAction(
248                         ACTION_ADD_EXCLUDED_PARAM,
249                         new String[] {PARAM_NAME},
250                         new String[] {PARAM_TYPE, PARAM_URL}));
251         this.addApiAction(
252                 new ApiAction(
253                         ACTION_MODIFY_EXCLUDED_PARAM,
254                         new String[] {PARAM_IDX},
255                         new String[] {PARAM_NAME, PARAM_TYPE, PARAM_URL}));
256         this.addApiAction(new ApiAction(ACTION_REMOVE_EXCLUDED_PARAM, new String[] {PARAM_IDX}));
257 
258         this.addApiAction(
259                 new ApiAction(ACTION_SKIP_SCANNER, new String[] {PARAM_SCAN_ID, PARAM_SCANNER_ID}));
260 
261         this.addApiView(new ApiView(VIEW_STATUS, null, new String[] {PARAM_SCAN_ID}));
262         this.addApiView(new ApiView(VIEW_SCAN_PROGRESS, null, new String[] {PARAM_SCAN_ID}));
263         this.addApiView(new ApiView(VIEW_MESSAGES_IDS, new String[] {PARAM_SCAN_ID}));
264         this.addApiView(new ApiView(VIEW_ALERTS_IDS, new String[] {PARAM_SCAN_ID}));
265         this.addApiView(new ApiView(VIEW_SCANS));
266         this.addApiView(new ApiView(VIEW_SCAN_POLICY_NAMES));
267         this.addApiView(new ApiView(VIEW_EXCLUDED_FROM_SCAN));
268         this.addApiView(
269                 new ApiView(
270                         VIEW_SCANNERS,
271                         null,
272                         new String[] {PARAM_SCAN_POLICY_NAME, PARAM_CATEGORY_ID}));
273         this.addApiView(
274                 new ApiView(
275                         VIEW_POLICIES,
276                         null,
277                         new String[] {PARAM_SCAN_POLICY_NAME, PARAM_CATEGORY_ID}));
278         this.addApiView(new ApiView(VIEW_ATTACK_MODE_QUEUE));
279 
280         this.addApiView(new ApiView(VIEW_EXCLUDED_PARAMS));
281         ApiView view = new ApiView(VIEW_OPTION_EXCLUDED_PARAM_LIST);
282         view.setDeprecated(true);
283         this.addApiView(view);
284         this.addApiView(new ApiView(VIEW_EXCLUDED_PARAM_TYPES));
285     }
286 
287     @Override
getPrefix()288     public String getPrefix() {
289         return PREFIX;
290     }
291 
292     @SuppressWarnings({"fallthrough"})
293     @Override
handleApiAction(String name, JSONObject params)294     public ApiResponse handleApiAction(String name, JSONObject params) throws ApiException {
295         log.debug("handleApiAction " + name + " " + params.toString());
296         ScanPolicy policy;
297         int policyId;
298 
299         User user = null;
300         Context context = null;
301         try {
302             switch (name) {
303                 case ACTION_SCAN_AS_USER:
304                     // These are not mandatory parameters on purpose, to keep the same order
305                     // of the parameters while having PARAM_URL as (now) optional.
306                     validateParamExists(params, PARAM_CONTEXT_ID);
307                     validateParamExists(params, PARAM_USER_ID);
308 
309                     int userID = ApiUtils.getIntParam(params, PARAM_USER_ID);
310                     ExtensionUserManagement usersExtension =
311                             Control.getSingleton()
312                                     .getExtensionLoader()
313                                     .getExtension(ExtensionUserManagement.class);
314                     if (usersExtension == null) {
315                         throw new ApiException(Type.NO_IMPLEMENTOR, ExtensionUserManagement.NAME);
316                     }
317                     context = ApiUtils.getContextByParamId(params, PARAM_CONTEXT_ID);
318                     user =
319                             usersExtension
320                                     .getContextUserAuthManager(context.getId())
321                                     .getUserById(userID);
322                     if (user == null) {
323                         throw new ApiException(Type.USER_NOT_FOUND, PARAM_USER_ID);
324                     }
325 
326                     // Same behaviour but with addition of the user to scan
327                     // $FALL-THROUGH$
328                 case ACTION_SCAN:
329                     String url = ApiUtils.getOptionalStringParam(params, PARAM_URL);
330 
331                     if (context == null
332                             && params.has(PARAM_CONTEXT_ID)
333                             && !params.getString(PARAM_CONTEXT_ID).isEmpty()) {
334                         context = ApiUtils.getContextByParamId(params, PARAM_CONTEXT_ID);
335                     }
336 
337                     boolean scanJustInScope =
338                             context != null
339                                     ? false
340                                     : this.getParam(params, PARAM_JUST_IN_SCOPE, false);
341 
342                     String policyName = null;
343                     policy = null;
344 
345                     try {
346                         policyName = params.getString(PARAM_SCAN_POLICY_NAME);
347                     } catch (Exception e1) {
348                         // Ignore
349                     }
350                     try {
351                         if (policyName != null && policyName.length() > 0) {
352                             // Not specified, use the default one
353                             log.debug("handleApiAction scan policy =" + policyName);
354                             policy = controller.getPolicyManager().getPolicy(policyName);
355                         }
356                     } catch (ConfigurationException e) {
357                         throw new ApiException(
358                                 ApiException.Type.DOES_NOT_EXIST, PARAM_SCAN_POLICY_NAME);
359                     }
360                     String method = this.getParam(params, PARAM_METHOD, HttpRequestHeader.GET);
361                     if (method.trim().length() == 0) {
362                         method = HttpRequestHeader.GET;
363                     }
364                     if (!Arrays.asList(HttpRequestHeader.METHODS).contains(method)) {
365                         throw new ApiException(ApiException.Type.ILLEGAL_PARAMETER, PARAM_METHOD);
366                     }
367 
368                     int scanId =
369                             scanURL(
370                                     url,
371                                     user,
372                                     this.getParam(params, PARAM_RECURSE, true),
373                                     scanJustInScope,
374                                     method,
375                                     this.getParam(params, PARAM_POST_DATA, ""),
376                                     policy,
377                                     context);
378 
379                     return new ApiResponseElement(name, Integer.toString(scanId));
380 
381                 case ACTION_PAUSE_SCAN:
382                     getActiveScan(params).pauseScan();
383                     break;
384                 case ACTION_RESUME_SCAN:
385                     getActiveScan(params).resumeScan();
386                     break;
387                 case ACTION_STOP_SCAN:
388                     getActiveScan(params).stopScan();
389                     break;
390                 case ACTION_REMOVE_SCAN:
391                     ActiveScan activeScan = controller.removeScan(params.getInt(PARAM_SCAN_ID));
392                     if (activeScan == null) {
393                         throw new ApiException(ApiException.Type.DOES_NOT_EXIST, PARAM_SCAN_ID);
394                     }
395                     break;
396                 case ACTION_PAUSE_ALL_SCANS:
397                     controller.pauseAllScans();
398                     break;
399                 case ACTION_RESUME_ALL_SCANS:
400                     controller.resumeAllScans();
401                     break;
402                 case ACTION_STOP_ALL_SCANS:
403                     controller.stopAllScans();
404                     break;
405                 case ACTION_REMOVE_ALL_SCANS:
406                     controller.removeAllScans();
407                     break;
408                 case ACTION_CLEAR_EXCLUDED_FROM_SCAN:
409                     try {
410                         Session session = Model.getSingleton().getSession();
411                         session.setExcludeFromScanRegexs(new ArrayList<>());
412                     } catch (DatabaseException e) {
413                         log.error(e.getMessage(), e);
414                         throw new ApiException(ApiException.Type.INTERNAL_ERROR, e.getMessage());
415                     }
416                     break;
417                 case ACTION_EXCLUDE_FROM_SCAN:
418                     String regex = params.getString(PARAM_REGEX);
419                     try {
420                         Session session = Model.getSingleton().getSession();
421                         session.addExcludeFromScanRegexs(regex);
422                     } catch (DatabaseException e) {
423                         log.error(e.getMessage(), e);
424                         throw new ApiException(ApiException.Type.INTERNAL_ERROR, e.getMessage());
425                     } catch (PatternSyntaxException e) {
426                         throw new ApiException(ApiException.Type.ILLEGAL_PARAMETER, PARAM_REGEX);
427                     }
428                     break;
429                 case ACTION_ENABLE_ALL_SCANNERS:
430                     policy = getScanPolicyFromParams(params);
431                     policy.getPluginFactory().setAllPluginEnabled(true);
432                     policy.save();
433                     break;
434                 case ACTION_DISABLE_ALL_SCANNERS:
435                     policy = getScanPolicyFromParams(params);
436                     policy.getPluginFactory().setAllPluginEnabled(false);
437                     policy.save();
438                     break;
439                 case ACTION_ENABLE_SCANNERS:
440                     policy = getScanPolicyFromParams(params);
441                     setScannersEnabled(policy, getParam(params, PARAM_IDS, "").split(","), true);
442                     policy.save();
443                     break;
444                 case ACTION_DISABLE_SCANNERS:
445                     policy = getScanPolicyFromParams(params);
446                     setScannersEnabled(policy, getParam(params, PARAM_IDS, "").split(","), false);
447                     policy.save();
448                     break;
449                 case ACTION_SET_ENABLED_POLICIES:
450                     policy = getScanPolicyFromParams(params);
451                     setEnabledPolicies(policy, getParam(params, PARAM_IDS, "").split(","));
452                     policy.save();
453                     break;
454                 case ACTION_SET_POLICY_ATTACK_STRENGTH:
455                     policyId = getPolicyIdFromParamId(params);
456                     policy = getScanPolicyFromParams(params);
457                     Plugin.AttackStrength attackStrength = getAttackStrengthFromParamAttack(params);
458 
459                     for (Plugin scanner : policy.getPluginFactory().getAllPlugin()) {
460                         if (scanner.getCategory() == policyId) {
461                             scanner.setAttackStrength(attackStrength);
462                         }
463                     }
464                     policy.save();
465                     break;
466                 case ACTION_SET_POLICY_ALERT_THRESHOLD:
467                     policyId = getPolicyIdFromParamId(params);
468                     policy = getScanPolicyFromParams(params);
469                     Plugin.AlertThreshold alertThreshold1 =
470                             getAlertThresholdFromParamAlertThreshold(params);
471 
472                     for (Plugin scanner : policy.getPluginFactory().getAllPlugin()) {
473                         if (scanner.getCategory() == policyId) {
474                             scanner.setAlertThreshold(alertThreshold1);
475                         }
476                     }
477                     policy.save();
478                     break;
479                 case ACTION_SET_SCANNER_ATTACK_STRENGTH:
480                     policy = getScanPolicyFromParams(params);
481                     Plugin scanner = getScannerFromParamId(policy, params);
482                     scanner.setAttackStrength(getAttackStrengthFromParamAttack(params));
483                     policy.save();
484                     break;
485                 case ACTION_SET_SCANNER_ALERT_THRESHOLD:
486                     policy = getScanPolicyFromParams(params);
487                     AlertThreshold alertThreshold2 =
488                             getAlertThresholdFromParamAlertThreshold(params);
489                     getScannerFromParamId(policy, params).setAlertThreshold(alertThreshold2);
490                     policy.save();
491                     break;
492                 case ACTION_ADD_SCAN_POLICY:
493                     String newPolicyName = params.getString(PARAM_SCAN_POLICY_NAME);
494                     if (controller.getPolicyManager().getAllPolicyNames().contains(newPolicyName)) {
495                         throw new ApiException(
496                                 ApiException.Type.ALREADY_EXISTS, PARAM_SCAN_POLICY_NAME);
497                     }
498                     if (!controller.getPolicyManager().isLegalPolicyName(newPolicyName)) {
499                         throw new ApiException(
500                                 ApiException.Type.ILLEGAL_PARAMETER, PARAM_SCAN_POLICY_NAME);
501                     }
502                     policy = controller.getPolicyManager().getTemplatePolicy();
503                     policy.setName(newPolicyName);
504                     setAlertThreshold(policy, params);
505                     setAttackStrength(policy, params);
506                     controller.getPolicyManager().savePolicy(policy);
507                     break;
508                 case ACTION_REMOVE_SCAN_POLICY:
509                     // Check it exists
510                     policy = getScanPolicyFromParams(params);
511                     if (controller.getPolicyManager().getAllPolicyNames().size() == 1) {
512                         // Dont remove the last one
513                         throw new ApiException(
514                                 ApiException.Type.ILLEGAL_PARAMETER,
515                                 "You are not allowed to remove the last scan policy");
516                     }
517                     controller.getPolicyManager().deletePolicy(policy.getName());
518                     break;
519                 case ACTION_UPDATE_SCAN_POLICY:
520                     policy = getScanPolicyFromParams(params);
521                     if (!isParamsChanged(policy, params)) {
522                         break;
523                     }
524                     updateAlertThreshold(policy, params);
525                     updateAttackStrength(policy, params);
526                     controller.getPolicyManager().savePolicy(policy);
527                     break;
528                 case ACTION_IMPORT_SCAN_POLICY:
529                     File file = new File(params.getString(PARAM_PATH));
530                     if (!file.exists()) {
531                         throw new ApiException(ApiException.Type.DOES_NOT_EXIST, PARAM_PATH);
532                     }
533                     if (!file.isFile()) {
534                         throw new ApiException(ApiException.Type.ILLEGAL_PARAMETER, PARAM_PATH);
535                     }
536 
537                     ScanPolicy scanPolicy;
538                     try {
539                         scanPolicy = new ScanPolicy(new ZapXmlConfiguration(file));
540                     } catch (IllegalArgumentException | ConfigurationException e) {
541                         throw new ApiException(
542                                 ApiException.Type.BAD_EXTERNAL_DATA, file.toString(), e);
543                     }
544 
545                     String scanPolicyName = scanPolicy.getName();
546                     if (scanPolicyName.isEmpty()) {
547                         scanPolicyName = file.getName();
548                     }
549                     if (controller
550                             .getPolicyManager()
551                             .getAllPolicyNames()
552                             .contains(scanPolicyName)) {
553                         throw new ApiException(ApiException.Type.ALREADY_EXISTS, scanPolicyName);
554                     }
555                     if (!controller.getPolicyManager().isLegalPolicyName(scanPolicyName)) {
556                         throw new ApiException(ApiException.Type.BAD_EXTERNAL_DATA, scanPolicyName);
557                     }
558 
559                     try {
560                         controller.getPolicyManager().savePolicy(scanPolicy);
561                     } catch (ConfigurationException e) {
562                         throw new ApiException(ApiException.Type.INTERNAL_ERROR, e);
563                     }
564                     break;
565                 case ACTION_ADD_EXCLUDED_PARAM:
566                     int type = getParam(params, PARAM_TYPE, NameValuePair.TYPE_UNDEFINED);
567                     if (!ScannerParamFilter.getTypes().containsKey(type)) {
568                         throw new ApiException(ApiException.Type.ILLEGAL_PARAMETER, PARAM_TYPE);
569                     }
570 
571                     url = getParam(params, PARAM_URL, "*");
572                     if (url.isEmpty()) {
573                         url = "*";
574                     }
575 
576                     ScannerParamFilter excludedParam =
577                             new ScannerParamFilter(params.getString(PARAM_NAME), type, url);
578 
579                     List<ScannerParamFilter> excludedParams =
580                             new ArrayList<>(controller.getScannerParam().getExcludedParamList());
581                     excludedParams.add(excludedParam);
582                     controller.getScannerParam().setExcludedParamList(excludedParams);
583                     break;
584                 case ACTION_MODIFY_EXCLUDED_PARAM:
585                     try {
586                         int idx = params.getInt(PARAM_IDX);
587                         if (idx < 0
588                                 || idx
589                                         >= controller
590                                                 .getScannerParam()
591                                                 .getExcludedParamList()
592                                                 .size()) {
593                             throw new ApiException(ApiException.Type.ILLEGAL_PARAMETER, PARAM_IDX);
594                         }
595 
596                         ScannerParamFilter oldExcludedParam =
597                                 controller.getScannerParam().getExcludedParamList().get(idx);
598                         String epName =
599                                 getParam(params, PARAM_NAME, oldExcludedParam.getParamName());
600                         if (epName.isEmpty()) {
601                             epName = oldExcludedParam.getParamName();
602                         }
603 
604                         type = getParam(params, PARAM_TYPE, oldExcludedParam.getType());
605                         if (!ScannerParamFilter.getTypes().containsKey(type)) {
606                             throw new ApiException(ApiException.Type.ILLEGAL_PARAMETER, PARAM_TYPE);
607                         }
608 
609                         url = getParam(params, PARAM_URL, oldExcludedParam.getWildcardedUrl());
610                         if (url.isEmpty()) {
611                             url = "*";
612                         }
613 
614                         ScannerParamFilter newExcludedParam =
615                                 new ScannerParamFilter(epName, type, url);
616                         if (oldExcludedParam.equals(newExcludedParam)) {
617                             break;
618                         }
619 
620                         excludedParams =
621                                 new ArrayList<>(
622                                         controller.getScannerParam().getExcludedParamList());
623                         excludedParams.set(idx, newExcludedParam);
624                         controller.getScannerParam().setExcludedParamList(excludedParams);
625                     } catch (JSONException e) {
626                         throw new ApiException(ApiException.Type.ILLEGAL_PARAMETER, PARAM_IDX, e);
627                     }
628                     break;
629                 case ACTION_REMOVE_EXCLUDED_PARAM:
630                     try {
631                         int idx = params.getInt(PARAM_IDX);
632                         if (idx < 0
633                                 || idx
634                                         >= controller
635                                                 .getScannerParam()
636                                                 .getExcludedParamList()
637                                                 .size()) {
638                             throw new ApiException(ApiException.Type.ILLEGAL_PARAMETER, PARAM_IDX);
639                         }
640 
641                         excludedParams =
642                                 new ArrayList<>(
643                                         controller.getScannerParam().getExcludedParamList());
644                         excludedParams.remove(idx);
645                         controller.getScannerParam().setExcludedParamList(excludedParams);
646                     } catch (JSONException e) {
647                         throw new ApiException(ApiException.Type.ILLEGAL_PARAMETER, PARAM_IDX, e);
648                     }
649                     break;
650                 case ACTION_SKIP_SCANNER:
651                     int pluginId = getParam(params, PARAM_SCANNER_ID, -1);
652                     if (pluginId == -1) {
653                         throw new ApiException(
654                                 ApiException.Type.ILLEGAL_PARAMETER, PARAM_SCANNER_ID);
655                     }
656 
657                     String reason =
658                             Constant.messages.getString("ascan.progress.label.skipped.reason.user");
659                     getActiveScan(params)
660                             .getHostProcesses()
661                             .forEach(hp -> hp.pluginSkipped(pluginId, reason));
662                     break;
663                 default:
664                     throw new ApiException(ApiException.Type.BAD_ACTION);
665             }
666         } catch (ConfigurationException e) {
667             throw new ApiException(ApiException.Type.INTERNAL_ERROR, e.getMessage());
668         }
669         return ApiResponseElement.OK;
670     }
671 
setAlertThreshold(ScanPolicy policy, JSONObject params)672     private void setAlertThreshold(ScanPolicy policy, JSONObject params) throws ApiException {
673         if (isParamExists(params, PARAM_ALERT_THRESHOLD)) {
674             policy.setDefaultThreshold(getAlertThresholdFromParamAlertThreshold(params));
675         }
676     }
677 
setAttackStrength(ScanPolicy policy, JSONObject params)678     private void setAttackStrength(ScanPolicy policy, JSONObject params) throws ApiException {
679         if (isParamExists(params, PARAM_ATTACK_STRENGTH)) {
680             policy.setDefaultStrength(getAttackStrengthFromParamAttack(params));
681         }
682     }
683 
isParamsChanged(ScanPolicy policy, JSONObject params)684     private boolean isParamsChanged(ScanPolicy policy, JSONObject params) throws ApiException {
685         return isAlertThresholdChanged(policy, params) || isAttackStrengthChanged(policy, params);
686     }
687 
isAlertThresholdChanged(ScanPolicy policy, JSONObject params)688     private boolean isAlertThresholdChanged(ScanPolicy policy, JSONObject params)
689             throws ApiException {
690         if (!isParamExists(params, PARAM_ALERT_THRESHOLD)) {
691             return false;
692         }
693 
694         AlertThreshold updatedAlertThreshold = getAlertThresholdFromParamAlertThreshold(params);
695         AlertThreshold currentThreshold = policy.getDefaultThreshold();
696         return !currentThreshold.equals(updatedAlertThreshold);
697     }
698 
isAttackStrengthChanged(ScanPolicy policy, JSONObject params)699     private boolean isAttackStrengthChanged(ScanPolicy policy, JSONObject params)
700             throws ApiException {
701         if (!isParamExists(params, PARAM_ATTACK_STRENGTH)) {
702             return false;
703         }
704 
705         Plugin.AttackStrength updatedAttackStrength = getAttackStrengthFromParamAttack(params);
706         Plugin.AttackStrength currentAttackStrength = policy.getDefaultStrength();
707         return !currentAttackStrength.equals(updatedAttackStrength);
708     }
709 
updateAlertThreshold(ScanPolicy policy, JSONObject params)710     private void updateAlertThreshold(ScanPolicy policy, JSONObject params) throws ApiException {
711         if (isAlertThresholdChanged(policy, params)) {
712             policy.setDefaultThreshold(getAlertThresholdFromParamAlertThreshold(params));
713         }
714     }
715 
updateAttackStrength(ScanPolicy policy, JSONObject params)716     private void updateAttackStrength(ScanPolicy policy, JSONObject params) throws ApiException {
717         if (isAttackStrengthChanged(policy, params)) {
718             policy.setDefaultStrength(getAttackStrengthFromParamAttack(params));
719         }
720     }
721 
isParamExists(JSONObject params, String key)722     private boolean isParamExists(JSONObject params, String key) {
723         return params.has(key) && StringUtils.isNotBlank(params.getString(key));
724     }
725 
getScanPolicyFromParams(JSONObject params)726     private ScanPolicy getScanPolicyFromParams(JSONObject params) throws ApiException {
727         String policyName = null;
728         try {
729             policyName = params.getString(PARAM_SCAN_POLICY_NAME);
730         } catch (Exception e1) {
731             // Ignore
732         }
733         if (policyName == null || policyName.length() == 0) {
734             // Not specified, use the default one
735             return controller.getPolicyManager().getDefaultScanPolicy();
736         }
737         try {
738             return controller.getPolicyManager().getPolicy(policyName);
739         } catch (ConfigurationException e) {
740             throw new ApiException(ApiException.Type.DOES_NOT_EXIST, PARAM_SCAN_POLICY_NAME);
741         }
742     }
743 
744     /**
745      * Returns a {@link ActiveScan} from the available active scans or the last active scan. If a
746      * scan ID ( {@link #PARAM_SCAN_ID}) is present in the given {@code params} it will be used to
747      * the get the {@code ActiveScan} from the available active scans, otherwise it's returned the
748      * last active scan.
749      *
750      * @param params the parameters of the API call
751      * @return the {@code ActiveScan} with the given scan ID or, if not present, the last active
752      *     scan
753      * @throws ApiException if there's no scan with the given scan ID
754      */
getActiveScan(JSONObject params)755     private ActiveScan getActiveScan(JSONObject params) throws ApiException {
756         int id = getParam(params, PARAM_SCAN_ID, -1);
757 
758         ActiveScan activeScan = null;
759 
760         if (id == -1) {
761             activeScan = controller.getLastScan();
762         } else {
763             activeScan = controller.getScan(id);
764         }
765 
766         if (activeScan == null) {
767             throw new ApiException(ApiException.Type.DOES_NOT_EXIST, PARAM_SCAN_ID);
768         }
769 
770         return activeScan;
771     }
772 
setScannersEnabled(ScanPolicy policy, String[] ids, boolean enabled)773     private void setScannersEnabled(ScanPolicy policy, String[] ids, boolean enabled)
774             throws ConfigurationException, ApiException {
775         if (ids.length > 0) {
776             for (String id : ids) {
777                 try {
778                     Plugin scanner =
779                             policy.getPluginFactory().getPlugin(Integer.valueOf(id.trim()));
780                     if (scanner != null) {
781                         scanner.setEnabled(enabled);
782                     }
783                 } catch (NumberFormatException e) {
784                     log.warn("Failed to parse scanner ID: ", e);
785                 }
786             }
787         }
788     }
789 
setEnabledPolicies(ScanPolicy policy, String[] ids)790     private void setEnabledPolicies(ScanPolicy policy, String[] ids) {
791         policy.getPluginFactory().setAllPluginEnabled(false);
792         if (ids.length > 0) {
793             for (String id : ids) {
794                 try {
795                     int policyId = Integer.valueOf(id.trim());
796                     if (hasPolicyWithId(policyId)) {
797                         for (Plugin scanner : policy.getPluginFactory().getAllPlugin()) {
798                             if (scanner.getCategory() == policyId) {
799                                 scanner.setEnabled(true);
800                             }
801                         }
802                     }
803                 } catch (NumberFormatException e) {
804                     log.warn("Failed to parse policy ID: ", e);
805                 }
806             }
807         }
808     }
809 
hasPolicyWithId(int policyId)810     private static boolean hasPolicyWithId(int policyId) {
811         return Arrays.asList(Category.getAllNames()).contains(Category.getName(policyId));
812     }
813 
getPolicyIdFromParamId(JSONObject params)814     private int getPolicyIdFromParamId(JSONObject params) throws ApiException {
815         final int id = getParam(params, PARAM_ID, -1);
816         if (id == -1) {
817             throw new ApiException(ApiException.Type.ILLEGAL_PARAMETER, PARAM_ID);
818         }
819         if (!hasPolicyWithId(id)) {
820             throw new ApiException(ApiException.Type.DOES_NOT_EXIST, PARAM_ID);
821         }
822         return id;
823     }
824 
getAttackStrengthFromParamAttack(JSONObject params)825     private Plugin.AttackStrength getAttackStrengthFromParamAttack(JSONObject params)
826             throws ApiException {
827         final String paramAttackStrength =
828                 params.getString(PARAM_ATTACK_STRENGTH).trim().toUpperCase();
829         try {
830             return Plugin.AttackStrength.valueOf(paramAttackStrength);
831         } catch (IllegalArgumentException e) {
832             throw new ApiException(ApiException.Type.DOES_NOT_EXIST, PARAM_ATTACK_STRENGTH);
833         }
834     }
835 
getAlertThresholdFromParamAlertThreshold(JSONObject params)836     private Plugin.AlertThreshold getAlertThresholdFromParamAlertThreshold(JSONObject params)
837             throws ApiException {
838         final String paramAlertThreshold =
839                 params.getString(PARAM_ALERT_THRESHOLD).trim().toUpperCase();
840         try {
841             return Plugin.AlertThreshold.valueOf(paramAlertThreshold);
842         } catch (IllegalArgumentException e) {
843             throw new ApiException(ApiException.Type.DOES_NOT_EXIST, PARAM_ALERT_THRESHOLD);
844         }
845     }
846 
getScannerFromParamId(ScanPolicy policy, JSONObject params)847     private Plugin getScannerFromParamId(ScanPolicy policy, JSONObject params) throws ApiException {
848         final int id = getParam(params, PARAM_ID, -1);
849         if (id == -1) {
850             throw new ApiException(ApiException.Type.ILLEGAL_PARAMETER, PARAM_ID);
851         }
852         Plugin scanner = policy.getPluginFactory().getPlugin(id);
853         if (scanner == null) {
854             throw new ApiException(ApiException.Type.DOES_NOT_EXIST, PARAM_ID);
855         }
856         return scanner;
857     }
858 
scanURL( String url, User user, boolean scanChildren, boolean scanJustInScope, String method, String postData, ScanPolicy policy, Context context)859     private int scanURL(
860             String url,
861             User user,
862             boolean scanChildren,
863             boolean scanJustInScope,
864             String method,
865             String postData,
866             ScanPolicy policy,
867             Context context)
868             throws ApiException {
869 
870         boolean useUrl = true;
871         if (url == null || url.isEmpty()) {
872             if (context == null || !context.hasNodesInContextFromSiteTree()) {
873                 throw new ApiException(Type.MISSING_PARAMETER, PARAM_URL);
874             }
875             useUrl = false;
876         } else if (context != null && !context.isInContext(url)) {
877             throw new ApiException(Type.URL_NOT_IN_CONTEXT, PARAM_URL);
878         }
879 
880         StructuralNode node = null;
881         if (useUrl) {
882             URI startURI;
883             try {
884                 if (scanChildren && url.endsWith("/")) {
885                     // Always choose the non leaf node if scanChildren option selected
886                     url = url.substring(0, url.length() - 1);
887                 }
888                 startURI = new URI(url, true);
889             } catch (URIException e) {
890                 throw new ApiException(ApiException.Type.ILLEGAL_PARAMETER, PARAM_URL, e);
891             }
892             String scheme = startURI.getScheme();
893             if (scheme == null
894                     || (!scheme.equalsIgnoreCase("http") && !scheme.equalsIgnoreCase("https"))) {
895                 throw new ApiException(
896                         ApiException.Type.ILLEGAL_PARAMETER,
897                         PARAM_URL + " does not have a scheme.");
898             }
899 
900             try {
901                 Model model = Model.getSingleton();
902                 node = SessionStructure.find(model, startURI, method, postData);
903                 if (node == null && "GET".equalsIgnoreCase(method)) {
904                     // Check if there's a non-leaf node that matches the URI, to scan the subtree.
905                     // (GET is the default method, but non-leaf nodes do not have any method.)
906                     node = SessionStructure.find(model, startURI, null, postData);
907                 }
908             } catch (Exception e) {
909                 throw new ApiException(ApiException.Type.INTERNAL_ERROR, e);
910             }
911 
912             if (node == null) {
913                 throw new ApiException(ApiException.Type.URL_NOT_FOUND);
914             }
915         }
916         Target target;
917         if (useUrl) {
918             target = new Target(node);
919             target.setContext(context);
920         } else {
921             target = new Target(context);
922         }
923         target.setRecurse(scanChildren);
924         target.setInScopeOnly(scanJustInScope);
925 
926         switch (Control.getSingleton().getMode()) {
927             case safe:
928                 throw new ApiException(ApiException.Type.MODE_VIOLATION);
929             case protect:
930                 if ((useUrl && !Model.getSingleton().getSession().isInScope(url))
931                         || (context != null && !context.isInScope())) {
932                     throw new ApiException(ApiException.Type.MODE_VIOLATION);
933                 }
934                 // No problem
935                 break;
936             case standard:
937                 // No problem
938                 break;
939             case attack:
940                 // No problem
941                 break;
942         }
943 
944         Object[] objs = new Object[] {};
945         if (policy != null) {
946             objs = new Object[] {policy};
947         }
948 
949         return controller.startScan(null, target, user, objs);
950     }
951 
952     @Override
handleApiView(String name, JSONObject params)953     public ApiResponse handleApiView(String name, JSONObject params) throws ApiException {
954         ApiResponse result;
955         ActiveScan activeScan = null;
956         ScanPolicy policy;
957         int categoryId;
958 
959         switch (name) {
960             case VIEW_STATUS:
961                 activeScan = getActiveScan(params);
962                 int progress = 0;
963                 if (activeScan.isStopped()) {
964                     progress = 100;
965                 } else {
966                     progress = activeScan.getProgress();
967                 }
968                 result = new ApiResponseElement(name, String.valueOf(progress));
969                 break;
970             case VIEW_SCANS:
971                 ApiResponseList resultList = new ApiResponseList(name);
972                 for (ActiveScan scan : controller.getAllScans()) {
973                     Map<String, String> map = new HashMap<>();
974                     map.put("id", Integer.toString(scan.getScanId()));
975                     map.put("progress", Integer.toString(scan.getProgress()));
976                     map.put("state", scan.getState().name());
977                     map.put("reqCount", Integer.toString(scan.getTotalRequests()));
978                     map.put("alertCount", Integer.toString(scan.getAlertsIds().size()));
979                     map.put("newAlertCount", Integer.toString(scan.getTotalNewAlerts()));
980                     resultList.addItem(new ApiResponseSet<>("scan", map));
981                 }
982                 result = resultList;
983                 break;
984             case VIEW_SCAN_PROGRESS:
985                 resultList = new ApiResponseList(name);
986                 activeScan = getActiveScan(params);
987                 for (HostProcess hp : activeScan.getHostProcesses()) {
988                     ApiResponseList hpList = new ApiResponseList("HostProcess");
989                     resultList.addItem(new ApiResponseElement("id", hp.getHostAndPort()));
990 
991                     for (Plugin plugin : hp.getCompleted()) {
992                         long timeTaken =
993                                 plugin.getTimeFinished().getTime()
994                                         - plugin.getTimeStarted().getTime();
995                         int reqs = hp.getPluginRequestCount(plugin.getId());
996                         int alertCount = hp.getPluginStats(plugin.getId()).getAlertCount();
997                         hpList.addItem(
998                                 createPluginProgressEntry(
999                                         plugin,
1000                                         getStatus(hp, plugin, "Complete"),
1001                                         timeTaken,
1002                                         reqs,
1003                                         alertCount));
1004                     }
1005 
1006                     for (Plugin plugin : hp.getRunning()) {
1007                         int pc = hp.getTestCurrentCount(plugin) * 100 / hp.getTestTotalCount();
1008                         // Make sure not return 100 (or more) if still running...
1009                         // That might happen if more nodes are being scanned that the ones
1010                         // enumerated at the beginning.
1011                         if (pc >= 100) {
1012                             pc = 99;
1013                         }
1014                         long timeTaken = new Date().getTime() - plugin.getTimeStarted().getTime();
1015                         int reqs = hp.getPluginRequestCount(plugin.getId());
1016                         int alertCount = hp.getPluginStats(plugin.getId()).getAlertCount();
1017                         hpList.addItem(
1018                                 createPluginProgressEntry(
1019                                         plugin, pc + "%", timeTaken, reqs, alertCount));
1020                     }
1021 
1022                     for (Plugin plugin : hp.getPending()) {
1023                         hpList.addItem(
1024                                 createPluginProgressEntry(
1025                                         plugin, getStatus(hp, plugin, "Pending"), 0, 0, 0));
1026                     }
1027                     resultList.addItem(hpList);
1028                 }
1029                 result = resultList;
1030                 break;
1031             case VIEW_MESSAGES_IDS:
1032                 resultList = new ApiResponseList(name);
1033                 activeScan = getActiveScan(params);
1034                 synchronized (activeScan.getMessagesIds()) {
1035                     for (Integer id : activeScan.getMessagesIds()) {
1036                         resultList.addItem(new ApiResponseElement("id", id.toString()));
1037                     }
1038                 }
1039                 result = resultList;
1040                 break;
1041             case VIEW_ALERTS_IDS:
1042                 resultList = new ApiResponseList(name);
1043                 activeScan = getActiveScan(params);
1044                 synchronized (activeScan.getAlertsIds()) {
1045                     for (Integer id : activeScan.getAlertsIds()) {
1046                         resultList.addItem(new ApiResponseElement("id", id.toString()));
1047                     }
1048                 }
1049                 result = resultList;
1050                 break;
1051             case VIEW_EXCLUDED_FROM_SCAN:
1052                 result = new ApiResponseList(name);
1053                 Session session = Model.getSingleton().getSession();
1054                 List<String> regexs = session.getExcludeFromScanRegexs();
1055                 for (String regex : regexs) {
1056                     ((ApiResponseList) result).addItem(new ApiResponseElement("regex", regex));
1057                 }
1058                 break;
1059             case VIEW_SCANNERS:
1060                 policy = getScanPolicyFromParams(params);
1061                 List<Plugin> scanners = policy.getPluginFactory().getAllPlugin();
1062 
1063                 categoryId = getParam(params, PARAM_CATEGORY_ID, -1);
1064                 if (categoryId != -1 && !hasPolicyWithId(categoryId)) {
1065                     throw new ApiException(ApiException.Type.DOES_NOT_EXIST, PARAM_CATEGORY_ID);
1066                 }
1067                 resultList = new ApiResponseList(name);
1068                 for (Plugin scanner : scanners) {
1069                     if (categoryId == -1 || categoryId == scanner.getCategory()) {
1070                         resultList.addItem(new ScannerApiResponse(policy, scanner));
1071                     }
1072                 }
1073 
1074                 result = resultList;
1075                 break;
1076             case VIEW_POLICIES:
1077                 policy = getScanPolicyFromParams(params);
1078                 String[] policies = Category.getAllNames();
1079 
1080                 resultList = new ApiResponseList(name);
1081                 for (String pluginName : policies) {
1082                     categoryId = Category.getCategory(pluginName);
1083                     Plugin.AttackStrength attackStrength =
1084                             getPolicyAttackStrength(policy, categoryId);
1085                     Plugin.AlertThreshold alertThreshold =
1086                             getPolicyAlertThreshold(policy, categoryId);
1087                     Map<String, String> map = new HashMap<>();
1088                     map.put("id", String.valueOf(categoryId));
1089                     map.put("name", pluginName);
1090                     map.put(
1091                             "attackStrength",
1092                             attackStrength == null ? "" : String.valueOf(attackStrength));
1093                     map.put(
1094                             "alertThreshold",
1095                             alertThreshold == null ? "" : String.valueOf(alertThreshold));
1096                     map.put("enabled", String.valueOf(isPolicyEnabled(policy, categoryId)));
1097                     resultList.addItem(new ApiResponseSet<>("policy", map));
1098                 }
1099 
1100                 result = resultList;
1101                 break;
1102             case VIEW_SCAN_POLICY_NAMES:
1103                 resultList = new ApiResponseList(name);
1104                 for (String policyName : controller.getPolicyManager().getAllPolicyNames()) {
1105                     resultList.addItem(new ApiResponseElement("policy", policyName));
1106                 }
1107                 result = resultList;
1108                 break;
1109             case VIEW_ATTACK_MODE_QUEUE:
1110                 result =
1111                         new ApiResponseElement(
1112                                 name, String.valueOf(controller.getAttackModeStackSize()));
1113                 break;
1114             case VIEW_OPTION_EXCLUDED_PARAM_LIST:
1115             case VIEW_EXCLUDED_PARAMS:
1116                 resultList = new ApiResponseList(name);
1117                 List<ScannerParamFilter> excludedParams =
1118                         controller.getScannerParam().getExcludedParamList();
1119                 for (int i = 0; i < excludedParams.size(); i++) {
1120                     resultList.addItem(new ExcludedParamApiResponse(excludedParams.get(i), i));
1121                 }
1122                 result = resultList;
1123                 break;
1124             case VIEW_EXCLUDED_PARAM_TYPES:
1125                 resultList = new ApiResponseList(name);
1126                 for (Entry<Integer, String> type : ScannerParamFilter.getTypes().entrySet()) {
1127                     Map<String, String> typeData = new HashMap<>();
1128                     typeData.put("id", Integer.toString(type.getKey()));
1129                     typeData.put("name", type.getValue());
1130                     resultList.addItem(new ApiResponseSet<>("type", typeData));
1131                 }
1132                 result = resultList;
1133                 break;
1134             default:
1135                 throw new ApiException(ApiException.Type.BAD_VIEW);
1136         }
1137         return result;
1138     }
1139 
createPluginProgressEntry( Plugin plugin, String status, long timeTaken, int requestCount, int alertCount)1140     private static ApiResponseList createPluginProgressEntry(
1141             Plugin plugin, String status, long timeTaken, int requestCount, int alertCount) {
1142         ApiResponseList pList = new ApiResponseList("Plugin");
1143         pList.addItem(new ApiResponseElement("name", plugin.getName()));
1144         pList.addItem(new ApiResponseElement("id", Integer.toString(plugin.getId())));
1145         pList.addItem(new ApiResponseElement("quality", plugin.getStatus().toString()));
1146         pList.addItem(new ApiResponseElement("status", status));
1147         pList.addItem(new ApiResponseElement("timeInMs", Long.toString(timeTaken)));
1148         pList.addItem(new ApiResponseElement("reqCount", Integer.toString(requestCount)));
1149         pList.addItem(new ApiResponseElement("alertCount", Integer.toString(alertCount)));
1150         return pList;
1151     }
1152 
isPolicyEnabled(ScanPolicy policy, int category)1153     private boolean isPolicyEnabled(ScanPolicy policy, int category) {
1154         for (Plugin scanner : policy.getPluginFactory().getAllPlugin()) {
1155             if (scanner.getCategory() == category && !scanner.isEnabled()) {
1156                 return false;
1157             }
1158         }
1159         return true;
1160     }
1161 
getPolicyAttackStrength(ScanPolicy policy, int categoryd)1162     private Plugin.AttackStrength getPolicyAttackStrength(ScanPolicy policy, int categoryd) {
1163         Plugin.AttackStrength attackStrength = null;
1164         for (Plugin scanner : policy.getPluginFactory().getAllPlugin()) {
1165             if (scanner.getCategory() == categoryd) {
1166                 if (attackStrength == null) {
1167                     attackStrength = scanner.getAttackStrength(true);
1168                 } else if (!attackStrength.equals(scanner.getAttackStrength(true))) {
1169                     // Not all the same
1170                     return null;
1171                 }
1172             }
1173         }
1174         return attackStrength;
1175     }
1176 
getPolicyAlertThreshold(ScanPolicy policy, int categoryId)1177     private Plugin.AlertThreshold getPolicyAlertThreshold(ScanPolicy policy, int categoryId) {
1178         Plugin.AlertThreshold alertThreshold = null;
1179         for (Plugin scanner : policy.getPluginFactory().getAllPlugin()) {
1180             if (scanner.getCategory() == categoryId) {
1181                 if (alertThreshold == null) {
1182                     alertThreshold = scanner.getAlertThreshold(true);
1183                 } else if (!alertThreshold.equals(scanner.getAlertThreshold(true))) {
1184                     // Not all the same
1185                     return null;
1186                 }
1187             }
1188         }
1189         return alertThreshold;
1190     }
1191 
getStatus(HostProcess hp, Plugin plugin, String defaultStatus)1192     private static String getStatus(HostProcess hp, Plugin plugin, String defaultStatus) {
1193         if (!hp.isSkipped(plugin)) {
1194             return defaultStatus;
1195         }
1196 
1197         String skippedReason = hp.getSkippedReason(plugin);
1198         if (skippedReason == null) {
1199             return Constant.messages.getString("ascan.progress.label.skipped");
1200         }
1201         return Constant.messages.getString("ascan.progress.label.skippedWithReason", skippedReason);
1202     }
1203 
1204     private static class ExcludedParamApiResponse extends ApiResponse {
1205 
1206         private final Map<String, String> excludedParamData;
1207         private final ApiResponseSet<String> type;
1208         private final Map<String, String> typeData;
1209 
ExcludedParamApiResponse(ScannerParamFilter param, int idx)1210         public ExcludedParamApiResponse(ScannerParamFilter param, int idx) {
1211             super("excludedParam");
1212 
1213             excludedParamData = new HashMap<>();
1214             excludedParamData.put("idx", Integer.toString(idx));
1215             excludedParamData.put("parameter", param.getParamName());
1216             excludedParamData.put("url", param.getWildcardedUrl());
1217 
1218             typeData = new HashMap<>();
1219             typeData.put("id", Integer.toString(param.getType()));
1220             typeData.put("name", param.getTypeString());
1221             type = new ApiResponseSet<>("type", typeData);
1222         }
1223 
1224         @Override
toXML(Document doc, Element parent)1225         public void toXML(Document doc, Element parent) {
1226             parent.setAttribute("type", "set");
1227             for (Entry<String, String> val : excludedParamData.entrySet()) {
1228                 Element el = doc.createElement(val.getKey());
1229                 el.appendChild(doc.createTextNode(XMLStringUtil.escapeControlChrs(val.getValue())));
1230                 parent.appendChild(el);
1231             }
1232 
1233             Element el = doc.createElement(type.getName());
1234             type.toXML(doc, el);
1235             parent.appendChild(el);
1236         }
1237 
1238         @Override
toJSON()1239         public JSON toJSON() {
1240             JSONObject jo = new JSONObject();
1241             for (Entry<String, String> val : excludedParamData.entrySet()) {
1242                 jo.put(val.getKey(), val.getValue());
1243             }
1244             jo.put(type.getName(), type.toJSON());
1245             return jo;
1246         }
1247 
1248         @Override
toHTML(StringBuilder sb)1249         public void toHTML(StringBuilder sb) {
1250             sb.append("<h2>" + this.getName() + "</h2>\n");
1251             sb.append("<table border=\"1\">\n");
1252             for (Entry<String, String> val : excludedParamData.entrySet()) {
1253                 sb.append("<tr><td>\n");
1254                 sb.append(val.getKey());
1255                 sb.append("</td><td>\n");
1256                 sb.append(StringEscapeUtils.escapeHtml(val.getValue()));
1257                 sb.append("</td></tr>\n");
1258             }
1259             sb.append("<tr><td>\n");
1260             sb.append(type.getName());
1261             sb.append("</td><td>\n");
1262             sb.append("<table border=\"1\">\n");
1263             for (Entry<String, ?> val : typeData.entrySet()) {
1264                 sb.append("<tr><td>\n");
1265                 sb.append(StringEscapeUtils.escapeHtml(val.getKey()));
1266                 sb.append("</td><td>\n");
1267                 Object value = val.getValue();
1268                 if (value != null) {
1269                     sb.append(StringEscapeUtils.escapeHtml(value.toString()));
1270                 }
1271                 sb.append("</td></tr>\n");
1272             }
1273             sb.append("</table>\n");
1274             sb.append("</td></tr>\n");
1275             sb.append("</table>\n");
1276         }
1277 
1278         @Override
toString(int indent)1279         public String toString(int indent) {
1280             StringBuilder sb = new StringBuilder();
1281             for (int i = 0; i < indent; i++) {
1282                 sb.append("\t");
1283             }
1284             sb.append("ApiResponseSet ");
1285             sb.append(this.getName());
1286             sb.append(" : [\n");
1287             for (Entry<String, String> val : excludedParamData.entrySet()) {
1288                 for (int i = 0; i < indent + 1; i++) {
1289                     sb.append("\t");
1290                 }
1291                 sb.append(val.getKey());
1292                 sb.append(" = ");
1293                 sb.append(val.getValue());
1294                 sb.append("\n");
1295             }
1296             sb.append(type.toString(indent + 1));
1297             for (int i = 0; i < indent; i++) {
1298                 sb.append("\t");
1299             }
1300             sb.append("]\n");
1301             return sb.toString();
1302         }
1303     }
1304 
1305     private class ScannerApiResponse extends ApiResponse {
1306 
1307         final Map<String, String> scannerData;
1308         final ApiResponseList dependencies;
1309 
ScannerApiResponse(ScanPolicy policy, Plugin scanner)1310         public ScannerApiResponse(ScanPolicy policy, Plugin scanner) {
1311             super("scanner");
1312 
1313             scannerData = new HashMap<>();
1314             scannerData.put("id", String.valueOf(scanner.getId()));
1315             scannerData.put("name", scanner.getName());
1316             scannerData.put("cweId", String.valueOf(scanner.getCweId()));
1317             scannerData.put("wascId", String.valueOf(scanner.getWascId()));
1318             scannerData.put("attackStrength", String.valueOf(scanner.getAttackStrength(true)));
1319             scannerData.put("alertThreshold", String.valueOf(scanner.getAlertThreshold(true)));
1320             scannerData.put("policyId", String.valueOf(scanner.getCategory()));
1321             scannerData.put("enabled", String.valueOf(scanner.isEnabled()));
1322             scannerData.put("quality", scanner.getStatus().toString());
1323 
1324             boolean allDepsAvailable =
1325                     policy.getPluginFactory().hasAllDependenciesAvailable(scanner);
1326             scannerData.put("allDependenciesAvailable", Boolean.toString(allDepsAvailable));
1327 
1328             dependencies = new ApiResponseList("dependencies");
1329             for (Plugin dependency : policy.getPluginFactory().getDependencies(scanner)) {
1330                 dependencies.addItem(
1331                         new ApiResponseElement("dependency", Integer.toString(dependency.getId())));
1332             }
1333         }
1334 
1335         @Override
toXML(Document doc, Element parent)1336         public void toXML(Document doc, Element parent) {
1337             parent.setAttribute("type", "set");
1338             for (Entry<String, String> val : scannerData.entrySet()) {
1339                 Element el = doc.createElement(val.getKey());
1340                 el.appendChild(doc.createTextNode(XMLStringUtil.escapeControlChrs(val.getValue())));
1341                 parent.appendChild(el);
1342             }
1343 
1344             Element el = doc.createElement(dependencies.getName());
1345             dependencies.toXML(doc, el);
1346             parent.appendChild(el);
1347         }
1348 
1349         @Override
toJSON()1350         public JSON toJSON() {
1351             JSONObject jo = new JSONObject();
1352             for (Entry<String, String> val : scannerData.entrySet()) {
1353                 jo.put(val.getKey(), val.getValue());
1354             }
1355             jo.put(
1356                     dependencies.getName(),
1357                     ((JSONObject) dependencies.toJSON()).getJSONArray(dependencies.getName()));
1358             return jo;
1359         }
1360 
1361         @Override
toHTML(StringBuilder sb)1362         public void toHTML(StringBuilder sb) {
1363             sb.append("<h2>" + this.getName() + "</h2>\n");
1364             sb.append("<table border=\"1\">\n");
1365             for (Entry<String, String> val : scannerData.entrySet()) {
1366                 sb.append("<tr><td>\n");
1367                 sb.append(val.getKey());
1368                 sb.append("</td><td>\n");
1369                 sb.append(StringEscapeUtils.escapeHtml(val.getValue()));
1370                 sb.append("</td></tr>\n");
1371             }
1372             sb.append("<tr><td>\n");
1373             sb.append(dependencies.getName());
1374             sb.append("</td><td>\n");
1375             sb.append("<table border=\"1\">\n");
1376             for (ApiResponse resp : this.dependencies.getItems()) {
1377                 sb.append("<tr><td>\n");
1378                 resp.toHTML(sb);
1379                 sb.append("</td></tr>\n");
1380             }
1381             sb.append("</table>\n");
1382             sb.append("</td></tr>\n");
1383             sb.append("</table>\n");
1384         }
1385 
1386         @Override
toString(int indent)1387         public String toString(int indent) {
1388             StringBuilder sb = new StringBuilder();
1389             for (int i = 0; i < indent; i++) {
1390                 sb.append("	");
1391             }
1392             sb.append("ScannerApiResponse ");
1393             sb.append(this.getName());
1394             sb.append(" : [\n");
1395             for (Entry<String, String> val : scannerData.entrySet()) {
1396                 for (int i = 0; i < indent + 1; i++) {
1397                     sb.append("\t");
1398                 }
1399                 sb.append(val.getKey());
1400                 sb.append(" = ");
1401                 sb.append(val.getValue());
1402                 sb.append("\n");
1403             }
1404             dependencies.toString(indent + 1);
1405             for (int i = 0; i < indent; i++) {
1406                 sb.append("\t");
1407             }
1408             sb.append("]\n");
1409             return sb.toString();
1410         }
1411     }
1412 }
1413