/* * Zed Attack Proxy (ZAP) and its related class files. * * ZAP is an HTTP/HTTPS proxy for assessing web application security. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.zaproxy.zap.extension.api; import java.util.ArrayList; import java.util.Collections; import java.util.Comparator; import java.util.List; import org.apache.commons.httpclient.URI; import org.parosproxy.paros.Constant; import org.parosproxy.paros.model.Model; import org.zaproxy.zap.extension.api.API.Format; import org.zaproxy.zap.extension.api.API.RequestType; public class WebUI { private API api; private boolean isDevTestNonce = false; // Manually change here to test nonces with the web UI public WebUI(API api) { this.api = api; } private ApiElement getElement(ApiImplementor impl, String name, RequestType reqType) throws ApiException { if (RequestType.action.equals(reqType) && name != null) { // Action form List<ApiAction> actionList = impl.getApiActions(); ApiAction action = null; for (ApiAction act : actionList) { if (name.equals(act.getName())) { action = act; break; } } if (action == null) { throw new ApiException(ApiException.Type.BAD_ACTION); } return action; } else if (RequestType.other.equals(reqType) && name != null) { // Other form List<ApiOther> otherList = impl.getApiOthers(); ApiOther other = null; for (ApiOther oth : otherList) { if (name.equals(oth.getName())) { other = oth; break; } } if (other == null) { throw new ApiException(ApiException.Type.BAD_OTHER); } return other; } else if (RequestType.view.equals(reqType) && name != null) { List<ApiView> viewList = impl.getApiViews(); ApiView view = null; for (ApiView v : viewList) { if (name.equals(v.getName())) { view = v; break; } } if (view == null) { throw new ApiException(ApiException.Type.BAD_VIEW); } return view; } else { throw new ApiException(ApiException.Type.BAD_TYPE); } } private void appendElements(StringBuilder sb, String component, String type, List<ApiElement> elementList) { Collections.sort(elementList, new Comparator<ApiElement>() { @Override public int compare(ApiElement ae1, ApiElement ae2) { return ae1.getName().compareTo(ae2.getName()); }}); sb.append("\n<table>\n"); for (ApiElement element : elementList) { List<String> mandatoryParams = element.getMandatoryParamNames(); List<String> optionalParams = element.getOptionalParamNames(); sb.append("<tr>"); sb.append("<td>"); sb.append("<a href=\"/"); sb.append(Format.UI.name()); sb.append('/'); sb.append(component); sb.append('/'); sb.append(type); sb.append('/'); sb.append(element.getName()); sb.append("/\">"); sb.append(element.getName()); if (mandatoryParams != null || optionalParams != null) { sb.append(" ("); if (mandatoryParams != null) { for (String param : mandatoryParams) { sb.append(param); sb.append("* "); } } if (optionalParams != null) { for (String param : optionalParams) { sb.append(param); sb.append(" "); } } sb.append(") "); } sb.append("</a>"); sb.append("</td><td>"); if (element.isDeprecated()) { sb.append(Constant.messages.getString("api.html.deprecated.endpoint")); sb.append("<br />"); String text = element.getDeprecatedDescription(); if (text != null && !text.isEmpty()) { sb.append(text); sb.append("<br />"); } } String descTag = element.getDescriptionTag(); if (descTag == null) { // This is the default, but it can be overriden by the getDescriptionTag method if required descTag = component + ".api." + type + "." + element.getName(); } try { sb.append(Constant.messages.getString(descTag)); } catch (Exception e) { // Might not be set, so ignore failures // Uncomment to see what tags are missing via the UI // sb.append(descTag); } sb.append("</td>"); sb.append("</tr>\n"); } sb.append("</table>\n"); } private void appendShortcuts(StringBuilder sb, String component, List<String> shortcutList) { Collections.sort(shortcutList); sb.append("\n<table>\n"); for (String shortcut : shortcutList) { sb.append("<tr>"); sb.append("<td>"); sb.append("<a href=\"/"); sb.append(shortcut); sb.append("/?"); sb.append(API.API_NONCE_PARAM); sb.append("="); sb.append(api.getOneTimeNonce("/" + shortcut + "/")); sb.append("\">"); sb.append(shortcut); sb.append("</a>"); sb.append("</td><td>"); sb.append("</td>"); sb.append("</tr>\n"); } sb.append("</table>\n"); } public String handleRequest(String component, ApiImplementor impl, RequestType reqType, String name) throws ApiException { // Generate HTML UI StringBuilder sb = new StringBuilder(); sb.append("<!DOCTYPE html>\n"); sb.append("<head>\n"); sb.append("<title>"); sb.append(Constant.messages.getString("api.html.title")); sb.append("</title>\n"); /* The script version prevents the cache being used if ZAP has been updated in the same day */ sb.append("<script src=\"/script.js/?v=" + CoreAPI.API_SCRIPT_VERSION + "&" + API.API_NONCE_PARAM + "=" + api.getOneTimeNonce("/script.js/") + "\" type=\"text/javascript\"></script>\n"); sb.append("</head>\n"); sb.append("<body>\n"); sb.append("<h1>"); sb.append("<a href=\"/"); sb.append(Format.UI.name()); sb.append("/\">"); sb.append(Constant.messages.getString("api.html.title")); sb.append("</a>"); sb.append("</h1>\n"); if (impl != null) { sb.append("<h2>"); sb.append("<a href=\"/"); sb.append(Format.UI.name()); sb.append("/"); sb.append(component); sb.append("/\">"); sb.append(Constant.messages.getString("api.html.component")); sb.append(component); sb.append("</a>"); sb.append("</h2>\n"); if (name != null) { ApiElement element = this.getElement(impl, name, reqType); List<String> mandatoryParams = element.getMandatoryParamNames(); List<String> optionalParams = element.getOptionalParamNames(); sb.append("<h3>"); sb.append(Constant.messages.getString("api.html." + reqType.name())); sb.append(element.getName()); sb.append("</h3>\n"); // Handle the (optional) description String descTag = element.getDescriptionTag(); if (descTag == null) { // This is the default, but it can be overriden by the getDescriptionTag method if required descTag = component + ".api." + reqType.name() + "." + name; } try { sb.append(Constant.messages.getString(descTag)); } catch (Exception e) { // Might not be set, so ignore failures } sb.append("\n<form id=\"zapform\" name=\"zapform\" action=\"override\">"); sb.append("<table>\n"); if ( ! RequestType.other.equals(reqType)) { sb.append("<tr><td>"); sb.append(Constant.messages.getString("api.html.format")); sb.append("</td><td>\n"); sb.append("<select id=\"zapapiformat\" name=\"zapapiformat\">\n"); sb.append("<option value=\"JSON\">JSON</option>\n"); if (getOptionsParamApi().isEnableJSONP()) { sb.append("<option value=\"JSONP\">JSONP</option>\n"); } else { sb.append("<option value=\"JSONP\" disabled>JSONP</option>\n"); } sb.append("<option value=\"HTML\">HTML</option>\n"); sb.append("<option value=\"XML\">XML</option>\n"); sb.append("</select>\n"); sb.append("</td></tr>\n"); } if (RequestType.action.equals(reqType) || RequestType.other.equals(reqType) || ! getOptionsParamApi().isNoKeyForSafeOps()) { String keyType = API.API_KEY_PARAM; if (this.isDevTestNonce && RequestType.other.equals(reqType)) { // We can use nonces as we know the return type keyType = API.API_NONCE_PARAM; } if (! getOptionsParamApi().isDisableKey()) { sb.append("<tr>"); sb.append("<td>"); sb.append(keyType); sb.append("*</td>"); sb.append("<td>"); sb.append("<input id=\""); sb.append(keyType); sb.append("\" name=\""); sb.append(keyType); sb.append("\" value=\""); if (this.isDevTestNonce && RequestType.other.equals(reqType)) { sb.append(api.getOneTimeNonce("/" + reqType.name().toUpperCase() + "/" + impl.getPrefix() + "/" + reqType.name() + "/" + element.getName() + "/")); } else { if (getOptionsParamApi().isAutofillKey()) { sb.append(getOptionsParamApi().getKey()); } } sb.append("\"/>"); sb.append("</td>"); sb.append("</tr>\n"); } sb.append("<tr>"); sb.append("<td>"); sb.append(Constant.messages.getString("api.html.formMethod")); sb.append("</td>"); sb.append("<td>"); sb.append("<select name=\"formMethod\">\n"); sb.append("<option value=\"GET\" selected>GET</option>\n"); sb.append("<option value=\"POST\">POST</option>\n"); sb.append("</select>\n"); sb.append("</td>"); sb.append("</tr>\n"); } if (mandatoryParams != null) { for (String param : mandatoryParams) { sb.append("<tr>"); sb.append("<td>"); sb.append(param); sb.append("*</td>"); sb.append("<td>"); sb.append("<input id=\""); sb.append(param); sb.append("\" name=\""); sb.append(param); sb.append("\"/>"); sb.append("</td>"); sb.append("</tr>\n"); } } if (optionalParams != null) { for (String param : optionalParams) { sb.append("<tr>"); sb.append("<td>"); sb.append(param); sb.append("</td>"); sb.append("<td>"); sb.append("<input id=\""); sb.append(param); sb.append("\" name=\""); sb.append(param); sb.append("\"/>"); sb.append("</td>"); sb.append("</tr>\n"); } } sb.append("<tr>"); sb.append("<td>"); sb.append("</td>"); sb.append("<td>"); sb.append("<input id=\"button\" value=\""); sb.append(element.getName()); sb.append("\" type=\"button\" zap-component=\"" + component + "\" zap-type=\"" + reqType + "\" zap-name=\"" + name + "\"/>\n"); sb.append("</td>"); sb.append("</tr>\n"); sb.append("</table>\n"); sb.append("</form>\n"); } else { List<ApiElement> elementList = new ArrayList<>(); List<ApiView> viewList = impl.getApiViews(); if (viewList != null && viewList.size() > 0) { sb.append("<h3>"); sb.append(Constant.messages.getString("api.html.views")); sb.append("</h3>\n"); elementList.addAll(viewList); this.appendElements(sb, component, RequestType.view.name(), elementList); } List<ApiAction> actionList = impl.getApiActions(); if (actionList != null && actionList.size() > 0) { sb.append("<h3>"); sb.append(Constant.messages.getString("api.html.actions")); sb.append("</h3>\n"); elementList = new ArrayList<>(); elementList.addAll(actionList); this.appendElements(sb, component, RequestType.action.name(), elementList); } List<ApiOther> otherList = impl.getApiOthers(); if (otherList != null && otherList.size() > 0) { sb.append("<h3>"); sb.append(Constant.messages.getString("api.html.others")); sb.append("</h3>\n"); elementList = new ArrayList<>(); elementList.addAll(otherList); this.appendElements(sb, component, RequestType.other.name(), elementList); } if (getOptionsParamApi().isDisableKey() || getOptionsParamApi().isAutofillKey() || this.isDevTestNonce) { // Only show shortcuts if they will work without the user having to add a key/nonce List<String> shortcutList = impl.getApiShortcuts(); if (shortcutList != null && shortcutList.size() > 0) { sb.append("<h3>"); sb.append(Constant.messages.getString("api.html.shortcuts")); sb.append("</h3>\n"); elementList = new ArrayList<>(); elementList.addAll(otherList); this.appendShortcuts(sb, component, shortcutList); } } } } else { sb.append("<h3>"); sb.append(Constant.messages.getString("api.html.components")); sb.append("</h3>\n"); ArrayList<String> components = new ArrayList<String>(api.getImplementors().keySet()); Collections.sort(components); sb.append("<table>\n"); for (String cmp : components) { sb.append("<tr>"); sb.append("<td>"); sb.append("<a href=\"/"); sb.append(Format.UI.name()); sb.append('/'); sb.append(cmp); sb.append("/\">"); sb.append(cmp); sb.append("</a>"); sb.append("</td>"); sb.append("</tr>\n"); } sb.append("</table>\n"); } sb.append("</body>\n"); return sb.toString(); } public String handleRequest(URI uri, boolean apiEnabled) { // Right now just generate a basic home page StringBuilder sb = new StringBuilder(); sb.append("<head>\n"); sb.append("<title>"); sb.append(Constant.messages.getString("api.html.title")); sb.append("</title>\n"); sb.append("</head>\n"); sb.append("<body>\n"); sb.append(Constant.messages.getString("api.home.topmsg")); sb.append(Constant.messages.getString("api.home.proxypac", "/?" + API.API_NONCE_PARAM + "=" + API.getInstance().getLongLivedNonce("/OTHER/core/other/proxy.pac/"))); sb.append(Constant.messages.getString("api.home.links.header")); if (apiEnabled) { sb.append(Constant.messages.getString("api.home.links.api.enabled")); } else { sb.append(Constant.messages.getString("api.home.links.api.disabled")); } sb.append(Constant.messages.getString("api.home.links.online")); sb.append("</body>\n"); return sb.toString(); } private OptionsParamApi getOptionsParamApi() { return Model.getSingleton().getOptionsParam().getApiParam(); } }