/* /* * 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.stats; import java.util.Map; import java.util.Map.Entry; import java.util.TreeMap; import net.sf.json.JSON; import net.sf.json.JSONArray; import net.sf.json.JSONObject; import org.apache.commons.httpclient.URI; import org.w3c.dom.Document; import org.w3c.dom.Element; import org.w3c.dom.Text; 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.model.SessionStructure; import org.zaproxy.zap.utils.Stats; import org.zaproxy.zap.utils.XMLStringUtil; public class StatsAPI extends ApiImplementor { private static final String PREFIX = "stats"; private static final String ACTION_CLEAR_STATS = "clearStats"; private static final String VIEW_STATS = "stats"; private static final String VIEW_SITE_STATS = "siteStats"; private static final String VIEW_ALL_SITES_STATS = "allSitesStats"; private static final String PARAM_KEY_PREFIX = "keyPrefix"; private static final String PARAM_SITE = "site"; private ExtensionStats extension; public StatsAPI(ExtensionStats extension) { this.extension = extension; this.addApiAction(new ApiAction(ACTION_CLEAR_STATS, null, new String[] {PARAM_KEY_PREFIX})); this.addApiView(new ApiView(VIEW_STATS, null, new String[] { PARAM_KEY_PREFIX })); this.addApiView(new ApiView(VIEW_ALL_SITES_STATS, null, new String[] {PARAM_KEY_PREFIX })); this.addApiView(new ApiView(VIEW_SITE_STATS, new String[] {PARAM_SITE}, new String[] {PARAM_KEY_PREFIX })); } @Override public String getPrefix() { return PREFIX; } @Override public ApiResponse handleApiAction(String name, JSONObject params) throws ApiException { if (ACTION_CLEAR_STATS.equals(name)) { Stats.clear(this.getParam(params, PARAM_KEY_PREFIX, "")); return ApiResponseElement.OK; } else { throw new ApiException(ApiException.Type.BAD_ACTION); } } @Override public ApiResponse handleApiView(String name, JSONObject params) throws ApiException { ApiResponse result = null; InMemoryStats memStats = extension.getInMemoryStats(); if (memStats == null) { throw new ApiException(ApiException.Type.DOES_NOT_EXIST); } if (VIEW_STATS.equals(name)) { Map<String, String> map = new TreeMap<>(); for (Entry<String, Long> stat : memStats.getStats(this.getParam(params, PARAM_KEY_PREFIX, "")).entrySet()) { map.put(stat.getKey(), stat.getValue().toString()); } result = new ApiResponseSet<String>(name, map); } else if (VIEW_ALL_SITES_STATS.equals(name)) { result = new ApiResponseList(name); for (Entry<String, Map<String, Long>> stats : memStats.getAllSiteStats(this.getParam(params, PARAM_KEY_PREFIX, "")).entrySet()) { ((ApiResponseList)result).addItem(new SiteStatsApiResponse(stats.getKey(), stats.getValue())); } } else if (VIEW_SITE_STATS.equals(name)) { String site = params.getString(PARAM_SITE); URI siteURI; try { siteURI = new URI(site, true); site = SessionStructure.getHostName(siteURI); } catch (Exception e) { throw new ApiException(ApiException.Type.ILLEGAL_PARAMETER, PARAM_SITE); } String scheme = siteURI.getScheme(); if (scheme == null || (!scheme.equalsIgnoreCase("http") && !scheme.equalsIgnoreCase("https"))) { throw new ApiException(ApiException.Type.ILLEGAL_PARAMETER, PARAM_SITE); } result = new SiteStatsApiResponse(site, memStats.getSiteStats(site, this.getParam(params, PARAM_KEY_PREFIX, ""))); } else { throw new ApiException(ApiException.Type.BAD_VIEW); } return result; } private static class SiteStatsApiResponse extends ApiResponseList { private String site; private Map<String, Long> stats; public SiteStatsApiResponse(String site, Map<String, Long> stats) { super("statistics"); this.site = site; this.stats = new TreeMap<>(stats); this.addItem(new ApiResponseSet<Long>(site, this.stats)); } @Override public void toXML(Document doc, Element parent) { parent.setAttribute("type", "list"); Element els = doc.createElement("site"); Text texts = doc.createTextNode(XMLStringUtil.escapeControlChrs(this.site)); els.appendChild(texts); parent.appendChild(els); for (Entry<String, Long> stat : this.stats.entrySet()) { Element el = doc.createElement("statistic"); el.setAttribute("type", "set"); Element elk = doc.createElement("key"); Text textk = doc.createTextNode(XMLStringUtil.escapeControlChrs(stat.getKey())); elk.appendChild(textk); el.appendChild(elk); Element elv = doc.createElement("value"); Text textv = doc.createTextNode(XMLStringUtil.escapeControlChrs(stat.getValue().toString())); elv.appendChild(textv); el.appendChild(elv); parent.appendChild(el); } } @Override public JSON toJSON() { JSONObject jo = new JSONObject(); JSONArray array = new JSONArray(); for (ApiResponse resp: this.getItems()) { if (resp instanceof ApiResponseElement) { array.add(((ApiResponseElement)resp).getValue()); } else { array.add(resp.toJSON()); } } // Use the site name instead of 'statistics' jo.put(this.site, array); return jo; } } }